@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.
Files changed (89) hide show
  1. package/LICENSE.md +18 -102
  2. package/dist/detect.d.ts +8 -0
  3. package/dist/detect.js +2 -0
  4. package/dist/index.d.ts +3 -1
  5. package/dist/index.js +2 -1
  6. package/dist/inspect.d.ts +9 -0
  7. package/dist/inspect.js +2 -0
  8. package/dist/integration.d.ts +69 -0
  9. package/dist/integration.js +162 -0
  10. package/dist/integration.js.map +1 -0
  11. package/dist/internal/config/configFiles.d.ts +6 -0
  12. package/dist/internal/config/configFiles.js +34 -0
  13. package/dist/internal/config/configFiles.js.map +1 -0
  14. package/dist/internal/config/resolveVueCompilerOptions.d.ts +6 -0
  15. package/dist/internal/config/resolveVueCompilerOptions.js +74 -56
  16. package/dist/internal/config/resolveVueCompilerOptions.js.map +1 -1
  17. package/dist/internal/extractFromVueSource.d.ts +4 -3
  18. package/dist/internal/extractFromVueSource.js +255 -12
  19. package/dist/internal/extractFromVueSource.js.map +1 -1
  20. package/dist/internal/project/consumerUsage.d.ts +34 -0
  21. package/dist/internal/project/consumerUsage.js +1220 -0
  22. package/dist/internal/project/consumerUsage.js.map +1 -0
  23. package/dist/internal/project/detectVueProject.d.ts +13 -0
  24. package/dist/internal/project/detectVueProject.js +65 -0
  25. package/dist/internal/project/detectVueProject.js.map +1 -0
  26. package/dist/internal/project/extractFromVueProject.d.ts +10 -0
  27. package/dist/internal/project/extractFromVueProject.js +579 -0
  28. package/dist/internal/project/extractFromVueProject.js.map +1 -0
  29. package/dist/internal/project/inspectVueProject.d.ts +48 -0
  30. package/dist/internal/project/inspectVueProject.js +143 -0
  31. package/dist/internal/project/inspectVueProject.js.map +1 -0
  32. package/dist/internal/project/manifest.d.ts +64 -0
  33. package/dist/internal/project/manifest.js +270 -0
  34. package/dist/internal/project/manifest.js.map +1 -0
  35. package/dist/internal/project/mergeVueProjectExtraction.d.ts +18 -0
  36. package/dist/internal/project/mergeVueProjectExtraction.js +79 -0
  37. package/dist/internal/project/mergeVueProjectExtraction.js.map +1 -0
  38. package/dist/internal/project/moduleResolver.d.ts +18 -0
  39. package/dist/internal/project/moduleResolver.js +325 -0
  40. package/dist/internal/project/moduleResolver.js.map +1 -0
  41. package/dist/internal/project/scopes.d.ts +31 -0
  42. package/dist/internal/project/scopes.js +182 -0
  43. package/dist/internal/project/scopes.js.map +1 -0
  44. package/dist/internal/project/sourcePatterns.d.ts +4 -0
  45. package/dist/internal/project/sourcePatterns.js +32 -0
  46. package/dist/internal/project/sourcePatterns.js.map +1 -0
  47. package/dist/internal/project/viteAliases.d.ts +42 -0
  48. package/dist/internal/project/viteAliases.js +1020 -0
  49. package/dist/internal/project/viteAliases.js.map +1 -0
  50. package/dist/internal/project/vueSourceClassification.d.ts +13 -0
  51. package/dist/internal/project/vueSourceClassification.js +146 -0
  52. package/dist/internal/project/vueSourceClassification.js.map +1 -0
  53. package/dist/internal/project/workspaces.d.ts +31 -0
  54. package/dist/internal/project/workspaces.js +326 -0
  55. package/dist/internal/project/workspaces.js.map +1 -0
  56. package/dist/internal/project/wrapperProvenance.d.ts +21 -0
  57. package/dist/internal/project/wrapperProvenance.js +287 -0
  58. package/dist/internal/project/wrapperProvenance.js.map +1 -0
  59. package/dist/internal/script/analyze.js +323 -108
  60. package/dist/internal/script/analyze.js.map +1 -1
  61. package/dist/internal/script/jsx.d.ts +7 -4
  62. package/dist/internal/script/jsx.js +351 -79
  63. package/dist/internal/script/jsx.js.map +1 -1
  64. package/dist/internal/script/knownValues.js +4 -0
  65. package/dist/internal/script/knownValues.js.map +1 -1
  66. package/dist/internal/script/localModules.d.ts +23 -2
  67. package/dist/internal/script/localModules.js +535 -20
  68. package/dist/internal/script/localModules.js.map +1 -1
  69. package/dist/internal/script/model.d.ts +10 -3
  70. package/dist/internal/script/parser.js +2 -1
  71. package/dist/internal/script/parser.js.map +1 -1
  72. package/dist/internal/script/runtimeModules.d.ts +10 -0
  73. package/dist/internal/script/runtimeModules.js +27 -0
  74. package/dist/internal/script/runtimeModules.js.map +1 -0
  75. package/dist/internal/stringCalls.js +9 -3
  76. package/dist/internal/stringCalls.js.map +1 -1
  77. package/dist/internal/template.js +263 -141
  78. package/dist/internal/template.js.map +1 -1
  79. package/dist/internal/types.d.ts +5 -2
  80. package/dist/internal/utils.d.ts +14 -3
  81. package/dist/internal/utils.js +63 -3
  82. package/dist/internal/utils.js.map +1 -1
  83. package/dist/internal/vueCompiler.d.ts +9 -1
  84. package/dist/internal/vueCompiler.js +37 -14
  85. package/dist/internal/vueCompiler.js.map +1 -1
  86. package/dist/project.d.ts +11 -0
  87. package/dist/project.js +3 -0
  88. package/dist/types.d.ts +95 -4
  89. package/package.json +30 -4
@@ -1,11 +1,12 @@
1
- import { addVueError, babelLocation, readStaticPrimitive, unwrapExpression } from "../utils.js";
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)) 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");
125
- if (vueHelper === "defineAsyncComponent" && firstArgument && firstArgument.type !== "ArgumentPlaceholder" && firstArgument.type !== "SpreadElement" && expressionContainsGTReference(firstArgument, path.scope, state)) 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");
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 msg()");
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 === "type") return;
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 === "type") continue;
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) for (const exportName of [
380
- ...COMPONENT_IMPORTS,
381
- "msg",
382
- "useGT",
383
- "useMessages"
384
- ]) recordDynamicImportBinding(appendTemplatePath(pattern.name, exportName), exportName, modulePath, state);
385
- else {
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
- for (const name of [
461
+ const stringFunctions = [
392
462
  "msg",
393
463
  "useGT",
394
464
  "useMessages"
395
- ]) state.analysis.uncertainStringFunctions.add(appendTemplatePath(pattern.name, name));
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
- recordUnresolvedGTShapedBinding(localName, exportName, state);
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
- recordMaterializedLocalUncertainty(localName, materializeLocalExport(resolution.target, state), state, true);
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 (!modulePath || !resolver?.getRecord(modulePath)) {
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 === "type") continue;
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
- recordUncertainTranslationHelperBinding(specifier.local.name, scope, state);
450
- if (resolution.status === "ambiguous") recordAmbiguousLocalBinding(specifier.local.name, state);
451
- else recordUnresolvedGTShapedBinding(specifier.local.name, exportName, state);
452
- continue;
453
- }
454
- const materialized = materializeLocalExport(resolution.target, state);
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 { value: knownExport(target.source, target.exportName) };
478
- if (target.type === "external-namespace") return { value: {
479
- mutable: false,
480
- source: target.source,
481
- type: "namespace"
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") return {
489
- callable: resolveCalledFunction(target.node, target.record.programScope, state, /* @__PURE__ */ new Set()),
490
- value: resolveKnownExpression(target.node, target.record.programScope, state, /* @__PURE__ */ new Set()),
491
- possibleGTComponent: expressionMayProduceComponent(target.node, target.record.programScope, state, /* @__PURE__ */ new Set(), "translation"),
492
- possibleStringFunction: expressionMayProduceStringFunction(target.node, target.record.programScope, state, /* @__PURE__ */ new Set())
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
- return {
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
- for (const component of COMPONENT_IMPORTS) {
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 === "type") continue;
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 === "type") continue;
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" ? [...[...COMPONENT_IMPORTS].map((name) => ({
1143
- type: "component",
1144
- name
1145
- })), {
1146
- type: "string",
1147
- kind: "msg"
1148
- }] : [...VUE_BUILTIN_IMPORTS].map((name) => ({
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" ? "msg" : entry.name;
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" ? "msg" : entry.name),
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 staticLeft = readStaticFromScope(expression.left, scope, /* @__PURE__ */ new Set(), expression.left.end ?? Number.POSITIVE_INFINITY, state.analysis);
1990
- if (!staticLeft.ok) return void 0;
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 = readStaticFromScope(expression.test, scope, /* @__PURE__ */ new Set(), expression.test.end ?? Number.POSITIVE_INFINITY, state.analysis);
1995
- if (condition.ok) return resolveKnownExpression(condition.value ? expression.consequent : expression.alternate, scope, state, new Set(seen));
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") return expressionMayProduceStringFunction(expression.consequent, scope, state, seen) || expressionMayProduceStringFunction(expression.alternate, scope, state, seen);
3486
- if (expression.type === "LogicalExpression") return expressionMayProduceStringFunction(expression.left, scope, state, seen) || expressionMayProduceStringFunction(expression.right, scope, state, seen);
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, true);
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, true) : {
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, allowNumericGlobals = false) {
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 { ok: false };
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)?.get(allowNumericGlobals) : void 0;
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 Map();
4668
- const modes = active.get(binding) ?? /* @__PURE__ */ new Set();
4669
- if (modes.has(allowNumericGlobals)) return { ok: false };
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, allowNumericGlobals) : { ok: false };
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
- const modes = results.get(binding) ?? /* @__PURE__ */ new Map();
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
  }