Skip to content

Commit 20db1fe

Browse files
authored
refactor: robust logic by typesafe (#210)
1 parent ff099e2 commit 20db1fe

File tree

6 files changed

+174
-124
lines changed

6 files changed

+174
-124
lines changed

eslint.config.mjs

-15
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,4 @@ export default [
6868
files: ['**/*.{js,cjs,mjs}'],
6969
...tseslint.configs.disableTypeChecked
7070
}
71-
72-
// custom rules
73-
/*
74-
{
75-
rules: {
76-
'object-curly-spacing': 'off',
77-
'@typescript-eslint/ban-types': 'off',
78-
'@typescript-eslint/ban-ts-comment': 'off',
79-
'@typescript-eslint/no-non-null-assertion': 'off',
80-
'@typescript-eslint/explicit-function-return-type': 'off',
81-
'@typescript-eslint/member-delimiter-style': 'off',
82-
'@typescript-eslint/no-use-before-define': 'off'
83-
}
84-
}
85-
*/
8671
]

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"vue-i18n": "^9.0.0"
1717
},
1818
"devDependencies": {
19+
"@babel/types": "^7.24.6",
1920
"@eslint/js": "^9.2.0",
2021
"@kazupon/lerna-changelog": "^4.3.0",
2122
"@microsoft/api-extractor": "^7.18.4",

pnpm-lock.yaml

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/report.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export const enum ReportCodes {
2424
INVALID_PARAMETER_TYPE,
2525
NOT_SUPPORTED_PRESERVE,
2626
OVERRIDE_ELEMENT_CHILDREN,
27+
NOT_RESOLVED_COMPOSER,
28+
UNEXPECTED_ERROR,
2729
__EXTEND_POINT__
2830
}
2931

@@ -35,7 +37,9 @@ const ReportMessages: { [code: number]: string } = {
3537
[ReportCodes.REQUIRED_PARAMETER]: `Required parameter: {0}`,
3638
[ReportCodes.INVALID_PARAMETER_TYPE]: `Required parameter: {0}`,
3739
[ReportCodes.NOT_SUPPORTED_PRESERVE]: `Not supported 'preserve': {0}`,
38-
[ReportCodes.OVERRIDE_ELEMENT_CHILDREN]: `v-t will override element children: {0}`
40+
[ReportCodes.OVERRIDE_ELEMENT_CHILDREN]: `v-t will override element children: {0}`,
41+
[ReportCodes.NOT_RESOLVED_COMPOSER]: `Not resolved vue-i18n composer: {0}`,
42+
[ReportCodes.UNEXPECTED_ERROR]: `Unexpected error: {0}`
3943
}
4044

4145
export function getReportMessage(code: ReportCodes, ...args: unknown[]): string {

src/transform.ts

+82-44
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,35 @@
1-
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-enum-comparison */
21
import {
3-
DirectiveTransform,
42
createSimpleExpression,
5-
Node,
63
NodeTypes,
4+
isText,
5+
createCompoundExpression,
6+
TO_DISPLAY_STRING
7+
} from '@vue/compiler-dom'
8+
import { isNumber, isObject, isString, isSymbol, toDisplayString } from '@intlify/shared'
9+
import { evaluateValue, parseVTExpression } from './transpiler'
10+
import { report, ReportCodes } from './report'
11+
import { createContentBuilder } from './builder'
12+
13+
import type {
14+
DirectiveTransform,
15+
Node,
716
SimpleExpressionNode,
817
CompoundExpressionNode,
9-
isText,
1018
TextNode,
1119
InterpolationNode,
12-
createCompoundExpression,
13-
TO_DISPLAY_STRING,
1420
TransformContext
1521
} from '@vue/compiler-dom'
16-
import { isNumber, isObject, isString, isSymbol, toDisplayString } from '@intlify/shared'
17-
import { I18n, I18nMode, Locale } from 'vue-i18n'
18-
import { evaluateValue, parseVTExpression, TranslationParams } from './transpiler'
19-
import { report, ReportCodes } from './report'
20-
import { createContentBuilder, ContentBuilder } from './builder'
22+
import type {
23+
I18n,
24+
I18nMode,
25+
Locale,
26+
Composer,
27+
VueI18n,
28+
NamedValue,
29+
TranslateOptions
30+
} from 'vue-i18n'
31+
import type { TranslationParams, NestedValue } from './transpiler'
32+
import type { ContentBuilder } from './builder'
2133

2234
// TODO: should be imported from vue-i18n
2335
type VTDirectiveValue = {
@@ -180,12 +192,23 @@ export function transformVTDirective<
180192
report(parseStatus, { args: [node.loc.source || ''], loc: node.loc })
181193
return { props: [] }
182194
}
195+
if (parsedValue == null) {
196+
report(ReportCodes.UNEXPECTED_ERROR, {
197+
args: [node.loc.source || ''],
198+
loc: node.loc
199+
})
200+
return { props: [] }
201+
}
183202

184-
const global =
185-
i18nInstance.mode === 'composition'
186-
? (i18nInstance.global as any) // eslint-disable-line @typescript-eslint/no-explicit-any
187-
: (i18nInstance.global as any).__composer // eslint-disable-line @typescript-eslint/no-explicit-any
188-
const content = global.t(...makeParams(parsedValue!))
203+
const global = getComposer(i18nInstance as unknown as I18n)
204+
if (global == null) {
205+
report(ReportCodes.NOT_RESOLVED_COMPOSER, {
206+
args: [node.loc.source || ''],
207+
loc: node.loc
208+
})
209+
return { props: [] }
210+
}
211+
const content = global.t(...makeParams(parsedValue))
189212

190213
node.children.push({
191214
type: NodeTypes.TEXT,
@@ -199,13 +222,7 @@ export function transformVTDirective<
199222
node.children.push({
200223
type: NodeTypes.INTERPOLATION,
201224
content: createCompoundExpression([
202-
createSimpleExpression(
203-
code,
204-
false,
205-
loc,
206-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
207-
ConstantTypes.NOT_CONSTANT as any
208-
)
225+
createSimpleExpression(code, false, loc, ConstantTypes.NOT_CONSTANT as number)
209226
])
210227
} as InterpolationNode)
211228
return { props: [] }
@@ -217,13 +234,7 @@ export function transformVTDirective<
217234
node.children.push({
218235
type: NodeTypes.INTERPOLATION,
219236
content: createCompoundExpression([
220-
createSimpleExpression(
221-
code,
222-
false,
223-
loc,
224-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
225-
ConstantTypes.NOT_CONSTANT as any
226-
)
237+
createSimpleExpression(code, false, loc, ConstantTypes.NOT_CONSTANT as number)
227238
])
228239
} as InterpolationNode)
229240
return { props: [] }
@@ -237,6 +248,34 @@ export function transformVTDirective<
237248
}
238249
}
239250

251+
function getComposer(i18n: I18n): Composer | null {
252+
return isI18nInstance(i18n) ? getComposerInternal(i18n) : null
253+
}
254+
255+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
256+
function isI18nInstance(i18n: any): i18n is I18n {
257+
return i18n != null && 'global' in i18n && 'mode' in i18n
258+
}
259+
260+
function getComposerInternal(i18n: I18n): Composer | null {
261+
if (i18n.mode === 'composition') {
262+
return isComposer(i18n.global) ? i18n.global : null
263+
} else {
264+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
265+
return isVueI18n(i18n.global) ? ((i18n.global as any).__composer as Composer) : null
266+
}
267+
}
268+
269+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
270+
function isComposer(target: any): target is Composer {
271+
return target != null && !('__composer' in target)
272+
}
273+
274+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
275+
function isVueI18n(target: any): target is VueI18n {
276+
return target != null && '__composer' in target
277+
}
278+
240279
function isSimpleExpressionNode(node: Node | undefined): node is SimpleExpressionNode {
241280
return node != null && node.type === NodeTypes.SIMPLE_EXPRESSION
242281
}
@@ -248,13 +287,13 @@ function isCompoundExpressionNode(node: Node | undefined): node is CompoundExpre
248287
function isConstant(node: SimpleExpressionNode): boolean {
249288
if ('isConstant' in node) {
250289
// for v3.0.3 earlier
251-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
252-
return (node as any).isConstant
290+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
291+
return (node as any).isConstant as boolean
253292
} else if ('constType' in node) {
254293
// for v3.0.3 or later
255-
return (node.constType as number) <= ConstantTypes.CAN_STRINGIFY
294+
return (node.constType as number) <= (ConstantTypes.CAN_STRINGIFY as unknown as number)
256295
} else {
257-
throw Error('unexpected error')
296+
throw Error('unexpected error in Vue SimpleExpressionNode')
258297
}
259298
}
260299

@@ -303,11 +342,11 @@ function parseValue(value: unknown): [VTDirectiveValue | null, ReportCodes] {
303342
}
304343
}
305344

306-
function makeParams(value: VTDirectiveValue): unknown[] {
345+
function makeParams(value: VTDirectiveValue): [string, NamedValue, TranslateOptions] {
307346
const { path, locale, args, choice, plural } = value
308-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
309-
const options = {} as any
310-
const named: { [prop: string]: unknown } = args || {}
347+
348+
const options = {} as TranslateOptions
349+
const named: NamedValue = args || {}
311350

312351
if (isString(locale)) {
313352
options.locale = locale
@@ -366,12 +405,13 @@ function generateComposableCode(context: TransformContext, params: TranslationPa
366405
return content
367406
}
368407

369-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
370-
function generateNamedCode(builder: ContentBuilder, named: any): void {
408+
function generateNamedCode(
409+
builder: ContentBuilder,
410+
named: NestedValue<Record<string, unknown>>
411+
): void {
371412
const keys = Object.keys(named)
372413
keys.forEach(k => {
373-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
374-
const v: any = named[k]
414+
const v = named[k]
375415
if (isObject(v)) {
376416
builder.push(`${k}: { `)
377417
generateNamedCode(builder, v)
@@ -419,5 +459,3 @@ function generateLegacyCode(context: TransformContext, params: TranslationParams
419459
const content = builder.content
420460
return content
421461
}
422-
423-
/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-enum-comparison */

0 commit comments

Comments
 (0)