mikuru 1.0.21 → 1.0.22
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/CHANGELOG.md +17 -1
- package/README.md +2 -0
- package/dist/compiler/generateHydration.js +302 -16
- package/dist/compiler/generateHydration.js.map +1 -1
- package/dist/compiler/generateSsr.js +188 -8
- package/dist/compiler/generateSsr.js.map +1 -1
- package/dist/runtime/asyncComponent.d.ts +1 -0
- package/dist/runtime/asyncComponent.js +95 -0
- package/dist/runtime/asyncComponent.js.map +1 -1
- package/dist/server.d.ts +5 -1
- package/dist/server.js +18 -6
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 1.0.22 - 2026-05-13
|
|
4
|
+
|
|
5
|
+
- Generalized component slot hydration to pass default, named, dynamic, and scoped slots through `props.children` and `props.slots`.
|
|
6
|
+
- Improved SSR component slots so dynamic slot names support scoped props and explicit default slot templates are exposed through `props.children`.
|
|
7
|
+
- Added async component hydration delegation so SSR-rendered async children inside `<AsyncBoundary>` can be reused after streaming SSR.
|
|
8
|
+
- Improved async component hydration fallback handling for loader errors, timeouts, and retry recovery.
|
|
9
|
+
- Added Teleport + AsyncBoundary hydration coverage for SSR target reuse, async child hydration, and sibling stability.
|
|
10
|
+
- Added route SSR Teleport collection and RouterView + Teleport hydration coverage.
|
|
11
|
+
- Added Teleport + ErrorBoundary hydration coverage for target-side DOM reuse and cleanup.
|
|
12
|
+
- Added Transition and TransitionGroup async-child hydration coverage for DOM reuse, keyed order, and cleanup.
|
|
13
|
+
- Added nested AsyncBoundary streaming SSR hydration coverage for parent/child async DOM reuse and cleanup.
|
|
14
|
+
- Added nested lazy RouterView SSR hydration coverage with route-level Teleport reuse.
|
|
15
|
+
- Expanded SSR/hydration examples and E2E coverage for lazy route Teleport and nested AsyncBoundary Teleport patterns.
|
|
16
|
+
- Added nested AsyncBoundary error and timeout hydration coverage for inner fallback retry, sibling stability, and cleanup.
|
|
17
|
+
- Added SSR `v-model` form-control state rendering for input, textarea, checkbox, radio, select, and multiple select hydration parity.
|
|
18
|
+
- Improved hydration diagnostics with phase/component/file context and `hydration:warning` devtools events.
|
|
19
|
+
- Updated README and docs to reflect current SSR/hydration, router, diagnostics, examples, and release checklist coverage.
|
|
4
20
|
|
|
5
21
|
## 1.0.21 - 2026-05-13
|
|
6
22
|
|
package/README.md
CHANGED
|
@@ -148,6 +148,7 @@ declare const Greeting: MikuruComponent<GreetingProps>;
|
|
|
148
148
|
- Hydration through `compileHydration()` and `hydrateRoute()`, reusing existing SSR DOM while attaching events, syncing text/attributes, recovering structural mismatches with an opt-out remount fallback, hydrating component context/lifecycle hooks, `v-show`, DOM and component `v-model`, `v-pre`, `v-cloak`, initial `v-if` / `v-for` DOM, Teleport target and disabled inline content, delegating child and route components to `hydrate()` when available, and optionally starting router history listening after route hydration
|
|
149
149
|
- Style injection and basic `<style scoped>` selector rewriting
|
|
150
150
|
- Compile errors with filenames, line/column information, code frames, and typo suggestions for built-in attributes, directives, and modifiers
|
|
151
|
+
- Debug diagnostics with optional generated `sourceURL`, unstable devtools metadata/events, and hydration warnings that include phase, component, and filename context
|
|
151
152
|
|
|
152
153
|
## Package Exports
|
|
153
154
|
|
|
@@ -268,4 +269,5 @@ npm run dev:mikuru-vue-like
|
|
|
268
269
|
- `docs/npm-usage.md` shows a manual Vite setup for package consumers.
|
|
269
270
|
- `docs/app-architecture.md` describes how to keep larger Mikuru apps split across components, API modules, stores, forms, auth, and tests.
|
|
270
271
|
- `docs/router.md` documents the runtime router.
|
|
272
|
+
- `docs/production-readiness.md` summarizes debugging, parser, package, SSR, and hydration caveats.
|
|
271
273
|
- `docs/v1-api-contract.md` describes the v1 compatibility boundary used by this repository.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createCompileError } from "./errors.js";
|
|
1
2
|
import { compileTemplateExpression, parseForExpression, validateAssignableExpression } from "./parseExpression.js";
|
|
2
3
|
export function generateHydration(descriptor, root, options = {}) {
|
|
3
4
|
const context = {
|
|
@@ -36,7 +37,9 @@ export function generateHydration(descriptor, root, options = {}) {
|
|
|
36
37
|
emit(context, 2, "},");
|
|
37
38
|
emit(context, 2, "registerEffect: (fn) => Promise.resolve().then(fn)");
|
|
38
39
|
emit(context, 1, "};");
|
|
39
|
-
emit(context, 1, "const
|
|
40
|
+
emit(context, 1, "const __mikuru_emitDebug = (type, payload) => { const hook = globalThis.__MIKURU_DEVTOOLS__; if (!hook) return; const event = { type, timestamp: Date.now(), payload }; hook.events ??= []; hook.events.push(event); if (typeof hook.emit === \"function\") hook.emit(event); for (const listener of hook.listeners ?? []) { try { listener(event); } catch (error) { setTimeout(() => { throw error; }); } } };");
|
|
41
|
+
emit(context, 1, "const __mikuru_hydrationDiagnostic = (message, details = {}) => ({ ...__mikuru_componentInfo, phase: \"hydration\", message, ...details });");
|
|
42
|
+
emit(context, 1, "const __mikuru_warn = (message, details = {}) => { const diagnostic = __mikuru_hydrationDiagnostic(message, details); __mikuru_emitDebug(\"hydration:warning\", diagnostic); if (typeof console !== \"undefined\" && console.warn) console.warn(`[Mikuru hydration] ${message} (phase: ${diagnostic.phase}, component: ${diagnostic.component}, file: ${diagnostic.filename})`); };");
|
|
40
43
|
emit(context, 1, "const __mikuru_describeNode = (node) => { if (!node) return \"missing\"; if (node.nodeType === 1) return `<${node.tagName?.toLowerCase?.() ?? \"element\"}>`; if (node.nodeType === 3) return `text(${JSON.stringify(node.nodeValue ?? \"\")})`; if (node.nodeType === 8) return `comment(${JSON.stringify(node.nodeValue ?? \"\")})`; return `nodeType(${node.nodeType})`; };");
|
|
41
44
|
emit(context, 1, "const __mikuru_restoreRegistrar = () => { if (__mikuru_previousRegistrar === undefined) { delete globalThis.__mikuru_currentRegistrar; } else { globalThis.__mikuru_currentRegistrar = __mikuru_previousRegistrar; } };");
|
|
42
45
|
emit(context, 1, "const __mikuru_recovery = {};");
|
|
@@ -134,6 +137,7 @@ function hydrateElement(context, node, elementVar, indent) {
|
|
|
134
137
|
hydrateAttrs(context, node, elementVar, indent);
|
|
135
138
|
hydrateEvents(context, node, elementVar, indent);
|
|
136
139
|
const contentDirective = getContentDirectiveAttr(node);
|
|
140
|
+
const textareaModel = node.tag === "textarea" && hasElementModel(node);
|
|
137
141
|
const hydrateChildrenBeforeModel = node.tag === "select" && !contentDirective;
|
|
138
142
|
if (hydrateChildrenBeforeModel) {
|
|
139
143
|
hydrateChildren(context, node.children, elementVar, indent);
|
|
@@ -141,7 +145,10 @@ function hydrateElement(context, node, elementVar, indent) {
|
|
|
141
145
|
hydrateModelAndShow(context, node, elementVar, indent);
|
|
142
146
|
hydrateContentDirective(context, node, elementVar, indent);
|
|
143
147
|
hydrateTemplateRef(context, node, elementVar, indent);
|
|
144
|
-
if (
|
|
148
|
+
if (textareaModel) {
|
|
149
|
+
emit(context, indent, `if (${elementVar}.childNodes.length > 0) { const __mikuru_textarea_value = ${elementVar}.value; ${elementVar}.textContent = ""; ${elementVar}.value = __mikuru_textarea_value; }`);
|
|
150
|
+
}
|
|
151
|
+
if (!contentDirective && !hydrateChildrenBeforeModel && !textareaModel) {
|
|
145
152
|
hydrateChildren(context, node.children, elementVar, indent);
|
|
146
153
|
}
|
|
147
154
|
}
|
|
@@ -446,35 +453,134 @@ function emitRouterViewRouteSlot(context, node, propsVar, indent) {
|
|
|
446
453
|
emit(context, indent, `if (typeof props.children === "function") { ${propsVar}.children = props.children; ${propsVar}.slots = { ...(${propsVar}.slots ?? {}), default: props.children }; }`);
|
|
447
454
|
}
|
|
448
455
|
function emitHydrationComponentSlots(context, node, propsVar, indent) {
|
|
449
|
-
const
|
|
450
|
-
if (
|
|
456
|
+
const slots = collectHydrationComponentSlots(context, node);
|
|
457
|
+
if (slots.length === 0) {
|
|
451
458
|
return;
|
|
452
459
|
}
|
|
460
|
+
const defaultSlot = slots.find((slot) => !slot.nameExpression && slot.name === "default");
|
|
461
|
+
if (defaultSlot) {
|
|
462
|
+
emitHydrationSlotFunction(context, `${propsVar}.children`, defaultSlot, indent);
|
|
463
|
+
}
|
|
464
|
+
emit(context, indent, `${propsVar}.slots = { ...(${propsVar}.slots ?? {}) };`);
|
|
465
|
+
for (const slot of slots) {
|
|
466
|
+
const property = slot.nameExpression ? `[${slot.nameExpression}]` : `[${quote(slot.name)}]`;
|
|
467
|
+
emitHydrationSlotFunction(context, `${propsVar}.slots${property}`, slot, indent);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function emitHydrationSlotFunction(context, target, slot, indent) {
|
|
453
471
|
const slotTargetVar = nextName(context, "slotTarget");
|
|
454
472
|
const slotPropsVar = nextName(context, "slotProps");
|
|
455
|
-
emit(context, indent, `${
|
|
456
|
-
|
|
473
|
+
emit(context, indent, `${target} = (${slotTargetVar}, ${slotPropsVar} = {}) => {`);
|
|
474
|
+
emitHydrationSlotScopeBindings(context, slot, slotPropsVar, indent + 1);
|
|
475
|
+
hydrateChildren(context, slot.children, slotTargetVar, indent + 1);
|
|
457
476
|
emit(context, indent, "};");
|
|
458
|
-
emit(context, indent, `${propsVar}.slots = { ...(${propsVar}.slots ?? {}), default: ${propsVar}.children };`);
|
|
459
477
|
}
|
|
460
|
-
function
|
|
478
|
+
function emitHydrationSlotScopeBindings(context, slot, slotPropsVar, indent) {
|
|
479
|
+
for (const binding of parseHydrationSlotScopeBindings(slot.scope, context, slot.scopeLoc ?? slot.loc)) {
|
|
480
|
+
if (binding.kind === "props") {
|
|
481
|
+
emit(context, indent, `const ${binding.alias} = ${slotPropsVar};`);
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
if (binding.kind === "rest") {
|
|
485
|
+
emit(context, indent, `const ${binding.alias} = { get value() { const rest = { ...${slotPropsVar} }; ${binding.exclude.map((key) => `delete rest[${quote(key)}];`).join(" ")} return rest; } };`);
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
const valueExpression = slotScopePathExpression(slotPropsVar, binding.path);
|
|
489
|
+
if (binding.defaultValue) {
|
|
490
|
+
emit(context, indent, `const ${binding.alias} = { get value() { const value = ${valueExpression}; return value === undefined ? (${binding.defaultValue}) : value; } };`);
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
emit(context, indent, `const ${binding.alias} = { get value() { return ${valueExpression}; } };`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function slotScopePathExpression(rootVar, path) {
|
|
497
|
+
return path.reduce((expression, key, index) => {
|
|
498
|
+
const property = /^[A-Za-z_$][\w$]*$/.test(key) ? `.${key}` : `[${quote(key)}]`;
|
|
499
|
+
return `${expression}${index === 0 ? property : `?.${property.slice(1)}`}`;
|
|
500
|
+
}, rootVar);
|
|
501
|
+
}
|
|
502
|
+
function collectHydrationComponentSlots(context, node) {
|
|
503
|
+
const slots = [];
|
|
504
|
+
const usedNames = new Set();
|
|
461
505
|
const defaultChildren = [];
|
|
462
506
|
for (const child of node.children) {
|
|
463
507
|
if (child.type === "element" && child.tag === "template") {
|
|
464
|
-
const
|
|
465
|
-
if (
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
508
|
+
const slotDirective = getHydrationSlotTemplateDirective(child, context);
|
|
509
|
+
if (slotDirective) {
|
|
510
|
+
if (usedNames.has(slotDirective.name)) {
|
|
511
|
+
throwTemplateError(`Duplicate slot template: ${slotDirective.name}`, context, slotDirective.loc);
|
|
512
|
+
}
|
|
513
|
+
usedNames.add(slotDirective.name);
|
|
514
|
+
slots.push({
|
|
515
|
+
name: slotDirective.name,
|
|
516
|
+
nameExpression: slotDirective.nameExpression,
|
|
517
|
+
children: child.children,
|
|
518
|
+
scope: slotDirective.scope,
|
|
519
|
+
loc: child.loc,
|
|
520
|
+
scopeLoc: slotDirective.scopeLoc
|
|
521
|
+
});
|
|
469
522
|
continue;
|
|
470
523
|
}
|
|
471
524
|
}
|
|
472
525
|
defaultChildren.push(child);
|
|
473
526
|
}
|
|
474
|
-
|
|
527
|
+
if (hasMeaningfulHydrationChildren(defaultChildren)) {
|
|
528
|
+
if (usedNames.has("default")) {
|
|
529
|
+
throwTemplateError("Duplicate slot template: default", context, node.loc);
|
|
530
|
+
}
|
|
531
|
+
slots.unshift({
|
|
532
|
+
name: "default",
|
|
533
|
+
children: defaultChildren,
|
|
534
|
+
scope: true,
|
|
535
|
+
loc: node.loc
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
return slots;
|
|
539
|
+
}
|
|
540
|
+
function getHydrationSlotTemplateDirective(node, context) {
|
|
541
|
+
const slotAttrs = node.attrs.filter((attr) => isSlotTemplateAttr(attr));
|
|
542
|
+
if (slotAttrs.length === 0) {
|
|
543
|
+
return undefined;
|
|
544
|
+
}
|
|
545
|
+
if (slotAttrs.length > 1) {
|
|
546
|
+
throwTemplateError("A slot template can only declare one slot target", context, slotAttrs[1]?.loc);
|
|
547
|
+
}
|
|
548
|
+
const attr = slotAttrs[0];
|
|
549
|
+
const name = getHydrationSlotTemplateName(attr, context);
|
|
550
|
+
return {
|
|
551
|
+
...name,
|
|
552
|
+
scope: attr.value,
|
|
553
|
+
loc: attr.loc,
|
|
554
|
+
scopeLoc: attr.valueLoc
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function getHydrationSlotTemplateName(attr, context) {
|
|
558
|
+
if (attr.name === "v-slot") {
|
|
559
|
+
return { name: "default" };
|
|
560
|
+
}
|
|
561
|
+
if (attr.name.startsWith("v-slot:")) {
|
|
562
|
+
return parseHydrationSlotTemplateName(attr.name.slice("v-slot:".length) || "default", attr, context);
|
|
563
|
+
}
|
|
564
|
+
if (attr.name.startsWith("#")) {
|
|
565
|
+
return parseHydrationSlotTemplateName(attr.name.slice(1) || "default", attr, context);
|
|
566
|
+
}
|
|
567
|
+
return { name: "default" };
|
|
475
568
|
}
|
|
476
|
-
function
|
|
477
|
-
|
|
569
|
+
function parseHydrationSlotTemplateName(rawName, attr, context) {
|
|
570
|
+
const name = rawName.trim();
|
|
571
|
+
const dynamicStart = name.indexOf("[");
|
|
572
|
+
const dynamicEnd = name.lastIndexOf("]");
|
|
573
|
+
if (dynamicStart >= 0 && dynamicEnd > dynamicStart) {
|
|
574
|
+
const expression = name.slice(dynamicStart + 1, dynamicEnd).trim();
|
|
575
|
+
if (!expression) {
|
|
576
|
+
throwTemplateError("Dynamic slot name requires an expression", context, attr.loc);
|
|
577
|
+
}
|
|
578
|
+
return {
|
|
579
|
+
name: `[${expression}]`,
|
|
580
|
+
nameExpression: compileTemplateExpression(expression, attr.name, toExpressionContext(context, attr.loc))
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
return { name };
|
|
478
584
|
}
|
|
479
585
|
function isSlotTemplateAttr(attr) {
|
|
480
586
|
return attr.name === "v-slot" || attr.name.startsWith("v-slot:") || attr.name.startsWith("#");
|
|
@@ -482,6 +588,102 @@ function isSlotTemplateAttr(attr) {
|
|
|
482
588
|
function hasMeaningfulHydrationChildren(children) {
|
|
483
589
|
return children.some((child) => child.type === "element" || child.parts.some((part) => part.value.trim()));
|
|
484
590
|
}
|
|
591
|
+
function parseHydrationSlotScopeBindings(scope, context, location) {
|
|
592
|
+
if (scope === true || !scope.trim()) {
|
|
593
|
+
return [];
|
|
594
|
+
}
|
|
595
|
+
const source = scope.trim();
|
|
596
|
+
if (isIdentifier(source)) {
|
|
597
|
+
return [{ kind: "props", alias: source }];
|
|
598
|
+
}
|
|
599
|
+
if (!source.startsWith("{") || !source.endsWith("}")) {
|
|
600
|
+
throwTemplateError("Slot scope must be an identifier or object destructuring pattern", context, location);
|
|
601
|
+
}
|
|
602
|
+
const body = source.slice(1, -1).trim();
|
|
603
|
+
if (!body) {
|
|
604
|
+
return [];
|
|
605
|
+
}
|
|
606
|
+
return parseHydrationSlotScopeObjectPattern(body, context, location, []);
|
|
607
|
+
}
|
|
608
|
+
function parseHydrationSlotScopeObjectPattern(body, context, location, pathPrefix) {
|
|
609
|
+
const bindings = [];
|
|
610
|
+
const excludedTopLevelKeys = [];
|
|
611
|
+
for (const part of splitTopLevel(body, ",")) {
|
|
612
|
+
const sourcePart = part.trim();
|
|
613
|
+
if (!sourcePart) {
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
if (sourcePart.startsWith("...")) {
|
|
617
|
+
const alias = sourcePart.slice(3).trim();
|
|
618
|
+
if (pathPrefix.length > 0) {
|
|
619
|
+
throwTemplateError("Slot scope rest destructuring is only supported at the top level", context, location);
|
|
620
|
+
}
|
|
621
|
+
if (!isIdentifier(alias)) {
|
|
622
|
+
throwTemplateError("Slot scope rest destructuring must use a simple identifier like ...rest", context, location);
|
|
623
|
+
}
|
|
624
|
+
bindings.push({ kind: "rest", alias, exclude: excludedTopLevelKeys });
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
const { left, right } = splitHydrationSlotScopeEntry(sourcePart, context, location);
|
|
628
|
+
if (!isIdentifier(left)) {
|
|
629
|
+
throwTemplateError(`Unsupported slot scope key "${left}". Use identifier keys in slot scope destructuring`, context, location);
|
|
630
|
+
}
|
|
631
|
+
if (pathPrefix.length === 0) {
|
|
632
|
+
excludedTopLevelKeys.push(left);
|
|
633
|
+
}
|
|
634
|
+
const path = [...pathPrefix, left];
|
|
635
|
+
if (right === undefined) {
|
|
636
|
+
bindings.push(...hydrationSlotScopeLeafBindings(left, path, context, location));
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
const value = right.trim();
|
|
640
|
+
if (value.startsWith("{") && value.endsWith("}")) {
|
|
641
|
+
bindings.push(...parseHydrationSlotScopeObjectPattern(value.slice(1, -1), context, location, path));
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
if (value.startsWith("[") || value.includes("{")) {
|
|
645
|
+
throwTemplateError("Slot scope destructuring supports nested object patterns only; array and mixed patterns are not supported", context, location);
|
|
646
|
+
}
|
|
647
|
+
bindings.push(...hydrationSlotScopeLeafBindings(value, path, context, location));
|
|
648
|
+
}
|
|
649
|
+
return bindings;
|
|
650
|
+
}
|
|
651
|
+
function splitHydrationSlotScopeEntry(source, context, location) {
|
|
652
|
+
const colonIndex = findTopLevelToken(source, ":");
|
|
653
|
+
if (colonIndex >= 0) {
|
|
654
|
+
return {
|
|
655
|
+
left: source.slice(0, colonIndex).trim(),
|
|
656
|
+
right: source.slice(colonIndex + 1).trim()
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
const equalsIndex = findTopLevelToken(source, "=");
|
|
660
|
+
if (equalsIndex >= 0) {
|
|
661
|
+
const left = source.slice(0, equalsIndex).trim();
|
|
662
|
+
if (!isIdentifier(left)) {
|
|
663
|
+
throwTemplateError("Slot scope default values can only be assigned to simple identifiers", context, location);
|
|
664
|
+
}
|
|
665
|
+
return { left, right: source };
|
|
666
|
+
}
|
|
667
|
+
return { left: source.trim() };
|
|
668
|
+
}
|
|
669
|
+
function hydrationSlotScopeLeafBindings(source, path, context, location) {
|
|
670
|
+
const equalsIndex = findTopLevelToken(source, "=");
|
|
671
|
+
const localSource = equalsIndex >= 0 ? source.slice(0, equalsIndex).trim() : source.trim();
|
|
672
|
+
const defaultSource = equalsIndex >= 0 ? source.slice(equalsIndex + 1).trim() : undefined;
|
|
673
|
+
if (!isIdentifier(localSource)) {
|
|
674
|
+
throwTemplateError(`Unsupported slot scope binding "${source}". Use identifiers, aliases, defaults, nested objects, or top-level ...rest`, context, location);
|
|
675
|
+
}
|
|
676
|
+
return [
|
|
677
|
+
{
|
|
678
|
+
kind: "property",
|
|
679
|
+
path,
|
|
680
|
+
alias: localSource,
|
|
681
|
+
defaultValue: defaultSource
|
|
682
|
+
? compileTemplateExpression(defaultSource, "slot scope default", toExpressionContext(context, location))
|
|
683
|
+
: undefined
|
|
684
|
+
}
|
|
685
|
+
];
|
|
686
|
+
}
|
|
485
687
|
function hydrateDynamicComponentAtIndex(context, node, parentVar, domIndex, indent) {
|
|
486
688
|
const isAttr = getDynamicComponentIsAttr(node);
|
|
487
689
|
if (!isAttr) {
|
|
@@ -1438,6 +1640,9 @@ function parseModelDirective(name) {
|
|
|
1438
1640
|
const [argument = "", ...modifiers] = name.slice("v-model:".length).split(".");
|
|
1439
1641
|
return { argument, modifiers: modifiers.filter(Boolean) };
|
|
1440
1642
|
}
|
|
1643
|
+
function hasElementModel(node) {
|
|
1644
|
+
return node.attrs.some((attr) => Boolean(parseModelDirective(attr.name)));
|
|
1645
|
+
}
|
|
1441
1646
|
function toComponentEventProp(eventName) {
|
|
1442
1647
|
return `on${eventName.split(":").map((part) => part ? part[0].toUpperCase() + part.slice(1) : "").join("")}`;
|
|
1443
1648
|
}
|
|
@@ -1510,9 +1715,90 @@ function compileHydrationExpression(context, expression, usage) {
|
|
|
1510
1715
|
filename: context.filename
|
|
1511
1716
|
});
|
|
1512
1717
|
}
|
|
1718
|
+
function toExpressionContext(context, location) {
|
|
1719
|
+
if (!context.source || !location) {
|
|
1720
|
+
return undefined;
|
|
1721
|
+
}
|
|
1722
|
+
return {
|
|
1723
|
+
source: context.source,
|
|
1724
|
+
offset: location.offset,
|
|
1725
|
+
filename: context.filename
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
function throwTemplateError(message, context, location) {
|
|
1729
|
+
if (context.source && location) {
|
|
1730
|
+
throw createCompileError(message, context.source, location.offset, context.filename);
|
|
1731
|
+
}
|
|
1732
|
+
throw new Error(message);
|
|
1733
|
+
}
|
|
1513
1734
|
function isIdentifier(value) {
|
|
1514
1735
|
return /^[A-Za-z_$][\w$]*$/.test(value);
|
|
1515
1736
|
}
|
|
1737
|
+
function splitTopLevel(source, delimiter) {
|
|
1738
|
+
const parts = [];
|
|
1739
|
+
let depth = 0;
|
|
1740
|
+
let quoteChar = "";
|
|
1741
|
+
let start = 0;
|
|
1742
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
1743
|
+
const char = source[index];
|
|
1744
|
+
const previous = source[index - 1];
|
|
1745
|
+
if (quoteChar) {
|
|
1746
|
+
if (char === quoteChar && previous !== "\\") {
|
|
1747
|
+
quoteChar = "";
|
|
1748
|
+
}
|
|
1749
|
+
continue;
|
|
1750
|
+
}
|
|
1751
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
1752
|
+
quoteChar = char;
|
|
1753
|
+
continue;
|
|
1754
|
+
}
|
|
1755
|
+
if (char === "{" || char === "[" || char === "(") {
|
|
1756
|
+
depth += 1;
|
|
1757
|
+
continue;
|
|
1758
|
+
}
|
|
1759
|
+
if (char === "}" || char === "]" || char === ")") {
|
|
1760
|
+
depth -= 1;
|
|
1761
|
+
continue;
|
|
1762
|
+
}
|
|
1763
|
+
if (depth === 0 && source.startsWith(delimiter, index)) {
|
|
1764
|
+
parts.push(source.slice(start, index));
|
|
1765
|
+
start = index + delimiter.length;
|
|
1766
|
+
index += delimiter.length - 1;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
parts.push(source.slice(start));
|
|
1770
|
+
return parts;
|
|
1771
|
+
}
|
|
1772
|
+
function findTopLevelToken(source, token) {
|
|
1773
|
+
let depth = 0;
|
|
1774
|
+
let quoteChar = "";
|
|
1775
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
1776
|
+
const char = source[index];
|
|
1777
|
+
const previous = source[index - 1];
|
|
1778
|
+
if (quoteChar) {
|
|
1779
|
+
if (char === quoteChar && previous !== "\\") {
|
|
1780
|
+
quoteChar = "";
|
|
1781
|
+
}
|
|
1782
|
+
continue;
|
|
1783
|
+
}
|
|
1784
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
1785
|
+
quoteChar = char;
|
|
1786
|
+
continue;
|
|
1787
|
+
}
|
|
1788
|
+
if (char === "{" || char === "[" || char === "(") {
|
|
1789
|
+
depth += 1;
|
|
1790
|
+
continue;
|
|
1791
|
+
}
|
|
1792
|
+
if (char === "}" || char === "]" || char === ")") {
|
|
1793
|
+
depth -= 1;
|
|
1794
|
+
continue;
|
|
1795
|
+
}
|
|
1796
|
+
if (depth === 0 && source.startsWith(token, index)) {
|
|
1797
|
+
return index;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
return -1;
|
|
1801
|
+
}
|
|
1516
1802
|
function withTemplateRefMode(context, mode, callback) {
|
|
1517
1803
|
const previousMode = context.templateRefMode;
|
|
1518
1804
|
context.templateRefMode = mode;
|