@generaltranslation/vue-extractor 0.0.0 → 0.1.0-iris.0

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 (87) 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 +252 -85
  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/localModules.d.ts +23 -2
  65. package/dist/internal/script/localModules.js +533 -20
  66. package/dist/internal/script/localModules.js.map +1 -1
  67. package/dist/internal/script/model.d.ts +9 -2
  68. package/dist/internal/script/parser.js +2 -1
  69. package/dist/internal/script/parser.js.map +1 -1
  70. package/dist/internal/script/runtimeModules.d.ts +10 -0
  71. package/dist/internal/script/runtimeModules.js +27 -0
  72. package/dist/internal/script/runtimeModules.js.map +1 -0
  73. package/dist/internal/stringCalls.js +1 -1
  74. package/dist/internal/stringCalls.js.map +1 -1
  75. package/dist/internal/template.js +263 -141
  76. package/dist/internal/template.js.map +1 -1
  77. package/dist/internal/types.d.ts +4 -1
  78. package/dist/internal/utils.d.ts +14 -3
  79. package/dist/internal/utils.js +63 -3
  80. package/dist/internal/utils.js.map +1 -1
  81. package/dist/internal/vueCompiler.d.ts +9 -1
  82. package/dist/internal/vueCompiler.js +37 -14
  83. package/dist/internal/vueCompiler.js.map +1 -1
  84. package/dist/project.d.ts +11 -0
  85. package/dist/project.js +3 -0
  86. package/dist/types.d.ts +95 -4
  87. package/package.json +30 -4
@@ -1,4 +1,4 @@
1
- import { addVueError, createInlineMetadata, readStaticPrimitive, unwrapExpression } from "./utils.js";
1
+ import { addVueError, applyStaticTMetadataValue, createInlineMetadata, normalizeTMetadataKey, readStaticGlobalPrimitive, readStaticPrimitive, unwrapExpression } from "./utils.js";
2
2
  import { processVueStringCall } from "./stringCalls.js";
3
3
  import { appendTemplatePath, recursiveTemplatePathSegment, unknownTemplatePathSegment } from "./templatePath.js";
4
4
  import { ElementTypes, NodeTypes } from "./compilerAst.js";
@@ -89,8 +89,9 @@ function visitTemplateChildren(children, shadowed, bindings, expressionPlugins,
89
89
  }
90
90
  for (const property of child.props) {
91
91
  if (property.type !== NodeTypes.DIRECTIVE) continue;
92
- if (property.arg?.type === NodeTypes.SIMPLE_EXPRESSION && !property.arg.isStatic) processTemplateExpression(property.arg, childShadowed, childBindings, expressionPlugins, context);
92
+ if (property.name !== "is" && property.arg?.type === NodeTypes.SIMPLE_EXPRESSION && !property.arg.isStatic) processTemplateExpression(property.arg, childShadowed, childBindings, expressionPlugins, context);
93
93
  if (property.name === "slot" || !property.exp) continue;
94
+ if (property.name === "is" && !isEffectiveValuedVIsSelector(child, property, context.valuedVIsReplacesElement)) continue;
94
95
  if (property.name === "for") {
95
96
  const parseResult = readForParseResult(property);
96
97
  if (parseResult?.source) processTemplateExpression(parseResult.source, shadowed, bindings, expressionPlugins, context);
@@ -98,22 +99,45 @@ function visitTemplateChildren(children, shadowed, bindings, expressionPlugins,
98
99
  }
99
100
  processTemplateExpression(property.exp, property.name === "if" || property.name === "else-if" ? shadowed : childShadowed, property.name === "if" || property.name === "else-if" ? bindings : childBindings, expressionPlugins, context);
100
101
  }
101
- const component = resolveGTComponent(child, childBindings, expressionPlugins, childShadowed);
102
- const suspense = resolveSuspenseComponent(child, childBindings, expressionPlugins, childShadowed);
103
- const fragment = resolveFragmentComponent(child, childBindings, expressionPlugins, childShadowed);
104
- const uncertainGTComponent = resolveUncertainComponentBinding(child, childBindings, childBindings.uncertainGTComponents, childBindings.uncertainRegisteredGTComponents, childBindings.gtComponentFactories, expressionPlugins, childShadowed);
105
- const possibleDynamicT = component ? void 0 : resolvePossibleDynamicT(child, childBindings, expressionPlugins, childShadowed);
102
+ const component = resolveGTComponent(child, childBindings, expressionPlugins, childShadowed, context.valuedVIsReplacesElement);
103
+ const uncertainGTComponent = resolveUncertainComponentBinding(child, childBindings, childBindings.uncertainGTComponents, childBindings.uncertainRegisteredGTComponents, childBindings.gtComponentFactories, expressionPlugins, childShadowed, context.valuedVIsReplacesElement);
104
+ const possibleDynamicT = component ? void 0 : resolvePossibleDynamicT(child, childBindings, expressionPlugins, childShadowed, context.valuedVIsReplacesElement);
106
105
  const unresolvedGTComponent = uncertainGTComponent ?? possibleDynamicT;
107
106
  if (!insideTranslation && !component && unresolvedGTComponent) addVueError(context, child.loc, `Could not statically resolve possible gt-vue component alias "${unresolvedGTComponent}"`, "Use a direct gt-vue import or an immutable alias that does not escape static analysis");
108
107
  if (component?.originalName === "Var" || component?.originalName === "Num" || component?.originalName === "DateTime" || component?.originalName === "Currency") validateVariableComponentShape(child, component.originalName, context);
109
108
  if (component?.originalName === "T" && !insideTranslation) extractTranslationComponent(child, childShadowed, childBindings, expressionPlugins, context);
110
- const childInsideTranslation = component?.originalName === "Var" || insideTranslation && isOpaqueComponent(child, component, suspense ?? fragment) ? false : insideTranslation || component?.originalName === "T";
111
- if (childInsideTranslation && suspense) {
112
- const suspenseElementIsFallback = hasStaticSlotName(child, "fallback");
113
- for (const suspenseChild of child.children) visitTemplateChildren([suspenseChild], childShadowed, childBindings, expressionPlugins, context, suspenseElementIsFallback ? false : !isSuspenseFallbackTemplate(suspenseChild));
114
- } else visitTemplateChildren(child.children, childShadowed, childBindings, expressionPlugins, context, childInsideTranslation);
109
+ const childInsideTranslation = component?.originalName === "Var" ? false : insideTranslation || component?.originalName === "T";
110
+ if (childInsideTranslation && child.tagType === ElementTypes.COMPONENT) visitComponentSlotChildren(child, childShadowed, childBindings, expressionPlugins, context, component?.originalName === "Branch" || component?.originalName === "Plural" || component?.originalName === "T");
111
+ else visitTemplateChildren(child.children, childShadowed, childBindings, expressionPlugins, context, childInsideTranslation);
115
112
  }
116
113
  }
114
+ /**
115
+ * Walks a component's authored slots with the same boundary used at runtime.
116
+ *
117
+ * A static default slot is React-equivalent `children` and therefore belongs
118
+ * to the surrounding translation. Ordinary named slots are not materialized
119
+ * by gt-vue's runtime, so they remain outside that translation and may contain
120
+ * their own independent T. Branch and Plural are the deliberate exception:
121
+ * their named slots are persisted as translation branches.
122
+ */
123
+ function visitComponentSlotChildren(element, shadowed, bindings, expressionPlugins, context, namedSlotsParticipate) {
124
+ const componentSlot = element.props.find((property) => property.type === NodeTypes.DIRECTIVE && property.name === "slot");
125
+ if (componentSlot) {
126
+ const name = readStaticSlotName(componentSlot);
127
+ visitTemplateChildren(element.children, shadowed, bindings, expressionPlugins, context, name === "default" || name === void 0 || namedSlotsParticipate);
128
+ return;
129
+ }
130
+ for (const child of element.children) {
131
+ const slotDirective = child.type === NodeTypes.ELEMENT && child.tag === "template" ? child.props.find((property) => property.type === NodeTypes.DIRECTIVE && property.name === "slot") : void 0;
132
+ const slotName = slotDirective ? readStaticSlotName(slotDirective) : "default";
133
+ visitTemplateChildren([child], shadowed, bindings, expressionPlugins, context, slotName === "default" || slotName === void 0 || namedSlotsParticipate);
134
+ }
135
+ }
136
+ /** Reads only a literal slot name without emitting extraction diagnostics. */
137
+ function readStaticSlotName(directive) {
138
+ if (!directive.arg) return "default";
139
+ return directive.arg.type === NodeTypes.SIMPLE_EXPRESSION && directive.arg.isStatic ? directive.arg.content : void 0;
140
+ }
117
141
  /** Finds local Vue aliases whose runtime value can be a GT component. */
118
142
  function collectPossibleScopedComponentAliases(element, bindings, shadowed, expressionPlugins) {
119
143
  const aliases = /* @__PURE__ */ new Set();
@@ -399,14 +423,31 @@ function resolvePossibleTemplateStringFunction(input, scope, shadowed, bindings)
399
423
  /** Reads a script-exposed primitive unless a Vue or expression scope masks it. */
400
424
  function readTemplatePrimitive(input, scope, shadowed, bindings) {
401
425
  return readStaticPrimitive(input, (identifier) => {
402
- if (shadowed.has(identifier.name) || scope.hasBinding(identifier.name)) return { ok: false };
403
- const value = bindings.staticValues.get(identifier.name);
404
- return bindings.staticValues.has(identifier.name) ? {
405
- ok: true,
406
- value
407
- } : { ok: false };
426
+ if (scope.hasBinding(identifier.name)) return { ok: false };
427
+ return readTemplateIdentifierPrimitive(identifier, shadowed, bindings);
408
428
  });
409
429
  }
430
+ /**
431
+ * Resolves a template primitive using Vue's compiler binding precedence.
432
+ *
433
+ * Script-setup bindings are compiler-visible and can shadow global names.
434
+ * Options API return keys live on the render context, but Vue emits immutable
435
+ * globals directly before consulting that context, so those exposures cannot
436
+ * shadow `undefined`, `NaN`, or `Infinity`.
437
+ */
438
+ function readTemplateIdentifierPrimitive(identifier, shadowed, bindings) {
439
+ if (shadowed.has(identifier.name)) return { ok: false };
440
+ if (bindings.directBindings.has(identifier.name)) return bindings.staticValues.has(identifier.name) ? {
441
+ ok: true,
442
+ value: bindings.staticValues.get(identifier.name)
443
+ } : { ok: false };
444
+ const global = readStaticGlobalPrimitive(identifier.name);
445
+ if (global.ok) return global;
446
+ return bindings.staticValues.has(identifier.name) ? {
447
+ ok: true,
448
+ value: bindings.staticValues.get(identifier.name)
449
+ } : { ok: false };
450
+ }
410
451
  /** Resolves a template call back to a statically imported gt-vue function. */
411
452
  function resolveTemplateStringFunction(input, scope, shadowed, bindings) {
412
453
  const node = unwrapExpression(input);
@@ -442,75 +483,88 @@ function visitBindingDefaultExpressions(pattern, visit) {
442
483
  }
443
484
  function extractTranslationComponent(element, shadowed, bindings, expressionPlugins, context) {
444
485
  const errorCount = context.errors.length;
445
- const translationContext = readTContext(element, shadowed, bindings, expressionPlugins, context);
446
- const slots = getSlotLayout(element, shadowed, expressionPlugins, context);
486
+ const translationMetadata = readTMetadata(element, shadowed, bindings, expressionPlugins, context);
487
+ const slots = getSlotLayout(element, shadowed, expressionPlugins, context, "reject");
447
488
  if (slots.namedSlots.size > 0) addVueError(context, element.loc, "Found a named slot on a gt-vue <T> component", "Place translatable content in the default slot");
448
489
  const serialized = serializeChildren(slots.defaultSlot.children, { value: 0 }, slots.defaultSlot.shadowed, bindings, expressionPlugins, context);
449
490
  if (context.errors.length !== errorCount) return;
450
491
  context.results.push({
451
492
  dataFormat: "JSX",
452
- source: collapseChildren(serialized),
453
- metadata: createInlineMetadata(context, element.loc, translationContext)
493
+ source: serialized.length > 0 ? collapseChildren(serialized) : void 0,
494
+ metadata: createInlineMetadata(context, element.loc, translationMetadata)
454
495
  });
455
496
  }
456
- function readTContext(element, shadowed, bindings, expressionPlugins, context) {
457
- let translationContext;
458
- let hasContext = false;
497
+ function readTMetadata(element, shadowed, bindings, expressionPlugins, context) {
498
+ const metadata = {};
499
+ const seen = /* @__PURE__ */ new Set();
500
+ const staticVueIsSelector = readStaticVueIsSelector(element);
459
501
  for (const property of element.props) {
460
502
  if (isDynamicComponentSelector(element, property)) continue;
503
+ if (staticVueIsSelector?.property === property) continue;
461
504
  if (property.type === NodeTypes.ATTRIBUTE) {
462
505
  if (RESERVED_T_PROPS.has(property.name)) continue;
463
- if (property.name !== "context" && property.name !== "$context") {
464
- addVueError(context, property.loc, `Found unsupported prop "${property.name}" on a gt-vue <T> component`, "gt-vue <T> currently supports only context");
506
+ const key = normalizeTMetadataKey(property.name, true);
507
+ if (!key) {
508
+ addVueError(context, property.loc, `Found unsupported prop "${property.name}" on a gt-vue <T> component`, "Use only static context, id, maxChars, or requiresReview metadata");
465
509
  continue;
466
510
  }
467
- if (hasContext) {
468
- addDuplicateContextError(property.loc, context);
469
- continue;
470
- }
471
- hasContext = true;
472
- translationContext = property.value?.content ?? "";
511
+ if (!registerTMetadataKey(key, seen, property.loc, context)) continue;
512
+ const value = key === "requiresReview" && property.value === void 0 ? true : property.value?.content ?? "";
513
+ if (key === "maxChars" || !applyStaticTMetadataValue(metadata, key, value)) addInvalidTMetadataValueError(key, property.loc, context);
473
514
  continue;
474
515
  }
475
516
  if (property.name !== "bind") continue;
476
517
  if (property.modifiers.length > 0) {
477
- const directive = property.rawName ?? "v-bind";
478
- addVueError(context, property.loc, `Found unsupported directive ${directive} on a gt-vue <T> component`, "Pass context without a v-bind modifier");
518
+ const directive = readDirectiveSourceName(property);
519
+ addVueError(context, property.loc, `Found unsupported directive ${directive} on a gt-vue <T> component`, "Pass translation metadata without a v-bind modifier");
479
520
  continue;
480
521
  }
481
522
  const key = readDirectiveKey(property);
482
523
  if (key === void 0) {
483
- addVueError(context, property.loc, "Found a dynamic or spread binding on a gt-vue <T> component", "Pass context as a static context or $context prop");
524
+ addVueError(context, property.loc, "Found a dynamic or spread binding on a gt-vue <T> component", "Pass each metadata value through an explicit static prop");
484
525
  continue;
485
526
  }
486
527
  if (RESERVED_T_PROPS.has(key)) continue;
487
- if (key !== "context" && key !== "$context") {
488
- addVueError(context, property.loc, `Found unsupported prop "${key}" on a gt-vue <T> component`, "gt-vue <T> currently supports only context");
528
+ const metadataKey = normalizeTMetadataKey(key, true);
529
+ if (!metadataKey) {
530
+ addVueError(context, property.loc, `Found unsupported prop "${key}" on a gt-vue <T> component`, "Use only static context, id, maxChars, or requiresReview metadata");
489
531
  continue;
490
532
  }
491
- if (hasContext) {
492
- addDuplicateContextError(property.loc, context);
493
- continue;
494
- }
495
- hasContext = true;
533
+ if (!registerTMetadataKey(metadataKey, seen, property.loc, context)) continue;
496
534
  const value = readExpressionPrimitive(property.exp, expressionPlugins, shadowed, bindings);
497
- if (!value.ok || typeof value.value !== "string") {
498
- addVueError(context, property.loc, "Found a dynamic context on a gt-vue <T> component", "Use a string literal or a template literal without expressions");
535
+ if (!value.ok || !applyStaticTMetadataValue(metadata, metadataKey, value.value)) {
536
+ addInvalidTMetadataValueError(metadataKey, property.loc, context);
499
537
  continue;
500
538
  }
501
- translationContext = value.value;
502
539
  }
503
- return translationContext;
540
+ return metadata;
541
+ }
542
+ function registerTMetadataKey(key, seen, location, context) {
543
+ if (!seen.has(key)) {
544
+ seen.add(key);
545
+ return true;
546
+ }
547
+ addVueError(context, location, `Found duplicate ${key} props on a gt-vue <T> component`, `Pass only one ${key} prop or its $-prefixed alias`);
548
+ return false;
504
549
  }
505
- function addDuplicateContextError(location, context) {
506
- addVueError(context, location, "Found duplicate context props on a gt-vue <T> component", "Pass only one context prop");
550
+ function addInvalidTMetadataValueError(key, location, context) {
551
+ const expected = key === "context" || key === "id" ? "a static string" : key === "maxChars" ? "a static integer through v-bind" : "a static boolean or a bare requiresReview prop";
552
+ addVueError(context, location, `Found an invalid or dynamic ${key} on a gt-vue <T> component`, `Use ${expected}`);
507
553
  }
508
554
  function serializeChildren(children, counter, shadowed, bindings, expressionPlugins, context) {
509
555
  validateCommentWhitespaceParity(children, context);
510
556
  const result = [];
557
+ let mergeWithPrevious = true;
511
558
  for (const child of children) {
559
+ if (child.type === NodeTypes.COMMENT) {
560
+ mergeWithPrevious = false;
561
+ continue;
562
+ }
512
563
  const values = serializeChild(child, counter, shadowed, bindings, expressionPlugins, context);
513
- for (const value of values) appendSerializedChild(result, value);
564
+ for (const value of values) {
565
+ appendSerializedChild(result, value, mergeWithPrevious);
566
+ mergeWithPrevious = true;
567
+ }
514
568
  }
515
569
  return result;
516
570
  }
@@ -551,22 +605,47 @@ function serializeChild(child, counter, shadowed, bindings, expressionPlugins, c
551
605
  addVueError(context, child.loc, "Found unsupported template syntax inside a gt-vue <T> component", "Use static template content and gt-vue rich translation components");
552
606
  return [];
553
607
  }
554
- if (resolveFragmentComponent(child, bindings, expressionPlugins, shadowed)) return serializeFragmentElement(child, counter, shadowed, bindings, expressionPlugins, context);
608
+ if (resolveFragmentComponent(child, bindings, expressionPlugins, shadowed, context.valuedVIsReplacesElement)) return [serializeFragmentElement(child, counter, shadowed, bindings, expressionPlugins, context)];
555
609
  return [serializeElement(child, counter, shadowed, bindings, expressionPlugins, context)];
556
610
  }
557
- /** Flattens an exact Vue Fragment default slot without consuming an ID. */
611
+ /** Serializes an authored Vue Fragment while leaving compiler wrappers flat. */
558
612
  function serializeFragmentElement(element, counter, shadowed, bindings, expressionPlugins, context) {
613
+ counter.value += 1;
614
+ const id = counter.value;
559
615
  validateRichElement(element, context, true);
560
- const slots = getSlotLayout(element, shadowed, expressionPlugins, context);
561
- if (slots.namedSlots.size > 0) addVueError(context, element.loc, "Found a named slot on Vue Fragment inside a gt-vue <T> component", "Keep Fragment content in its default slot");
562
- return serializeChildren(slots.defaultSlot.children, counter, slots.defaultSlot.shadowed, bindings, expressionPlugins, context);
616
+ const slots = getSlotLayout(element, shadowed, expressionPlugins, context, "opaque");
617
+ const children = serializeChildren(slots.defaultSlot.children, counter, slots.defaultSlot.shadowed, bindings, expressionPlugins, context);
618
+ const data = readContentProps(element, shadowed, bindings, expressionPlugins, context);
619
+ return {
620
+ t: `C${id}`,
621
+ i: id,
622
+ ...Object.keys(data).length > 0 && { d: data },
623
+ ...children.length > 0 && { c: collapseChildren(children) }
624
+ };
563
625
  }
564
626
  function serializeElement(element, counter, shadowed, bindings, expressionPlugins, context) {
565
627
  counter.value += 1;
566
628
  const id = counter.value;
567
- const component = resolveGTComponent(element, bindings, expressionPlugins, shadowed);
568
- const suspense = resolveSuspenseComponent(element, bindings, expressionPlugins, shadowed);
569
- const uncertainComponent = !component && !suspense ? resolveUncertainComponent(element, bindings, expressionPlugins, shadowed) : void 0;
629
+ const component = resolveGTComponent(element, bindings, expressionPlugins, shadowed, context.valuedVIsReplacesElement);
630
+ const suspense = resolveSuspenseComponent(element, bindings, expressionPlugins, shadowed, context.valuedVIsReplacesElement);
631
+ const staticVueIsTarget = readStaticVueIsTarget(element);
632
+ const directTagBindings = staticVueIsTarget === void 0 && element.tagType === ElementTypes.COMPONENT ? getNormalizedDirectTemplateBindings(element.tag, bindings.directBindings) : [];
633
+ const hasShapeConflictingDirectTag = directTagBindings.length > 1 || directTagBindings.some((localName) => bindings.staticValues.has(localName) || bindings.possibleStaticStrings.has(localName));
634
+ if (!component && !suspense && hasShapeConflictingDirectTag) {
635
+ addVueError(context, element.loc, `Found unsupported direct binding for component tag <${element.tag}> inside a gt-vue <T> component`, "Use a literal native tag, a statically known GT or Vue component, or move the binding outside <T>");
636
+ return {
637
+ t: element.tag,
638
+ i: id
639
+ };
640
+ }
641
+ if (staticVueIsTarget !== void 0 && !component && !suspense) {
642
+ addVueError(context, element.loc, `Found unsupported is="vue:${staticVueIsTarget}" content inside a gt-vue <T> component`, "Use a direct literal or component tag, or move the selector outside <T>");
643
+ return {
644
+ t: staticVueIsTarget,
645
+ i: id
646
+ };
647
+ }
648
+ const uncertainComponent = !component && !suspense ? resolveUncertainComponent(element, bindings, expressionPlugins, shadowed, context.valuedVIsReplacesElement) : void 0;
570
649
  if (uncertainComponent) addVueError(context, element.loc, `Could not statically resolve component alias "${uncertainComponent}" inside a gt-vue <T> component`, "Use a direct component import or an immutable alias that does not escape static analysis");
571
650
  validateRichElement(element, context, Boolean(component || suspense || uncertainComponent));
572
651
  const originalName = component?.originalName;
@@ -593,12 +672,7 @@ function serializeElement(element, counter, shadowed, bindings, expressionPlugin
593
672
  };
594
673
  }
595
674
  const data = readContentProps(element, shadowed, bindings, expressionPlugins, context);
596
- if (isOpaqueComponent(element, component, suspense)) return {
597
- t: element.tag,
598
- i: id,
599
- ...Object.keys(data).length > 0 && { d: data }
600
- };
601
- const slotLayout = element.tagType === ElementTypes.COMPONENT ? getSlotLayout(element, shadowed, expressionPlugins, context) : {
675
+ const slotLayout = element.tagType === ElementTypes.COMPONENT ? getSlotLayout(element, shadowed, expressionPlugins, context, originalName === "Branch" || originalName === "Plural" ? "serialize" : "opaque") : {
602
676
  defaultSlot: {
603
677
  children: element.children,
604
678
  rootCount: countVueSlotRoots(element.children),
@@ -608,7 +682,6 @@ function serializeElement(element, counter, shadowed, bindings, expressionPlugin
608
682
  };
609
683
  const children = serializeChildren(slotLayout.defaultSlot.children, counter, slotLayout.defaultSlot.shadowed, bindings, expressionPlugins, context);
610
684
  if (suspense && slotLayout.defaultSlot.rootCount > 1) addVueError(context, element.loc, "Found more than one default root inside Vue <Suspense> within a gt-vue <T> component", "Wrap the Suspense default content in a single element or move <T> inside <Suspense>");
611
- if ([...slotLayout.namedSlots].filter(([name]) => !suspense || name !== "fallback").length > 0 && originalName !== "Branch" && originalName !== "Plural") addVueError(context, element.loc, `Found named slots on <${originalName ?? element.tag}> inside a gt-vue <T> component`, "Move named-slot content outside <T> or use only the component default slot");
612
685
  if (originalName === "Branch" || originalName === "Plural") {
613
686
  const branches = readBranches(element, slotLayout, id, originalName, shadowed, bindings, expressionPlugins, context);
614
687
  if (Object.keys(branches).length > 0) {
@@ -617,54 +690,57 @@ function serializeElement(element, counter, shadowed, bindings, expressionPlugin
617
690
  }
618
691
  }
619
692
  return {
620
- t: originalName ?? suspense?.originalName ?? element.tag,
693
+ t: originalName ?? suspense?.originalName ?? readStaticVueIsTarget(element) ?? element.tag,
621
694
  i: id,
622
695
  ...Object.keys(data).length > 0 && { d: data },
623
696
  ...children.length > 0 && { c: collapseChildren(children) }
624
697
  };
625
698
  }
626
- /** Returns true when the runtime preserves a component's slots as opaque. */
627
- function isOpaqueComponent(element, component, vueBuiltin) {
628
- return element.tagType === ElementTypes.COMPONENT && !component && !vueBuiltin;
629
- }
630
699
  /** Resolves literal Suspense and statically proven aliases of Vue's builtin. */
631
- function resolveSuspenseComponent(element, bindings, expressionPlugins, shadowed) {
632
- if (element.tagType === ElementTypes.COMPONENT && element.tag.toLowerCase() === "suspense") return {
700
+ function resolveSuspenseComponent(element, bindings, expressionPlugins, shadowed, valuedVIsReplacesElement) {
701
+ const dynamicSelector = readDynamicComponentSelector(element, expressionPlugins, bindings, shadowed, valuedVIsReplacesElement);
702
+ const staticVueIsTarget = dynamicSelector ? void 0 : readStaticVueIsTarget(element);
703
+ if (staticVueIsTarget && isLiteralSuspenseName(staticVueIsTarget)) return {
704
+ localName: staticVueIsTarget,
705
+ originalName: "Suspense"
706
+ };
707
+ if (element.tagType === ElementTypes.COMPONENT && isLiteralSuspenseName(element.tag) && staticVueIsTarget === void 0 && !dynamicSelector) return {
633
708
  localName: element.tag,
634
709
  originalName: "Suspense"
635
710
  };
636
- const builtin = resolveVueBuiltinComponent(element, bindings, expressionPlugins, shadowed);
711
+ const builtin = resolveVueBuiltinComponent(element, bindings, expressionPlugins, shadowed, valuedVIsReplacesElement);
637
712
  return builtin?.originalName === "Suspense" ? {
638
713
  localName: builtin.localName,
639
714
  originalName: "Suspense"
640
715
  } : void 0;
641
716
  }
642
717
  /** Resolves an imported Vue Fragment alias without matching an ordinary tag. */
643
- function resolveFragmentComponent(element, bindings, expressionPlugins, shadowed) {
644
- const builtin = resolveVueBuiltinComponent(element, bindings, expressionPlugins, shadowed);
718
+ function resolveFragmentComponent(element, bindings, expressionPlugins, shadowed, valuedVIsReplacesElement) {
719
+ const builtin = resolveVueBuiltinComponent(element, bindings, expressionPlugins, shadowed, valuedVIsReplacesElement);
645
720
  return builtin?.originalName === "Fragment" ? {
646
721
  localName: builtin.localName,
647
722
  originalName: "Fragment"
648
723
  } : void 0;
649
724
  }
650
725
  /** Resolves a statically proven Vue builtin through direct or registered use. */
651
- function resolveVueBuiltinComponent(element, bindings, expressionPlugins, shadowed) {
652
- return resolveComponentBinding(element, bindings, bindings.vueBuiltins, bindings.registeredVueBuiltins, bindings.directBindings, expressionPlugins, shadowed);
726
+ function resolveVueBuiltinComponent(element, bindings, expressionPlugins, shadowed, valuedVIsReplacesElement) {
727
+ return resolveComponentBinding(element, bindings, bindings.vueBuiltins, bindings.registeredVueBuiltins, bindings.directBindings, expressionPlugins, shadowed, valuedVIsReplacesElement);
653
728
  }
654
729
  /** Resolves a component-shaped binding whose exact identity is uncertain. */
655
- function resolveUncertainComponent(element, bindings, expressionPlugins, shadowed) {
656
- return resolveUncertainComponentBinding(element, bindings, bindings.uncertainComponents, bindings.uncertainRegisteredComponents, bindings.componentFactories, expressionPlugins, shadowed);
730
+ function resolveUncertainComponent(element, bindings, expressionPlugins, shadowed, valuedVIsReplacesElement) {
731
+ return resolveUncertainComponentBinding(element, bindings, bindings.uncertainComponents, bindings.uncertainRegisteredComponents, bindings.componentFactories, expressionPlugins, shadowed, valuedVIsReplacesElement);
657
732
  }
658
733
  /** Resolves uncertainty while preserving direct versus registered precedence. */
659
- function resolveUncertainComponentBinding(element, bindings, uncertainDirectBindings, uncertainRegisteredBindings, uncertainFactories, expressionPlugins, shadowed) {
734
+ function resolveUncertainComponentBinding(element, bindings, uncertainDirectBindings, uncertainRegisteredBindings, uncertainFactories, expressionPlugins, shadowed, valuedVIsReplacesElement) {
660
735
  if (element.tagType !== ElementTypes.COMPONENT) return void 0;
661
- const selector = readDynamicComponentSelector(element, expressionPlugins, bindings, shadowed);
736
+ const selector = readDynamicComponentSelector(element, expressionPlugins, bindings, shadowed, valuedVIsReplacesElement);
662
737
  if (selector) {
663
738
  if ([...selector.componentFactories].some((name) => uncertainFactories.has(name)) || [...selector.gtComponentFactories].some((name) => uncertainFactories.has(name))) return selector.displayName;
664
739
  for (const candidate of selector.candidates) for (const localName of normalizeTemplateBindingNames(candidate.name)) if (candidate.kind === "string" ? uncertainRegisteredBindings.has(localName) : uncertainDirectBindings.has(localName)) return localName;
665
740
  return;
666
741
  }
667
- for (const localName of normalizeTemplateBindingNames(element.tag)) {
742
+ const effectiveTag = readStaticVueIsTarget(element) ?? element.tag;
743
+ for (const localName of normalizeTemplateBindingNames(effectiveTag)) {
668
744
  const direct = uncertainDirectBindings.has(localName);
669
745
  const registered = uncertainRegisteredBindings.has(localName);
670
746
  if (isDirectTemplateBinding(localName, bindings.directBindings)) {
@@ -673,28 +749,19 @@ function resolveUncertainComponentBinding(element, bindings, uncertainDirectBind
673
749
  }
674
750
  }
675
751
  /** Detects an unresolved dynamic selector whose possible result includes T. */
676
- function resolvePossibleDynamicT(element, bindings, expressionPlugins, shadowed) {
677
- const selector = readDynamicComponentSelector(element, expressionPlugins, bindings, shadowed);
752
+ function resolvePossibleDynamicT(element, bindings, expressionPlugins, shadowed, valuedVIsReplacesElement) {
753
+ const selector = readDynamicComponentSelector(element, expressionPlugins, bindings, shadowed, valuedVIsReplacesElement);
678
754
  if (!selector) return void 0;
679
755
  if (selector.possibleGT || selector.gtComponentFactories.size > 0) return selector.displayName;
680
756
  for (const candidate of selector.candidates) for (const localName of normalizeTemplateBindingNames(candidate.name)) if ((candidate.kind === "string" ? bindings.registeredComponents.get(localName) : bindings.components.get(localName)) === "T") return selector.displayName;
681
757
  }
682
- /** Identifies a statically named Suspense fallback slot during tree walking. */
683
- function isSuspenseFallbackTemplate(child) {
684
- return child.type === NodeTypes.ELEMENT && child.tag === "template" && hasStaticSlotName(child, "fallback");
685
- }
686
- /** Matches one statically named slot directive without emitting diagnostics. */
687
- function hasStaticSlotName(element, name) {
688
- const directive = element.props.find((property) => property.type === NodeTypes.DIRECTIVE && property.name === "slot");
689
- return directive?.arg?.type === NodeTypes.SIMPLE_EXPRESSION && directive.arg.isStatic && directive.arg.content === name;
690
- }
691
758
  function validateRichElement(element, context, resolvedDynamicComponent) {
692
759
  if (element.tagType === ElementTypes.SLOT || element.tag === "slot") addVueError(context, element.loc, "Found a <slot> inside a gt-vue <T> component", "Move runtime slot content outside <T> or wrap a stable value in <Var>");
693
760
  if (element.tag === "template" && !element.props.some((property) => property.type === NodeTypes.DIRECTIVE && property.name === "slot")) addVueError(context, element.loc, "Found a bare <template> inside a gt-vue <T> component", "Use an ordinary element or a statically named slot template");
694
- if (element.tag.toLowerCase() === "component" && !resolvedDynamicComponent) addVueError(context, element.loc, "Found a dynamic <component> inside a gt-vue <T> component", "Use a statically named element or component");
761
+ if ((element.tag.toLowerCase() === "component" || element.props.some((property) => isEffectiveValuedVIsSelector(element, property, context.valuedVIsReplacesElement))) && !resolvedDynamicComponent) addVueError(context, element.loc, "Found a dynamic <component> inside a gt-vue <T> component", "Use a statically named element or component");
695
762
  for (const property of element.props) {
696
763
  if (property.type !== NodeTypes.DIRECTIVE) continue;
697
- if (SOURCE_SHAPING_DIRECTIVES.has(property.name)) addVueError(context, property.loc, `Found source-shaping directive ${property.rawName ?? `v-${property.name}`} inside a gt-vue <T> component`, "Move conditional or repeated content outside <T>, or use Branch/Plural");
764
+ if (SOURCE_SHAPING_DIRECTIVES.has(property.name)) addVueError(context, property.loc, `Found source-shaping directive ${readDirectiveSourceName(property)} inside a gt-vue <T> component`, "Move conditional or repeated content outside <T>, or use Branch/Plural");
698
765
  else if (property.name === "bind" && !readDirectiveKey(property)) addVueError(context, property.loc, "Found a dynamic or spread v-bind inside a gt-vue <T> component", "Bind props by a static name so their effect on the translation source is known");
699
766
  }
700
767
  }
@@ -704,6 +771,8 @@ function validateVariableComponentShape(element, component, context) {
704
771
  context.validatedVariableComponents.add(element);
705
772
  if (component !== "Var") {
706
773
  if (!hasTemplateProp(element, "value")) addVueError(context, element.loc, `Found a gt-vue <${component}> component without a value prop`, `Pass the runtime value through <${component} :value="value" />`);
774
+ const nameProp = findTemplateProp(element, "name");
775
+ if (nameProp) addVueError(context, nameProp.loc, `Found unsupported name prop on a gt-vue <${component}> component`, "Remove name; named variables are not supported by gt-vue yet");
707
776
  if (hasMeaningfulSlotContent(element.children, context)) addVueError(context, element.loc, `Found children on a gt-vue <${component}> component`, `Use only the required value prop: <${component} :value="value" />`);
708
777
  return;
709
778
  }
@@ -715,7 +784,10 @@ function validateVariableComponentShape(element, component, context) {
715
784
  }
716
785
  /** Returns whether a statically named template prop is explicitly present. */
717
786
  function hasTemplateProp(element, expected) {
718
- return element.props.some((property) => {
787
+ return findTemplateProp(element, expected) !== void 0;
788
+ }
789
+ function findTemplateProp(element, expected) {
790
+ return element.props.find((property) => {
719
791
  if (property.type === NodeTypes.ATTRIBUTE) return property.name === expected;
720
792
  return property.name === "bind" && readDirectiveKey(property) === expected;
721
793
  });
@@ -741,13 +813,13 @@ function readBranches(element, slots, branchElementId, component, shadowed, bind
741
813
  branches[name] = collapseChildren(serializeChildren(slot.children, { value: branchElementId }, slot.shadowed, bindings, expressionPlugins, context));
742
814
  }
743
815
  for (const property of element.props) {
744
- if (isDynamicComponentSelector(element, property)) continue;
816
+ if (isCompilerConsumedComponentSelectorProperty(element, property)) continue;
745
817
  if (property.type === NodeTypes.DIRECTIVE && property.name === "slot") continue;
746
818
  const key = property.type === NodeTypes.ATTRIBUTE ? property.name : readDirectiveKey(property);
747
819
  if (property.type === NodeTypes.DIRECTIVE && property.name === "on") continue;
748
820
  if (key && (!isBranchAttributeName(key) || Object.prototype.hasOwnProperty.call(branches, key) || component === "Plural" && !isAcceptedPluralForm(key))) continue;
749
821
  if (property.type === NodeTypes.DIRECTIVE && (property.name !== "bind" || property.modifiers.length > 0)) {
750
- const directive = property.rawName ?? `v-${property.name}`;
822
+ const directive = readDirectiveSourceName(property);
751
823
  addVueError(context, property.loc, `Found unsupported directive ${directive} on a gt-vue <${component}> component`, `Move ${directive} to an element outside <${component}>`);
752
824
  continue;
753
825
  }
@@ -758,12 +830,21 @@ function readBranches(element, slots, branchElementId, component, shadowed, bind
758
830
  addVueError(context, property.loc, `Found dynamic branch prop "${key}" on a gt-vue <${component}> component`, "Use a static branch prop or a named slot");
759
831
  continue;
760
832
  }
833
+ if (value.value === void 0) continue;
761
834
  branches[key] = branchPropToChildren(value.value);
762
835
  }
763
836
  return branches;
764
837
  }
838
+ /**
839
+ * Converts a direct branch prop into its persisted GT wire value.
840
+ *
841
+ * React preserves boolean and null branch props as typed wire values, while
842
+ * the rich renderer handles their visible output separately. `JsxChildren`
843
+ * predates those wire literals, so the cast is confined to this exact
844
+ * compatibility boundary.
845
+ */
765
846
  function branchPropToChildren(value) {
766
- if (value == null || typeof value === "boolean") return [];
847
+ if (value === null || typeof value === "boolean") return value;
767
848
  return String(value);
768
849
  }
769
850
  /**
@@ -789,7 +870,7 @@ function isStaticallyNonBranchValue(property, expressionPlugins, shadowed, bindi
789
870
  if (babel.isIdentifier(node, { name: "undefined" })) return !shadowed.has(node.name) && !bindings.staticValues.has(node.name);
790
871
  return babel.isArrayExpression(node) || babel.isArrowFunctionExpression(node) || babel.isClassExpression(node) || babel.isFunctionExpression(node) || babel.isNewExpression(node) || babel.isObjectExpression(node) || babel.isRegExpLiteral(node) || babel.isUnaryExpression(node) && node.operator === "void";
791
872
  }
792
- function getSlotLayout(element, shadowed, expressionPlugins, context) {
873
+ function getSlotLayout(element, shadowed, expressionPlugins, context, namedSlotPolicy) {
793
874
  const namedSlots = /* @__PURE__ */ new Map();
794
875
  let defaultSlot = {
795
876
  children: [],
@@ -801,8 +882,8 @@ function getSlotLayout(element, shadowed, expressionPlugins, context) {
801
882
  let reportedDefaultConflict = false;
802
883
  const componentSlot = element.props.find((property) => property.type === NodeTypes.DIRECTIVE && property.name === "slot");
803
884
  if (componentSlot) {
804
- if (componentSlot.exp) addScopedSlotError(componentSlot.loc, context);
805
885
  const slotName = readSlotName(componentSlot, context);
886
+ if (componentSlot.exp && (slotName === "default" || namedSlotPolicy !== "opaque")) addScopedSlotError(componentSlot.loc, context);
806
887
  const slotShadowed = unionSets(shadowed, collectExpressionBindings(componentSlot.exp, expressionPlugins));
807
888
  if (slotName === "default") defaultSlot = {
808
889
  children: element.children,
@@ -827,9 +908,9 @@ function getSlotLayout(element, shadowed, expressionPlugins, context) {
827
908
  continue;
828
909
  }
829
910
  hasTemplateSlots = true;
830
- for (const property of child.props) if (property.type === NodeTypes.DIRECTIVE && property.name !== "slot") addVueError(context, property.loc, "Found a conditional or dynamic named slot inside a gt-vue <T> component", "Use an unconditional, statically named slot");
831
911
  const slotName = readSlotName(slotDirective, context);
832
- if (slotDirective.exp) addScopedSlotError(slotDirective.loc, context);
912
+ for (const property of child.props) if (property.type === NodeTypes.DIRECTIVE && property.name !== "slot" && (slotName === "default" || namedSlotPolicy !== "opaque")) addVueError(context, property.loc, "Found a conditional or dynamic named slot inside a gt-vue <T> component", "Use an unconditional, statically named slot");
913
+ if (slotDirective.exp && (slotName === "default" || namedSlotPolicy !== "opaque")) addScopedSlotError(slotDirective.loc, context);
833
914
  const slotShadowed = unionSets(shadowed, collectExpressionBindings(slotDirective.exp, expressionPlugins));
834
915
  if (slotName === "default") {
835
916
  if (hasMeaningfulSlotContent(defaultChildren, context) || hasExplicitDefaultSlot) {
@@ -934,17 +1015,13 @@ function readPropertyValue(property, expressionPlugins, shadowed, bindings) {
934
1015
  }
935
1016
  function readExpressionPrimitive(expression, expressionPlugins, shadowed, bindings) {
936
1017
  return readStaticPrimitive(expression ? getExpressionNode(expression, expressionPlugins) : void 0, (identifier) => {
937
- if (shadowed.has(identifier.name)) return { ok: false };
938
- const value = bindings.staticValues.get(identifier.name);
939
- return bindings.staticValues.has(identifier.name) ? {
940
- ok: true,
941
- value
942
- } : { ok: false };
1018
+ return readTemplateIdentifierPrimitive(identifier, shadowed, bindings);
943
1019
  });
944
1020
  }
945
1021
  function getExpressionNode(expression, expressionPlugins) {
946
1022
  if (expression.type !== NodeTypes.SIMPLE_EXPRESSION) return void 0;
947
- if (expression.ast && typeof expression.ast === "object") return expression.ast;
1023
+ const compilerAst = expression.ast;
1024
+ if (compilerAst && typeof compilerAst === "object") return compilerAst;
948
1025
  try {
949
1026
  return parseExpression(expression.content, { plugins: expressionPlugins });
950
1027
  } catch {
@@ -1001,9 +1078,9 @@ function readForParseResult(property) {
1001
1078
  }
1002
1079
  /** Creates one parser-neutral v-for field from Vue's original expression. */
1003
1080
  function cloneForExpression(expression, content) {
1081
+ const { ast: _ast, ...base } = expression;
1004
1082
  return {
1005
- ...expression,
1006
- ast: void 0,
1083
+ ...base,
1007
1084
  content,
1008
1085
  loc: {
1009
1086
  ...expression.loc,
@@ -1031,13 +1108,13 @@ function collectPatternNames(pattern, result) {
1031
1108
  else collectPatternNames(property.value, result);
1032
1109
  else if (pattern.type === "TSParameterProperty") collectPatternNames(pattern.parameter, result);
1033
1110
  }
1034
- function resolveGTComponent(element, bindings, expressionPlugins, shadowed) {
1035
- return resolveComponentBinding(element, bindings, bindings.components, bindings.registeredComponents, bindings.directBindings, expressionPlugins, shadowed);
1111
+ function resolveGTComponent(element, bindings, expressionPlugins, shadowed, valuedVIsReplacesElement) {
1112
+ return resolveComponentBinding(element, bindings, bindings.components, bindings.registeredComponents, bindings.directBindings, expressionPlugins, shadowed, valuedVIsReplacesElement);
1036
1113
  }
1037
1114
  /** Resolves a template tag or static dynamic selector through known bindings. */
1038
- function resolveComponentBinding(element, bindings, directBindings, registeredBindings, directTemplateBindings, expressionPlugins, shadowed) {
1115
+ function resolveComponentBinding(element, bindings, directBindings, registeredBindings, directTemplateBindings, expressionPlugins, shadowed, valuedVIsReplacesElement) {
1039
1116
  if (element.tagType !== ElementTypes.COMPONENT) return void 0;
1040
- const selector = readDynamicComponentSelector(element, expressionPlugins, bindings, shadowed);
1117
+ const selector = readDynamicComponentSelector(element, expressionPlugins, bindings, shadowed, valuedVIsReplacesElement);
1041
1118
  if (selector) {
1042
1119
  if (selector.unknown || selector.possibleGT || selector.componentFactories.size > 0 || selector.gtComponentFactories.size > 0 || selector.candidates.length === 0) return;
1043
1120
  const resolved = selector.candidates.map((candidate) => {
@@ -1052,8 +1129,18 @@ function resolveComponentBinding(element, bindings, directBindings, registeredBi
1052
1129
  const first = resolved[0];
1053
1130
  return first && resolved.every((candidate) => candidate?.originalName === first.originalName) ? first : void 0;
1054
1131
  }
1055
- for (const localName of normalizeTemplateBindingNames(element.tag)) {
1056
- const originalName = isDirectTemplateBinding(localName, directTemplateBindings) ? directBindings.get(localName) : registeredBindings.get(localName);
1132
+ const effectiveTag = readStaticVueIsTarget(element) ?? element.tag;
1133
+ const normalizedNames = [...normalizeTemplateBindingNames(effectiveTag)];
1134
+ const directNames = getNormalizedDirectTemplateBindings(effectiveTag, directTemplateBindings);
1135
+ if (directNames.length > 0) {
1136
+ const originalName = directBindings.get(directNames[0]);
1137
+ return originalName && directNames.every((localName) => directBindings.get(localName) === originalName) ? {
1138
+ localName: directNames[0],
1139
+ originalName
1140
+ } : void 0;
1141
+ }
1142
+ for (const localName of normalizedNames) {
1143
+ const originalName = registeredBindings.get(localName);
1057
1144
  if (originalName) return {
1058
1145
  localName,
1059
1146
  originalName
@@ -1069,14 +1156,17 @@ function normalizeTemplateBindingNames(sourceName) {
1069
1156
  pascalized
1070
1157
  ]);
1071
1158
  }
1159
+ /** Returns every direct binding in one Vue-normalized template-name family. */
1160
+ function getNormalizedDirectTemplateBindings(sourceName, directBindings) {
1161
+ return [...normalizeTemplateBindingNames(sourceName)].filter((localName) => isDirectTemplateBinding(localName, directBindings));
1162
+ }
1072
1163
  /** Returns whether a program binding shadows an Options API registration. */
1073
1164
  function isDirectTemplateBinding(localName, directBindings) {
1074
1165
  return directBindings.has(localName) || directBindings.has(localName.split(".", 1)[0] ?? localName);
1075
1166
  }
1076
1167
  /** Resolves a statically knowable Vue dynamic-component selector. */
1077
- function readDynamicComponentSelector(element, expressionPlugins, bindings, shadowed) {
1078
- if (element.tag.toLowerCase() !== "component") return void 0;
1079
- const property = element.props.find((candidate) => isDynamicComponentSelector(element, candidate));
1168
+ function readDynamicComponentSelector(element, expressionPlugins, bindings, shadowed, valuedVIsReplacesElement) {
1169
+ const property = element.props.find((candidate) => isDynamicComponentSelector(element, candidate)) ?? element.props.find((candidate) => isEffectiveValuedVIsSelector(element, candidate, valuedVIsReplacesElement));
1080
1170
  if (!property) return void 0;
1081
1171
  if (property.type === NodeTypes.ATTRIBUTE) return property.value ? {
1082
1172
  candidates: [{
@@ -1091,10 +1181,14 @@ function readDynamicComponentSelector(element, expressionPlugins, bindings, shad
1091
1181
  unknown: false
1092
1182
  } : void 0;
1093
1183
  const node = property.exp ? getExpressionNode(property.exp, expressionPlugins) : void 0;
1094
- if (!node) return void 0;
1184
+ const displayName = property.exp?.type === NodeTypes.SIMPLE_EXPRESSION && property.exp.content ? property.exp.content : "dynamic component";
1185
+ if (!node) return {
1186
+ ...emptyDynamicSelectorAnalysis(true),
1187
+ displayName
1188
+ };
1095
1189
  return {
1096
1190
  ...collectDynamicSelectorCandidates(node, bindings, shadowed, /* @__PURE__ */ new Set()),
1097
- displayName: property.exp?.type === NodeTypes.SIMPLE_EXPRESSION ? property.exp.content : "dynamic component"
1191
+ displayName
1098
1192
  };
1099
1193
  }
1100
1194
  /** Collects every statically visible result of a dynamic selector expression. */
@@ -1452,7 +1546,7 @@ function collectTemplateTransformCandidates(call, bindings, shadowed, seen) {
1452
1546
  if (method === "flatMap") return flattenTemplateTransformAnalysis(mapValues(source, readArgument(0), true, readArgument(1)), 1, bindings, shadowed, seen);
1453
1547
  if (method === "flat") {
1454
1548
  const depthNode = readArgument(0);
1455
- const depthValue = depthNode ? readTemplateStaticPrimitive(depthNode, bindings, shadowed, true) : {
1549
+ const depthValue = depthNode ? readTemplateStaticPrimitive(depthNode, bindings, shadowed) : {
1456
1550
  ok: true,
1457
1551
  value: 1
1458
1552
  };
@@ -2141,22 +2235,9 @@ function readStaticTemplateMemberProperty(node, bindings, shadowed) {
2141
2235
  const property = readTemplateStaticPrimitive(node.property, bindings, shadowed);
2142
2236
  return property.ok && (typeof property.value === "string" || typeof property.value === "number") ? String(property.value) : void 0;
2143
2237
  }
2144
- function readTemplateStaticPrimitive(node, bindings, shadowed, allowNumericGlobals = false) {
2238
+ function readTemplateStaticPrimitive(node, bindings, shadowed) {
2145
2239
  return readStaticPrimitive(node, (identifier) => {
2146
- if (shadowed.has(identifier.name)) return { ok: false };
2147
- if (bindings.staticValues.has(identifier.name)) return {
2148
- ok: true,
2149
- value: bindings.staticValues.get(identifier.name)
2150
- };
2151
- if (allowNumericGlobals && !bindings.directBindings.has(identifier.name) && identifier.name === "Infinity") return {
2152
- ok: true,
2153
- value: Number.POSITIVE_INFINITY
2154
- };
2155
- if (allowNumericGlobals && !bindings.directBindings.has(identifier.name) && identifier.name === "NaN") return {
2156
- ok: true,
2157
- value: NaN
2158
- };
2159
- return { ok: false };
2240
+ return readTemplateIdentifierPrimitive(identifier, shadowed, bindings);
2160
2241
  });
2161
2242
  }
2162
2243
  /** Reads a dotted template member chain without evaluating runtime code. */
@@ -2175,15 +2256,56 @@ function isDynamicComponentSelector(element, property) {
2175
2256
  if (property.type === NodeTypes.ATTRIBUTE) return property.name === "is";
2176
2257
  return property.name === "bind" && readDirectiveKey(property) === "is";
2177
2258
  }
2259
+ /** Finds the first ordinary `is` property using Vue's own source-order rule. */
2260
+ function findFirstComponentIsProperty(element) {
2261
+ return element.props.find((property) => {
2262
+ if (property.type === NodeTypes.ATTRIBUTE) return property.name === "is";
2263
+ return property.name === "bind" && readDirectiveKey(property) === "is";
2264
+ });
2265
+ }
2266
+ /** Reads Vue's effective static `is="vue:..."` component replacement. */
2267
+ function readStaticVueIsSelector(element) {
2268
+ if (element.tagType !== ElementTypes.COMPONENT || element.tag.toLowerCase() === "component") return;
2269
+ const property = findFirstComponentIsProperty(element);
2270
+ if (property?.type !== NodeTypes.ATTRIBUTE || !property.value?.content.startsWith("vue:")) return;
2271
+ return {
2272
+ property,
2273
+ target: property.value.content.slice(4)
2274
+ };
2275
+ }
2276
+ /** Reads only the target name of Vue's effective static replacement. */
2277
+ function readStaticVueIsTarget(element) {
2278
+ return readStaticVueIsSelector(element)?.target;
2279
+ }
2280
+ /** Matches the only two spellings Vue treats as its built-in Suspense. */
2281
+ function isLiteralSuspenseName(name) {
2282
+ return name === "Suspense" || name === "suspense";
2283
+ }
2284
+ /** Matches the selector directive actually consumed by Vue 3.3's transform. */
2285
+ function isEffectiveValuedVIsSelector(element, property, valuedVIsReplacesElement) {
2286
+ return valuedVIsReplacesElement && element.tagType === ElementTypes.COMPONENT && element.tag.toLowerCase() !== "component" && property.type === NodeTypes.DIRECTIVE && property.name === "is" && property.exp !== void 0;
2287
+ }
2288
+ /** Mirrors selector properties omitted from Vue's generated VNode props. */
2289
+ function isCompilerConsumedComponentSelectorProperty(element, property) {
2290
+ if (property.type === NodeTypes.ATTRIBUTE) return property.name === "is" && (element.tag.toLowerCase() === "component" || Boolean(property.value?.content.startsWith("vue:")));
2291
+ if (property.name === "is") return true;
2292
+ return property.name === "bind" && element.tag.toLowerCase() === "component" && readDirectiveKey(property) === "is";
2293
+ }
2294
+ /** Reads directive source spelling absent from Vue 3.3's public AST types. */
2295
+ function readDirectiveSourceName(directive) {
2296
+ const rawName = "rawName" in directive && typeof directive.rawName === "string" ? directive.rawName : void 0;
2297
+ if (rawName) return rawName;
2298
+ return directive.loc.source.split("=", 1)[0]?.trim() || `v-${directive.name}`;
2299
+ }
2178
2300
  function readDirectiveKey(directive) {
2179
2301
  return directive.arg?.type === NodeTypes.SIMPLE_EXPRESSION && directive.arg.isStatic ? directive.arg.content : void 0;
2180
2302
  }
2181
2303
  function toDisplayString(value) {
2182
2304
  return value == null ? "" : String(value);
2183
2305
  }
2184
- function appendSerializedChild(result, value) {
2306
+ function appendSerializedChild(result, value, mergeWithPrevious) {
2185
2307
  const previous = result[result.length - 1];
2186
- if (typeof previous === "string" && typeof value === "string") result[result.length - 1] = previous + value;
2308
+ if (mergeWithPrevious && typeof previous === "string" && typeof value === "string") result[result.length - 1] = previous + value;
2187
2309
  else result.push(value);
2188
2310
  }
2189
2311
  function collapseChildren(children) {