@generaltranslation/vue-extractor 0.0.0 → 0.1.0-iris.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +18 -102
- package/dist/detect.d.ts +8 -0
- package/dist/detect.js +2 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/inspect.d.ts +9 -0
- package/dist/inspect.js +2 -0
- package/dist/integration.d.ts +69 -0
- package/dist/integration.js +162 -0
- package/dist/integration.js.map +1 -0
- package/dist/internal/config/configFiles.d.ts +6 -0
- package/dist/internal/config/configFiles.js +34 -0
- package/dist/internal/config/configFiles.js.map +1 -0
- package/dist/internal/config/resolveVueCompilerOptions.d.ts +6 -0
- package/dist/internal/config/resolveVueCompilerOptions.js +74 -56
- package/dist/internal/config/resolveVueCompilerOptions.js.map +1 -1
- package/dist/internal/extractFromVueSource.d.ts +4 -3
- package/dist/internal/extractFromVueSource.js +255 -12
- package/dist/internal/extractFromVueSource.js.map +1 -1
- package/dist/internal/project/consumerUsage.d.ts +34 -0
- package/dist/internal/project/consumerUsage.js +1220 -0
- package/dist/internal/project/consumerUsage.js.map +1 -0
- package/dist/internal/project/detectVueProject.d.ts +13 -0
- package/dist/internal/project/detectVueProject.js +65 -0
- package/dist/internal/project/detectVueProject.js.map +1 -0
- package/dist/internal/project/extractFromVueProject.d.ts +10 -0
- package/dist/internal/project/extractFromVueProject.js +579 -0
- package/dist/internal/project/extractFromVueProject.js.map +1 -0
- package/dist/internal/project/inspectVueProject.d.ts +48 -0
- package/dist/internal/project/inspectVueProject.js +143 -0
- package/dist/internal/project/inspectVueProject.js.map +1 -0
- package/dist/internal/project/manifest.d.ts +64 -0
- package/dist/internal/project/manifest.js +270 -0
- package/dist/internal/project/manifest.js.map +1 -0
- package/dist/internal/project/mergeVueProjectExtraction.d.ts +18 -0
- package/dist/internal/project/mergeVueProjectExtraction.js +79 -0
- package/dist/internal/project/mergeVueProjectExtraction.js.map +1 -0
- package/dist/internal/project/moduleResolver.d.ts +18 -0
- package/dist/internal/project/moduleResolver.js +325 -0
- package/dist/internal/project/moduleResolver.js.map +1 -0
- package/dist/internal/project/scopes.d.ts +31 -0
- package/dist/internal/project/scopes.js +182 -0
- package/dist/internal/project/scopes.js.map +1 -0
- package/dist/internal/project/sourcePatterns.d.ts +4 -0
- package/dist/internal/project/sourcePatterns.js +32 -0
- package/dist/internal/project/sourcePatterns.js.map +1 -0
- package/dist/internal/project/viteAliases.d.ts +42 -0
- package/dist/internal/project/viteAliases.js +1020 -0
- package/dist/internal/project/viteAliases.js.map +1 -0
- package/dist/internal/project/vueSourceClassification.d.ts +13 -0
- package/dist/internal/project/vueSourceClassification.js +146 -0
- package/dist/internal/project/vueSourceClassification.js.map +1 -0
- package/dist/internal/project/workspaces.d.ts +31 -0
- package/dist/internal/project/workspaces.js +326 -0
- package/dist/internal/project/workspaces.js.map +1 -0
- package/dist/internal/project/wrapperProvenance.d.ts +21 -0
- package/dist/internal/project/wrapperProvenance.js +287 -0
- package/dist/internal/project/wrapperProvenance.js.map +1 -0
- package/dist/internal/script/analyze.js +323 -108
- package/dist/internal/script/analyze.js.map +1 -1
- package/dist/internal/script/jsx.d.ts +7 -4
- package/dist/internal/script/jsx.js +351 -79
- package/dist/internal/script/jsx.js.map +1 -1
- package/dist/internal/script/knownValues.js +4 -0
- package/dist/internal/script/knownValues.js.map +1 -1
- package/dist/internal/script/localModules.d.ts +23 -2
- package/dist/internal/script/localModules.js +535 -20
- package/dist/internal/script/localModules.js.map +1 -1
- package/dist/internal/script/model.d.ts +10 -3
- package/dist/internal/script/parser.js +2 -1
- package/dist/internal/script/parser.js.map +1 -1
- package/dist/internal/script/runtimeModules.d.ts +10 -0
- package/dist/internal/script/runtimeModules.js +27 -0
- package/dist/internal/script/runtimeModules.js.map +1 -0
- package/dist/internal/stringCalls.js +9 -3
- package/dist/internal/stringCalls.js.map +1 -1
- package/dist/internal/template.js +263 -141
- package/dist/internal/template.js.map +1 -1
- package/dist/internal/types.d.ts +5 -2
- package/dist/internal/utils.d.ts +14 -3
- package/dist/internal/utils.js +63 -3
- package/dist/internal/utils.js.map +1 -1
- package/dist/internal/vueCompiler.d.ts +9 -1
- package/dist/internal/vueCompiler.js +37 -14
- package/dist/internal/vueCompiler.js.map +1 -1
- package/dist/project.d.ts +11 -0
- package/dist/project.js +3 -0
- package/dist/types.d.ts +95 -4
- package/package.json +30 -4
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseScriptAst } from "./parser.js";
|
|
2
|
+
import { isKnownNonVueGTRuntime } from "./runtimeModules.js";
|
|
3
|
+
import { addVueError, babelLocation, readStaticGlobalPrimitive, readStaticPrimitive, unwrapExpression } from "../utils.js";
|
|
2
4
|
import { processVueStringCall } from "../stringCalls.js";
|
|
3
5
|
import { appendTemplatePath, unknownTemplatePathSegment } from "../templatePath.js";
|
|
4
6
|
import { createReplayAnalysis } from "./replay.js";
|
|
5
7
|
import { createSnapshotAnalysis } from "./snapshot.js";
|
|
6
8
|
import { COMPONENT_IMPORTS, ORDINARY_GLOBAL_VALUES, READONLY_ARRAY_TRANSFORMS, VUE_BUILTIN_IMPORTS, knownExport, knownValueKey } from "./knownValues.js";
|
|
7
9
|
import { extractVueJSXTranslation, isNestedInVueJSXTranslation, resolveVueJSXElementIdentity, validateVueJSXVariableComponent } from "./jsx.js";
|
|
8
|
-
import { parseScriptAst } from "./parser.js";
|
|
9
10
|
import { isAssignmentTargetWrapper, isBindingPattern, isStaticallyUndefined, patternContains, readMemberProperty, readPatternIdentifier, readPropertyKey } from "./syntax.js";
|
|
10
11
|
import traverseModule from "@babel/traverse";
|
|
11
12
|
//#region src/internal/script/analyze.ts
|
|
@@ -33,6 +34,7 @@ function createVueScriptAnalysis(stats) {
|
|
|
33
34
|
directBindings: /* @__PURE__ */ new Set(),
|
|
34
35
|
gtComponentFactories: /* @__PURE__ */ new Set(),
|
|
35
36
|
gtContainerFactories: /* @__PURE__ */ new Set(),
|
|
37
|
+
hasGTSourceReference: false,
|
|
36
38
|
possibleGTContainers: /* @__PURE__ */ new Set(),
|
|
37
39
|
possibleStaticStrings: /* @__PURE__ */ new Map(),
|
|
38
40
|
staticValues: /* @__PURE__ */ new Map(),
|
|
@@ -81,6 +83,7 @@ function collectVueScriptImports(source, language, analysis) {
|
|
|
81
83
|
/** Extracts calls and component bindings from one padded Vue script block. */
|
|
82
84
|
function parseVueScript(source, language, context, templateBindings, exposeToTemplate, analysis = createVueScriptAnalysis(), analyzeOptionsApi = true) {
|
|
83
85
|
if (!source.trim()) return true;
|
|
86
|
+
const initialErrorCount = context.errors.length;
|
|
84
87
|
let ast;
|
|
85
88
|
try {
|
|
86
89
|
ast = parseScriptAst(source, language);
|
|
@@ -121,18 +124,25 @@ function parseVueScript(source, language, context, templateBindings, exposeToTem
|
|
|
121
124
|
if (state.activeTranslationFunctions.size > 0 && resolveKnownCallTargetWithoutParameterSubstitutions(path.node.callee, path.scope, path.node.start ?? Number.POSITIVE_INFINITY, state).value?.type === "string") return;
|
|
122
125
|
const vueHelper = readImportedFunctionName(path.node.callee, path.scope, "vue", state);
|
|
123
126
|
const firstArgument = path.node.arguments[0];
|
|
124
|
-
if (vueHelper && ["createVNode", "h"].includes(vueHelper) && firstArgument && firstArgument.type !== "ArgumentPlaceholder" && firstArgument.type !== "SpreadElement" && expressionContainsGTReference(firstArgument, path.scope, state))
|
|
125
|
-
|
|
127
|
+
if (vueHelper && ["createVNode", "h"].includes(vueHelper) && firstArgument && firstArgument.type !== "ArgumentPlaceholder" && firstArgument.type !== "SpreadElement" && expressionContainsGTReference(firstArgument, path.scope, state)) {
|
|
128
|
+
state.analysis.hasGTSourceReference = true;
|
|
129
|
+
addVueError(context, consumerLocation, "Found an unsupported gt-vue <T> component in a Vue render function", "Move the rich translation into a Vue single-file component template");
|
|
130
|
+
}
|
|
131
|
+
if (vueHelper === "defineAsyncComponent" && firstArgument && firstArgument.type !== "ArgumentPlaceholder" && firstArgument.type !== "SpreadElement" && expressionContainsGTReference(firstArgument, path.scope, state)) {
|
|
132
|
+
state.analysis.hasGTSourceReference = true;
|
|
133
|
+
addVueError(context, consumerLocation, "Could not statically extract a gt-vue <T> component behind defineAsyncComponent()", "Use a direct gt-vue component binding in the Vue template");
|
|
134
|
+
}
|
|
126
135
|
const callTarget = resolveKnownCallTargetAtPosition(path.node.callee, path.scope, path.node.start ?? Number.POSITIVE_INFINITY, state);
|
|
127
136
|
const value = callTarget.value;
|
|
128
137
|
if (value?.type !== "string") {
|
|
129
138
|
const localCallable = resolveCalledFunction(path.node.callee, path.scope, state, /* @__PURE__ */ new Set());
|
|
130
139
|
const calleePath = readResolvedMemberPath(path.node.callee, path.scope, state);
|
|
131
140
|
const uncertainTranslationHelper = Boolean(calleePath && pathMayReferenceUncertainTranslationHelper(path.node.callee, calleePath, path.scope, state) && callArgumentsMayProvideTranslationFunction(path.node, path.scope, state));
|
|
132
|
-
if (!callTarget.definitelyOrdinary && !localCallable && calleePath && (callTarget.possibleStringFunction || uncertainTranslationHelper || templateBindings.uncertainStringFunctions.has(calleePath) || state.analysis.uncertainStringFunctions.has(calleePath) || calleePath.endsWith(".value") && templateBindings.uncertainStringFunctions.has(calleePath.slice(0, -6)))) addVueError(context, consumerLocation, `Could not statically resolve possible gt-vue string function alias "${calleePath}"`, "Use a direct, immutable alias of useGT(), useMessages(), or
|
|
141
|
+
if (!callTarget.definitelyOrdinary && !localCallable && calleePath && (callTarget.possibleStringFunction || uncertainTranslationHelper || templateBindings.uncertainStringFunctions.has(calleePath) || state.analysis.uncertainStringFunctions.has(calleePath) || calleePath.endsWith(".value") && templateBindings.uncertainStringFunctions.has(calleePath.slice(0, -6)))) addVueError(context, consumerLocation, `Could not statically resolve possible gt-vue string function alias "${calleePath}"`, "Use a direct, immutable alias of useGT(), useMessages(), msg(), or t()");
|
|
133
142
|
processForwardedTranslationCalls(path, consumerLocation);
|
|
134
143
|
return;
|
|
135
144
|
}
|
|
145
|
+
state.analysis.hasGTSourceReference = true;
|
|
136
146
|
processVueStringCall(path.node, value.kind, consumerLocation, context, (node) => {
|
|
137
147
|
if (!node) return { ok: false };
|
|
138
148
|
const materialized = materializeParameterExpression(node, path.scope, state);
|
|
@@ -172,16 +182,20 @@ function parseVueScript(source, language, context, templateBindings, exposeToTem
|
|
|
172
182
|
OptionalCallExpression: processCall,
|
|
173
183
|
TaggedTemplateExpression(path) {
|
|
174
184
|
if (resolveKnownExpression(path.node.tag, path.scope, state, /* @__PURE__ */ new Set())?.type !== "string") return;
|
|
185
|
+
state.analysis.hasGTSourceReference = true;
|
|
175
186
|
addVueError(context, babelLocation(path.node.loc), "Found an unsupported tagged template translation in gt-vue", "Call the translation function with a string literal instead");
|
|
176
187
|
},
|
|
177
188
|
JSXElement(path) {
|
|
178
189
|
const value = resolveVueJSXElementIdentity(path.node.openingElement.name, path.scope, jsxAnalysis);
|
|
190
|
+
if (value?.type === "component") state.analysis.hasGTSourceReference = true;
|
|
179
191
|
if (value?.type === "component" && (value.name === "Var" || value.name === "Num" || value.name === "DateTime" || value.name === "Currency")) validateVueJSXVariableComponent(path.node, value.name, context);
|
|
180
192
|
if (value?.type !== "component" || value.name !== "T") return;
|
|
181
193
|
if (isNestedInVueJSXTranslation(path, jsxAnalysis)) return;
|
|
182
194
|
extractVueJSXTranslation(path, context, jsxAnalysis);
|
|
183
195
|
}
|
|
184
196
|
});
|
|
197
|
+
if (state.unsupportedDynamicGTReference && context.errors.length === initialErrorCount) addVueError(context, void 0, "Could not statically resolve possible gt-vue string function alias from a dynamic import", "Use a static gt-vue import so translation ownership can be extracted deterministically");
|
|
198
|
+
if (state.unsupportedMalformedGTReference && context.errors.length === initialErrorCount) addVueError(context, void 0, "Could not statically resolve a gt-vue reference imported through a malformed local module", "Fix the local module syntax so translation ownership can be extracted deterministically");
|
|
185
199
|
return true;
|
|
186
200
|
}
|
|
187
201
|
/** Indexes Babel's lexical scopes so deferred function analysis stays exact. */
|
|
@@ -234,6 +248,8 @@ function createScriptState(ast, analysis) {
|
|
|
234
248
|
transformArrayEntries: /* @__PURE__ */ new Map(),
|
|
235
249
|
transformArrayEntriesInProgress: /* @__PURE__ */ new Set(),
|
|
236
250
|
uncertainTranslationHelperBindings: /* @__PURE__ */ new Set(),
|
|
251
|
+
unsupportedDynamicGTReference: false,
|
|
252
|
+
unsupportedMalformedGTReference: false,
|
|
237
253
|
unsafeMutableNamespaceSources: /* @__PURE__ */ new Set()
|
|
238
254
|
};
|
|
239
255
|
traverse(ast, { enter(path) {
|
|
@@ -281,8 +297,9 @@ function memberChainWrites(memberPath) {
|
|
|
281
297
|
function collectImports(ast, state, importerFile = state.analysis.entryFile) {
|
|
282
298
|
traverse(ast, {
|
|
283
299
|
ImportDeclaration(path) {
|
|
284
|
-
if (path.node.importKind
|
|
300
|
+
if (isTypeOnlyImportKind(path.node.importKind)) return;
|
|
285
301
|
const source = path.node.source.value;
|
|
302
|
+
if (source === "gt-vue" && hasRuntimeImport(path.node) && importerFile === state.analysis.entryFile) state.analysis.hasGTSourceReference = true;
|
|
286
303
|
if (source !== "gt-vue" && source !== "vue") {
|
|
287
304
|
registerLocalImportDeclaration(path.node, path.scope, importerFile, state);
|
|
288
305
|
return;
|
|
@@ -297,16 +314,32 @@ function collectImports(ast, state, importerFile = state.analysis.entryFile) {
|
|
|
297
314
|
}, specifierPath.scope, state);
|
|
298
315
|
continue;
|
|
299
316
|
}
|
|
300
|
-
if (specifier.type !== "ImportSpecifier" || specifier.importKind
|
|
317
|
+
if (specifier.type !== "ImportSpecifier" || isTypeOnlyImportKind(specifier.importKind)) continue;
|
|
301
318
|
const value = knownExport(source, specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value);
|
|
302
319
|
if (value) registerImport(specifier.local.name, value, specifierPath.scope, state);
|
|
303
320
|
}
|
|
304
321
|
},
|
|
322
|
+
ExportNamedDeclaration(path) {
|
|
323
|
+
if (path.node.exportKind === "type" || !path.node.source || importerFile !== state.analysis.entryFile) return;
|
|
324
|
+
const exportNames = path.node.specifiers.flatMap((specifier) => {
|
|
325
|
+
if (specifier.type === "ExportSpecifier" && isTypeOnlyImportKind(specifier.exportKind)) return [];
|
|
326
|
+
if (specifier.type === "ExportNamespaceSpecifier") return ["*"];
|
|
327
|
+
if (specifier.type !== "ExportSpecifier") return [];
|
|
328
|
+
return [specifier.local.name];
|
|
329
|
+
});
|
|
330
|
+
recordRuntimeReexport(path.node.source.value, exportNames, importerFile, state);
|
|
331
|
+
},
|
|
332
|
+
ExportAllDeclaration(path) {
|
|
333
|
+
if (path.node.exportKind === "type" || importerFile !== state.analysis.entryFile) return;
|
|
334
|
+
recordRuntimeReexport(path.node.source.value, ["*"], importerFile, state);
|
|
335
|
+
},
|
|
305
336
|
TSImportEqualsDeclaration(path) {
|
|
337
|
+
if (path.node.importKind === "type") return;
|
|
306
338
|
const reference = path.node.moduleReference;
|
|
307
339
|
if (reference.type !== "TSExternalModuleReference" || reference.expression.type !== "StringLiteral") return;
|
|
308
340
|
const source = reference.expression.value;
|
|
309
341
|
if (source !== "gt-vue" && source !== "vue") return;
|
|
342
|
+
if (source === "gt-vue" && importerFile === state.analysis.entryFile) state.analysis.hasGTSourceReference = true;
|
|
310
343
|
registerImport(path.node.id.name, {
|
|
311
344
|
type: "namespace",
|
|
312
345
|
source,
|
|
@@ -321,11 +354,39 @@ function collectImports(ast, state, importerFile = state.analysis.entryFile) {
|
|
|
321
354
|
}
|
|
322
355
|
const namespace = readRequireNamespace(path.node.init, path.scope);
|
|
323
356
|
if (!namespace) return;
|
|
357
|
+
if (namespace.source === "gt-vue" && importerFile === state.analysis.entryFile) state.analysis.hasGTSourceReference = true;
|
|
324
358
|
registerRequirePattern(path.node.id, namespace, path.scope, state);
|
|
325
359
|
}
|
|
326
360
|
});
|
|
327
361
|
propagateUncertainTranslationHelperAliases(ast, state);
|
|
328
362
|
}
|
|
363
|
+
/** Marks only runtime re-exports statically connected to gt-vue. */
|
|
364
|
+
function recordRuntimeReexport(source, exportNames, importerFile, state) {
|
|
365
|
+
if (!importerFile || exportNames.length === 0) return;
|
|
366
|
+
if (source === "gt-vue") {
|
|
367
|
+
state.analysis.hasGTSourceReference = true;
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
if (source === "vue" || isKnownNonVueGTRuntime(source)) return;
|
|
371
|
+
const resolver = state.analysis.localModules;
|
|
372
|
+
const modulePath = resolver?.resolveModule(importerFile, source);
|
|
373
|
+
if (!resolver || !modulePath || !resolver.isProjectModule(modulePath)) return;
|
|
374
|
+
if ((exportNames.includes("*") ? [
|
|
375
|
+
...COMPONENT_IMPORTS,
|
|
376
|
+
"msg",
|
|
377
|
+
"t",
|
|
378
|
+
"useGT",
|
|
379
|
+
"useMessages"
|
|
380
|
+
] : exportNames).some((name) => resolver.hasGTExportReference(modulePath, name))) state.analysis.hasGTSourceReference = true;
|
|
381
|
+
}
|
|
382
|
+
/** Returns whether an import causes a runtime module reference. */
|
|
383
|
+
function hasRuntimeImport(declaration) {
|
|
384
|
+
return declaration.specifiers.length === 0 || declaration.specifiers.some((specifier) => specifier.type !== "ImportSpecifier" || !isTypeOnlyImportKind(specifier.importKind));
|
|
385
|
+
}
|
|
386
|
+
/** Returns whether Babel classified an ESM binding as type-only. */
|
|
387
|
+
function isTypeOnlyImportKind(kind) {
|
|
388
|
+
return kind === "type" || kind === "typeof";
|
|
389
|
+
}
|
|
329
390
|
/** Retains unresolved-helper provenance through simple immutable aliases. */
|
|
330
391
|
function propagateUncertainTranslationHelperAliases(ast, state) {
|
|
331
392
|
const candidates = [];
|
|
@@ -371,28 +432,39 @@ function readDynamicImport(node) {
|
|
|
371
432
|
/** Marks GT-shaped values selected from a dynamic namespace as unresolved. */
|
|
372
433
|
function recordDynamicImportPattern(pattern, source, scope, importerFile, state) {
|
|
373
434
|
if (pattern.type === "VoidPattern") return;
|
|
435
|
+
if (source && isKnownNonVueGTRuntime(source)) return;
|
|
436
|
+
if (source === "gt-vue" && importerFile === state.analysis.entryFile) {
|
|
437
|
+
state.analysis.hasGTSourceReference = true;
|
|
438
|
+
state.unsupportedDynamicGTReference = true;
|
|
439
|
+
}
|
|
374
440
|
const resolver = state.analysis.localModules;
|
|
375
441
|
const modulePath = source && source !== "gt-vue" && source !== "vue" && importerFile ? resolver?.resolveModule(importerFile, source) : void 0;
|
|
376
442
|
if (source === "vue") return;
|
|
377
443
|
if (source && source !== "gt-vue" && !modulePath) return;
|
|
444
|
+
if (modulePath && resolver && !resolver.isProjectModule(modulePath)) return;
|
|
378
445
|
if (pattern.type === "Identifier") {
|
|
379
|
-
if (modulePath && resolver)
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
446
|
+
if (modulePath && resolver) {
|
|
447
|
+
const binding = scope.getBinding(pattern.name);
|
|
448
|
+
for (const exportName of [
|
|
449
|
+
...COMPONENT_IMPORTS,
|
|
450
|
+
"msg",
|
|
451
|
+
"t",
|
|
452
|
+
"useGT",
|
|
453
|
+
"useMessages"
|
|
454
|
+
]) recordDynamicImportBinding(appendTemplatePath(pattern.name, exportName), exportName, modulePath, state, bindingReferencesNamespaceMember(binding, exportName, state));
|
|
455
|
+
} else {
|
|
386
456
|
for (const component of COMPONENT_IMPORTS) {
|
|
387
457
|
const path = appendTemplatePath(pattern.name, component);
|
|
388
458
|
state.analysis.uncertainComponents.add(path);
|
|
389
459
|
state.analysis.uncertainGTComponents.add(path);
|
|
390
460
|
}
|
|
391
|
-
|
|
461
|
+
const stringFunctions = [
|
|
392
462
|
"msg",
|
|
393
463
|
"useGT",
|
|
394
464
|
"useMessages"
|
|
395
|
-
]
|
|
465
|
+
];
|
|
466
|
+
if (source === "gt-vue") stringFunctions.push("t");
|
|
467
|
+
for (const name of stringFunctions) state.analysis.uncertainStringFunctions.add(appendTemplatePath(pattern.name, name));
|
|
396
468
|
}
|
|
397
469
|
return;
|
|
398
470
|
}
|
|
@@ -401,61 +473,102 @@ function recordDynamicImportPattern(pattern, source, scope, importerFile, state)
|
|
|
401
473
|
if (property.type !== "ObjectProperty") continue;
|
|
402
474
|
const exportedName = readPropertyKey(property);
|
|
403
475
|
const localName = readPatternIdentifier(property.value);
|
|
404
|
-
if (exportedName && localName) if (modulePath) recordDynamicImportBinding(localName, exportedName, modulePath, state);
|
|
405
|
-
else recordUnresolvedGTShapedBinding(localName, exportedName, state);
|
|
476
|
+
if (exportedName && localName) if (modulePath) recordDynamicImportBinding(localName, exportedName, modulePath, state, importerFile === state.analysis.entryFile);
|
|
477
|
+
else recordUnresolvedGTShapedBinding(localName, exportedName, state, source === "gt-vue");
|
|
406
478
|
}
|
|
407
479
|
for (const property of pattern.properties) {
|
|
408
480
|
if (property.type !== "ObjectProperty" || !property.computed) continue;
|
|
409
481
|
const exportedName = readResolvedPropertyKey(property, scope, state);
|
|
410
482
|
const localName = readPatternIdentifier(property.value);
|
|
411
|
-
if (exportedName && localName) if (modulePath) recordDynamicImportBinding(localName, exportedName, modulePath, state);
|
|
412
|
-
else recordUnresolvedGTShapedBinding(localName, exportedName, state);
|
|
483
|
+
if (exportedName && localName) if (modulePath) recordDynamicImportBinding(localName, exportedName, modulePath, state, importerFile === state.analysis.entryFile);
|
|
484
|
+
else recordUnresolvedGTShapedBinding(localName, exportedName, state, source === "gt-vue");
|
|
413
485
|
}
|
|
414
486
|
}
|
|
487
|
+
/** Returns whether one namespace binding is used through a static member. */
|
|
488
|
+
function bindingReferencesNamespaceMember(binding, memberName, state, seen = /* @__PURE__ */ new Set()) {
|
|
489
|
+
if (!binding || seen.has(binding)) return false;
|
|
490
|
+
const nextSeen = new Set(seen).add(binding);
|
|
491
|
+
return Boolean(binding.referencePaths.some((referencePath) => {
|
|
492
|
+
let reference = referencePath;
|
|
493
|
+
while (reference.parentPath && unwrapExpression(reference.parentPath.node) === reference.node) reference = reference.parentPath;
|
|
494
|
+
const parent = reference.parentPath;
|
|
495
|
+
if (parent?.isMemberExpression() || parent?.isOptionalMemberExpression()) return parent.node.object === reference.node && readResolvedMemberProperty(parent.node, parent.scope, state) === memberName;
|
|
496
|
+
if (parent?.isJSXMemberExpression()) return parent.node.object === reference.node && parent.node.property.name === memberName;
|
|
497
|
+
if (parent?.isVariableDeclarator() && parent.node.init === reference.node) {
|
|
498
|
+
if (parent.node.id.type === "Identifier") return bindingReferencesNamespaceMember(parent.scope.getBinding(parent.node.id.name), memberName, state, nextSeen);
|
|
499
|
+
if (parent.node.id.type === "ObjectPattern") return parent.node.id.properties.some((property) => property.type === "RestElement" || property.type === "ObjectProperty" && readResolvedPropertyKey(property, parent.scope, state) === memberName);
|
|
500
|
+
}
|
|
501
|
+
if (parent?.isCallExpression() || parent?.isNewExpression() || parent?.isReturnStatement() || parent?.isSpreadElement() || parent?.isArrayExpression() || parent?.isObjectProperty()) return true;
|
|
502
|
+
return false;
|
|
503
|
+
}));
|
|
504
|
+
}
|
|
415
505
|
/** Marks only GT roles proven reachable from one static dynamic import. */
|
|
416
|
-
function recordDynamicImportBinding(localName, exportName, modulePath, state) {
|
|
506
|
+
function recordDynamicImportBinding(localName, exportName, modulePath, state, establishesEntryProvenance) {
|
|
417
507
|
const resolution = state.analysis.localModules?.resolveExport(modulePath, exportName);
|
|
418
508
|
if (!resolution || resolution.status === "absent") return;
|
|
419
509
|
if (resolution.status !== "resolved") {
|
|
420
|
-
|
|
510
|
+
if (resolution.status === "invalid" && materializeRecoveredOrdinaryResolution(resolution, state) !== void 0) return;
|
|
511
|
+
if (establishesEntryProvenance && resolution.status === "invalid" && resolution.hasGTSourceReference) {
|
|
512
|
+
state.analysis.hasGTSourceReference = true;
|
|
513
|
+
state.unsupportedDynamicGTReference = true;
|
|
514
|
+
state.unsupportedMalformedGTReference = true;
|
|
515
|
+
}
|
|
516
|
+
if (resolution.status === "invalid" && resolution.hasGTSourceReference) recordInvalidGTResolution(localName, resolution, state);
|
|
517
|
+
else recordUnresolvedGTShapedBinding(localName, exportName, state);
|
|
421
518
|
return;
|
|
422
519
|
}
|
|
423
|
-
|
|
520
|
+
const materialized = materializeLocalExport(resolution.target, state);
|
|
521
|
+
if (establishesEntryProvenance && materialized.hasGTSourceReference) {
|
|
522
|
+
state.analysis.hasGTSourceReference = true;
|
|
523
|
+
state.unsupportedDynamicGTReference = true;
|
|
524
|
+
}
|
|
525
|
+
recordMaterializedLocalUncertainty(localName, materialized, state, true);
|
|
424
526
|
}
|
|
425
527
|
/** Resolves one ESM import through a local, read-only source module graph. */
|
|
426
528
|
function registerLocalImportDeclaration(declaration, scope, importerFile, state) {
|
|
427
529
|
const resolver = state.analysis.localModules;
|
|
428
530
|
const source = declaration.source.value;
|
|
531
|
+
if (isKnownNonVueGTRuntime(source)) return;
|
|
429
532
|
const modulePath = resolver && importerFile ? resolver.resolveModule(importerFile, source) : void 0;
|
|
430
|
-
if (
|
|
533
|
+
if (modulePath && resolver && !resolver.isProjectModule(modulePath)) return;
|
|
534
|
+
if (!resolver || !modulePath) {
|
|
431
535
|
recordUnresolvedTranslationHelperImports(declaration, scope, state);
|
|
432
536
|
if (source.startsWith(".") || source.startsWith("/") || resolver?.hasCustomResolver) recordUnresolvedGTShapedImports(declaration, state);
|
|
433
537
|
return;
|
|
434
538
|
}
|
|
435
539
|
for (const specifier of declaration.specifiers) {
|
|
436
|
-
if (specifier.type === "ImportSpecifier" && specifier.importKind
|
|
540
|
+
if (specifier.type === "ImportSpecifier" && isTypeOnlyImportKind(specifier.importKind)) continue;
|
|
437
541
|
if (specifier.type === "ImportNamespaceSpecifier") {
|
|
438
542
|
const value = {
|
|
439
543
|
type: "local-namespace",
|
|
440
544
|
modulePath
|
|
441
545
|
};
|
|
442
546
|
registerImport(specifier.local.name, value, scope, state);
|
|
443
|
-
if (importerFile === state.analysis.entryFile) recordLocalNamespaceMembers(specifier.local.name, modulePath, state);
|
|
547
|
+
if (importerFile === state.analysis.entryFile) recordLocalNamespaceMembers(specifier.local.name, modulePath, state, scope.getBinding(specifier.local.name));
|
|
444
548
|
continue;
|
|
445
549
|
}
|
|
446
550
|
const exportName = specifier.type === "ImportDefaultSpecifier" ? "default" : specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
|
|
447
551
|
const resolution = resolver.resolveExport(modulePath, exportName);
|
|
552
|
+
let materialized;
|
|
448
553
|
if (resolution.status !== "resolved") {
|
|
449
|
-
|
|
450
|
-
if (
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
554
|
+
if (resolution.status === "invalid") materialized = materializeRecoveredOrdinaryResolution(resolution, state, true);
|
|
555
|
+
if (!materialized) {
|
|
556
|
+
if (importerFile === state.analysis.entryFile && resolution.status === "invalid" && resolution.hasGTSourceReference) {
|
|
557
|
+
state.analysis.hasGTSourceReference = true;
|
|
558
|
+
state.unsupportedMalformedGTReference = true;
|
|
559
|
+
}
|
|
560
|
+
recordUncertainTranslationHelperBinding(specifier.local.name, scope, state);
|
|
561
|
+
if (resolution.status === "ambiguous") recordAmbiguousLocalBinding(specifier.local.name, state);
|
|
562
|
+
else if (resolution.status === "invalid" && resolution.hasGTSourceReference) recordInvalidGTResolution(specifier.local.name, resolution, state);
|
|
563
|
+
else recordUnresolvedGTShapedBinding(specifier.local.name, exportName, state);
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
} else materialized = materializeLocalExport(resolution.target, state);
|
|
567
|
+
if (importerFile === state.analysis.entryFile && materialized.hasGTSourceReference) state.analysis.hasGTSourceReference = true;
|
|
455
568
|
const binding = scope.getBinding(specifier.local.name);
|
|
456
569
|
if (materialized.value) {
|
|
457
570
|
registerImport(specifier.local.name, materialized.value, scope, state);
|
|
458
|
-
if (materialized.value.type === "local-namespace" && importerFile === state.analysis.entryFile) recordLocalNamespaceMembers(specifier.local.name, materialized.value.modulePath, state);
|
|
571
|
+
if (materialized.value.type === "local-namespace" && importerFile === state.analysis.entryFile) recordLocalNamespaceMembers(specifier.local.name, materialized.value.modulePath, state, binding);
|
|
459
572
|
}
|
|
460
573
|
if (binding && materialized.callable) state.localCallableBindings.set(binding, materialized.callable);
|
|
461
574
|
recordMaterializedLocalUncertainty(specifier.local.name, materialized, state);
|
|
@@ -472,25 +585,35 @@ function attachLocalModule(record, state) {
|
|
|
472
585
|
} });
|
|
473
586
|
collectImports(record.ast, state, record.filePath);
|
|
474
587
|
}
|
|
475
|
-
/** Converts a graph export into an analyzer identity or callable. */
|
|
476
588
|
function materializeLocalExport(target, state) {
|
|
477
|
-
if (target.type === "external") return {
|
|
478
|
-
if (target.type === "external
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
589
|
+
if (target.type === "ordinary-external") return {};
|
|
590
|
+
if (target.type === "external") return {
|
|
591
|
+
hasGTSourceReference: target.source === "gt-vue",
|
|
592
|
+
value: knownExport(target.source, target.exportName)
|
|
593
|
+
};
|
|
594
|
+
if (target.type === "external-namespace") return {
|
|
595
|
+
hasGTSourceReference: target.source === "gt-vue",
|
|
596
|
+
value: {
|
|
597
|
+
mutable: false,
|
|
598
|
+
source: target.source,
|
|
599
|
+
type: "namespace"
|
|
600
|
+
}
|
|
601
|
+
};
|
|
483
602
|
if (target.type === "namespace") return { value: {
|
|
484
603
|
type: "local-namespace",
|
|
485
604
|
modulePath: target.modulePath
|
|
486
605
|
} };
|
|
487
606
|
attachLocalModule(target.record, state);
|
|
488
|
-
if (target.type === "expression")
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
607
|
+
if (target.type === "expression") {
|
|
608
|
+
const materialized = {
|
|
609
|
+
callable: resolveCalledFunction(target.node, target.record.programScope, state, /* @__PURE__ */ new Set()),
|
|
610
|
+
value: resolveKnownExpression(target.node, target.record.programScope, state, /* @__PURE__ */ new Set()),
|
|
611
|
+
possibleGTComponent: expressionMayProduceComponent(target.node, target.record.programScope, state, /* @__PURE__ */ new Set(), "translation"),
|
|
612
|
+
possibleStringFunction: expressionMayProduceStringFunction(target.node, target.record.programScope, state, /* @__PURE__ */ new Set())
|
|
613
|
+
};
|
|
614
|
+
materialized.hasGTSourceReference = isKnownGTValue(materialized.value);
|
|
615
|
+
return materialized;
|
|
616
|
+
}
|
|
494
617
|
const binding = target.record.programScope.getBinding(target.exportName);
|
|
495
618
|
if (!binding) return {};
|
|
496
619
|
const possibleGTComponent = expressionMayProduceComponent(binding.identifier, target.record.programScope, state, /* @__PURE__ */ new Set(), "translation");
|
|
@@ -500,21 +623,66 @@ function materializeLocalExport(target, state) {
|
|
|
500
623
|
possibleStringFunction,
|
|
501
624
|
unsafeCallable: bindingMayReferenceLocalCallable(binding, state)
|
|
502
625
|
};
|
|
503
|
-
|
|
626
|
+
const materialized = {
|
|
504
627
|
callable: resolveCalledFunction(binding.identifier, target.record.programScope, state, /* @__PURE__ */ new Set()),
|
|
505
628
|
possibleGTComponent,
|
|
506
629
|
possibleStringFunction,
|
|
507
630
|
value: resolveKnownBinding(binding, state, /* @__PURE__ */ new Set())
|
|
508
631
|
};
|
|
632
|
+
materialized.hasGTSourceReference = isKnownGTValue(materialized.value);
|
|
633
|
+
return materialized;
|
|
634
|
+
}
|
|
635
|
+
/** Returns whether an exact analyzer identity came from gt-vue. */
|
|
636
|
+
function isKnownGTValue(value) {
|
|
637
|
+
return value?.type === "component" || value?.type === "hook" || value?.type === "string" || value?.type === "namespace" && value.source === "gt-vue";
|
|
638
|
+
}
|
|
639
|
+
/** Materializes the one ordinary origin proven through a malformed module. */
|
|
640
|
+
function materializeRecoveredOrdinaryResolution(resolution, state, preserveLocalIdentity = false) {
|
|
641
|
+
if (resolution.hasGTSourceReference || resolution.gtExportName || resolution.recoveredTargets?.length !== 1) return;
|
|
642
|
+
const target = resolution.recoveredTargets[0];
|
|
643
|
+
if (target.type === "ordinary-external") return {};
|
|
644
|
+
if (target.type === "external" || target.type === "external-namespace") return target.source === "vue" ? materializeLocalExport(target, state) : void 0;
|
|
645
|
+
if (target.type === "namespace") return void 0;
|
|
646
|
+
const materialized = materializeLocalExport(target, state);
|
|
647
|
+
if (materialized.hasGTSourceReference || materialized.possibleGTComponent || materialized.possibleStringFunction || materialized.unsafeCallable || isKnownGTValue(materialized.value)) return;
|
|
648
|
+
if (localTargetAliasesKnownNonVueRuntime(target)) return materialized;
|
|
649
|
+
return preserveLocalIdentity && materialized.callable && isOrdinaryIdentityFunction(materialized.callable.node) ? materialized : void 0;
|
|
650
|
+
}
|
|
651
|
+
/** Proves a direct immutable alias back to an official non-Vue GT runtime. */
|
|
652
|
+
function localTargetAliasesKnownNonVueRuntime(target) {
|
|
653
|
+
if (target.type === "expression") return expressionAliasesKnownNonVueRuntime(target.node, target.record.programScope, /* @__PURE__ */ new Set());
|
|
654
|
+
const binding = target.record.programScope.getBinding(target.exportName);
|
|
655
|
+
if (!binding) return false;
|
|
656
|
+
const source = getBindingSource(binding);
|
|
657
|
+
if (!source || source.pattern.type !== "Identifier") return false;
|
|
658
|
+
return expressionAliasesKnownNonVueRuntime(source.expression.node, source.expression.scope, new Set([binding]));
|
|
659
|
+
}
|
|
660
|
+
/** Follows only immutable identifier aliases to named or default imports. */
|
|
661
|
+
function expressionAliasesKnownNonVueRuntime(node, scope, seen) {
|
|
662
|
+
const expression = unwrapExpression(node);
|
|
663
|
+
if (expression?.type !== "Identifier") return false;
|
|
664
|
+
const binding = scope.getBinding(expression.name);
|
|
665
|
+
if (!binding || seen.has(binding)) return false;
|
|
666
|
+
const declaration = binding.path.parentPath;
|
|
667
|
+
if (declaration?.isImportDeclaration() && isKnownNonVueGTRuntime(declaration.node.source.value)) return true;
|
|
668
|
+
const source = getBindingSource(binding);
|
|
669
|
+
return Boolean(source && source.pattern.type === "Identifier" && expressionAliasesKnownNonVueRuntime(source.expression.node, source.expression.scope, new Set(seen).add(binding)));
|
|
670
|
+
}
|
|
671
|
+
/** Proves the narrow local helper form without following unknown dependencies. */
|
|
672
|
+
function isOrdinaryIdentityFunction(node) {
|
|
673
|
+
if (node.async || node.generator || node.params.length !== 1 || node.params[0]?.type !== "Identifier") return false;
|
|
674
|
+
const expression = unwrapExpression(node.body.type === "BlockStatement" ? node.body.body.length === 1 && node.body.body[0]?.type === "ReturnStatement" ? node.body.body[0].argument : void 0 : node.body);
|
|
675
|
+
return expression?.type === "Identifier" && expression.name === node.params[0].name;
|
|
509
676
|
}
|
|
510
677
|
/** Makes statically known members of a local namespace visible to templates. */
|
|
511
|
-
function recordLocalNamespaceMembers(localName, modulePath, state) {
|
|
678
|
+
function recordLocalNamespaceMembers(localName, modulePath, state, binding) {
|
|
512
679
|
const resolver = state.analysis.localModules;
|
|
513
680
|
if (!resolver) return;
|
|
514
681
|
const exportNames = new Set([
|
|
515
682
|
...resolver.listExportNames(modulePath),
|
|
516
683
|
...COMPONENT_IMPORTS,
|
|
517
684
|
"msg",
|
|
685
|
+
"t",
|
|
518
686
|
"useGT",
|
|
519
687
|
"useMessages"
|
|
520
688
|
]);
|
|
@@ -523,8 +691,14 @@ function recordLocalNamespaceMembers(localName, modulePath, state) {
|
|
|
523
691
|
const resolution = resolver.resolveExport(modulePath, exportName);
|
|
524
692
|
if (resolution.status === "absent") continue;
|
|
525
693
|
if (resolution.status !== "resolved") {
|
|
694
|
+
if (resolution.status === "invalid" && materializeRecoveredOrdinaryResolution(resolution, state) !== void 0) continue;
|
|
695
|
+
if (resolution.status === "invalid" && resolution.hasGTSourceReference && bindingReferencesNamespaceMember(binding, exportName, state)) {
|
|
696
|
+
state.analysis.hasGTSourceReference = true;
|
|
697
|
+
state.unsupportedMalformedGTReference = true;
|
|
698
|
+
}
|
|
526
699
|
state.analysis.uncertainTranslationHelpers.add(memberPath);
|
|
527
700
|
if (resolution.status === "ambiguous") recordAmbiguousLocalBinding(memberPath, state);
|
|
701
|
+
else if (resolution.status === "invalid" && resolution.hasGTSourceReference) recordInvalidGTResolution(memberPath, resolution, state);
|
|
528
702
|
else recordUnresolvedGTShapedBinding(memberPath, exportName, state);
|
|
529
703
|
continue;
|
|
530
704
|
}
|
|
@@ -533,6 +707,33 @@ function recordLocalNamespaceMembers(localName, modulePath, state) {
|
|
|
533
707
|
recordMaterializedLocalUncertainty(memberPath, materialized, state);
|
|
534
708
|
}
|
|
535
709
|
}
|
|
710
|
+
/** Retains the original gt-vue role hidden by a malformed re-export alias. */
|
|
711
|
+
function recordInvalidGTResolution(localName, resolution, state) {
|
|
712
|
+
if (resolution.gtExportName === "*") {
|
|
713
|
+
recordUnresolvedGTNamespace(localName, state, true);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
if (resolution.gtExportName) {
|
|
717
|
+
recordUnresolvedGTShapedBinding(localName, resolution.gtExportName, state, true);
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
state.analysis.uncertainTranslationHelpers.add(localName);
|
|
721
|
+
}
|
|
722
|
+
/** Marks the GT-shaped members of one unresolved namespace path. */
|
|
723
|
+
function recordUnresolvedGTNamespace(localName, state, includeImmediateTranslation = false) {
|
|
724
|
+
for (const component of COMPONENT_IMPORTS) {
|
|
725
|
+
const componentPath = appendTemplatePath(localName, component);
|
|
726
|
+
state.analysis.uncertainComponents.add(componentPath);
|
|
727
|
+
state.analysis.uncertainGTComponents.add(componentPath);
|
|
728
|
+
}
|
|
729
|
+
const stringFunctions = [
|
|
730
|
+
"msg",
|
|
731
|
+
"useGT",
|
|
732
|
+
"useMessages"
|
|
733
|
+
];
|
|
734
|
+
if (includeImmediateTranslation) stringFunctions.push("t");
|
|
735
|
+
for (const name of stringFunctions) state.analysis.uncertainStringFunctions.add(appendTemplatePath(localName, name));
|
|
736
|
+
}
|
|
536
737
|
/** Records unresolved roles only when a local export can actually be GT. */
|
|
537
738
|
function recordMaterializedLocalUncertainty(localName, materialized, state, forceKnownValues = false) {
|
|
538
739
|
if (materialized.possibleGTComponent || forceKnownValues && materialized.value?.type === "component") {
|
|
@@ -552,19 +753,10 @@ function bindingMayReferenceLocalCallable(binding, state) {
|
|
|
552
753
|
function recordUnresolvedGTShapedImports(declaration, state) {
|
|
553
754
|
for (const specifier of declaration.specifiers) {
|
|
554
755
|
if (specifier.type === "ImportNamespaceSpecifier") {
|
|
555
|
-
|
|
556
|
-
const path = appendTemplatePath(specifier.local.name, component);
|
|
557
|
-
state.analysis.uncertainComponents.add(path);
|
|
558
|
-
state.analysis.uncertainGTComponents.add(path);
|
|
559
|
-
}
|
|
560
|
-
for (const name of [
|
|
561
|
-
"msg",
|
|
562
|
-
"useGT",
|
|
563
|
-
"useMessages"
|
|
564
|
-
]) state.analysis.uncertainStringFunctions.add(appendTemplatePath(specifier.local.name, name));
|
|
756
|
+
recordUnresolvedGTNamespace(specifier.local.name, state);
|
|
565
757
|
continue;
|
|
566
758
|
}
|
|
567
|
-
if (specifier.type === "ImportSpecifier" && specifier.importKind
|
|
759
|
+
if (specifier.type === "ImportSpecifier" && isTypeOnlyImportKind(specifier.importKind)) continue;
|
|
568
760
|
const importedName = specifier.type === "ImportDefaultSpecifier" ? specifier.local.name : specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
|
|
569
761
|
recordUnresolvedGTShapedBinding(specifier.local.name, importedName, state);
|
|
570
762
|
}
|
|
@@ -572,7 +764,7 @@ function recordUnresolvedGTShapedImports(declaration, state) {
|
|
|
572
764
|
/** Marks unresolved imports only when a call later passes a translator. */
|
|
573
765
|
function recordUnresolvedTranslationHelperImports(declaration, scope, state) {
|
|
574
766
|
for (const specifier of declaration.specifiers) {
|
|
575
|
-
if (specifier.type === "ImportSpecifier" && specifier.importKind
|
|
767
|
+
if (specifier.type === "ImportSpecifier" && isTypeOnlyImportKind(specifier.importKind)) continue;
|
|
576
768
|
recordUncertainTranslationHelperBinding(specifier.local.name, scope, state);
|
|
577
769
|
}
|
|
578
770
|
}
|
|
@@ -583,12 +775,12 @@ function recordUncertainTranslationHelperBinding(localName, scope, state) {
|
|
|
583
775
|
if (binding) state.uncertainTranslationHelperBindings.add(binding);
|
|
584
776
|
}
|
|
585
777
|
/** Records the conservative GT roles implied by an unresolved export name. */
|
|
586
|
-
function recordUnresolvedGTShapedBinding(localName, exportName, state) {
|
|
778
|
+
function recordUnresolvedGTShapedBinding(localName, exportName, state, includeImmediateTranslation = false) {
|
|
587
779
|
if (COMPONENT_IMPORTS.has(exportName)) {
|
|
588
780
|
state.analysis.uncertainComponents.add(localName);
|
|
589
781
|
state.analysis.uncertainGTComponents.add(localName);
|
|
590
782
|
}
|
|
591
|
-
if (exportName === "msg" || exportName === "useGT" || exportName === "useMessages") state.analysis.uncertainStringFunctions.add(localName);
|
|
783
|
+
if (exportName === "msg" || includeImmediateTranslation && exportName === "t" || exportName === "useGT" || exportName === "useMessages") state.analysis.uncertainStringFunctions.add(localName);
|
|
592
784
|
}
|
|
593
785
|
/** Fails closed when a resolved local export is ambiguous or mutable. */
|
|
594
786
|
function recordAmbiguousLocalBinding(localName, state) {
|
|
@@ -875,6 +1067,7 @@ function exposeKnownValue(localName, value, templateBindings, analysis) {
|
|
|
875
1067
|
else if (value.type === "namespace" && value.source === "gt-vue") {
|
|
876
1068
|
for (const component of COMPONENT_IMPORTS) templateBindings.components.set(appendTemplatePath(localName, component), component);
|
|
877
1069
|
templateBindings.stringFunctions.set(appendTemplatePath(localName, "msg"), "msg");
|
|
1070
|
+
templateBindings.stringFunctions.set(appendTemplatePath(localName, "t"), "t");
|
|
878
1071
|
} else if (value.type === "namespace" && value.source === "vue") {
|
|
879
1072
|
for (const builtin of VUE_BUILTIN_IMPORTS) templateBindings.vueBuiltins.set(appendTemplatePath(localName, builtin), builtin);
|
|
880
1073
|
templateBindings.identityFunctions.add(appendTemplatePath(localName, "markRaw"));
|
|
@@ -919,6 +1112,7 @@ function exposeUncertainKnownValue(localName, value, uncertainComponents, uncert
|
|
|
919
1112
|
uncertainGTComponents.add(appendTemplatePath(localName, component));
|
|
920
1113
|
}
|
|
921
1114
|
uncertainStringFunctions.add(appendTemplatePath(localName, "msg"));
|
|
1115
|
+
uncertainStringFunctions.add(appendTemplatePath(localName, "t"));
|
|
922
1116
|
} else if (value.type === "namespace" && value.source === "vue") for (const builtin of VUE_BUILTIN_IMPORTS) uncertainComponents.add(appendTemplatePath(localName, builtin));
|
|
923
1117
|
}
|
|
924
1118
|
/** Exposes a callable whose return can affect template component identity. */
|
|
@@ -1139,21 +1333,28 @@ function collectNamespaceRestMemberCandidates(localName, binding, pattern, value
|
|
|
1139
1333
|
return key !== void 0 ? [key] : [];
|
|
1140
1334
|
}));
|
|
1141
1335
|
const certain = isSafelyReadContainerBinding(binding, binding.identifier, Number.POSITIVE_INFINITY, true, state);
|
|
1142
|
-
return (namespace.source === "gt-vue" ? [
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1336
|
+
return (namespace.source === "gt-vue" ? [
|
|
1337
|
+
...[...COMPONENT_IMPORTS].map((name) => ({
|
|
1338
|
+
type: "component",
|
|
1339
|
+
name
|
|
1340
|
+
})),
|
|
1341
|
+
{
|
|
1342
|
+
type: "string",
|
|
1343
|
+
kind: "msg"
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
type: "string",
|
|
1347
|
+
kind: "t"
|
|
1348
|
+
}
|
|
1349
|
+
] : [...VUE_BUILTIN_IMPORTS].map((name) => ({
|
|
1149
1350
|
type: "vue-builtin",
|
|
1150
1351
|
name
|
|
1151
1352
|
}))).filter((entry) => {
|
|
1152
|
-
const name = entry.type === "component" ? entry.name : entry.type === "string" ?
|
|
1353
|
+
const name = entry.type === "component" ? entry.name : entry.type === "string" ? entry.kind : entry.name;
|
|
1153
1354
|
return !excluded.has(name);
|
|
1154
1355
|
}).map((entry) => ({
|
|
1155
1356
|
certain,
|
|
1156
|
-
name: appendTemplatePath(localName, entry.type === "component" ? entry.name : entry.type === "string" ?
|
|
1357
|
+
name: appendTemplatePath(localName, entry.type === "component" ? entry.name : entry.type === "string" ? entry.kind : entry.name),
|
|
1157
1358
|
value: entry
|
|
1158
1359
|
}));
|
|
1159
1360
|
}
|
|
@@ -1986,13 +2187,12 @@ function resolveKnownExpression(node, scope, state, seen) {
|
|
|
1986
2187
|
if (expression.type === "LogicalExpression") {
|
|
1987
2188
|
const left = resolveKnownExpression(expression.left, scope, state, new Set(seen));
|
|
1988
2189
|
if (left) return expression.operator === "&&" ? resolveKnownExpression(expression.right, scope, state, new Set(seen)) : left;
|
|
1989
|
-
const
|
|
1990
|
-
|
|
1991
|
-
return (expression.operator === "??" ? staticLeft.value == null : expression.operator === "||" ? !staticLeft.value : Boolean(staticLeft.value)) ? resolveKnownExpression(expression.right, scope, state, new Set(seen)) : void 0;
|
|
2190
|
+
const selection = readStaticLogicalSelection(expression, scope, state, new Set(seen));
|
|
2191
|
+
return selection ? resolveKnownExpression(expression[selection], scope, state, new Set(seen)) : void 0;
|
|
1992
2192
|
}
|
|
1993
2193
|
if (expression.type === "ConditionalExpression") {
|
|
1994
|
-
const condition =
|
|
1995
|
-
if (condition
|
|
2194
|
+
const condition = readStaticTruthiness(expression.test, scope, state, new Set(seen));
|
|
2195
|
+
if (condition) return resolveKnownExpression(condition === "truthy" ? expression.consequent : expression.alternate, scope, state, new Set(seen));
|
|
1996
2196
|
const consequent = resolveKnownExpression(expression.consequent, scope, state, new Set(seen));
|
|
1997
2197
|
const alternate = resolveKnownExpression(expression.alternate, scope, state, new Set(seen));
|
|
1998
2198
|
return consequent && alternate && knownValueKey(consequent) === knownValueKey(alternate) ? consequent : void 0;
|
|
@@ -3444,6 +3644,31 @@ function bindingMayReferenceStringFunction(binding, state) {
|
|
|
3444
3644
|
if (declaration.type === "VariableDeclarator" && declaration.init && patternMayProduceStringFunction(declaration.id, declaration.init, binding.identifier.name, binding.path.scope, state, new Set([binding]))) return true;
|
|
3445
3645
|
return binding.constantViolations.some((violation) => violation.isAssignmentExpression() && patternContains(violation.node.left, binding.identifier.name) && expressionMayProduceStringFunction(violation.node.right, violation.scope, state, new Set([binding])));
|
|
3446
3646
|
}
|
|
3647
|
+
/** Evaluates only side-effect-free truthiness needed for static branch choice. */
|
|
3648
|
+
function readStaticTruthiness(node, scope, state, seen) {
|
|
3649
|
+
const expression = unwrapExpression(node);
|
|
3650
|
+
if (!expression) return void 0;
|
|
3651
|
+
const primitive = readStaticFromScope(expression, scope, new Set(seen), expression.end ?? Number.POSITIVE_INFINITY, state.analysis);
|
|
3652
|
+
if (primitive.ok) {
|
|
3653
|
+
if (primitive.value == null) return "nullish";
|
|
3654
|
+
return primitive.value ? "truthy" : "falsy";
|
|
3655
|
+
}
|
|
3656
|
+
if (expression.type === "ConditionalExpression" || expression.type === "LogicalExpression") return;
|
|
3657
|
+
if (resolveKnownExpression(expression, scope, state, new Set(seen))) return "truthy";
|
|
3658
|
+
if (expression.type === "ArrayExpression" || expression.type === "ArrowFunctionExpression" || expression.type === "ClassExpression" || expression.type === "FunctionExpression" || expression.type === "ObjectExpression" || expression.type === "RegExpLiteral") return "truthy";
|
|
3659
|
+
if (expression.type === "Identifier") {
|
|
3660
|
+
const binding = scope.getBinding(expression.name);
|
|
3661
|
+
if (!binding) return expression.name !== "undefined" && ORDINARY_GLOBAL_VALUES.has(expression.name) ? "truthy" : void 0;
|
|
3662
|
+
if (!binding.constant || seen.has(binding)) return void 0;
|
|
3663
|
+
if (binding.path.isClassDeclaration() || binding.path.isFunctionDeclaration()) return "truthy";
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
/** Selects a logical branch when the left value's truthiness is static. */
|
|
3667
|
+
function readStaticLogicalSelection(expression, scope, state, seen = /* @__PURE__ */ new Set()) {
|
|
3668
|
+
const value = readStaticTruthiness(expression.left, scope, state, seen);
|
|
3669
|
+
if (!value) return void 0;
|
|
3670
|
+
return (expression.operator === "??" ? value === "nullish" : expression.operator === "||" ? value !== "truthy" : value === "truthy") ? "right" : "left";
|
|
3671
|
+
}
|
|
3447
3672
|
/** Tracks expressions whose resulting value can be a gt-vue string function. */
|
|
3448
3673
|
function expressionMayProduceStringFunction(node, scope, state, seen) {
|
|
3449
3674
|
if (state.analysis.stats) state.analysis.stats.stringFunctionVisits += 1;
|
|
@@ -3482,8 +3707,16 @@ function expressionMayProduceStringFunction(node, scope, state, seen) {
|
|
|
3482
3707
|
}
|
|
3483
3708
|
return expression.arguments.some((argument) => argument.type !== "ArgumentPlaceholder" && (expressionMayProduceStringFunction(argument.type === "SpreadElement" ? argument.argument : argument, scope, state, seen) || containerMayContainStringFunction(argument.type === "SpreadElement" ? argument.argument : argument, scope, state, /* @__PURE__ */ new Set())));
|
|
3484
3709
|
}
|
|
3485
|
-
if (expression.type === "ConditionalExpression")
|
|
3486
|
-
|
|
3710
|
+
if (expression.type === "ConditionalExpression") {
|
|
3711
|
+
const condition = readStaticTruthiness(expression.test, scope, state, seen);
|
|
3712
|
+
if (condition) return expressionMayProduceStringFunction(condition === "truthy" ? expression.consequent : expression.alternate, scope, state, seen);
|
|
3713
|
+
return expressionMayProduceStringFunction(expression.consequent, scope, state, seen) || expressionMayProduceStringFunction(expression.alternate, scope, state, seen);
|
|
3714
|
+
}
|
|
3715
|
+
if (expression.type === "LogicalExpression") {
|
|
3716
|
+
const selection = readStaticLogicalSelection(expression, scope, state);
|
|
3717
|
+
if (selection) return expressionMayProduceStringFunction(expression[selection], scope, state, seen);
|
|
3718
|
+
return expressionMayProduceStringFunction(expression.left, scope, state, seen) || expressionMayProduceStringFunction(expression.right, scope, state, seen);
|
|
3719
|
+
}
|
|
3487
3720
|
if (expression.type === "SequenceExpression") return expressionMayProduceStringFunction(expression.expressions.at(-1), scope, state, seen);
|
|
3488
3721
|
if (expression.type === "AssignmentExpression") return expressionMayProduceStringFunction(expression.right, scope, state, seen);
|
|
3489
3722
|
if (expression.type === "AwaitExpression" || expression.type === "YieldExpression") return expressionMayProduceStringFunction(expression.argument, scope, state, seen);
|
|
@@ -4187,7 +4420,7 @@ function collectTransformArrayEntries(node, scope, state, seen, atPosition = nod
|
|
|
4187
4420
|
ok: true,
|
|
4188
4421
|
value: fallback
|
|
4189
4422
|
};
|
|
4190
|
-
const value = readStaticFromScope(node, scope, /* @__PURE__ */ new Set(), node.end ?? atPosition, state.analysis
|
|
4423
|
+
const value = readStaticFromScope(node, scope, /* @__PURE__ */ new Set(), node.end ?? atPosition, state.analysis);
|
|
4191
4424
|
if (!value.ok || typeof value.value === "bigint") return { ok: false };
|
|
4192
4425
|
try {
|
|
4193
4426
|
const numeric = Number(value.value);
|
|
@@ -4402,7 +4635,7 @@ function collectTransformArrayEntries(node, scope, state, seen, atPosition = nod
|
|
|
4402
4635
|
let entries = collectTransformArrayEntries(callee.object, scope, state, nextSeen, atPosition);
|
|
4403
4636
|
if (!entries) return void 0;
|
|
4404
4637
|
const depthNode = argument(0);
|
|
4405
|
-
const staticDepth = depthNode ? readStaticFromScope(depthNode, scope, /* @__PURE__ */ new Set(), depthNode.end ?? atPosition, state.analysis
|
|
4638
|
+
const staticDepth = depthNode ? readStaticFromScope(depthNode, scope, /* @__PURE__ */ new Set(), depthNode.end ?? atPosition, state.analysis) : {
|
|
4406
4639
|
ok: true,
|
|
4407
4640
|
value: 1
|
|
4408
4641
|
};
|
|
@@ -4640,35 +4873,24 @@ function memberChainCanMutate(memberPath, allowCalls) {
|
|
|
4640
4873
|
}
|
|
4641
4874
|
return true;
|
|
4642
4875
|
}
|
|
4643
|
-
function readStaticFromScope(node, scope, seen, atPosition, analysis
|
|
4876
|
+
function readStaticFromScope(node, scope, seen, atPosition, analysis) {
|
|
4644
4877
|
return readStaticPrimitive(node, (identifier) => {
|
|
4645
4878
|
const binding = scope.getBinding(identifier.name);
|
|
4646
4879
|
if (!binding) {
|
|
4647
|
-
const value = analysis?.staticValues.get(identifier.name);
|
|
4648
4880
|
if (analysis?.staticValues.has(identifier.name)) return {
|
|
4649
4881
|
ok: true,
|
|
4650
|
-
value
|
|
4651
|
-
};
|
|
4652
|
-
if (allowNumericGlobals && identifier.name === "Infinity") return {
|
|
4653
|
-
ok: true,
|
|
4654
|
-
value: Number.POSITIVE_INFINITY
|
|
4655
|
-
};
|
|
4656
|
-
if (allowNumericGlobals && identifier.name === "NaN") return {
|
|
4657
|
-
ok: true,
|
|
4658
|
-
value: NaN
|
|
4882
|
+
value: analysis.staticValues.get(identifier.name)
|
|
4659
4883
|
};
|
|
4660
|
-
return
|
|
4884
|
+
return readStaticGlobalPrimitive(identifier.name);
|
|
4661
4885
|
}
|
|
4662
4886
|
const cacheable = analysis !== void 0 && atPosition === Number.POSITIVE_INFINITY;
|
|
4663
|
-
const cached = cacheable ? staticBindingResults.get(analysis)?.get(binding)
|
|
4887
|
+
const cached = cacheable ? staticBindingResults.get(analysis)?.get(binding) : void 0;
|
|
4664
4888
|
if (cached) return cached;
|
|
4665
4889
|
if (seen.has(binding)) return { ok: false };
|
|
4666
4890
|
if (cacheable) {
|
|
4667
|
-
const active = staticBindingsInProgress.get(analysis) ?? /* @__PURE__ */ new
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
modes.add(allowNumericGlobals);
|
|
4671
|
-
active.set(binding, modes);
|
|
4891
|
+
const active = staticBindingsInProgress.get(analysis) ?? /* @__PURE__ */ new Set();
|
|
4892
|
+
if (active.has(binding)) return { ok: false };
|
|
4893
|
+
active.add(binding);
|
|
4672
4894
|
staticBindingsInProgress.set(analysis, active);
|
|
4673
4895
|
}
|
|
4674
4896
|
const nextSeen = new Set(seen);
|
|
@@ -4676,22 +4898,15 @@ function readStaticFromScope(node, scope, seen, atPosition, analysis, allowNumer
|
|
|
4676
4898
|
try {
|
|
4677
4899
|
const source = getBindingSource(binding);
|
|
4678
4900
|
const expression = source && (source.expression.node.end ?? 0) <= atPosition ? selectPatternExpression(source.pattern, source.expression.node, binding.identifier.name, source.expression.scope, source.expression.node.end ?? Number.POSITIVE_INFINITY) : void 0;
|
|
4679
|
-
const result = expression ? readStaticFromScope(expression.node, expression.scope, nextSeen, atPosition, analysis
|
|
4901
|
+
const result = expression ? readStaticFromScope(expression.node, expression.scope, nextSeen, atPosition, analysis) : { ok: false };
|
|
4680
4902
|
if (cacheable) {
|
|
4681
4903
|
const results = staticBindingResults.get(analysis) ?? /* @__PURE__ */ new Map();
|
|
4682
|
-
|
|
4683
|
-
modes.set(allowNumericGlobals, result);
|
|
4684
|
-
results.set(binding, modes);
|
|
4904
|
+
results.set(binding, result);
|
|
4685
4905
|
staticBindingResults.set(analysis, results);
|
|
4686
4906
|
}
|
|
4687
4907
|
return result;
|
|
4688
4908
|
} finally {
|
|
4689
|
-
if (cacheable)
|
|
4690
|
-
const active = staticBindingsInProgress.get(analysis);
|
|
4691
|
-
const modes = active?.get(binding);
|
|
4692
|
-
modes?.delete(allowNumericGlobals);
|
|
4693
|
-
if (modes?.size === 0) active?.delete(binding);
|
|
4694
|
-
}
|
|
4909
|
+
if (cacheable) staticBindingsInProgress.get(analysis)?.delete(binding);
|
|
4695
4910
|
}
|
|
4696
4911
|
});
|
|
4697
4912
|
}
|