@generaltranslation/vue-extractor 0.0.0 → 0.1.0-iris.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +18 -102
- package/dist/detect.d.ts +8 -0
- package/dist/detect.js +2 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/inspect.d.ts +9 -0
- package/dist/inspect.js +2 -0
- package/dist/integration.d.ts +69 -0
- package/dist/integration.js +162 -0
- package/dist/integration.js.map +1 -0
- package/dist/internal/config/configFiles.d.ts +6 -0
- package/dist/internal/config/configFiles.js +34 -0
- package/dist/internal/config/configFiles.js.map +1 -0
- package/dist/internal/config/resolveVueCompilerOptions.d.ts +6 -0
- package/dist/internal/config/resolveVueCompilerOptions.js +74 -56
- package/dist/internal/config/resolveVueCompilerOptions.js.map +1 -1
- package/dist/internal/extractFromVueSource.d.ts +4 -3
- package/dist/internal/extractFromVueSource.js +255 -12
- package/dist/internal/extractFromVueSource.js.map +1 -1
- package/dist/internal/project/consumerUsage.d.ts +34 -0
- package/dist/internal/project/consumerUsage.js +1220 -0
- package/dist/internal/project/consumerUsage.js.map +1 -0
- package/dist/internal/project/detectVueProject.d.ts +13 -0
- package/dist/internal/project/detectVueProject.js +65 -0
- package/dist/internal/project/detectVueProject.js.map +1 -0
- package/dist/internal/project/extractFromVueProject.d.ts +10 -0
- package/dist/internal/project/extractFromVueProject.js +579 -0
- package/dist/internal/project/extractFromVueProject.js.map +1 -0
- package/dist/internal/project/inspectVueProject.d.ts +48 -0
- package/dist/internal/project/inspectVueProject.js +143 -0
- package/dist/internal/project/inspectVueProject.js.map +1 -0
- package/dist/internal/project/manifest.d.ts +64 -0
- package/dist/internal/project/manifest.js +270 -0
- package/dist/internal/project/manifest.js.map +1 -0
- package/dist/internal/project/mergeVueProjectExtraction.d.ts +18 -0
- package/dist/internal/project/mergeVueProjectExtraction.js +79 -0
- package/dist/internal/project/mergeVueProjectExtraction.js.map +1 -0
- package/dist/internal/project/moduleResolver.d.ts +18 -0
- package/dist/internal/project/moduleResolver.js +325 -0
- package/dist/internal/project/moduleResolver.js.map +1 -0
- package/dist/internal/project/scopes.d.ts +31 -0
- package/dist/internal/project/scopes.js +182 -0
- package/dist/internal/project/scopes.js.map +1 -0
- package/dist/internal/project/sourcePatterns.d.ts +4 -0
- package/dist/internal/project/sourcePatterns.js +32 -0
- package/dist/internal/project/sourcePatterns.js.map +1 -0
- package/dist/internal/project/viteAliases.d.ts +42 -0
- package/dist/internal/project/viteAliases.js +1020 -0
- package/dist/internal/project/viteAliases.js.map +1 -0
- package/dist/internal/project/vueSourceClassification.d.ts +13 -0
- package/dist/internal/project/vueSourceClassification.js +146 -0
- package/dist/internal/project/vueSourceClassification.js.map +1 -0
- package/dist/internal/project/workspaces.d.ts +31 -0
- package/dist/internal/project/workspaces.js +326 -0
- package/dist/internal/project/workspaces.js.map +1 -0
- package/dist/internal/project/wrapperProvenance.d.ts +21 -0
- package/dist/internal/project/wrapperProvenance.js +287 -0
- package/dist/internal/project/wrapperProvenance.js.map +1 -0
- package/dist/internal/script/analyze.js +323 -108
- package/dist/internal/script/analyze.js.map +1 -1
- package/dist/internal/script/jsx.d.ts +7 -4
- package/dist/internal/script/jsx.js +351 -79
- package/dist/internal/script/jsx.js.map +1 -1
- package/dist/internal/script/knownValues.js +4 -0
- package/dist/internal/script/knownValues.js.map +1 -1
- package/dist/internal/script/localModules.d.ts +23 -2
- package/dist/internal/script/localModules.js +535 -20
- package/dist/internal/script/localModules.js.map +1 -1
- package/dist/internal/script/model.d.ts +10 -3
- package/dist/internal/script/parser.js +2 -1
- package/dist/internal/script/parser.js.map +1 -1
- package/dist/internal/script/runtimeModules.d.ts +10 -0
- package/dist/internal/script/runtimeModules.js +27 -0
- package/dist/internal/script/runtimeModules.js.map +1 -0
- package/dist/internal/stringCalls.js +9 -3
- package/dist/internal/stringCalls.js.map +1 -1
- package/dist/internal/template.js +263 -141
- package/dist/internal/template.js.map +1 -1
- package/dist/internal/types.d.ts +5 -2
- package/dist/internal/utils.d.ts +14 -3
- package/dist/internal/utils.js +63 -3
- package/dist/internal/utils.js.map +1 -1
- package/dist/internal/vueCompiler.d.ts +9 -1
- package/dist/internal/vueCompiler.js +37 -14
- package/dist/internal/vueCompiler.js.map +1 -1
- package/dist/project.d.ts +11 -0
- package/dist/project.js +3 -0
- package/dist/types.d.ts +95 -4
- package/package.json +30 -4
|
@@ -1,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
|
|
103
|
-
const
|
|
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"
|
|
111
|
-
if (childInsideTranslation &&
|
|
112
|
-
|
|
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 (
|
|
403
|
-
|
|
404
|
-
return bindings.staticValues.has(identifier.name) ? {
|
|
405
|
-
ok: true,
|
|
406
|
-
value
|
|
407
|
-
} : { ok: false };
|
|
426
|
+
if (scope.getBinding(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
|
|
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,
|
|
493
|
+
source: serialized.length > 0 ? collapseChildren(serialized) : void 0,
|
|
494
|
+
metadata: createInlineMetadata(context, element.loc, translationMetadata)
|
|
454
495
|
});
|
|
455
496
|
}
|
|
456
|
-
function
|
|
457
|
-
|
|
458
|
-
|
|
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
|
-
|
|
464
|
-
|
|
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 (
|
|
468
|
-
|
|
469
|
-
|
|
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
|
|
478
|
-
addVueError(context, property.loc, `Found unsupported directive ${directive} on a gt-vue <T> component`, "Pass
|
|
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
|
|
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
|
-
|
|
488
|
-
|
|
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 (
|
|
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 ||
|
|
498
|
-
|
|
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
|
|
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
|
|
506
|
-
|
|
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)
|
|
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
|
-
/**
|
|
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
|
-
|
|
562
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
1056
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
2238
|
+
function readTemplateStaticPrimitive(node, bindings, shadowed) {
|
|
2145
2239
|
return readStaticPrimitive(node, (identifier) => {
|
|
2146
|
-
|
|
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) {
|