mikuru 1.0.18 → 1.0.20
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 +82 -0
- package/README.md +19 -12
- package/dist/compiler/analyzeTemplate.js +116 -12
- package/dist/compiler/analyzeTemplate.js.map +1 -1
- package/dist/compiler/compile.js +2 -2
- package/dist/compiler/compile.js.map +1 -1
- package/dist/compiler/compileHydration.d.ts +2 -0
- package/dist/compiler/compileHydration.js +27 -0
- package/dist/compiler/compileHydration.js.map +1 -0
- package/dist/compiler/compileSsr.d.ts +2 -0
- package/dist/compiler/compileSsr.js +21 -0
- package/dist/compiler/compileSsr.js.map +1 -0
- package/dist/compiler/generate.d.ts +6 -1
- package/dist/compiler/generate.js +1473 -113
- package/dist/compiler/generate.js.map +1 -1
- package/dist/compiler/generateHydration.d.ts +2 -0
- package/dist/compiler/generateHydration.js +1281 -0
- package/dist/compiler/generateHydration.js.map +1 -0
- package/dist/compiler/generateSsr.d.ts +2 -0
- package/dist/compiler/generateSsr.js +764 -0
- package/dist/compiler/generateSsr.js.map +1 -0
- package/dist/compiler/index.d.ts +3 -1
- package/dist/compiler/index.js +2 -0
- package/dist/compiler/index.js.map +1 -1
- package/dist/compiler/parseTemplate.js +16 -1
- package/dist/compiler/parseTemplate.js.map +1 -1
- package/dist/compiler/sourceMap.d.ts +2 -2
- package/dist/compiler/sourceMap.js +110 -6
- package/dist/compiler/sourceMap.js.map +1 -1
- package/dist/compiler/types.d.ts +8 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/router/index.js +16 -3
- package/dist/router/index.js.map +1 -1
- package/dist/runtime/asyncComponent.d.ts +26 -0
- package/dist/runtime/asyncComponent.js +65 -5
- package/dist/runtime/asyncComponent.js.map +1 -1
- package/dist/runtime/devtools.d.ts +51 -0
- package/dist/runtime/devtools.js +113 -0
- package/dist/runtime/devtools.js.map +1 -0
- package/dist/runtime/dom.d.ts +5 -1
- package/dist/runtime/dom.js +100 -3
- package/dist/runtime/dom.js.map +1 -1
- package/dist/runtime/index.d.ts +7 -5
- package/dist/runtime/index.js +3 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/lifecycle.d.ts +6 -0
- package/dist/runtime/lifecycle.js +66 -10
- package/dist/runtime/lifecycle.js.map +1 -1
- package/dist/runtime/reactivity.d.ts +33 -1
- package/dist/runtime/reactivity.js +254 -13
- package/dist/runtime/reactivity.js.map +1 -1
- package/dist/server.d.ts +31 -0
- package/dist/server.js +282 -0
- package/dist/server.js.map +1 -0
- package/dist/vite.d.ts +1 -0
- package/dist/vite.js +30 -13
- package/dist/vite.js.map +1 -1
- package/package.json +104 -100
|
@@ -1,29 +1,57 @@
|
|
|
1
|
-
import { parse } from "acorn";
|
|
1
|
+
import { parse, parseExpressionAt } from "acorn";
|
|
2
2
|
import { createCompileError } from "./errors.js";
|
|
3
3
|
import { compileTemplateExpression, parseForExpression, validateAssignableExpression, validateTemplateExpression } from "./parseExpression.js";
|
|
4
|
-
export function generate(descriptor, root) {
|
|
4
|
+
export function generate(descriptor, root, options = {}) {
|
|
5
5
|
const context = {
|
|
6
6
|
lines: [],
|
|
7
7
|
index: 0,
|
|
8
8
|
source: descriptor.source,
|
|
9
9
|
filename: descriptor.filename,
|
|
10
|
-
scopeAttr: descriptor.styleScoped ? createScopeAttr(descriptor) : undefined
|
|
10
|
+
scopeAttr: descriptor.styleScoped ? createScopeAttr(descriptor) : undefined,
|
|
11
|
+
debug: options.debug === true,
|
|
12
|
+
batchedUpdates: options.batchedUpdates === true
|
|
11
13
|
};
|
|
12
14
|
const script = normalizeScript(descriptor);
|
|
13
15
|
for (const importLine of script.imports) {
|
|
14
16
|
emit(context, 0, importLine);
|
|
15
17
|
}
|
|
16
|
-
const
|
|
18
|
+
const runtimeBaseImports = ["computed", "effect", "ref", "setAttribute", "unwrap"];
|
|
19
|
+
if (context.batchedUpdates) {
|
|
20
|
+
runtimeBaseImports.push("queueJob");
|
|
21
|
+
}
|
|
22
|
+
if (context.debug) {
|
|
23
|
+
runtimeBaseImports.push("emitDebugEvent", "registerDebugComponent");
|
|
24
|
+
}
|
|
25
|
+
const runtimeImports = mergeRuntimeImports(runtimeBaseImports, script.runtimeImports);
|
|
17
26
|
emit(context, 0, `import { ${runtimeImports.join(", ")} } from "mikuru/runtime";`);
|
|
18
27
|
emit(context, 0, "");
|
|
28
|
+
if (context.batchedUpdates) {
|
|
29
|
+
emit(context, 0, "const __mikuru_effect = (fn) => effect(fn, { scheduler: queueJob });");
|
|
30
|
+
emit(context, 0, "");
|
|
31
|
+
}
|
|
19
32
|
emit(context, 0, "export function mount(target, props = {}) {");
|
|
33
|
+
emit(context, 1, `const __mikuru_componentInfo = { component: ${quote(descriptor.filename ?? "anonymous.mikuru")}, filename: ${quote(descriptor.filename ?? "anonymous.mikuru")} };`);
|
|
34
|
+
emitDevtoolsRegistration(context, 1);
|
|
20
35
|
emit(context, 1, "const __mikuru_cleanup = [];");
|
|
21
36
|
emit(context, 1, "const __mikuru_afterUnmount = [];");
|
|
22
37
|
emit(context, 1, "const __mikuru_mounted = [];");
|
|
23
|
-
emit(context, 1, "const
|
|
38
|
+
emit(context, 1, "const __mikuru_activated = [];");
|
|
39
|
+
emit(context, 1, "const __mikuru_deactivated = [];");
|
|
40
|
+
emit(context, 1, `const __mikuru_context = { parent: props.__mikuru_context, provides: new Map(), errorHandler: props.__mikuru_context?.errorHandler${context.debug ? ", debugId: __mikuru_debug.id" : ""}, ...__mikuru_componentInfo };`);
|
|
41
|
+
emit(context, 1, "const __mikuru_errorInfo = (phase) => ({ ...__mikuru_componentInfo, phase });");
|
|
42
|
+
emit(context, 1, "const __mikuru_reportError = (error, errorHandler = __mikuru_context.errorHandler, phase = \"runtime\") => {");
|
|
43
|
+
if (context.debug) {
|
|
44
|
+
emit(context, 2, "emitDebugEvent(\"component:error\", { component: __mikuru_componentInfo, error, errorInfo: __mikuru_errorInfo(phase), componentId: __mikuru_debug.id });");
|
|
45
|
+
}
|
|
46
|
+
emit(context, 2, "if (typeof errorHandler === \"function\") { Promise.resolve().then(() => errorHandler(error, __mikuru_errorInfo(phase))); return; }");
|
|
47
|
+
emit(context, 2, "setTimeout(() => { throw error; });");
|
|
48
|
+
emit(context, 1, "};");
|
|
49
|
+
emit(context, 1, "const __mikuru_try = (fn, errorHandler, phase) => { try { return fn(); } catch (error) { __mikuru_reportError(error, errorHandler, phase); } };");
|
|
50
|
+
emit(context, 1, "const __mikuru_memoEqual = (previous, next) => Array.isArray(previous) && Array.isArray(next) && previous.length === next.length && previous.every((value, index) => Object.is(value, next[index]));");
|
|
51
|
+
emit(context, 1, "const __mikuru_guardEventHandler = (fn, errorHandler = __mikuru_context.errorHandler) => (...args) => __mikuru_try(() => fn(...args), errorHandler, \"event\");");
|
|
24
52
|
emit(context, 1, "const __mikuru_runCleanup = (cleanups) => {");
|
|
25
53
|
emit(context, 2, "for (const cleanup of cleanups.splice(0).reverse()) {");
|
|
26
|
-
emit(context, 3, "cleanup
|
|
54
|
+
emit(context, 3, "__mikuru_try(cleanup, undefined, \"cleanup\");");
|
|
27
55
|
emit(context, 2, "}");
|
|
28
56
|
emit(context, 1, "};");
|
|
29
57
|
emit(context, 1, "const __mikuru_transitionFrame = (fn) => {");
|
|
@@ -58,6 +86,12 @@ export function generate(descriptor, root) {
|
|
|
58
86
|
emit(context, 3, "__mikuru_transitionDone(element, () => element.classList.remove(active, to));");
|
|
59
87
|
emit(context, 2, "});");
|
|
60
88
|
emit(context, 1, "};");
|
|
89
|
+
emit(context, 1, "const __mikuru_applyTransitionMove = (element, transition) => {");
|
|
90
|
+
emit(context, 2, "if (!element || element.nodeType !== 1 || !transition?.name) { return; }");
|
|
91
|
+
emit(context, 2, "const move = transition.moveClass || `${transition.name}-move`;");
|
|
92
|
+
emit(context, 2, "element.classList.add(move);");
|
|
93
|
+
emit(context, 2, "__mikuru_transitionFrame(() => __mikuru_transitionDone(element, () => element.classList.remove(move)));");
|
|
94
|
+
emit(context, 1, "};");
|
|
61
95
|
emit(context, 1, "const __mikuru_removeNode = (node) => {");
|
|
62
96
|
emit(context, 2, "if (!node || !node.parentNode) { return; }");
|
|
63
97
|
emit(context, 2, "if (node.nodeType !== 1 || !node.__mikuru_transition || node.__mikuru_transitionLeaving) { node.remove(); return; }");
|
|
@@ -94,6 +128,8 @@ export function generate(descriptor, root) {
|
|
|
94
128
|
emit(context, 1, "const __mikuru_previousRegistrar = globalThis.__mikuru_currentRegistrar;");
|
|
95
129
|
emit(context, 1, "globalThis.__mikuru_currentRegistrar = {");
|
|
96
130
|
emit(context, 2, "registerMounted: (fn) => __mikuru_mounted.push(fn),");
|
|
131
|
+
emit(context, 2, "registerActivated: (fn) => __mikuru_activated.push(fn),");
|
|
132
|
+
emit(context, 2, "registerDeactivated: (fn) => __mikuru_deactivated.push(fn),");
|
|
97
133
|
emit(context, 2, "registerBeforeUnmount: (fn) => __mikuru_cleanup.push(fn),");
|
|
98
134
|
emit(context, 2, "registerUnmounted: (fn) => __mikuru_afterUnmount.push(fn),");
|
|
99
135
|
emit(context, 2, "provide: (key, value) => __mikuru_context.provides.set(key, value),");
|
|
@@ -124,7 +160,7 @@ export function generate(descriptor, root) {
|
|
|
124
160
|
emit(context, 2, "const handlerName = \"on\" + String(name).split(/[-:]/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(\"\");");
|
|
125
161
|
emit(context, 2, "const handler = props[handlerName];");
|
|
126
162
|
emit(context, 2, "if (handler) {");
|
|
127
|
-
emit(context, 3, "handler(...args);");
|
|
163
|
+
emit(context, 3, "__mikuru_try(() => handler(...args), undefined, \"emit\");");
|
|
128
164
|
emit(context, 2, "}");
|
|
129
165
|
emit(context, 1, "};");
|
|
130
166
|
}
|
|
@@ -132,14 +168,25 @@ export function generate(descriptor, root) {
|
|
|
132
168
|
emit(context, 1, "");
|
|
133
169
|
}
|
|
134
170
|
const rootVar = generateNode(context, root, "target", "__mikuru_cleanup", 1);
|
|
171
|
+
emitDevtoolsRootUpdate(context, rootVar, 1);
|
|
135
172
|
emit(context, 1, "// call mounted callbacks registered during setup and remove registrar");
|
|
136
|
-
emit(context, 1, "for (const cb of __mikuru_mounted.splice(0)) {
|
|
173
|
+
emit(context, 1, "for (const cb of __mikuru_mounted.splice(0)) { __mikuru_try(cb, undefined, \"mounted\"); }");
|
|
137
174
|
emit(context, 1, "if (__mikuru_previousRegistrar === undefined) { delete globalThis.__mikuru_currentRegistrar; } else { globalThis.__mikuru_currentRegistrar = __mikuru_previousRegistrar; }");
|
|
138
175
|
emit(context, 1, "return {");
|
|
139
176
|
emit(context, 2, `element: ${rootVar},`);
|
|
177
|
+
emit(context, 2, "activate() {");
|
|
178
|
+
emit(context, 3, "for (const cb of __mikuru_activated) { __mikuru_try(cb, undefined, \"activated\"); }");
|
|
179
|
+
emit(context, 2, "},");
|
|
180
|
+
emit(context, 2, "deactivate() {");
|
|
181
|
+
emit(context, 3, "for (const cb of __mikuru_deactivated) { __mikuru_try(cb, undefined, \"deactivated\"); }");
|
|
182
|
+
emit(context, 2, "},");
|
|
140
183
|
emit(context, 2, "unmount() {");
|
|
184
|
+
if (context.debug) {
|
|
185
|
+
emit(context, 3, "emitDebugEvent(\"component:unmount\", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id });");
|
|
186
|
+
emit(context, 3, "__mikuru_unregisterDevtools();");
|
|
187
|
+
}
|
|
141
188
|
emit(context, 3, "__mikuru_runCleanup(__mikuru_cleanup);");
|
|
142
|
-
emit(context, 3, "for (const cb of __mikuru_afterUnmount.splice(0).reverse()) {
|
|
189
|
+
emit(context, 3, "for (const cb of __mikuru_afterUnmount.splice(0).reverse()) { __mikuru_try(cb, undefined, \"unmounted\"); }");
|
|
143
190
|
emit(context, 3, `__mikuru_removeNode(${rootVar});`);
|
|
144
191
|
emit(context, 2, "}");
|
|
145
192
|
emit(context, 1, "};");
|
|
@@ -147,7 +194,10 @@ export function generate(descriptor, root) {
|
|
|
147
194
|
emit(context, 0, "");
|
|
148
195
|
emit(context, 0, `const __mikuru_component = { mount${script.inheritAttrs ? "" : ", inheritAttrs: false"} };`);
|
|
149
196
|
emit(context, 0, "export default __mikuru_component;");
|
|
150
|
-
|
|
197
|
+
const lines = context.batchedUpdates
|
|
198
|
+
? context.lines.map((line) => line.replace("= effect(() => {", "= __mikuru_effect(() => {"))
|
|
199
|
+
: context.lines;
|
|
200
|
+
return `${lines.join("\n")}\n`;
|
|
151
201
|
}
|
|
152
202
|
function emitStyleInjection(context, descriptor, indent) {
|
|
153
203
|
const styleId = `mikuru-${hash(`${descriptor.filename ?? ""}\n${descriptor.style ?? ""}`)}`;
|
|
@@ -161,10 +211,42 @@ function emitStyleInjection(context, descriptor, indent) {
|
|
|
161
211
|
emit(context, indent + 1, "document.head.appendChild(style);");
|
|
162
212
|
emit(context, indent, "}");
|
|
163
213
|
}
|
|
214
|
+
function emitDevtoolsRegistration(context, indent) {
|
|
215
|
+
if (!context.debug) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
emit(context, indent, "const __mikuru_publicProps = Object.fromEntries(Object.entries(props).filter(([key]) => !key.startsWith(\"__mikuru_\")));");
|
|
219
|
+
emit(context, indent, "const __mikuru_debugAttrs = props.__mikuru_attrs && typeof props.__mikuru_attrs === \"object\" ? { ...props.__mikuru_attrs } : {};");
|
|
220
|
+
emit(context, indent, "const __mikuru_debug = registerDebugComponent({");
|
|
221
|
+
emit(context, indent + 1, "name: __mikuru_componentInfo.component,");
|
|
222
|
+
emit(context, indent + 1, "filename: __mikuru_componentInfo.filename,");
|
|
223
|
+
emit(context, indent + 1, "parentId: props.__mikuru_context?.debugId,");
|
|
224
|
+
emit(context, indent + 1, "props: __mikuru_publicProps,");
|
|
225
|
+
emit(context, indent + 1, "propKeys: Object.keys(__mikuru_publicProps),");
|
|
226
|
+
emit(context, indent + 1, "attrs: __mikuru_debugAttrs,");
|
|
227
|
+
emit(context, indent + 1, "attrKeys: Object.keys(__mikuru_debugAttrs)");
|
|
228
|
+
emit(context, indent, "});");
|
|
229
|
+
emit(context, indent, "const __mikuru_unregisterDevtools = () => __mikuru_debug.unregister();");
|
|
230
|
+
}
|
|
231
|
+
function emitDevtoolsRootUpdate(context, rootVar, indent) {
|
|
232
|
+
if (!context.debug) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
emit(context, indent, `__mikuru_debug.update({ root: ${rootVar} });`);
|
|
236
|
+
emit(context, indent, "emitDebugEvent(\"component:mount\", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id, root: __mikuru_debug.metadata.root, props: __mikuru_debug.metadata.props, attrs: __mikuru_debug.metadata.attrs });");
|
|
237
|
+
}
|
|
164
238
|
function generateNode(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
165
239
|
if (node.type === "text") {
|
|
166
240
|
return generateText(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
167
241
|
}
|
|
242
|
+
if (hasAttr(node, "v-pre")) {
|
|
243
|
+
validatePreAttribute(context, node);
|
|
244
|
+
return generatePreElement(context, node, parentVar, indent, beforeVar);
|
|
245
|
+
}
|
|
246
|
+
if (hasAttr(node, "v-once") && !hasAttr(node, "v-for")) {
|
|
247
|
+
validateOnceAttribute(context, node);
|
|
248
|
+
return withOnceMode(context, () => generateNode(context, withoutAttrs(node, ["v-once"]), parentVar, cleanupVar, indent, beforeVar));
|
|
249
|
+
}
|
|
168
250
|
const forExpression = getStringAttr(node, "v-for");
|
|
169
251
|
if (forExpression) {
|
|
170
252
|
return generateFor(context, node, parentVar, cleanupVar, indent, forExpression, beforeVar);
|
|
@@ -183,12 +265,21 @@ function generateNode(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
|
183
265
|
if (node.tag === "Transition") {
|
|
184
266
|
return generateTransition(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
185
267
|
}
|
|
268
|
+
if (node.tag === "TransitionGroup") {
|
|
269
|
+
return generateTransitionGroup(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
270
|
+
}
|
|
186
271
|
if (node.tag === "Teleport") {
|
|
187
272
|
return generateTeleport(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
188
273
|
}
|
|
274
|
+
if (node.tag === "AsyncBoundary") {
|
|
275
|
+
return generateAsyncBoundary(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
276
|
+
}
|
|
189
277
|
if (node.tag === "ErrorBoundary") {
|
|
190
278
|
return generateErrorBoundary(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
191
279
|
}
|
|
280
|
+
if (node.tag === "KeepAlive") {
|
|
281
|
+
return generateKeepAlive(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
282
|
+
}
|
|
192
283
|
if (isComponentTag(node.tag)) {
|
|
193
284
|
return generateComponent(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
194
285
|
}
|
|
@@ -213,6 +304,30 @@ function generateTransition(context, node, parentVar, cleanupVar, indent, before
|
|
|
213
304
|
emitTransitionRegistration(context, childVar, transitionVar, indent);
|
|
214
305
|
return childVar;
|
|
215
306
|
}
|
|
307
|
+
function generateTransitionGroup(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
308
|
+
validateTransitionGroupAttributes(context, node);
|
|
309
|
+
const children = getSingleElementChild(context, node, "<TransitionGroup>");
|
|
310
|
+
const child = children[0];
|
|
311
|
+
const forExpression = getStringAttr(child, "v-for");
|
|
312
|
+
const keyExpression = getKeyExpression(child);
|
|
313
|
+
if (!forExpression || !keyExpression) {
|
|
314
|
+
throwTemplateError("<TransitionGroup> requires a single keyed v-for child in v1", context, child.loc);
|
|
315
|
+
}
|
|
316
|
+
const { item: itemName, index: indexName, source: sourceExpression } = parseForExpression(forExpression, toExpressionContext(context, getStringAttrLocation(child, "v-for")));
|
|
317
|
+
const groupVar = nextVar(context, "transitionGroup");
|
|
318
|
+
const groupCleanupVar = nextVar(context, "transitionGroupCleanup");
|
|
319
|
+
const transitionVar = nextVar(context, "transition");
|
|
320
|
+
emit(context, indent, `const ${transitionVar} = ${getTransitionOptionsExpression(context, node)};`);
|
|
321
|
+
emit(context, indent, `const ${groupVar} = document.createElement(String(unwrap(${getTransitionAttrExpression(context, node, "tag", "span")}) ?? "span"));`);
|
|
322
|
+
appendNode(context, parentVar, groupVar, indent, beforeVar);
|
|
323
|
+
emit(context, indent, `const ${groupCleanupVar} = [];`);
|
|
324
|
+
generateKeyedFor(context, child, groupVar, groupCleanupVar, indent, itemName, indexName, sourceExpression, keyExpression, undefined, transitionVar);
|
|
325
|
+
emit(context, indent, `${cleanupVar}.push(() => {`);
|
|
326
|
+
emit(context, indent + 1, `__mikuru_runCleanup(${groupCleanupVar});`);
|
|
327
|
+
emit(context, indent + 1, `__mikuru_removeNode(${groupVar});`);
|
|
328
|
+
emit(context, indent, "});");
|
|
329
|
+
return groupVar;
|
|
330
|
+
}
|
|
216
331
|
function generateTeleport(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
217
332
|
validateTeleportAttributes(context, node);
|
|
218
333
|
const toExpression = getTeleportToExpression(context, node);
|
|
@@ -274,37 +389,239 @@ function generateErrorBoundary(context, node, parentVar, cleanupVar, indent, bef
|
|
|
274
389
|
validateErrorBoundaryAttributes(context, node);
|
|
275
390
|
const children = getSingleElementChild(context, node, "<ErrorBoundary>");
|
|
276
391
|
const fallbackExpression = getErrorBoundaryFallbackExpression(context, node);
|
|
392
|
+
const resetKeyExpression = getErrorBoundaryResetKeyExpression(context, node);
|
|
277
393
|
const startVar = nextVar(context, "errorBoundaryStart");
|
|
278
394
|
const endVar = nextVar(context, "errorBoundaryEnd");
|
|
279
395
|
const boundaryCleanupVar = nextVar(context, "errorBoundaryCleanup");
|
|
280
396
|
const renderVar = nextVar(context, "renderErrorBoundary");
|
|
397
|
+
const fallbackRenderVar = nextVar(context, "renderErrorBoundaryFallback");
|
|
281
398
|
const errorVar = nextVar(context, "error");
|
|
399
|
+
const errorInfoVar = nextVar(context, "errorInfo");
|
|
400
|
+
const normalizedErrorInfoVar = nextVar(context, "errorInfo");
|
|
282
401
|
const fallbackVar = nextVar(context, "errorFallback");
|
|
283
402
|
const fallbackFragmentVar = nextVar(context, "errorFallback");
|
|
284
403
|
const fallbackInstanceVar = nextVar(context, "errorFallback");
|
|
404
|
+
const previousErrorHandlerVar = nextVar(context, "previousErrorHandler");
|
|
405
|
+
const boundaryContextVar = nextVar(context, "errorBoundaryContext");
|
|
406
|
+
const previousComponentContextVar = context.componentContextVar;
|
|
407
|
+
const boundaryResetInitializedVar = resetKeyExpression ? nextVar(context, "errorBoundaryResetInitialized") : undefined;
|
|
408
|
+
const boundaryResetValueVar = resetKeyExpression ? nextVar(context, "errorBoundaryResetValue") : undefined;
|
|
409
|
+
const boundaryResetNextVar = resetKeyExpression ? nextVar(context, "errorBoundaryResetValue") : undefined;
|
|
410
|
+
const boundaryResetStopVar = resetKeyExpression ? nextVar(context, "stop") : undefined;
|
|
285
411
|
emit(context, indent, `const ${startVar} = document.createComment("error-boundary");`);
|
|
286
412
|
emit(context, indent, `const ${endVar} = document.createComment("/error-boundary");`);
|
|
287
413
|
appendNode(context, parentVar, startVar, indent, beforeVar);
|
|
288
414
|
appendNode(context, parentVar, endVar, indent, beforeVar);
|
|
289
415
|
emit(context, indent, `const ${boundaryCleanupVar} = [];`);
|
|
416
|
+
emit(context, indent, `const ${fallbackRenderVar} = (${errorVar}, ${errorInfoVar} = __mikuru_errorInfo("runtime")) => {`);
|
|
417
|
+
emit(context, indent + 1, `__mikuru_runCleanup(${boundaryCleanupVar});`);
|
|
418
|
+
emitRemoveBetween(context, indent + 1, startVar, endVar);
|
|
419
|
+
emit(context, indent + 1, `const ${fallbackVar} = unwrap(${fallbackExpression});`);
|
|
420
|
+
emit(context, indent + 1, `if (!${fallbackVar} || typeof ${fallbackVar}.mount !== "function") { throw ${errorVar}; }`);
|
|
421
|
+
emit(context, indent + 1, `const ${normalizedErrorInfoVar} = ${errorInfoVar} && typeof ${errorInfoVar} === "object" ? ${errorInfoVar} : {};`);
|
|
422
|
+
if (context.debug) {
|
|
423
|
+
emit(context, indent + 1, `emitDebugEvent("component:error", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id, error: ${errorVar}, errorInfo: { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo } });`);
|
|
424
|
+
}
|
|
425
|
+
emit(context, indent + 1, `const ${fallbackFragmentVar} = document.createDocumentFragment();`);
|
|
426
|
+
emit(context, indent + 1, `const ${fallbackInstanceVar} = ${fallbackVar}.mount(${fallbackFragmentVar}, { error: ${errorVar}, errorInfo: { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo }, retry: ${renderVar}, reset: ${renderVar}, __mikuru_context });`);
|
|
427
|
+
emit(context, indent + 1, `${boundaryCleanupVar}.push(() => ${fallbackInstanceVar}.unmount());`);
|
|
428
|
+
appendNode(context, parentVar, fallbackFragmentVar, indent + 1, endVar);
|
|
429
|
+
emit(context, indent, "};");
|
|
290
430
|
emit(context, indent, `const ${renderVar} = () => {`);
|
|
291
431
|
emit(context, indent + 1, `__mikuru_runCleanup(${boundaryCleanupVar});`);
|
|
292
432
|
emitRemoveBetween(context, indent + 1, startVar, endVar);
|
|
433
|
+
emit(context, indent + 1, `const ${previousErrorHandlerVar} = __mikuru_context.errorHandler;`);
|
|
434
|
+
emit(context, indent + 1, `const ${boundaryContextVar} = { parent: __mikuru_context, provides: new Map(), errorHandler: ${fallbackRenderVar}, ...__mikuru_componentInfo };`);
|
|
435
|
+
emit(context, indent + 1, `__mikuru_context.errorHandler = ${fallbackRenderVar};`);
|
|
293
436
|
emit(context, indent + 1, "try {");
|
|
437
|
+
context.componentContextVar = boundaryContextVar;
|
|
294
438
|
generateNode(context, children[0], parentVar, boundaryCleanupVar, indent + 2, endVar);
|
|
439
|
+
context.componentContextVar = previousComponentContextVar;
|
|
295
440
|
emit(context, indent + 1, `} catch (${errorVar}) {`);
|
|
296
|
-
emit(context, indent + 2,
|
|
297
|
-
|
|
298
|
-
emit(context, indent + 2, `
|
|
299
|
-
emit(context, indent +
|
|
300
|
-
emit(context, indent
|
|
301
|
-
emit(context, indent
|
|
302
|
-
|
|
303
|
-
|
|
441
|
+
emit(context, indent + 2, `${fallbackRenderVar}(${errorVar}, __mikuru_errorInfo("mount"));`);
|
|
442
|
+
emit(context, indent + 1, "} finally {");
|
|
443
|
+
emit(context, indent + 2, `__mikuru_context.errorHandler = ${previousErrorHandlerVar};`);
|
|
444
|
+
emit(context, indent + 1, "}");
|
|
445
|
+
emit(context, indent, "};");
|
|
446
|
+
emit(context, indent, `${renderVar}();`);
|
|
447
|
+
if (resetKeyExpression && boundaryResetInitializedVar && boundaryResetValueVar && boundaryResetNextVar && boundaryResetStopVar) {
|
|
448
|
+
emit(context, indent, `let ${boundaryResetInitializedVar} = false;`);
|
|
449
|
+
emit(context, indent, `let ${boundaryResetValueVar};`);
|
|
450
|
+
emit(context, indent, `const ${boundaryResetStopVar} = effect(() => {`);
|
|
451
|
+
emit(context, indent + 1, `const ${boundaryResetNextVar} = unwrap(${resetKeyExpression});`);
|
|
452
|
+
emit(context, indent + 1, `if (!${boundaryResetInitializedVar}) { ${boundaryResetInitializedVar} = true; ${boundaryResetValueVar} = ${boundaryResetNextVar}; return; }`);
|
|
453
|
+
emit(context, indent + 1, `if (Object.is(${boundaryResetValueVar}, ${boundaryResetNextVar})) { return; }`);
|
|
454
|
+
emit(context, indent + 1, `${boundaryResetValueVar} = ${boundaryResetNextVar};`);
|
|
455
|
+
emit(context, indent + 1, `${renderVar}();`);
|
|
456
|
+
emit(context, indent, "});");
|
|
457
|
+
emit(context, indent, `${cleanupVar}.push(${boundaryResetStopVar});`);
|
|
458
|
+
}
|
|
459
|
+
emit(context, indent, `${cleanupVar}.push(() => {`);
|
|
460
|
+
emit(context, indent + 1, `__mikuru_runCleanup(${boundaryCleanupVar});`);
|
|
461
|
+
emitRemoveBetween(context, indent + 1, startVar, endVar);
|
|
462
|
+
emit(context, indent + 1, `${startVar}.remove();`);
|
|
463
|
+
emit(context, indent + 1, `${endVar}.remove();`);
|
|
464
|
+
emit(context, indent, "});");
|
|
465
|
+
return startVar;
|
|
466
|
+
}
|
|
467
|
+
function generateAsyncBoundary(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
468
|
+
validateAsyncBoundaryAttributes(context, node);
|
|
469
|
+
const children = getAsyncBoundaryChildren(context, node);
|
|
470
|
+
const loadingExpression = getAsyncBoundaryLoadingExpression(context, node);
|
|
471
|
+
const fallbackExpression = getAsyncBoundaryFallbackExpression(context, node);
|
|
472
|
+
const delayExpression = getAsyncBoundaryDelayExpression(context, node);
|
|
473
|
+
const timeoutExpression = getAsyncBoundaryTimeoutExpression(context, node);
|
|
474
|
+
const startVar = nextVar(context, "asyncBoundaryStart");
|
|
475
|
+
const endVar = nextVar(context, "asyncBoundaryEnd");
|
|
476
|
+
const boundaryCleanupVar = nextVar(context, "asyncBoundaryCleanup");
|
|
477
|
+
const loadingCleanupVar = nextVar(context, "asyncBoundaryLoadingCleanup");
|
|
478
|
+
const fallbackCleanupVar = nextVar(context, "asyncBoundaryFallbackCleanup");
|
|
479
|
+
const pendingVar = nextVar(context, "asyncBoundaryPending");
|
|
480
|
+
const failedVar = nextVar(context, "asyncBoundaryFailed");
|
|
481
|
+
const lastRetryVar = nextVar(context, "asyncBoundaryRetry");
|
|
482
|
+
const renderLoadingVar = nextVar(context, "renderAsyncBoundaryLoading");
|
|
483
|
+
const clearLoadingVar = nextVar(context, "clearAsyncBoundaryLoading");
|
|
484
|
+
const renderFallbackVar = nextVar(context, "renderAsyncBoundaryFallback");
|
|
485
|
+
const clearFallbackVar = nextVar(context, "clearAsyncBoundaryFallback");
|
|
486
|
+
const scheduleLoadingVar = nextVar(context, "scheduleAsyncBoundaryLoading");
|
|
487
|
+
const scheduleTimeoutVar = nextVar(context, "scheduleAsyncBoundaryTimeout");
|
|
488
|
+
const clearTimersVar = nextVar(context, "clearAsyncBoundaryTimers");
|
|
489
|
+
const renderVar = nextVar(context, "renderAsyncBoundary");
|
|
490
|
+
const loadingVar = nextVar(context, "asyncBoundaryLoading");
|
|
491
|
+
const loadingFragmentVar = nextVar(context, "asyncBoundaryLoading");
|
|
492
|
+
const loadingInstanceVar = nextVar(context, "asyncBoundaryLoading");
|
|
493
|
+
const fallbackVar = nextVar(context, "asyncBoundaryFallback");
|
|
494
|
+
const fallbackFragmentVar = nextVar(context, "asyncBoundaryFallback");
|
|
495
|
+
const fallbackInstanceVar = nextVar(context, "asyncBoundaryFallback");
|
|
496
|
+
const errorVar = nextVar(context, "error");
|
|
497
|
+
const errorInfoVar = nextVar(context, "errorInfo");
|
|
498
|
+
const errorsVar = nextVar(context, "asyncBoundaryErrors");
|
|
499
|
+
const normalizedErrorInfoVar = nextVar(context, "errorInfo");
|
|
500
|
+
const retryVar = nextVar(context, "retry");
|
|
501
|
+
const settledVar = nextVar(context, "settled");
|
|
502
|
+
const delayVar = nextVar(context, "asyncBoundaryDelay");
|
|
503
|
+
const timeoutVar = nextVar(context, "asyncBoundaryTimeout");
|
|
504
|
+
const delayTimerVar = nextVar(context, "asyncBoundaryDelayTimer");
|
|
505
|
+
const timeoutTimerVar = nextVar(context, "asyncBoundaryTimeoutTimer");
|
|
506
|
+
const timeoutErrorVar = nextVar(context, "asyncBoundaryTimeoutError");
|
|
507
|
+
const asyncContextVar = nextVar(context, "asyncBoundaryContext");
|
|
508
|
+
const previousComponentContextVar = context.componentContextVar;
|
|
509
|
+
emit(context, indent, `const ${startVar} = document.createComment("async-boundary");`);
|
|
510
|
+
emit(context, indent, `const ${endVar} = document.createComment("/async-boundary");`);
|
|
511
|
+
appendNode(context, parentVar, startVar, indent, beforeVar);
|
|
512
|
+
appendNode(context, parentVar, endVar, indent, beforeVar);
|
|
513
|
+
emit(context, indent, `const ${boundaryCleanupVar} = [];`);
|
|
514
|
+
emit(context, indent, `const ${loadingCleanupVar} = [];`);
|
|
515
|
+
emit(context, indent, `const ${fallbackCleanupVar} = [];`);
|
|
516
|
+
emit(context, indent, `let ${pendingVar} = 0;`);
|
|
517
|
+
emit(context, indent, `let ${failedVar} = false;`);
|
|
518
|
+
emit(context, indent, `let ${errorsVar} = [];`);
|
|
519
|
+
emit(context, indent, `let ${lastRetryVar} = () => {};`);
|
|
520
|
+
emit(context, indent, `let ${delayTimerVar};`);
|
|
521
|
+
emit(context, indent, `let ${timeoutTimerVar};`);
|
|
522
|
+
emit(context, indent, `const ${clearLoadingVar} = () => __mikuru_runCleanup(${loadingCleanupVar});`);
|
|
523
|
+
emit(context, indent, `const ${clearFallbackVar} = () => __mikuru_runCleanup(${fallbackCleanupVar});`);
|
|
524
|
+
emit(context, indent, `const ${clearTimersVar} = () => {`);
|
|
525
|
+
emit(context, indent + 1, `if (${delayTimerVar}) { clearTimeout(${delayTimerVar}); ${delayTimerVar} = undefined; }`);
|
|
526
|
+
emit(context, indent + 1, `if (${timeoutTimerVar}) { clearTimeout(${timeoutTimerVar}); ${timeoutTimerVar} = undefined; }`);
|
|
527
|
+
emit(context, indent, "};");
|
|
528
|
+
emit(context, indent, `const ${renderLoadingVar} = () => {`);
|
|
529
|
+
emit(context, indent + 1, `if (${failedVar}) { return; }`);
|
|
530
|
+
emit(context, indent + 1, `${clearLoadingVar}();`);
|
|
531
|
+
emit(context, indent + 1, `const ${loadingVar} = unwrap(${loadingExpression});`);
|
|
532
|
+
emit(context, indent + 1, `if (!${loadingVar} || typeof ${loadingVar}.mount !== "function") { return; }`);
|
|
533
|
+
emit(context, indent + 1, `const ${loadingFragmentVar} = document.createDocumentFragment();`);
|
|
534
|
+
emit(context, indent + 1, `const ${loadingInstanceVar} = ${loadingVar}.mount(${loadingFragmentVar}, { pending: ${pendingVar}, __mikuru_context });`);
|
|
535
|
+
emit(context, indent + 1, `${loadingCleanupVar}.push(() => ${loadingInstanceVar}.unmount());`);
|
|
536
|
+
appendNode(context, parentVar, loadingFragmentVar, indent + 1, endVar);
|
|
537
|
+
emit(context, indent, "};");
|
|
538
|
+
emit(context, indent, `const ${renderFallbackVar} = (${errorVar}, ${errorInfoVar} = __mikuru_errorInfo("async-loader")) => {`);
|
|
539
|
+
emit(context, indent + 1, `${failedVar} = true;`);
|
|
540
|
+
emit(context, indent + 1, `${clearTimersVar}();`);
|
|
541
|
+
emit(context, indent + 1, `${clearLoadingVar}();`);
|
|
542
|
+
emit(context, indent + 1, `${clearFallbackVar}();`);
|
|
543
|
+
emit(context, indent + 1, `__mikuru_runCleanup(${boundaryCleanupVar});`);
|
|
544
|
+
emitRemoveBetween(context, indent + 1, startVar, endVar);
|
|
545
|
+
emit(context, indent + 1, `const ${fallbackVar} = unwrap(${fallbackExpression});`);
|
|
546
|
+
emit(context, indent + 1, `if (!${fallbackVar} || typeof ${fallbackVar}.mount !== "function") { throw ${errorVar}; }`);
|
|
547
|
+
emit(context, indent + 1, `const ${normalizedErrorInfoVar} = ${errorInfoVar} && typeof ${errorInfoVar} === "object" ? ${errorInfoVar} : {};`);
|
|
548
|
+
if (context.debug) {
|
|
549
|
+
emit(context, indent + 1, `emitDebugEvent("async:rejected", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id, error: ${errorVar}, errors: [...${errorsVar}], pending: ${pendingVar}, errorInfo: { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo } });`);
|
|
550
|
+
}
|
|
551
|
+
emit(context, indent + 1, `const ${fallbackFragmentVar} = document.createDocumentFragment();`);
|
|
552
|
+
emit(context, indent + 1, `const ${fallbackInstanceVar} = ${fallbackVar}.mount(${fallbackFragmentVar}, { error: ${errorVar}, errors: [...${errorsVar}], errorInfo: { ...${normalizedErrorInfoVar}, boundary: __mikuru_componentInfo }, pending: ${pendingVar}, retry: ${lastRetryVar}, reset: ${lastRetryVar}, __mikuru_context });`);
|
|
553
|
+
emit(context, indent + 1, `${fallbackCleanupVar}.push(() => ${fallbackInstanceVar}.unmount());`);
|
|
554
|
+
appendNode(context, parentVar, fallbackFragmentVar, indent + 1, endVar);
|
|
555
|
+
emit(context, indent, "};");
|
|
556
|
+
emit(context, indent, `const ${scheduleLoadingVar} = () => {`);
|
|
557
|
+
emit(context, indent + 1, `const ${delayVar} = Number(unwrap(${delayExpression}) ?? 0);`);
|
|
558
|
+
emit(context, indent + 1, `if (${delayVar} <= 0) { ${renderLoadingVar}(); return; }`);
|
|
559
|
+
emit(context, indent + 1, `if (${delayTimerVar}) { return; }`);
|
|
560
|
+
emit(context, indent + 1, `${delayTimerVar} = setTimeout(() => {`);
|
|
561
|
+
emit(context, indent + 2, `${delayTimerVar} = undefined;`);
|
|
562
|
+
emit(context, indent + 2, `if (${pendingVar} > 0 && !${failedVar}) { ${renderLoadingVar}(); }`);
|
|
563
|
+
emit(context, indent + 1, `}, ${delayVar});`);
|
|
564
|
+
emit(context, indent, "};");
|
|
565
|
+
emit(context, indent, `const ${scheduleTimeoutVar} = () => {`);
|
|
566
|
+
emit(context, indent + 1, `const ${timeoutVar} = unwrap(${timeoutExpression});`);
|
|
567
|
+
emit(context, indent + 1, `if (${timeoutVar} == null || Number(${timeoutVar}) <= 0 || ${timeoutTimerVar}) { return; }`);
|
|
568
|
+
emit(context, indent + 1, `${timeoutTimerVar} = setTimeout(() => {`);
|
|
569
|
+
emit(context, indent + 2, `${timeoutTimerVar} = undefined;`);
|
|
570
|
+
emit(context, indent + 2, `if (${pendingVar} <= 0 || ${failedVar}) { return; }`);
|
|
571
|
+
emit(context, indent + 2, `const ${timeoutErrorVar} = new Error("Async boundary timed out");`);
|
|
572
|
+
emit(context, indent + 2, `${errorsVar}.push(${timeoutErrorVar});`);
|
|
573
|
+
emit(context, indent + 2, `${renderFallbackVar}(${timeoutErrorVar}, __mikuru_errorInfo("async-timeout"));`);
|
|
574
|
+
emit(context, indent + 1, `}, Number(${timeoutVar}));`);
|
|
575
|
+
emit(context, indent, "};");
|
|
576
|
+
emit(context, indent, `const ${asyncContextVar} = { parent: __mikuru_context, provides: new Map(), errorHandler: __mikuru_context.errorHandler, asyncBoundary: {`);
|
|
577
|
+
emit(context, indent + 1, `start({ retry: ${retryVar} }) {`);
|
|
578
|
+
emit(context, indent + 2, `${pendingVar} += 1;`);
|
|
579
|
+
emit(context, indent + 2, `${lastRetryVar} = ${renderVar};`);
|
|
580
|
+
if (context.debug) {
|
|
581
|
+
emit(context, indent + 2, `emitDebugEvent("async:pending", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id, pending: ${pendingVar} });`);
|
|
582
|
+
}
|
|
583
|
+
emit(context, indent + 2, `if (${pendingVar} === 1) { ${scheduleLoadingVar}(); ${scheduleTimeoutVar}(); } else if (${loadingCleanupVar}.length > 0) { ${renderLoadingVar}(); }`);
|
|
584
|
+
emit(context, indent + 2, `let ${settledVar} = false;`);
|
|
585
|
+
emit(context, indent + 2, "return {");
|
|
586
|
+
emit(context, indent + 3, "resolve() {");
|
|
587
|
+
emit(context, indent + 4, `if (${settledVar}) { return; }`);
|
|
588
|
+
emit(context, indent + 4, `${settledVar} = true;`);
|
|
589
|
+
emit(context, indent + 4, `${pendingVar} = Math.max(0, ${pendingVar} - 1);`);
|
|
590
|
+
if (context.debug) {
|
|
591
|
+
emit(context, indent + 4, `emitDebugEvent("async:resolved", { component: __mikuru_componentInfo, componentId: __mikuru_debug.id, pending: ${pendingVar} });`);
|
|
592
|
+
}
|
|
593
|
+
emit(context, indent + 4, `if (${pendingVar} === 0) { ${clearTimersVar}(); ${clearLoadingVar}(); } else if (${loadingCleanupVar}.length > 0) { ${renderLoadingVar}(); }`);
|
|
594
|
+
emit(context, indent + 3, "},");
|
|
595
|
+
emit(context, indent + 3, `reject(${errorVar}, ${errorInfoVar}) {`);
|
|
596
|
+
emit(context, indent + 4, `if (${settledVar}) { return; }`);
|
|
597
|
+
emit(context, indent + 4, `${settledVar} = true;`);
|
|
598
|
+
emit(context, indent + 4, `${pendingVar} = Math.max(0, ${pendingVar} - 1);`);
|
|
599
|
+
emit(context, indent + 4, `${errorsVar}.push(${errorVar});`);
|
|
600
|
+
emit(context, indent + 4, `${clearTimersVar}();`);
|
|
601
|
+
emit(context, indent + 4, `${renderFallbackVar}(${errorVar}, ${errorInfoVar});`);
|
|
602
|
+
emit(context, indent + 3, "}");
|
|
603
|
+
emit(context, indent + 2, "};");
|
|
304
604
|
emit(context, indent + 1, "}");
|
|
605
|
+
emit(context, indent, `}, ...__mikuru_componentInfo };`);
|
|
606
|
+
emit(context, indent, `const ${renderVar} = () => {`);
|
|
607
|
+
emit(context, indent + 1, `${failedVar} = false;`);
|
|
608
|
+
emit(context, indent + 1, `${pendingVar} = 0;`);
|
|
609
|
+
emit(context, indent + 1, `${errorsVar} = [];`);
|
|
610
|
+
emit(context, indent + 1, `${lastRetryVar} = ${renderVar};`);
|
|
611
|
+
emit(context, indent + 1, `${clearTimersVar}();`);
|
|
612
|
+
emit(context, indent + 1, `${clearLoadingVar}();`);
|
|
613
|
+
emit(context, indent + 1, `${clearFallbackVar}();`);
|
|
614
|
+
emit(context, indent + 1, `__mikuru_runCleanup(${boundaryCleanupVar});`);
|
|
615
|
+
emitRemoveBetween(context, indent + 1, startVar, endVar);
|
|
616
|
+
context.componentContextVar = asyncContextVar;
|
|
617
|
+
generateChildren(context, children, parentVar, boundaryCleanupVar, indent + 1, endVar);
|
|
618
|
+
context.componentContextVar = previousComponentContextVar;
|
|
305
619
|
emit(context, indent, "};");
|
|
306
620
|
emit(context, indent, `${renderVar}();`);
|
|
307
621
|
emit(context, indent, `${cleanupVar}.push(() => {`);
|
|
622
|
+
emit(context, indent + 1, `${clearTimersVar}();`);
|
|
623
|
+
emit(context, indent + 1, `${clearLoadingVar}();`);
|
|
624
|
+
emit(context, indent + 1, `${clearFallbackVar}();`);
|
|
308
625
|
emit(context, indent + 1, `__mikuru_runCleanup(${boundaryCleanupVar});`);
|
|
309
626
|
emitRemoveBetween(context, indent + 1, startVar, endVar);
|
|
310
627
|
emit(context, indent + 1, `${startVar}.remove();`);
|
|
@@ -458,6 +775,149 @@ function generateDynamicComponent(context, node, parentVar, cleanupVar, indent,
|
|
|
458
775
|
emit(context, indent, "});");
|
|
459
776
|
return startVar;
|
|
460
777
|
}
|
|
778
|
+
function generateKeepAlive(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
779
|
+
validateKeepAliveAttributes(context, node);
|
|
780
|
+
const children = getSingleElementChild(context, node, "<KeepAlive>");
|
|
781
|
+
const child = children[0];
|
|
782
|
+
if (child.tag !== "component") {
|
|
783
|
+
throwTemplateError("<KeepAlive> requires a single <component :is=\"...\" /> child in v1", context, child.loc);
|
|
784
|
+
}
|
|
785
|
+
const isAttr = child.attrs.find((attr) => getBindingName(attr.name) === "is");
|
|
786
|
+
if (!isAttr) {
|
|
787
|
+
throwTemplateError("<KeepAlive> dynamic child requires :is to resolve to a component object", context, child.loc);
|
|
788
|
+
}
|
|
789
|
+
const expression = compileTemplateExpression(requireAttrValue(isAttr), isAttr.name, toExpressionContext(context, isAttr.valueLoc));
|
|
790
|
+
const includeExpression = getKeepAliveOptionExpression(context, node, "include");
|
|
791
|
+
const excludeExpression = getKeepAliveOptionExpression(context, node, "exclude");
|
|
792
|
+
const maxExpression = getKeepAliveOptionExpression(context, node, "max");
|
|
793
|
+
const dynamicNode = withoutAttrs(child, ["is", ":is", "v-bind:is"]);
|
|
794
|
+
const startVar = nextVar(context, "keepAliveStart");
|
|
795
|
+
const endVar = nextVar(context, "keepAliveEnd");
|
|
796
|
+
const cacheVar = nextVar(context, "keepAliveCache");
|
|
797
|
+
const currentTypeVar = nextVar(context, "keepAliveCurrent");
|
|
798
|
+
const currentRecordVar = nextVar(context, "keepAliveCurrent");
|
|
799
|
+
const componentTypeVar = nextVar(context, "keepAliveType");
|
|
800
|
+
const componentNameVar = nextVar(context, "keepAliveName");
|
|
801
|
+
const componentNameHelperVar = nextVar(context, "keepAliveName");
|
|
802
|
+
const matchHelperVar = nextVar(context, "keepAliveMatches");
|
|
803
|
+
const includeVar = nextVar(context, "keepAliveInclude");
|
|
804
|
+
const excludeVar = nextVar(context, "keepAliveExclude");
|
|
805
|
+
const maxVar = nextVar(context, "keepAliveMax");
|
|
806
|
+
const cacheableVar = nextVar(context, "keepAliveCacheable");
|
|
807
|
+
const recordVar = nextVar(context, "keepAliveRecord");
|
|
808
|
+
const recordNodesVar = nextVar(context, "keepAliveNodes");
|
|
809
|
+
const activateRecordVar = nextVar(context, "keepAliveActivate");
|
|
810
|
+
const deactivateRecordVar = nextVar(context, "keepAliveDeactivate");
|
|
811
|
+
const collectNodesVar = nextVar(context, "keepAliveCollectNodes");
|
|
812
|
+
const collectNodeVar = nextVar(context, "keepAliveNode");
|
|
813
|
+
const staleKeyVar = nextVar(context, "keepAliveStaleKey");
|
|
814
|
+
const staleRecordVar = nextVar(context, "keepAliveStaleRecord");
|
|
815
|
+
const fragmentVar = nextVar(context, "fragment");
|
|
816
|
+
const attrsVar = nextVar(context, "attrs");
|
|
817
|
+
const propsVar = nextVar(context, "props");
|
|
818
|
+
const componentVar = nextVar(context, "component");
|
|
819
|
+
const recordCleanupVar = nextVar(context, "keepAliveCleanup");
|
|
820
|
+
const stopVar = nextVar(context, "stop");
|
|
821
|
+
emit(context, indent, `const ${startVar} = document.createComment("keep-alive");`);
|
|
822
|
+
emit(context, indent, `const ${endVar} = document.createComment("/keep-alive");`);
|
|
823
|
+
appendNode(context, parentVar, startVar, indent, beforeVar);
|
|
824
|
+
appendNode(context, parentVar, endVar, indent, beforeVar);
|
|
825
|
+
emit(context, indent, `const ${cacheVar} = new Map();`);
|
|
826
|
+
emit(context, indent, `let ${currentTypeVar};`);
|
|
827
|
+
emit(context, indent, `let ${currentRecordVar};`);
|
|
828
|
+
emit(context, indent, `const ${componentNameHelperVar} = (component) => component?.name ?? component?.displayName ?? component?.__name ?? component?.constructor?.name ?? "";`);
|
|
829
|
+
emit(context, indent, `const ${matchHelperVar} = (pattern, name) => pattern == null ? false : typeof pattern === "string" ? pattern.split(",").map((part) => part.trim()).filter(Boolean).includes(name) : Array.isArray(pattern) ? pattern.some((entry) => ${matchHelperVar}(entry, name)) : pattern instanceof RegExp ? pattern.test(name) : false;`);
|
|
830
|
+
emit(context, indent, `const ${activateRecordVar} = (record) => { if (record && !record.active) { record.active = true; record.instance.activate?.(); } };`);
|
|
831
|
+
emit(context, indent, `const ${deactivateRecordVar} = (record) => { if (record?.active) { record.active = false; record.instance.deactivate?.(); } };`);
|
|
832
|
+
emit(context, indent, `const ${collectNodesVar} = () => { const nodes = []; for (let ${collectNodeVar} = ${startVar}.nextSibling; ${collectNodeVar} && ${collectNodeVar} !== ${endVar}; ${collectNodeVar} = ${collectNodeVar}.nextSibling) { nodes.push(${collectNodeVar}); } return nodes; };`);
|
|
833
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
834
|
+
emit(context, indent + 1, `const ${componentTypeVar} = unwrap(${expression});`);
|
|
835
|
+
emit(context, indent + 1, `if (${componentTypeVar} === ${currentTypeVar}) { return; }`);
|
|
836
|
+
emit(context, indent + 1, `if (${currentRecordVar}) { ${currentRecordVar}.nodes = ${collectNodesVar}(); }`);
|
|
837
|
+
emit(context, indent + 1, `if (${currentRecordVar}?.transient) { __mikuru_runCleanup(${currentRecordVar}.cleanups); } else { ${deactivateRecordVar}(${currentRecordVar}); }`);
|
|
838
|
+
emitRemoveBetween(context, indent + 1, startVar, endVar);
|
|
839
|
+
emit(context, indent + 1, `if (!${componentTypeVar}) { ${currentTypeVar} = ${componentTypeVar}; ${currentRecordVar} = undefined; return; }`);
|
|
840
|
+
emit(context, indent + 1, `if (typeof ${componentTypeVar} !== "object" || typeof ${componentTypeVar}.mount !== "function") {`);
|
|
841
|
+
emit(context, indent + 2, `throw new Error("KeepAlive child :is must resolve to a component object with mount()");`);
|
|
842
|
+
emit(context, indent + 1, "}");
|
|
843
|
+
emit(context, indent + 1, `const ${componentNameVar} = ${componentNameHelperVar}(${componentTypeVar});`);
|
|
844
|
+
emit(context, indent + 1, `const ${includeVar} = unwrap(${includeExpression ?? "undefined"});`);
|
|
845
|
+
emit(context, indent + 1, `const ${excludeVar} = unwrap(${excludeExpression ?? "undefined"});`);
|
|
846
|
+
emit(context, indent + 1, `const ${maxVar} = Number(unwrap(${maxExpression ?? "undefined"}));`);
|
|
847
|
+
emit(context, indent + 1, `const ${cacheableVar} = (${includeVar} == null || ${matchHelperVar}(${includeVar}, ${componentNameVar})) && !${matchHelperVar}(${excludeVar}, ${componentNameVar});`);
|
|
848
|
+
emit(context, indent + 1, `if (!${cacheableVar}) {`);
|
|
849
|
+
emit(context, indent + 2, `const ${fragmentVar} = document.createDocumentFragment();`);
|
|
850
|
+
emit(context, indent + 2, `const ${recordCleanupVar} = [];`);
|
|
851
|
+
emitComponentAttrs(context, dynamicNode, attrsVar, indent + 2);
|
|
852
|
+
emitComponentProps(context, dynamicNode, propsVar, attrsVar, indent + 2);
|
|
853
|
+
emit(context, indent + 2, `const ${componentVar} = ${componentTypeVar}.mount(${fragmentVar}, ${propsVar});`);
|
|
854
|
+
if (context.scopeAttr) {
|
|
855
|
+
emit(context, indent + 2, `if (${componentVar}.element?.nodeType === 1) {`);
|
|
856
|
+
emit(context, indent + 3, `${componentVar}.element.setAttribute(${quote(context.scopeAttr)}, "");`);
|
|
857
|
+
emit(context, indent + 2, "}");
|
|
858
|
+
}
|
|
859
|
+
emit(context, indent + 2, `if (${componentTypeVar}.inheritAttrs !== false) {`);
|
|
860
|
+
emitComponentFallthrough(context, dynamicNode, componentVar, recordCleanupVar, indent + 3);
|
|
861
|
+
emit(context, indent + 2, "}");
|
|
862
|
+
emitComponentShow(context, dynamicNode, componentVar, recordCleanupVar, indent + 2);
|
|
863
|
+
emitTemplateRef(context, dynamicNode, componentVar, recordCleanupVar, indent + 2);
|
|
864
|
+
emit(context, indent + 2, `const ${recordNodesVar} = Array.from(${fragmentVar}.childNodes);`);
|
|
865
|
+
appendNode(context, parentVar, fragmentVar, indent + 2, endVar);
|
|
866
|
+
emit(context, indent + 2, `${currentTypeVar} = ${componentTypeVar};`);
|
|
867
|
+
emit(context, indent + 2, `${currentRecordVar} = { instance: ${componentVar}, element: ${componentVar}.element, nodes: ${recordNodesVar}, cleanups: ${recordCleanupVar}, transient: true };`);
|
|
868
|
+
emit(context, indent + 2, `${recordCleanupVar}.push(() => ${componentVar}.unmount());`);
|
|
869
|
+
emit(context, indent + 2, "return;");
|
|
870
|
+
emit(context, indent + 1, "}");
|
|
871
|
+
emit(context, indent + 1, `let ${recordVar} = ${cacheVar}.get(${componentTypeVar});`);
|
|
872
|
+
emit(context, indent + 1, `if (!${recordVar}) {`);
|
|
873
|
+
emit(context, indent + 2, `const ${fragmentVar} = document.createDocumentFragment();`);
|
|
874
|
+
emit(context, indent + 2, `const ${recordCleanupVar} = [];`);
|
|
875
|
+
emitComponentAttrs(context, dynamicNode, attrsVar, indent + 2);
|
|
876
|
+
emitComponentProps(context, dynamicNode, propsVar, attrsVar, indent + 2);
|
|
877
|
+
emit(context, indent + 2, `const ${componentVar} = ${componentTypeVar}.mount(${fragmentVar}, ${propsVar});`);
|
|
878
|
+
if (context.scopeAttr) {
|
|
879
|
+
emit(context, indent + 2, `if (${componentVar}.element?.nodeType === 1) {`);
|
|
880
|
+
emit(context, indent + 3, `${componentVar}.element.setAttribute(${quote(context.scopeAttr)}, "");`);
|
|
881
|
+
emit(context, indent + 2, "}");
|
|
882
|
+
}
|
|
883
|
+
emit(context, indent + 2, `if (${componentTypeVar}.inheritAttrs !== false) {`);
|
|
884
|
+
emitComponentFallthrough(context, dynamicNode, componentVar, recordCleanupVar, indent + 3);
|
|
885
|
+
emit(context, indent + 2, "}");
|
|
886
|
+
emitComponentShow(context, dynamicNode, componentVar, recordCleanupVar, indent + 2);
|
|
887
|
+
emitTemplateRef(context, dynamicNode, componentVar, recordCleanupVar, indent + 2);
|
|
888
|
+
emit(context, indent + 2, `const ${recordNodesVar} = Array.from(${fragmentVar}.childNodes);`);
|
|
889
|
+
emit(context, indent + 2, `${recordVar} = { instance: ${componentVar}, element: ${componentVar}.element, nodes: ${recordNodesVar}, cleanups: ${recordCleanupVar}, active: false };`);
|
|
890
|
+
emit(context, indent + 2, `${cacheVar}.set(${componentTypeVar}, ${recordVar});`);
|
|
891
|
+
emit(context, indent + 2, `if (Number.isFinite(${maxVar}) && ${maxVar} > 0) {`);
|
|
892
|
+
emit(context, indent + 3, `while (${cacheVar}.size > ${maxVar}) {`);
|
|
893
|
+
emit(context, indent + 4, `const ${staleKeyVar} = ${cacheVar}.keys().next().value;`);
|
|
894
|
+
emit(context, indent + 4, `if (${staleKeyVar} === ${componentTypeVar}) { break; }`);
|
|
895
|
+
emit(context, indent + 4, `const ${staleRecordVar} = ${cacheVar}.get(${staleKeyVar});`);
|
|
896
|
+
emit(context, indent + 4, `${cacheVar}.delete(${staleKeyVar});`);
|
|
897
|
+
emit(context, indent + 4, `if (${staleRecordVar}) { ${deactivateRecordVar}(${staleRecordVar}); __mikuru_runCleanup(${staleRecordVar}.cleanups); ${staleRecordVar}.instance.unmount(); }`);
|
|
898
|
+
emit(context, indent + 3, "}");
|
|
899
|
+
emit(context, indent + 2, "}");
|
|
900
|
+
appendNode(context, parentVar, fragmentVar, indent + 2, endVar);
|
|
901
|
+
emit(context, indent + 1, "} else {");
|
|
902
|
+
emit(context, indent + 2, `${cacheVar}.delete(${componentTypeVar});`);
|
|
903
|
+
emit(context, indent + 2, `${cacheVar}.set(${componentTypeVar}, ${recordVar});`);
|
|
904
|
+
emit(context, indent + 2, `for (const node of ${recordVar}.nodes) { ${parentVar}.insertBefore(node, ${endVar}); }`);
|
|
905
|
+
emit(context, indent + 1, "}");
|
|
906
|
+
emit(context, indent + 1, `${currentTypeVar} = ${componentTypeVar};`);
|
|
907
|
+
emit(context, indent + 1, `${currentRecordVar} = ${recordVar};`);
|
|
908
|
+
emit(context, indent + 1, `${activateRecordVar}(${recordVar});`);
|
|
909
|
+
emit(context, indent, "});");
|
|
910
|
+
emit(context, indent, `${cleanupVar}.push(() => {`);
|
|
911
|
+
emit(context, indent + 1, `${stopVar}();`);
|
|
912
|
+
emit(context, indent + 1, `if (${currentRecordVar}?.transient) { __mikuru_runCleanup(${currentRecordVar}.cleanups); } else { ${deactivateRecordVar}(${currentRecordVar}); }`);
|
|
913
|
+
emit(context, indent + 1, `for (const ${recordVar} of ${cacheVar}.values()) { __mikuru_runCleanup(${recordVar}.cleanups); ${recordVar}.instance.unmount(); }`);
|
|
914
|
+
emit(context, indent + 1, `${cacheVar}.clear();`);
|
|
915
|
+
emitRemoveBetween(context, indent + 1, startVar, endVar);
|
|
916
|
+
emit(context, indent + 1, `${startVar}.remove();`);
|
|
917
|
+
emit(context, indent + 1, `${endVar}.remove();`);
|
|
918
|
+
emit(context, indent, "});");
|
|
919
|
+
return startVar;
|
|
920
|
+
}
|
|
461
921
|
function emitComponentShow(context, node, componentVar, cleanupVar, indent) {
|
|
462
922
|
const showAttr = node.attrs.find((attr) => attr.name === "v-show");
|
|
463
923
|
if (!showAttr) {
|
|
@@ -479,7 +939,10 @@ function emitComponentShow(context, node, componentVar, cleanupVar, indent) {
|
|
|
479
939
|
function emitComponentAttrs(context, node, attrsVar, indent) {
|
|
480
940
|
const objectBindExpressions = node.attrs
|
|
481
941
|
.filter((attr) => isObjectBindAttr(attr))
|
|
482
|
-
.map((attr) =>
|
|
942
|
+
.map((attr) => {
|
|
943
|
+
validateObjectBindModifiers(parseObjectBindDirective(attr.name) ?? { modifiers: [] }, attr, context, "component");
|
|
944
|
+
return compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
945
|
+
});
|
|
483
946
|
const classParts = componentFallthroughExpressions(context, node, "class", objectBindExpressions);
|
|
484
947
|
const styleParts = componentFallthroughExpressions(context, node, "style", objectBindExpressions);
|
|
485
948
|
const directAttrs = componentDirectAttributeFallthroughs(context, node);
|
|
@@ -553,7 +1016,10 @@ function emitComponentAttrsProxy(context, attrsVar, baseVar, objectBindExpressio
|
|
|
553
1016
|
function emitComponentFallthrough(context, node, componentVar, cleanupVar, indent) {
|
|
554
1017
|
const objectBindExpressions = node.attrs
|
|
555
1018
|
.filter((attr) => isObjectBindAttr(attr))
|
|
556
|
-
.map((attr) =>
|
|
1019
|
+
.map((attr) => {
|
|
1020
|
+
validateObjectBindModifiers(parseObjectBindDirective(attr.name) ?? { modifiers: [] }, attr, context, "component");
|
|
1021
|
+
return compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
1022
|
+
});
|
|
557
1023
|
const classParts = componentFallthroughExpressions(context, node, "class", objectBindExpressions);
|
|
558
1024
|
const styleParts = componentFallthroughExpressions(context, node, "style", objectBindExpressions);
|
|
559
1025
|
const directAttrs = componentDirectAttributeFallthroughs(context, node);
|
|
@@ -688,7 +1154,7 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
688
1154
|
if (slotDirectiveAttr) {
|
|
689
1155
|
throwTemplateError("v-slot must be used on a <template> child in Mikuru", context, slotDirectiveAttr.loc);
|
|
690
1156
|
}
|
|
691
|
-
validateAttributes(node);
|
|
1157
|
+
validateAttributes(context, node);
|
|
692
1158
|
const elementVar = nextVar(context, "el");
|
|
693
1159
|
emit(context, indent, `const ${elementVar} = document.createElement(${quote(node.tag)});`);
|
|
694
1160
|
if (context.scopeAttr) {
|
|
@@ -704,11 +1170,20 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
704
1170
|
emit(context, indent, `setAttribute(${elementVar}, ${quote(attr.name)}, ${quote(attr.value === true ? "" : attr.value)});`);
|
|
705
1171
|
}
|
|
706
1172
|
emitTemplateRef(context, node, elementVar, cleanupVar, indent);
|
|
707
|
-
|
|
1173
|
+
const contentDirective = getContentDirectiveAttr(node);
|
|
1174
|
+
if (contentDirective) {
|
|
1175
|
+
emitContentDirective(context, node, elementVar, contentDirective, cleanupVar, indent);
|
|
1176
|
+
}
|
|
1177
|
+
else {
|
|
1178
|
+
generateChildren(context, node.children, elementVar, cleanupVar, indent);
|
|
1179
|
+
}
|
|
708
1180
|
for (const attr of node.attrs) {
|
|
709
1181
|
const modelDirective = parseModelDirective(attr.name);
|
|
710
1182
|
if (modelDirective) {
|
|
711
1183
|
validateModelModifiers(modelDirective, attr, context);
|
|
1184
|
+
if (modelDirective.argument) {
|
|
1185
|
+
throwTemplateError("v-model arguments are only supported on components in v1", context, attr.loc);
|
|
1186
|
+
}
|
|
712
1187
|
const expression = validateAssignableExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
713
1188
|
const stopVar = nextVar(context, "stop");
|
|
714
1189
|
const handlerVar = nextVar(context, "handler");
|
|
@@ -726,15 +1201,17 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
726
1201
|
const eventName = modelMode === "text" && !modelDirective.modifiers.includes("lazy") ? "input" : "change";
|
|
727
1202
|
const propertyName = modelMode === "checkbox" || modelMode === "radio" ? "checked" : "value";
|
|
728
1203
|
const renderedValue = modelMode === "checkbox"
|
|
729
|
-
? `
|
|
1204
|
+
? `(() => { const value = unwrap(${expression}); const checkboxValue = ${modelElementValueExpression(`${elementVar}`, modelDirective.modifiers)}; return Array.isArray(value) ? value.some((item) => Object.is(item, checkboxValue)) : Boolean(value); })()`
|
|
730
1205
|
: modelMode === "radio"
|
|
731
|
-
? `(${
|
|
1206
|
+
? `Object.is(${modelElementValueExpression(`${elementVar}`, modelDirective.modifiers)}, unwrap(${expression}))`
|
|
732
1207
|
: modelMode === "select-multiple"
|
|
733
|
-
? `Array.from(${elementVar}.options).forEach((option) => { option.selected = (unwrap(${expression}) ?? []).
|
|
734
|
-
:
|
|
735
|
-
|
|
1208
|
+
? `Array.from(${elementVar}.options).forEach((option) => { const optionValue = ${modelElementValueExpression("option", modelDirective.modifiers)}; option.selected = (unwrap(${expression}) ?? []).some((item) => Object.is(item, optionValue)); })`
|
|
1209
|
+
: modelMode === "select"
|
|
1210
|
+
? `Array.from(${elementVar}.options).forEach((option) => { option.selected = Object.is(${modelElementValueExpression("option", modelDirective.modifiers)}, unwrap(${expression})); })`
|
|
1211
|
+
: `String(unwrap(${expression}) ?? "")`;
|
|
1212
|
+
const assignedValue = modelAssignedValue(modelMode, modelDirective.modifiers, expression);
|
|
736
1213
|
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
737
|
-
if (modelMode === "select-multiple") {
|
|
1214
|
+
if (modelMode === "select-multiple" || modelMode === "select") {
|
|
738
1215
|
emit(context, indent + 1, renderedValue);
|
|
739
1216
|
}
|
|
740
1217
|
else {
|
|
@@ -744,20 +1221,25 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
744
1221
|
}
|
|
745
1222
|
emit(context, indent, "});");
|
|
746
1223
|
emit(context, indent, `${cleanupVar}.push(${stopVar});`);
|
|
747
|
-
emit(context, indent, `const ${handlerVar} = ($event) => {`);
|
|
1224
|
+
emit(context, indent, `const ${handlerVar} = __mikuru_guardEventHandler(($event) => {`);
|
|
748
1225
|
emit(context, indent + 1, `${expression}.value = ${assignedValue};`);
|
|
749
|
-
emit(context, indent, "};");
|
|
1226
|
+
emit(context, indent, "});");
|
|
750
1227
|
emit(context, indent, `${elementVar}.addEventListener(${quote(eventName)}, ${handlerVar});`);
|
|
751
1228
|
emit(context, indent, `${cleanupVar}.push(() => ${elementVar}.removeEventListener(${quote(eventName)}, ${handlerVar}));`);
|
|
752
1229
|
continue;
|
|
753
1230
|
}
|
|
754
1231
|
if (attr.name === "v-show") {
|
|
755
1232
|
const expression = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
1233
|
+
if (context.once) {
|
|
1234
|
+
emit(context, indent, `${elementVar}.style.display = unwrap(${expression}) ? "" : "none";`);
|
|
1235
|
+
}
|
|
1236
|
+
else {
|
|
1237
|
+
const stopVar = nextVar(context, "stop");
|
|
1238
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
1239
|
+
emit(context, indent + 1, `${elementVar}.style.display = unwrap(${expression}) ? "" : "none";`);
|
|
1240
|
+
emit(context, indent, "});");
|
|
1241
|
+
emit(context, indent, `${cleanupVar}.push(${stopVar});`);
|
|
1242
|
+
}
|
|
761
1243
|
continue;
|
|
762
1244
|
}
|
|
763
1245
|
if (isObjectBindAttr(attr)) {
|
|
@@ -771,16 +1253,22 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
771
1253
|
const event = parseEventDirective(attr.name);
|
|
772
1254
|
if (event) {
|
|
773
1255
|
validateEventModifiers(event, attr, context);
|
|
774
|
-
const handler =
|
|
1256
|
+
const handler = validateEventHandlerExpression(requireAttrValue(attr), context, attr.valueLoc);
|
|
775
1257
|
const handlerVar = nextVar(context, "handler");
|
|
776
1258
|
const handlerExpression = eventHandlerExpression(handler, context, attr.valueLoc);
|
|
777
1259
|
if (event.modifiers.length) {
|
|
778
1260
|
const baseHandlerVar = nextVar(context, "handler");
|
|
1261
|
+
const errorHandlerVar = nextVar(context, "errorHandler");
|
|
779
1262
|
emit(context, indent, `const ${baseHandlerVar} = ${handlerExpression};`);
|
|
780
|
-
emit(context, indent, `const ${
|
|
1263
|
+
emit(context, indent, `const ${errorHandlerVar} = __mikuru_context.errorHandler;`);
|
|
1264
|
+
emit(context, indent, `const ${handlerVar} = ($event) => __mikuru_try(() => {`);
|
|
781
1265
|
if (event.modifiers.includes("self")) {
|
|
782
1266
|
emit(context, indent + 1, `if ($event.target !== ${elementVar}) { return; }`);
|
|
783
1267
|
}
|
|
1268
|
+
const modifierGuard = eventModifierGuardExpression(event);
|
|
1269
|
+
if (modifierGuard) {
|
|
1270
|
+
emit(context, indent + 1, `if (${modifierGuard}) { return; }`);
|
|
1271
|
+
}
|
|
784
1272
|
if (event.modifiers.includes("prevent")) {
|
|
785
1273
|
emit(context, indent + 1, "$event.preventDefault();");
|
|
786
1274
|
}
|
|
@@ -788,35 +1276,138 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
788
1276
|
emit(context, indent + 1, "$event.stopPropagation();");
|
|
789
1277
|
}
|
|
790
1278
|
emit(context, indent + 1, `return ${baseHandlerVar}($event);`);
|
|
791
|
-
emit(context, indent,
|
|
1279
|
+
emit(context, indent, `}, ${errorHandlerVar}, "event");`);
|
|
792
1280
|
}
|
|
793
1281
|
else {
|
|
794
|
-
emit(context, indent, `const ${handlerVar} = ${handlerExpression};`);
|
|
1282
|
+
emit(context, indent, `const ${handlerVar} = __mikuru_guardEventHandler(${handlerExpression});`);
|
|
1283
|
+
}
|
|
1284
|
+
if (event.nameExpression) {
|
|
1285
|
+
emitDynamicEventListener(context, elementVar, event, handlerVar, attr, cleanupVar, indent);
|
|
1286
|
+
}
|
|
1287
|
+
else {
|
|
1288
|
+
const eventOptions = eventListenerOptions(event);
|
|
1289
|
+
emit(context, indent, `${elementVar}.addEventListener(${quote(event.name ?? "")}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""});`);
|
|
1290
|
+
emit(context, indent, `${cleanupVar}.push(() => ${elementVar}.removeEventListener(${quote(event.name ?? "")}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""}));`);
|
|
795
1291
|
}
|
|
796
|
-
const eventOptions = eventListenerOptions(event);
|
|
797
|
-
emit(context, indent, `${elementVar}.addEventListener(${quote(event.name)}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""});`);
|
|
798
|
-
emit(context, indent, `${cleanupVar}.push(() => ${elementVar}.removeEventListener(${quote(event.name)}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""}));`);
|
|
799
1292
|
continue;
|
|
800
1293
|
}
|
|
801
|
-
const
|
|
802
|
-
|
|
1294
|
+
const bindDirective = parseBindDirective(attr.name);
|
|
1295
|
+
const dynamicBinding = bindDirective?.nameExpression ? bindDirective : undefined;
|
|
1296
|
+
if (dynamicBinding) {
|
|
1297
|
+
validateBindModifiers(dynamicBinding, attr, context, "element");
|
|
1298
|
+
emitDynamicAttributeBinding(context, node, elementVar, attr, dynamicBinding, cleanupVar, indent);
|
|
1299
|
+
continue;
|
|
1300
|
+
}
|
|
1301
|
+
const bindingName = bindDirective?.name;
|
|
1302
|
+
if (bindingName && bindDirective) {
|
|
1303
|
+
validateBindModifiers(bindDirective, attr, context, "element");
|
|
803
1304
|
if (bindingName === "ref") {
|
|
804
1305
|
continue;
|
|
805
1306
|
}
|
|
806
1307
|
const expression = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
807
1308
|
const stopVar = nextVar(context, "stop");
|
|
808
|
-
const
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1309
|
+
const staticClass = getStaticAttrValue(node, "class");
|
|
1310
|
+
const staticStyle = getStaticAttrValue(node, "style");
|
|
1311
|
+
const valueExpression = bindingName === "class" && staticClass
|
|
1312
|
+
? `[${quote(staticClass)}, ${expression}]`
|
|
1313
|
+
: bindingName === "style" && staticStyle
|
|
1314
|
+
? `[${quote(staticStyle)}, ${expression}]`
|
|
1315
|
+
: expression;
|
|
1316
|
+
if (context.once) {
|
|
1317
|
+
emit(context, indent, `setAttribute(${elementVar}, ${quote(bindingName)}, unwrap(${valueExpression})${bindOptionsExpression(bindDirective)});`);
|
|
1318
|
+
}
|
|
1319
|
+
else {
|
|
1320
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
1321
|
+
emit(context, indent + 1, `setAttribute(${elementVar}, ${quote(bindingName)}, unwrap(${valueExpression})${bindOptionsExpression(bindDirective)});`);
|
|
1322
|
+
emit(context, indent, "});");
|
|
1323
|
+
emit(context, indent, `${cleanupVar}.push(${stopVar});`);
|
|
1324
|
+
}
|
|
815
1325
|
}
|
|
816
1326
|
}
|
|
817
1327
|
appendNode(context, parentVar, elementVar, indent, beforeVar);
|
|
818
1328
|
return elementVar;
|
|
819
1329
|
}
|
|
1330
|
+
function emitDynamicAttributeBinding(context, node, elementVar, attr, binding, cleanupVar, indent) {
|
|
1331
|
+
const compiledName = compileTemplateExpression(binding.nameExpression ?? "", attr.name, toExpressionContext(context, attr.loc));
|
|
1332
|
+
const compiledValue = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
1333
|
+
const staticClass = getStaticAttrValue(node, "class");
|
|
1334
|
+
const staticStyle = getStaticAttrValue(node, "style");
|
|
1335
|
+
const previousNameVar = nextVar(context, "attrName");
|
|
1336
|
+
const nameVar = nextVar(context, "attrName");
|
|
1337
|
+
const valueVar = nextVar(context, "attrValue");
|
|
1338
|
+
const stopVar = nextVar(context, "stop");
|
|
1339
|
+
emit(context, indent, `let ${previousNameVar};`);
|
|
1340
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
1341
|
+
emit(context, indent + 1, `const ${nameVar} = ${bindNameExpression(`String(unwrap(${compiledName}) ?? "")`, binding)};`);
|
|
1342
|
+
emit(context, indent + 1, `if (!${nameVar}) { if (${previousNameVar}) setAttribute(${elementVar}, ${previousNameVar}, null); ${previousNameVar} = undefined; return; }`);
|
|
1343
|
+
emit(context, indent + 1, `if (${previousNameVar} && ${previousNameVar} !== ${nameVar}) { setAttribute(${elementVar}, ${previousNameVar}, null); }`);
|
|
1344
|
+
emit(context, indent + 1, `const ${valueVar} = unwrap(${compiledValue});`);
|
|
1345
|
+
if (staticClass || staticStyle) {
|
|
1346
|
+
emit(context, indent + 1, `setAttribute(${elementVar}, ${nameVar}, ${nameVar} === "class" && ${staticClass ? "true" : "false"} ? [${quote(staticClass ?? "")}, ${valueVar}] : ${nameVar} === "style" && ${staticStyle ? "true" : "false"} ? [${quote(staticStyle ?? "")}, ${valueVar}] : ${valueVar}${bindOptionsExpression(binding)});`);
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
emit(context, indent + 1, `setAttribute(${elementVar}, ${nameVar}, ${valueVar}${bindOptionsExpression(binding)});`);
|
|
1350
|
+
}
|
|
1351
|
+
emit(context, indent + 1, `${previousNameVar} = ${nameVar};`);
|
|
1352
|
+
emit(context, indent, "});");
|
|
1353
|
+
emit(context, indent, `${cleanupVar}.push(() => { ${stopVar}(); if (${previousNameVar}) setAttribute(${elementVar}, ${previousNameVar}, null); });`);
|
|
1354
|
+
}
|
|
1355
|
+
function emitDynamicEventListener(context, elementVar, event, handlerVar, attr, cleanupVar, indent) {
|
|
1356
|
+
const expression = compileTemplateExpression(event.nameExpression ?? "\"\"", attr.name, toExpressionContext(context, attr.loc));
|
|
1357
|
+
const currentEventVar = nextVar(context, "eventName");
|
|
1358
|
+
const nextEventVar = nextVar(context, "eventName");
|
|
1359
|
+
const stopVar = nextVar(context, "stop");
|
|
1360
|
+
const eventOptions = eventListenerOptions(event);
|
|
1361
|
+
emit(context, indent, `let ${currentEventVar};`);
|
|
1362
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
1363
|
+
emit(context, indent + 1, `const ${nextEventVar} = String(unwrap(${expression}) ?? "");`);
|
|
1364
|
+
emit(context, indent + 1, `if (${nextEventVar} === ${currentEventVar}) { return; }`);
|
|
1365
|
+
emit(context, indent + 1, `if (${currentEventVar}) { ${elementVar}.removeEventListener(${currentEventVar}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""}); }`);
|
|
1366
|
+
emit(context, indent + 1, `${currentEventVar} = ${nextEventVar};`);
|
|
1367
|
+
emit(context, indent + 1, `if (${currentEventVar}) { ${elementVar}.addEventListener(${currentEventVar}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""}); }`);
|
|
1368
|
+
emit(context, indent, "});");
|
|
1369
|
+
emit(context, indent, `${cleanupVar}.push(() => { ${stopVar}(); if (${currentEventVar}) { ${elementVar}.removeEventListener(${currentEventVar}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""}); } });`);
|
|
1370
|
+
}
|
|
1371
|
+
function emitContentDirective(context, node, elementVar, attr, cleanupVar, indent) {
|
|
1372
|
+
const expression = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
1373
|
+
const property = attr.name === "v-html" ? "innerHTML" : "textContent";
|
|
1374
|
+
if (context.once) {
|
|
1375
|
+
emit(context, indent, `${elementVar}.${property} = String(unwrap(${expression}) ?? "");`);
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1378
|
+
const stopVar = nextVar(context, "stop");
|
|
1379
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
1380
|
+
emit(context, indent + 1, `${elementVar}.${property} = String(unwrap(${expression}) ?? "");`);
|
|
1381
|
+
emit(context, indent, "});");
|
|
1382
|
+
emit(context, indent, `${cleanupVar}.push(${stopVar});`);
|
|
1383
|
+
}
|
|
1384
|
+
function generatePreNode(context, node, parentVar, indent, beforeVar) {
|
|
1385
|
+
if (node.type === "text") {
|
|
1386
|
+
const textVar = nextVar(context, "text");
|
|
1387
|
+
emit(context, indent, `const ${textVar} = document.createTextNode(${quote(node.parts.map((part) => part.value).join(""))});`);
|
|
1388
|
+
appendNode(context, parentVar, textVar, indent, beforeVar);
|
|
1389
|
+
return textVar;
|
|
1390
|
+
}
|
|
1391
|
+
return generatePreElement(context, node, parentVar, indent, beforeVar);
|
|
1392
|
+
}
|
|
1393
|
+
function generatePreElement(context, node, parentVar, indent, beforeVar) {
|
|
1394
|
+
const elementVar = nextVar(context, "el");
|
|
1395
|
+
emit(context, indent, `const ${elementVar} = document.createElement(${quote(node.tag)});`);
|
|
1396
|
+
if (context.scopeAttr) {
|
|
1397
|
+
emit(context, indent, `${elementVar}.setAttribute(${quote(context.scopeAttr)}, "");`);
|
|
1398
|
+
}
|
|
1399
|
+
for (const attr of node.attrs) {
|
|
1400
|
+
if (attr.name === "v-pre") {
|
|
1401
|
+
continue;
|
|
1402
|
+
}
|
|
1403
|
+
emit(context, indent, `setAttribute(${elementVar}, ${quote(attr.name)}, ${quote(attr.value === true ? "" : attr.value)});`);
|
|
1404
|
+
}
|
|
1405
|
+
for (const child of node.children) {
|
|
1406
|
+
generatePreNode(context, child, elementVar, indent);
|
|
1407
|
+
}
|
|
1408
|
+
appendNode(context, parentVar, elementVar, indent, beforeVar);
|
|
1409
|
+
return elementVar;
|
|
1410
|
+
}
|
|
820
1411
|
function emitTemplateRef(context, node, valueExpression, cleanupVar, indent) {
|
|
821
1412
|
const refAttr = getTemplateRefAttr(node);
|
|
822
1413
|
if (!refAttr) {
|
|
@@ -848,11 +1439,16 @@ function generateText(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
|
848
1439
|
const textVar = nextVar(context, "text");
|
|
849
1440
|
emit(context, indent, `const ${textVar} = document.createTextNode("");`);
|
|
850
1441
|
if (node.parts.some((part) => part.type === "expression")) {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1442
|
+
if (context.once) {
|
|
1443
|
+
emit(context, indent, `${textVar}.textContent = ${textExpression(node.parts, context)};`);
|
|
1444
|
+
}
|
|
1445
|
+
else {
|
|
1446
|
+
const stopVar = nextVar(context, "stop");
|
|
1447
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
1448
|
+
emit(context, indent + 1, `${textVar}.textContent = ${textExpression(node.parts, context)};`);
|
|
1449
|
+
emit(context, indent, "});");
|
|
1450
|
+
emit(context, indent, `${cleanupVar}.push(${stopVar});`);
|
|
1451
|
+
}
|
|
856
1452
|
}
|
|
857
1453
|
else {
|
|
858
1454
|
emit(context, indent, `${textVar}.textContent = ${quote(node.parts.map((part) => part.value).join(""))};`);
|
|
@@ -861,37 +1457,45 @@ function generateText(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
|
861
1457
|
return textVar;
|
|
862
1458
|
}
|
|
863
1459
|
function emitObjectBind(context, node, elementVar, attr, cleanupVar, indent) {
|
|
1460
|
+
const binding = parseObjectBindDirective(attr.name);
|
|
1461
|
+
if (!binding) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
validateObjectBindModifiers(binding, attr, context, "element");
|
|
864
1465
|
const expression = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
865
1466
|
const prevKeysVar = nextVar(context, "boundKeys");
|
|
866
1467
|
const stopVar = nextVar(context, "stop");
|
|
867
1468
|
const attrsVar = nextVar(context, "attrs");
|
|
868
1469
|
const nextKeysVar = nextVar(context, "boundKeys");
|
|
869
1470
|
const keyVar = nextVar(context, "key");
|
|
1471
|
+
const boundKeyVar = nextVar(context, "key");
|
|
870
1472
|
const valueVar = nextVar(context, "value");
|
|
871
1473
|
const staleKeyVar = nextVar(context, "key");
|
|
872
1474
|
const staticClass = getStaticAttrValue(node, "class");
|
|
1475
|
+
const staticStyle = getStaticAttrValue(node, "style");
|
|
873
1476
|
emit(context, indent, `const ${prevKeysVar} = new Set();`);
|
|
874
1477
|
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
875
1478
|
emit(context, indent + 1, `const ${attrsVar} = unwrap(${expression}) ?? {};`);
|
|
876
1479
|
emit(context, indent + 1, `const ${nextKeysVar} = new Set();`);
|
|
877
1480
|
emit(context, indent + 1, `if (${attrsVar} && typeof ${attrsVar} === "object") {`);
|
|
878
1481
|
emit(context, indent + 2, `for (const [${keyVar}, ${valueVar}] of Object.entries(${attrsVar})) {`);
|
|
879
|
-
emit(context, indent + 3,
|
|
880
|
-
|
|
881
|
-
|
|
1482
|
+
emit(context, indent + 3, `const ${boundKeyVar} = ${objectBindKeyExpression(keyVar, binding)};`);
|
|
1483
|
+
emit(context, indent + 3, `${nextKeysVar}.add(${boundKeyVar});`);
|
|
1484
|
+
if (staticClass || staticStyle) {
|
|
1485
|
+
emit(context, indent + 3, `setAttribute(${elementVar}, ${boundKeyVar}, ${boundKeyVar} === "class" && ${staticClass ? "true" : "false"} ? [${quote(staticClass ?? "")}, unwrap(${valueVar})] : ${boundKeyVar} === "style" && ${staticStyle ? "true" : "false"} ? [${quote(staticStyle ?? "")}, unwrap(${valueVar})] : unwrap(${valueVar})${bindOptionsExpression(binding)});`);
|
|
882
1486
|
}
|
|
883
1487
|
else {
|
|
884
|
-
emit(context, indent + 3, `setAttribute(${elementVar}, ${
|
|
1488
|
+
emit(context, indent + 3, `setAttribute(${elementVar}, ${boundKeyVar}, unwrap(${valueVar})${bindOptionsExpression(binding)});`);
|
|
885
1489
|
}
|
|
886
1490
|
emit(context, indent + 2, "}");
|
|
887
1491
|
emit(context, indent + 1, "}");
|
|
888
1492
|
emit(context, indent + 1, `for (const ${staleKeyVar} of ${prevKeysVar}) {`);
|
|
889
1493
|
emit(context, indent + 2, `if (!${nextKeysVar}.has(${staleKeyVar})) {`);
|
|
890
|
-
if (staticClass) {
|
|
891
|
-
emit(context, indent + 3, `setAttribute(${elementVar}, ${staleKeyVar}, ${staleKeyVar} === "class" ? ${quote(staticClass)} : null);`);
|
|
1494
|
+
if (staticClass || staticStyle) {
|
|
1495
|
+
emit(context, indent + 3, `setAttribute(${elementVar}, ${staleKeyVar}, ${staleKeyVar} === "class" && ${staticClass ? "true" : "false"} ? ${quote(staticClass ?? "")} : ${staleKeyVar} === "style" && ${staticStyle ? "true" : "false"} ? ${quote(staticStyle ?? "")} : null${bindOptionsExpression(binding)});`);
|
|
892
1496
|
}
|
|
893
1497
|
else {
|
|
894
|
-
emit(context, indent + 3, `setAttribute(${elementVar}, ${staleKeyVar}, null);`);
|
|
1498
|
+
emit(context, indent + 3, `setAttribute(${elementVar}, ${staleKeyVar}, null${bindOptionsExpression(binding)});`);
|
|
895
1499
|
}
|
|
896
1500
|
emit(context, indent + 2, "}");
|
|
897
1501
|
emit(context, indent + 1, "}");
|
|
@@ -903,24 +1507,32 @@ function emitObjectBind(context, node, elementVar, attr, cleanupVar, indent) {
|
|
|
903
1507
|
emit(context, indent, `${cleanupVar}.push(${stopVar});`);
|
|
904
1508
|
}
|
|
905
1509
|
function emitObjectListeners(context, elementVar, attr, cleanupVar, indent) {
|
|
1510
|
+
const event = parseObjectOnDirective(attr.name);
|
|
1511
|
+
if (!event) {
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
validateObjectOnModifiers(event, attr, context, "element");
|
|
1515
|
+
const eventOptions = eventListenerOptions(event);
|
|
906
1516
|
const expression = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
907
1517
|
const listenersVar = nextVar(context, "listeners");
|
|
908
1518
|
const stopVar = nextVar(context, "stop");
|
|
909
1519
|
const sourceVar = nextVar(context, "listeners");
|
|
910
1520
|
const eventVar = nextVar(context, "event");
|
|
911
1521
|
const handlerVar = nextVar(context, "handler");
|
|
1522
|
+
const wrappedHandlerVar = nextVar(context, "handler");
|
|
912
1523
|
emit(context, indent, `const ${listenersVar} = new Map();`);
|
|
913
1524
|
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
914
1525
|
emit(context, indent + 1, `for (const [${eventVar}, ${handlerVar}] of ${listenersVar}) {`);
|
|
915
|
-
emit(context, indent + 2, `${elementVar}.removeEventListener(${eventVar}, ${handlerVar});`);
|
|
1526
|
+
emit(context, indent + 2, `${elementVar}.removeEventListener(${eventVar}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""});`);
|
|
916
1527
|
emit(context, indent + 1, "}");
|
|
917
1528
|
emit(context, indent + 1, `${listenersVar}.clear();`);
|
|
918
1529
|
emit(context, indent + 1, `const ${sourceVar} = unwrap(${expression}) ?? {};`);
|
|
919
1530
|
emit(context, indent + 1, `if (${sourceVar} && typeof ${sourceVar} === "object") {`);
|
|
920
1531
|
emit(context, indent + 2, `for (const [${eventVar}, ${handlerVar}] of Object.entries(${sourceVar})) {`);
|
|
921
1532
|
emit(context, indent + 3, `if (typeof ${handlerVar} === "function") {`);
|
|
922
|
-
emit(context, indent + 4,
|
|
923
|
-
emit(context, indent + 4, `${
|
|
1533
|
+
emit(context, indent + 4, `const ${wrappedHandlerVar} = __mikuru_guardEventHandler(${handlerVar});`);
|
|
1534
|
+
emit(context, indent + 4, `${elementVar}.addEventListener(${eventVar}, ${wrappedHandlerVar}${eventOptions ? `, ${eventOptions}` : ""});`);
|
|
1535
|
+
emit(context, indent + 4, `${listenersVar}.set(${eventVar}, ${wrappedHandlerVar});`);
|
|
924
1536
|
emit(context, indent + 3, "}");
|
|
925
1537
|
emit(context, indent + 2, "}");
|
|
926
1538
|
emit(context, indent + 1, "}");
|
|
@@ -928,7 +1540,7 @@ function emitObjectListeners(context, elementVar, attr, cleanupVar, indent) {
|
|
|
928
1540
|
emit(context, indent, `${cleanupVar}.push(() => {`);
|
|
929
1541
|
emit(context, indent + 1, `${stopVar}();`);
|
|
930
1542
|
emit(context, indent + 1, `for (const [${eventVar}, ${handlerVar}] of ${listenersVar}) {`);
|
|
931
|
-
emit(context, indent + 2, `${elementVar}.removeEventListener(${eventVar}, ${handlerVar});`);
|
|
1543
|
+
emit(context, indent + 2, `${elementVar}.removeEventListener(${eventVar}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""});`);
|
|
932
1544
|
emit(context, indent + 1, "}");
|
|
933
1545
|
emit(context, indent + 1, `${listenersVar}.clear();`);
|
|
934
1546
|
emit(context, indent, "});");
|
|
@@ -1084,13 +1696,14 @@ function generateFor(context, node, parentVar, cleanupVar, indent, expression, b
|
|
|
1084
1696
|
emit(context, indent, "});");
|
|
1085
1697
|
return startVar;
|
|
1086
1698
|
}
|
|
1087
|
-
function generateKeyedFor(context, node, parentVar, cleanupVar, indent, itemName, indexName, sourceExpression, keyExpression, beforeVar) {
|
|
1699
|
+
function generateKeyedFor(context, node, parentVar, cleanupVar, indent, itemName, indexName, sourceExpression, keyExpression, beforeVar, transitionVar) {
|
|
1088
1700
|
const startVar = nextVar(context, "forStart");
|
|
1089
1701
|
const endVar = nextVar(context, "forEnd");
|
|
1090
1702
|
const recordsVar = nextVar(context, "forRecords");
|
|
1091
1703
|
const stopVar = nextVar(context, "stop");
|
|
1092
1704
|
const compiledSource = compileTemplateExpression(sourceExpression, "v-for source", toExpressionContext(context, getStringAttrLocation(node, "v-for")));
|
|
1093
1705
|
const compiledKey = compileTemplateExpression(keyExpression, "v-for key", toExpressionContext(context, getKeyAttrLocation(node)));
|
|
1706
|
+
const compiledMemo = getMemoExpression(context, node) ?? getOnceMemoExpression(context, node);
|
|
1094
1707
|
emit(context, indent, `const ${recordsVar} = new Map();`);
|
|
1095
1708
|
emit(context, indent, `const ${startVar} = document.createComment("for");`);
|
|
1096
1709
|
emit(context, indent, `const ${endVar} = document.createComment("/for");`);
|
|
@@ -1107,6 +1720,8 @@ function generateKeyedFor(context, node, parentVar, cleanupVar, indent, itemName
|
|
|
1107
1720
|
const recordCleanupVar = nextVar(context, "forRecordCleanup");
|
|
1108
1721
|
const itemRefVar = nextVar(context, "forItemRef");
|
|
1109
1722
|
const indexRefVar = nextVar(context, "forIndexRef");
|
|
1723
|
+
const memoVar = compiledMemo ? nextVar(context, "forMemo") : undefined;
|
|
1724
|
+
const memoChangedVar = compiledMemo ? nextVar(context, "forMemoChanged") : undefined;
|
|
1110
1725
|
emit(context, indent + 1, `const ${sourceVar} = unwrap(${compiledSource}) ?? [];`);
|
|
1111
1726
|
emit(context, indent + 1, `const ${nextRecordsVar} = new Map();`);
|
|
1112
1727
|
emit(context, indent + 1, `for (let ${indexVar} = 0; ${indexVar} < ${sourceVar}.length; ${indexVar} += 1) {`);
|
|
@@ -1117,6 +1732,9 @@ function generateKeyedFor(context, node, parentVar, cleanupVar, indent, itemName
|
|
|
1117
1732
|
emit(context, indent + 2, `const ${indexName} = ${rawIndexVar};`);
|
|
1118
1733
|
}
|
|
1119
1734
|
emit(context, indent + 2, `const ${keyVar} = unwrap(${compiledKey});`);
|
|
1735
|
+
if (compiledMemo && memoVar) {
|
|
1736
|
+
emit(context, indent + 2, `const ${memoVar} = ${compiledMemo};`);
|
|
1737
|
+
}
|
|
1120
1738
|
emit(context, indent + 2, `let ${recordVar} = ${recordsVar}.get(${keyVar});`);
|
|
1121
1739
|
emit(context, indent + 2, `if (!${recordVar}) {`);
|
|
1122
1740
|
emit(context, indent + 3, `const ${recordCleanupVar} = [];`);
|
|
@@ -1130,14 +1748,32 @@ function generateKeyedFor(context, node, parentVar, cleanupVar, indent, itemName
|
|
|
1130
1748
|
emit(context, indent + 4, `const ${indexName} = ${indexRefVar};`);
|
|
1131
1749
|
}
|
|
1132
1750
|
const elementVar = withTemplateRefMode(context, "array", () => generateNode(context, withoutForAttrs(node), parentVar, recordCleanupVar, indent + 4, endVar));
|
|
1133
|
-
|
|
1751
|
+
if (transitionVar) {
|
|
1752
|
+
emitTransitionRegistration(context, elementVar, transitionVar, indent + 4);
|
|
1753
|
+
}
|
|
1754
|
+
emit(context, indent + 4, `${recordVar} = { element: ${elementVar}, cleanups: ${recordCleanupVar}, item: ${itemRefVar}${indexName ? `, index: ${indexRefVar}` : ""}${compiledMemo && memoVar ? `, memo: ${memoVar}` : ""} };`);
|
|
1134
1755
|
emit(context, indent + 3, `}`);
|
|
1135
1756
|
emit(context, indent + 2, `} else {`);
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
emit(context, indent + 3,
|
|
1757
|
+
if (compiledMemo && memoVar && memoChangedVar) {
|
|
1758
|
+
emit(context, indent + 3, `const ${memoChangedVar} = !__mikuru_memoEqual(${recordVar}.memo, ${memoVar});`);
|
|
1759
|
+
emit(context, indent + 3, `if (${memoChangedVar}) {`);
|
|
1760
|
+
emit(context, indent + 4, `${recordVar}.memo = ${memoVar};`);
|
|
1761
|
+
emit(context, indent + 4, `${recordVar}.item.value = ${rawItemVar};`);
|
|
1762
|
+
if (indexName) {
|
|
1763
|
+
emit(context, indent + 4, `${recordVar}.index.value = ${rawIndexVar};`);
|
|
1764
|
+
}
|
|
1765
|
+
emit(context, indent + 3, "}");
|
|
1766
|
+
}
|
|
1767
|
+
else {
|
|
1768
|
+
emit(context, indent + 3, `${recordVar}.item.value = ${rawItemVar};`);
|
|
1769
|
+
if (indexName) {
|
|
1770
|
+
emit(context, indent + 3, `${recordVar}.index.value = ${rawIndexVar};`);
|
|
1771
|
+
}
|
|
1139
1772
|
}
|
|
1140
1773
|
emit(context, indent + 3, `${parentVar}.insertBefore(${recordVar}.element, ${endVar});`);
|
|
1774
|
+
if (transitionVar) {
|
|
1775
|
+
emit(context, indent + 3, `__mikuru_applyTransitionMove(${recordVar}.element, ${transitionVar});`);
|
|
1776
|
+
}
|
|
1141
1777
|
emit(context, indent + 2, `}`);
|
|
1142
1778
|
emit(context, indent + 2, `${nextRecordsVar}.set(${keyVar}, ${recordVar});`);
|
|
1143
1779
|
emit(context, indent + 1, `}`);
|
|
@@ -1188,6 +1824,16 @@ function withTemplateRefMode(context, mode, callback) {
|
|
|
1188
1824
|
context.templateRefMode = previousMode;
|
|
1189
1825
|
}
|
|
1190
1826
|
}
|
|
1827
|
+
function withOnceMode(context, callback) {
|
|
1828
|
+
const previousOnce = context.once;
|
|
1829
|
+
context.once = true;
|
|
1830
|
+
try {
|
|
1831
|
+
return callback();
|
|
1832
|
+
}
|
|
1833
|
+
finally {
|
|
1834
|
+
context.once = previousOnce;
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1191
1837
|
function splitTopLevel(source, delimiter) {
|
|
1192
1838
|
const parts = [];
|
|
1193
1839
|
let depth = 0;
|
|
@@ -1619,11 +2265,241 @@ function isIdentifier(value) {
|
|
|
1619
2265
|
return /^[A-Za-z_$][\w$]*$/.test(value);
|
|
1620
2266
|
}
|
|
1621
2267
|
function eventHandlerExpression(expression, context, location) {
|
|
1622
|
-
const validatedExpression =
|
|
2268
|
+
const validatedExpression = validateEventHandlerExpression(expression, context, location);
|
|
1623
2269
|
if (/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(validatedExpression)) {
|
|
1624
2270
|
return validatedExpression;
|
|
1625
2271
|
}
|
|
1626
|
-
return `($event) => (${
|
|
2272
|
+
return `($event) => { return (${compileEventHandlerExpression(validatedExpression, context, location)}); }`;
|
|
2273
|
+
}
|
|
2274
|
+
function validateEventHandlerExpression(expression, context, location) {
|
|
2275
|
+
const source = expression.trim().replace(/;\s*$/, "");
|
|
2276
|
+
if (!source) {
|
|
2277
|
+
throwTemplateError("Invalid template expression for event handler: expression is empty", context, location);
|
|
2278
|
+
}
|
|
2279
|
+
try {
|
|
2280
|
+
const ast = parseExpressionAt(source, 0, { ecmaVersion: "latest" });
|
|
2281
|
+
if (ast.end !== source.length) {
|
|
2282
|
+
throw new Error("Unexpected trailing content");
|
|
2283
|
+
}
|
|
2284
|
+
validateEventHandlerNode(ast, context, source, location);
|
|
2285
|
+
return source;
|
|
2286
|
+
}
|
|
2287
|
+
catch (error) {
|
|
2288
|
+
if (error instanceof Error && error.message.startsWith("Unsupported event handler")) {
|
|
2289
|
+
throwTemplateError(error.message, context, location);
|
|
2290
|
+
}
|
|
2291
|
+
try {
|
|
2292
|
+
return validateTemplateExpression(source, "event handler", toExpressionContext(context, location));
|
|
2293
|
+
}
|
|
2294
|
+
catch {
|
|
2295
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2296
|
+
throwTemplateError(`Invalid template expression for event handler: ${source} (${message})`, context, location);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
function compileEventHandlerExpression(expression, context, location) {
|
|
2301
|
+
const ast = parseExpressionAt(expression, 0, { ecmaVersion: "latest" });
|
|
2302
|
+
const edits = [];
|
|
2303
|
+
collectEventHandlerEdits(ast, expression, edits, "read");
|
|
2304
|
+
return applyScriptEdits(expression, edits);
|
|
2305
|
+
}
|
|
2306
|
+
function validateEventHandlerNode(node, context, source, location) {
|
|
2307
|
+
switch (node.type) {
|
|
2308
|
+
case "Identifier":
|
|
2309
|
+
case "Literal":
|
|
2310
|
+
case "TemplateElement":
|
|
2311
|
+
return;
|
|
2312
|
+
case "ThisExpression":
|
|
2313
|
+
case "NewExpression":
|
|
2314
|
+
throw new Error(`Unsupported event handler expression: ${source} (${node.type})`);
|
|
2315
|
+
case "AssignmentExpression":
|
|
2316
|
+
validateEventAssignmentTarget(node.left, context, source, location);
|
|
2317
|
+
validateEventHandlerNode(node.right, context, source, location);
|
|
2318
|
+
return;
|
|
2319
|
+
case "UpdateExpression":
|
|
2320
|
+
validateEventAssignmentTarget(node.argument, context, source, location);
|
|
2321
|
+
return;
|
|
2322
|
+
case "CallExpression":
|
|
2323
|
+
validateEventCallExpression(node, context, source, location);
|
|
2324
|
+
return;
|
|
2325
|
+
case "MemberExpression":
|
|
2326
|
+
validateEventMemberExpression(node, context, source, location);
|
|
2327
|
+
return;
|
|
2328
|
+
case "ChainExpression":
|
|
2329
|
+
validateEventHandlerNode(node.expression, context, source, location);
|
|
2330
|
+
return;
|
|
2331
|
+
case "UnaryExpression":
|
|
2332
|
+
validateEventHandlerNode(node.argument, context, source, location);
|
|
2333
|
+
return;
|
|
2334
|
+
case "BinaryExpression":
|
|
2335
|
+
case "LogicalExpression":
|
|
2336
|
+
validateEventHandlerNode(node.left, context, source, location);
|
|
2337
|
+
validateEventHandlerNode(node.right, context, source, location);
|
|
2338
|
+
return;
|
|
2339
|
+
case "ConditionalExpression":
|
|
2340
|
+
validateEventHandlerNode(node.test, context, source, location);
|
|
2341
|
+
validateEventHandlerNode(node.consequent, context, source, location);
|
|
2342
|
+
validateEventHandlerNode(node.alternate, context, source, location);
|
|
2343
|
+
return;
|
|
2344
|
+
case "ArrayExpression":
|
|
2345
|
+
for (const element of node.elements ?? []) {
|
|
2346
|
+
if (element) {
|
|
2347
|
+
validateEventHandlerNode(element, context, source, location);
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
return;
|
|
2351
|
+
case "ObjectExpression":
|
|
2352
|
+
for (const property of node.properties ?? []) {
|
|
2353
|
+
validateEventHandlerNode(property, context, source, location);
|
|
2354
|
+
}
|
|
2355
|
+
return;
|
|
2356
|
+
case "Property":
|
|
2357
|
+
if (node.computed) {
|
|
2358
|
+
validateEventHandlerNode(node.key, context, source, location);
|
|
2359
|
+
}
|
|
2360
|
+
validateEventHandlerNode(node.value, context, source, location);
|
|
2361
|
+
return;
|
|
2362
|
+
case "TemplateLiteral":
|
|
2363
|
+
for (const quasi of node.quasis ?? []) {
|
|
2364
|
+
validateEventHandlerNode(quasi, context, source, location);
|
|
2365
|
+
}
|
|
2366
|
+
for (const part of node.expressions ?? []) {
|
|
2367
|
+
validateEventHandlerNode(part, context, source, location);
|
|
2368
|
+
}
|
|
2369
|
+
return;
|
|
2370
|
+
default:
|
|
2371
|
+
throw new Error(`Unsupported event handler expression: ${source} (${node.type})`);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
function validateEventAssignmentTarget(node, context, source, location) {
|
|
2375
|
+
if (node.type === "Identifier") {
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2378
|
+
if (node.type === "MemberExpression") {
|
|
2379
|
+
validateEventMemberExpression(node, context, source, location);
|
|
2380
|
+
return;
|
|
2381
|
+
}
|
|
2382
|
+
throw new Error(`Unsupported event handler assignment target: ${source} (${node.type})`);
|
|
2383
|
+
}
|
|
2384
|
+
function validateEventCallExpression(node, context, source, location) {
|
|
2385
|
+
const callee = node.callee;
|
|
2386
|
+
const calleeName = getEventStaticCalleeName(callee, source);
|
|
2387
|
+
if (calleeName === "eval" || calleeName === "Function") {
|
|
2388
|
+
throw new Error(`Unsupported event handler expression: ${source} (${calleeName})`);
|
|
2389
|
+
}
|
|
2390
|
+
validateEventHandlerNode(callee, context, source, location);
|
|
2391
|
+
for (const argument of node.arguments ?? []) {
|
|
2392
|
+
validateEventHandlerNode(argument, context, source, location);
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
function validateEventMemberExpression(node, context, source, location) {
|
|
2396
|
+
validateEventHandlerNode(node.object, context, source, location);
|
|
2397
|
+
if (node.computed) {
|
|
2398
|
+
validateEventHandlerNode(node.property, context, source, location);
|
|
2399
|
+
return;
|
|
2400
|
+
}
|
|
2401
|
+
const propertyName = getEventStaticPropertyName(node.property, source);
|
|
2402
|
+
if (propertyName === "constructor" || propertyName === "__proto__" || propertyName === "prototype") {
|
|
2403
|
+
throw new Error(`Unsupported event handler expression: ${source} (${propertyName})`);
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
function collectEventHandlerEdits(node, source, edits, mode) {
|
|
2407
|
+
if (!node) {
|
|
2408
|
+
return;
|
|
2409
|
+
}
|
|
2410
|
+
switch (node.type) {
|
|
2411
|
+
case "Identifier":
|
|
2412
|
+
if (node.name === "$event") {
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2415
|
+
if (mode === "write") {
|
|
2416
|
+
edits.push({ start: node.start, end: node.end, replacement: `${node.name}.value` });
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
if (mode === "callee") {
|
|
2420
|
+
return;
|
|
2421
|
+
}
|
|
2422
|
+
edits.push({ start: node.start, end: node.end, replacement: `unwrap(${node.name})` });
|
|
2423
|
+
return;
|
|
2424
|
+
case "Literal":
|
|
2425
|
+
case "TemplateElement":
|
|
2426
|
+
case "ThisExpression":
|
|
2427
|
+
return;
|
|
2428
|
+
case "AssignmentExpression":
|
|
2429
|
+
collectEventHandlerEdits(node.left, source, edits, "write");
|
|
2430
|
+
collectEventHandlerEdits(node.right, source, edits, "read");
|
|
2431
|
+
return;
|
|
2432
|
+
case "UpdateExpression":
|
|
2433
|
+
collectEventHandlerEdits(node.argument, source, edits, "write");
|
|
2434
|
+
return;
|
|
2435
|
+
case "CallExpression":
|
|
2436
|
+
collectEventHandlerEdits(node.callee, source, edits, "callee");
|
|
2437
|
+
for (const argument of node.arguments ?? []) {
|
|
2438
|
+
collectEventHandlerEdits(argument, source, edits, "read");
|
|
2439
|
+
}
|
|
2440
|
+
return;
|
|
2441
|
+
case "MemberExpression":
|
|
2442
|
+
collectEventHandlerEdits(node.object, source, edits, mode === "callee" ? "read" : mode);
|
|
2443
|
+
if (node.computed) {
|
|
2444
|
+
collectEventHandlerEdits(node.property, source, edits, "read");
|
|
2445
|
+
}
|
|
2446
|
+
return;
|
|
2447
|
+
case "ChainExpression":
|
|
2448
|
+
collectEventHandlerEdits(node.expression, source, edits, mode);
|
|
2449
|
+
return;
|
|
2450
|
+
case "UnaryExpression":
|
|
2451
|
+
collectEventHandlerEdits(node.argument, source, edits, "read");
|
|
2452
|
+
return;
|
|
2453
|
+
case "BinaryExpression":
|
|
2454
|
+
case "LogicalExpression":
|
|
2455
|
+
collectEventHandlerEdits(node.left, source, edits, "read");
|
|
2456
|
+
collectEventHandlerEdits(node.right, source, edits, "read");
|
|
2457
|
+
return;
|
|
2458
|
+
case "ConditionalExpression":
|
|
2459
|
+
collectEventHandlerEdits(node.test, source, edits, "read");
|
|
2460
|
+
collectEventHandlerEdits(node.consequent, source, edits, "read");
|
|
2461
|
+
collectEventHandlerEdits(node.alternate, source, edits, "read");
|
|
2462
|
+
return;
|
|
2463
|
+
case "ArrayExpression":
|
|
2464
|
+
for (const element of node.elements ?? []) {
|
|
2465
|
+
collectEventHandlerEdits(element, source, edits, "read");
|
|
2466
|
+
}
|
|
2467
|
+
return;
|
|
2468
|
+
case "ObjectExpression":
|
|
2469
|
+
for (const property of node.properties ?? []) {
|
|
2470
|
+
collectEventHandlerEdits(property, source, edits, "read");
|
|
2471
|
+
}
|
|
2472
|
+
return;
|
|
2473
|
+
case "Property":
|
|
2474
|
+
if (node.computed) {
|
|
2475
|
+
collectEventHandlerEdits(node.key, source, edits, "read");
|
|
2476
|
+
}
|
|
2477
|
+
collectEventHandlerEdits(node.value, source, edits, "read");
|
|
2478
|
+
return;
|
|
2479
|
+
case "TemplateLiteral":
|
|
2480
|
+
for (const part of node.expressions ?? []) {
|
|
2481
|
+
collectEventHandlerEdits(part, source, edits, "read");
|
|
2482
|
+
}
|
|
2483
|
+
return;
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
function getEventStaticCalleeName(node, source) {
|
|
2487
|
+
if (node.type === "Identifier") {
|
|
2488
|
+
return source.slice(node.start, node.end);
|
|
2489
|
+
}
|
|
2490
|
+
if (node.type === "MemberExpression") {
|
|
2491
|
+
return getEventStaticPropertyName(node.property, source);
|
|
2492
|
+
}
|
|
2493
|
+
return undefined;
|
|
2494
|
+
}
|
|
2495
|
+
function getEventStaticPropertyName(node, source) {
|
|
2496
|
+
if (node.type === "Identifier") {
|
|
2497
|
+
return source.slice(node.start, node.end);
|
|
2498
|
+
}
|
|
2499
|
+
if (node.type === "Literal") {
|
|
2500
|
+
return String(node.value);
|
|
2501
|
+
}
|
|
2502
|
+
return undefined;
|
|
1627
2503
|
}
|
|
1628
2504
|
function getStringAttr(node, name) {
|
|
1629
2505
|
const attr = node.attrs.find((candidate) => candidate.name === name);
|
|
@@ -1651,6 +2527,24 @@ function getKeyExpression(node) {
|
|
|
1651
2527
|
function getKeyAttrLocation(node) {
|
|
1652
2528
|
return getStringAttrLocation(node, ":key") ?? getStringAttrLocation(node, "v-bind:key");
|
|
1653
2529
|
}
|
|
2530
|
+
function getMemoExpression(context, node) {
|
|
2531
|
+
const attr = node.attrs.find((candidate) => candidate.name === "v-memo");
|
|
2532
|
+
if (!attr) {
|
|
2533
|
+
return undefined;
|
|
2534
|
+
}
|
|
2535
|
+
const expression = validateTemplateExpression(requireAttrValue(attr), "v-memo", toExpressionContext(context, attr.valueLoc));
|
|
2536
|
+
if (!expression.trim().startsWith("[") || !expression.trim().endsWith("]")) {
|
|
2537
|
+
throwTemplateError("v-memo requires an array expression", context, attr.valueLoc ?? attr.loc);
|
|
2538
|
+
}
|
|
2539
|
+
return compileTemplateExpression(expression, "v-memo", toExpressionContext(context, attr.valueLoc));
|
|
2540
|
+
}
|
|
2541
|
+
function getOnceMemoExpression(context, node) {
|
|
2542
|
+
if (!hasAttr(node, "v-once")) {
|
|
2543
|
+
return undefined;
|
|
2544
|
+
}
|
|
2545
|
+
validateOnceAttribute(context, node);
|
|
2546
|
+
return "[]";
|
|
2547
|
+
}
|
|
1654
2548
|
function toExpressionContext(context, location) {
|
|
1655
2549
|
if (!context.source || !location) {
|
|
1656
2550
|
return undefined;
|
|
@@ -1665,7 +2559,7 @@ function withoutAttr(node, name) {
|
|
|
1665
2559
|
return withoutAttrs(node, [name]);
|
|
1666
2560
|
}
|
|
1667
2561
|
function withoutForAttrs(node) {
|
|
1668
|
-
return withoutAttrs(node, ["v-for", "key", ":key", "v-bind:key"]);
|
|
2562
|
+
return withoutAttrs(node, ["v-for", "key", ":key", "v-bind:key", "v-memo", "v-once"]);
|
|
1669
2563
|
}
|
|
1670
2564
|
function withoutAttrs(node, names) {
|
|
1671
2565
|
return {
|
|
@@ -1697,12 +2591,18 @@ function emitComponentProps(context, node, propsVar, attrsVar, indent) {
|
|
|
1697
2591
|
.flatMap((attr) => componentPropEntries(context, attr));
|
|
1698
2592
|
const objectBindAttrs = node.attrs.filter((attr) => isObjectBindAttr(attr));
|
|
1699
2593
|
const objectOnAttrs = node.attrs.filter((attr) => isObjectOnAttr(attr));
|
|
2594
|
+
for (const attr of objectBindAttrs) {
|
|
2595
|
+
validateObjectBindModifiers(parseObjectBindDirective(attr.name) ?? { modifiers: [] }, attr, context, "component");
|
|
2596
|
+
}
|
|
2597
|
+
for (const attr of objectOnAttrs) {
|
|
2598
|
+
validateObjectOnModifiers(parseObjectOnDirective(attr.name) ?? { modifiers: [] }, attr, context, "component");
|
|
2599
|
+
}
|
|
1700
2600
|
const slots = collectComponentSlots(context, node);
|
|
1701
2601
|
const defaultSlot = slots.find((slot) => !slot.nameExpression && slot.name === "default");
|
|
1702
2602
|
const needsProxy = objectBindAttrs.length > 0 || objectOnAttrs.length > 0;
|
|
1703
2603
|
const propsTargetVar = needsProxy ? nextVar(context, "propsBase") : propsVar;
|
|
1704
2604
|
emit(context, indent, `const ${propsTargetVar} = {`);
|
|
1705
|
-
emit(context, indent + 1, "__mikuru_context
|
|
2605
|
+
emit(context, indent + 1, `__mikuru_context: ${context.componentContextVar ?? "__mikuru_context"},`);
|
|
1706
2606
|
emit(context, indent + 1, `__mikuru_attrs: ${attrsVar},`);
|
|
1707
2607
|
for (const prop of props) {
|
|
1708
2608
|
emit(context, indent + 1, `${prop},`);
|
|
@@ -2026,12 +2926,15 @@ function componentPropEntries(context, attr) {
|
|
|
2026
2926
|
validateModelModifiers(modelDirective, attr, context);
|
|
2027
2927
|
const expression = validateAssignableExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
2028
2928
|
const valueExpression = compileTemplateExpression(expression, attr.name, toExpressionContext(context, attr.valueLoc));
|
|
2929
|
+
const propName = modelDirective.argument ?? "modelValue";
|
|
2930
|
+
const updatePropName = toComponentEventProp(`update:${propName}`);
|
|
2931
|
+
const modifiersPropName = modelDirective.argument ? `${propName}Modifiers` : "modelModifiers";
|
|
2029
2932
|
const entries = [
|
|
2030
|
-
`get
|
|
2031
|
-
|
|
2933
|
+
`get ${quotePropertyName(propName)}() { return unwrap(${valueExpression}); }`,
|
|
2934
|
+
`${quotePropertyName(updatePropName)}: __mikuru_guardEventHandler(($value) => { ${expression}.value = $value; })`
|
|
2032
2935
|
];
|
|
2033
2936
|
if (modelDirective.modifiers.length > 0) {
|
|
2034
|
-
entries.push(
|
|
2937
|
+
entries.push(`${quotePropertyName(modifiersPropName)}: { ${modelDirective.modifiers.map((modifier) => `${quotePropertyName(modifier)}: true`).join(", ")} }`);
|
|
2035
2938
|
}
|
|
2036
2939
|
return entries;
|
|
2037
2940
|
}
|
|
@@ -2039,13 +2942,36 @@ function componentPropEntries(context, attr) {
|
|
|
2039
2942
|
if (event) {
|
|
2040
2943
|
validateComponentEventModifiers(event, attr, context);
|
|
2041
2944
|
const handler = validateTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
2945
|
+
const handlerExpression = componentEventHandlerExpression(event, eventHandlerExpression(handler, context, attr.valueLoc), context);
|
|
2946
|
+
if (event.nameExpression) {
|
|
2947
|
+
const eventNameExpression = compileTemplateExpression(event.nameExpression, attr.name, toExpressionContext(context, attr.loc));
|
|
2948
|
+
return [
|
|
2949
|
+
`[${componentEventPropRuntimeExpression(`String(unwrap(${eventNameExpression}) ?? "")`)}]: __mikuru_guardEventHandler(${handlerExpression})`
|
|
2950
|
+
];
|
|
2951
|
+
}
|
|
2042
2952
|
return [
|
|
2043
|
-
`${quotePropertyName(toComponentEventProp(event.name))}: ${componentEventHandlerExpression(event, eventHandlerExpression(handler, context, attr.valueLoc), context)}`
|
|
2953
|
+
`${quotePropertyName(toComponentEventProp(event.name ?? ""))}: __mikuru_guardEventHandler(${componentEventHandlerExpression(event, eventHandlerExpression(handler, context, attr.valueLoc), context)})`
|
|
2044
2954
|
];
|
|
2045
2955
|
}
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2956
|
+
const bindDirective = parseBindDirective(attr.name);
|
|
2957
|
+
const dynamicBinding = bindDirective?.nameExpression ? bindDirective : undefined;
|
|
2958
|
+
if (dynamicBinding) {
|
|
2959
|
+
validateBindModifiers(dynamicBinding, attr, context, "component");
|
|
2960
|
+
const nameExpression = compileTemplateExpression(dynamicBinding.nameExpression ?? "", attr.name, toExpressionContext(context, attr.loc));
|
|
2961
|
+
const valueExpression = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
2962
|
+
const propertyName = bindNameExpression(`String(unwrap(${nameExpression}) ?? "")`, dynamicBinding);
|
|
2963
|
+
if (context.once) {
|
|
2964
|
+
return [`[${propertyName}]: unwrap(${valueExpression})`];
|
|
2965
|
+
}
|
|
2966
|
+
return [`get [${propertyName}]() { return unwrap(${valueExpression}); }`];
|
|
2967
|
+
}
|
|
2968
|
+
const bindingName = bindDirective?.name;
|
|
2969
|
+
if (bindingName && bindDirective) {
|
|
2970
|
+
validateBindModifiers(bindDirective, attr, context, "component");
|
|
2048
2971
|
const expression = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
2972
|
+
if (context.once) {
|
|
2973
|
+
return [`${quotePropertyName(bindingName)}: unwrap(${expression})`];
|
|
2974
|
+
}
|
|
2049
2975
|
return [`get ${quotePropertyName(bindingName)}() { return unwrap(${expression}); }`];
|
|
2050
2976
|
}
|
|
2051
2977
|
if (attr.name === "v-show") {
|
|
@@ -2105,7 +3031,8 @@ function getTransitionOptionsExpression(context, node) {
|
|
|
2105
3031
|
["enter-to-class", "enterToClass"],
|
|
2106
3032
|
["leave-from-class", "leaveFromClass"],
|
|
2107
3033
|
["leave-active-class", "leaveActiveClass"],
|
|
2108
|
-
["leave-to-class", "leaveToClass"]
|
|
3034
|
+
["leave-to-class", "leaveToClass"],
|
|
3035
|
+
["move-class", "moveClass"]
|
|
2109
3036
|
];
|
|
2110
3037
|
for (const [attrName, optionName] of classAttrs) {
|
|
2111
3038
|
const expression = getTransitionAttrExpression(context, node, attrName);
|
|
@@ -2130,7 +3057,7 @@ function getTransitionAttrExpression(context, node, name, fallback) {
|
|
|
2130
3057
|
return fallback === undefined ? undefined : quote(fallback);
|
|
2131
3058
|
}
|
|
2132
3059
|
function validateTransitionAttributes(context, node) {
|
|
2133
|
-
const supported =
|
|
3060
|
+
const supported = [
|
|
2134
3061
|
"name",
|
|
2135
3062
|
"appear",
|
|
2136
3063
|
"mode",
|
|
@@ -2140,13 +3067,33 @@ function validateTransitionAttributes(context, node) {
|
|
|
2140
3067
|
"leave-from-class",
|
|
2141
3068
|
"leave-active-class",
|
|
2142
3069
|
"leave-to-class"
|
|
2143
|
-
]);
|
|
3070
|
+
].map((name) => ({ name, display: name }));
|
|
2144
3071
|
for (const attr of node.attrs) {
|
|
2145
3072
|
const name = getBindingName(attr.name) ?? attr.name;
|
|
2146
|
-
if (supported.
|
|
3073
|
+
if (supported.some((candidate) => candidate.name === name)) {
|
|
2147
3074
|
continue;
|
|
2148
3075
|
}
|
|
2149
|
-
|
|
3076
|
+
throwUnsupportedSpecialAttribute(context, "Transition", attr, supported, "name, appear, mode, and CSS class override attributes");
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
function validateTransitionGroupAttributes(context, node) {
|
|
3080
|
+
const supported = [
|
|
3081
|
+
"name",
|
|
3082
|
+
"tag",
|
|
3083
|
+
"enter-from-class",
|
|
3084
|
+
"enter-active-class",
|
|
3085
|
+
"enter-to-class",
|
|
3086
|
+
"leave-from-class",
|
|
3087
|
+
"leave-active-class",
|
|
3088
|
+
"leave-to-class",
|
|
3089
|
+
"move-class"
|
|
3090
|
+
].map((name) => ({ name, display: name }));
|
|
3091
|
+
for (const attr of node.attrs) {
|
|
3092
|
+
const name = getBindingName(attr.name) ?? attr.name;
|
|
3093
|
+
if (supported.some((candidate) => candidate.name === name)) {
|
|
3094
|
+
continue;
|
|
3095
|
+
}
|
|
3096
|
+
throwUnsupportedSpecialAttribute(context, "TransitionGroup", attr, supported, "name, tag, and CSS class override attributes");
|
|
2150
3097
|
}
|
|
2151
3098
|
}
|
|
2152
3099
|
function getSingleElementChild(context, node, label) {
|
|
@@ -2156,6 +3103,13 @@ function getSingleElementChild(context, node, label) {
|
|
|
2156
3103
|
}
|
|
2157
3104
|
return [meaningful[0]];
|
|
2158
3105
|
}
|
|
3106
|
+
function getAsyncBoundaryChildren(context, node) {
|
|
3107
|
+
const meaningful = node.children.filter((child) => child.type === "element" || child.parts.some((part) => part.value.trim()));
|
|
3108
|
+
if (meaningful.length === 0) {
|
|
3109
|
+
throwTemplateError("<AsyncBoundary> requires at least one child", context, node.loc);
|
|
3110
|
+
}
|
|
3111
|
+
return node.children;
|
|
3112
|
+
}
|
|
2159
3113
|
function getErrorBoundaryFallbackExpression(context, node) {
|
|
2160
3114
|
const fallbackAttr = node.attrs.find((attr) => getBindingName(attr.name) === "fallback");
|
|
2161
3115
|
if (!fallbackAttr) {
|
|
@@ -2163,12 +3117,89 @@ function getErrorBoundaryFallbackExpression(context, node) {
|
|
|
2163
3117
|
}
|
|
2164
3118
|
return compileTemplateExpression(requireAttrValue(fallbackAttr), fallbackAttr.name, toExpressionContext(context, fallbackAttr.valueLoc));
|
|
2165
3119
|
}
|
|
3120
|
+
function getErrorBoundaryResetKeyExpression(context, node) {
|
|
3121
|
+
const resetKeyAttr = node.attrs.find((attr) => getBindingName(attr.name) === "reset-key");
|
|
3122
|
+
if (!resetKeyAttr) {
|
|
3123
|
+
return undefined;
|
|
3124
|
+
}
|
|
3125
|
+
return compileTemplateExpression(requireAttrValue(resetKeyAttr), resetKeyAttr.name, toExpressionContext(context, resetKeyAttr.valueLoc));
|
|
3126
|
+
}
|
|
3127
|
+
function getAsyncBoundaryLoadingExpression(context, node) {
|
|
3128
|
+
const loadingAttr = node.attrs.find((attr) => getBindingName(attr.name) === "loading");
|
|
3129
|
+
if (!loadingAttr) {
|
|
3130
|
+
throwTemplateError("<AsyncBoundary> requires :loading to resolve to a component object", context, node.loc);
|
|
3131
|
+
}
|
|
3132
|
+
return compileTemplateExpression(requireAttrValue(loadingAttr), loadingAttr.name, toExpressionContext(context, loadingAttr.valueLoc));
|
|
3133
|
+
}
|
|
3134
|
+
function getAsyncBoundaryFallbackExpression(context, node) {
|
|
3135
|
+
const fallbackAttr = node.attrs.find((attr) => getBindingName(attr.name) === "fallback");
|
|
3136
|
+
if (!fallbackAttr) {
|
|
3137
|
+
throwTemplateError("<AsyncBoundary> requires :fallback to resolve to a component object", context, node.loc);
|
|
3138
|
+
}
|
|
3139
|
+
return compileTemplateExpression(requireAttrValue(fallbackAttr), fallbackAttr.name, toExpressionContext(context, fallbackAttr.valueLoc));
|
|
3140
|
+
}
|
|
3141
|
+
function getAsyncBoundaryDelayExpression(context, node) {
|
|
3142
|
+
const delayAttr = node.attrs.find((attr) => getBindingName(attr.name) === "delay");
|
|
3143
|
+
if (!delayAttr) {
|
|
3144
|
+
return "0";
|
|
3145
|
+
}
|
|
3146
|
+
return compileTemplateExpression(requireAttrValue(delayAttr), delayAttr.name, toExpressionContext(context, delayAttr.valueLoc));
|
|
3147
|
+
}
|
|
3148
|
+
function getAsyncBoundaryTimeoutExpression(context, node) {
|
|
3149
|
+
const timeoutAttr = node.attrs.find((attr) => getBindingName(attr.name) === "timeout");
|
|
3150
|
+
if (!timeoutAttr) {
|
|
3151
|
+
return "undefined";
|
|
3152
|
+
}
|
|
3153
|
+
return compileTemplateExpression(requireAttrValue(timeoutAttr), timeoutAttr.name, toExpressionContext(context, timeoutAttr.valueLoc));
|
|
3154
|
+
}
|
|
3155
|
+
function getKeepAliveOptionExpression(context, node, name) {
|
|
3156
|
+
const dynamicAttr = node.attrs.find((attr) => getBindingName(attr.name) === name);
|
|
3157
|
+
if (dynamicAttr) {
|
|
3158
|
+
return compileTemplateExpression(requireAttrValue(dynamicAttr), dynamicAttr.name, toExpressionContext(context, dynamicAttr.valueLoc));
|
|
3159
|
+
}
|
|
3160
|
+
const staticValue = getStaticAttrValue(node, name);
|
|
3161
|
+
return staticValue === undefined ? undefined : quote(staticValue);
|
|
3162
|
+
}
|
|
2166
3163
|
function validateErrorBoundaryAttributes(context, node) {
|
|
3164
|
+
const supported = [
|
|
3165
|
+
{ name: "fallback", display: ":fallback" },
|
|
3166
|
+
{ name: "reset-key", display: ":reset-key" }
|
|
3167
|
+
];
|
|
3168
|
+
for (const attr of node.attrs) {
|
|
3169
|
+
const bindingName = getBindingName(attr.name);
|
|
3170
|
+
if (bindingName === "fallback" || bindingName === "reset-key") {
|
|
3171
|
+
continue;
|
|
3172
|
+
}
|
|
3173
|
+
throwUnsupportedSpecialAttribute(context, "ErrorBoundary", attr, supported);
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
function validateAsyncBoundaryAttributes(context, node) {
|
|
3177
|
+
const supported = [
|
|
3178
|
+
{ name: "loading", display: ":loading" },
|
|
3179
|
+
{ name: "fallback", display: ":fallback" },
|
|
3180
|
+
{ name: "delay", display: ":delay" },
|
|
3181
|
+
{ name: "timeout", display: ":timeout" }
|
|
3182
|
+
];
|
|
2167
3183
|
for (const attr of node.attrs) {
|
|
2168
|
-
|
|
3184
|
+
const bindingName = getBindingName(attr.name);
|
|
3185
|
+
if (bindingName === "loading" || bindingName === "fallback" || bindingName === "delay" || bindingName === "timeout") {
|
|
2169
3186
|
continue;
|
|
2170
3187
|
}
|
|
2171
|
-
|
|
3188
|
+
throwUnsupportedSpecialAttribute(context, "AsyncBoundary", attr, supported);
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
function validateKeepAliveAttributes(context, node) {
|
|
3192
|
+
const supported = [
|
|
3193
|
+
{ name: "include", display: ":include" },
|
|
3194
|
+
{ name: "exclude", display: ":exclude" },
|
|
3195
|
+
{ name: "max", display: ":max" }
|
|
3196
|
+
];
|
|
3197
|
+
for (const attr of node.attrs) {
|
|
3198
|
+
const name = getBindingName(attr.name) ?? attr.name;
|
|
3199
|
+
if (name === "include" || name === "exclude" || name === "max") {
|
|
3200
|
+
continue;
|
|
3201
|
+
}
|
|
3202
|
+
throwUnsupportedSpecialAttribute(context, "KeepAlive", attr, supported, "include, exclude, and max");
|
|
2172
3203
|
}
|
|
2173
3204
|
}
|
|
2174
3205
|
function getTeleportToExpression(context, node) {
|
|
@@ -2190,14 +3221,56 @@ function getTeleportDisabledExpression(context, node) {
|
|
|
2190
3221
|
return hasStaticBooleanAttr(node, "disabled") ? "true" : "false";
|
|
2191
3222
|
}
|
|
2192
3223
|
function validateTeleportAttributes(context, node) {
|
|
3224
|
+
const supported = [
|
|
3225
|
+
{ name: "to", display: "to" },
|
|
3226
|
+
{ name: "disabled", display: "disabled" }
|
|
3227
|
+
];
|
|
2193
3228
|
for (const attr of node.attrs) {
|
|
2194
3229
|
const name = getBindingName(attr.name) ?? attr.name;
|
|
2195
3230
|
if (name === "to" || name === "disabled") {
|
|
2196
3231
|
continue;
|
|
2197
3232
|
}
|
|
2198
|
-
|
|
3233
|
+
throwUnsupportedSpecialAttribute(context, "Teleport", attr, supported);
|
|
2199
3234
|
}
|
|
2200
3235
|
}
|
|
3236
|
+
function throwUnsupportedSpecialAttribute(context, tagName, attr, supported, supportedLabel = supported.map((candidate) => candidate.display).join(", ")) {
|
|
3237
|
+
const normalizedName = getBindingName(attr.name) ?? attr.name;
|
|
3238
|
+
const suggestion = suggestAttributeName(normalizedName, supported);
|
|
3239
|
+
const suggestionMessage = suggestion ? ` Did you mean ${suggestion.display}?` : "";
|
|
3240
|
+
throwTemplateError(`Unsupported attribute ${quote(attr.name)} on <${tagName}>.${suggestionMessage} <${tagName}> only supports ${supportedLabel} in v1.`, context, attr.loc);
|
|
3241
|
+
}
|
|
3242
|
+
function suggestAttributeName(name, supported) {
|
|
3243
|
+
return suggestName(name, supported);
|
|
3244
|
+
}
|
|
3245
|
+
function suggestName(name, supported) {
|
|
3246
|
+
let best;
|
|
3247
|
+
for (const candidate of supported) {
|
|
3248
|
+
const distance = editDistance(name, candidate.name);
|
|
3249
|
+
if (!best || distance < best.distance) {
|
|
3250
|
+
best = { candidate, distance };
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
if (!best) {
|
|
3254
|
+
return undefined;
|
|
3255
|
+
}
|
|
3256
|
+
const maxDistance = Math.max(1, Math.floor(best.candidate.name.length / 3));
|
|
3257
|
+
return best.distance <= maxDistance ? best.candidate : undefined;
|
|
3258
|
+
}
|
|
3259
|
+
function editDistance(left, right) {
|
|
3260
|
+
const previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
3261
|
+
const current = Array.from({ length: right.length + 1 }, () => 0);
|
|
3262
|
+
for (let leftIndex = 1; leftIndex <= left.length; leftIndex += 1) {
|
|
3263
|
+
current[0] = leftIndex;
|
|
3264
|
+
for (let rightIndex = 1; rightIndex <= right.length; rightIndex += 1) {
|
|
3265
|
+
const cost = left[leftIndex - 1] === right[rightIndex - 1] ? 0 : 1;
|
|
3266
|
+
current[rightIndex] = Math.min(previous[rightIndex] + 1, current[rightIndex - 1] + 1, previous[rightIndex - 1] + cost);
|
|
3267
|
+
}
|
|
3268
|
+
for (let index = 0; index < previous.length; index += 1) {
|
|
3269
|
+
previous[index] = current[index];
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
return previous[right.length] ?? 0;
|
|
3273
|
+
}
|
|
2201
3274
|
function toComponentEventProp(eventName) {
|
|
2202
3275
|
return `on${eventName
|
|
2203
3276
|
.split(/[-:]/)
|
|
@@ -2205,18 +3278,67 @@ function toComponentEventProp(eventName) {
|
|
|
2205
3278
|
.map((part) => part[0]?.toUpperCase() + part.slice(1))
|
|
2206
3279
|
.join("")}`;
|
|
2207
3280
|
}
|
|
2208
|
-
function validateAttributes(node) {
|
|
3281
|
+
function validateAttributes(context, node) {
|
|
3282
|
+
const htmlAttr = getAttr(node, "v-html");
|
|
3283
|
+
const textAttr = getAttr(node, "v-text");
|
|
3284
|
+
if (htmlAttr && textAttr) {
|
|
3285
|
+
throwTemplateError("v-html and v-text cannot be used on the same element", context, textAttr.loc);
|
|
3286
|
+
}
|
|
2209
3287
|
for (const attr of node.attrs) {
|
|
2210
3288
|
if (attr.name.startsWith("v-") && !isSupportedDirectiveAttr(attr)) {
|
|
2211
|
-
|
|
3289
|
+
throwUnsupportedDirective(context, attr);
|
|
3290
|
+
}
|
|
3291
|
+
if ((attr.name === "v-html" || attr.name === "v-text") && attr.value === true) {
|
|
3292
|
+
throwTemplateError(`${attr.name} requires a value`, context, attr.loc);
|
|
3293
|
+
}
|
|
3294
|
+
if ((attr.name === "v-pre" || attr.name === "v-cloak") && attr.value !== true) {
|
|
3295
|
+
throwTemplateError(`${attr.name} does not accept a value`, context, attr.valueLoc ?? attr.loc);
|
|
3296
|
+
}
|
|
3297
|
+
if (attr.name === "v-once") {
|
|
3298
|
+
validateOnceAttribute(context, node);
|
|
2212
3299
|
}
|
|
3300
|
+
if (attr.name === "v-memo") {
|
|
3301
|
+
getMemoExpression(context, node);
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3305
|
+
function validateOnceAttribute(context, node) {
|
|
3306
|
+
const attr = node.attrs.find((candidate) => candidate.name === "v-once");
|
|
3307
|
+
if (attr && attr.value !== true) {
|
|
3308
|
+
throwTemplateError("v-once does not accept a value", context, attr.valueLoc ?? attr.loc);
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
function validatePreAttribute(context, node) {
|
|
3312
|
+
const attr = node.attrs.find((candidate) => candidate.name === "v-pre");
|
|
3313
|
+
if (attr && attr.value !== true) {
|
|
3314
|
+
throwTemplateError("v-pre does not accept a value", context, attr.valueLoc ?? attr.loc);
|
|
2213
3315
|
}
|
|
2214
3316
|
}
|
|
3317
|
+
function throwUnsupportedDirective(context, attr) {
|
|
3318
|
+
const suggestion = suggestDirectiveName(attr.name);
|
|
3319
|
+
const suggestionMessage = suggestion ? ` Did you mean ${suggestion}?` : "";
|
|
3320
|
+
throwTemplateError(`Unsupported directive ${quote(attr.name)}.${suggestionMessage}`, context, attr.loc);
|
|
3321
|
+
}
|
|
3322
|
+
function suggestDirectiveName(name) {
|
|
3323
|
+
const supported = ["v-if", "v-else-if", "v-else", "v-for", "v-show", "v-html", "v-text", "v-pre", "v-cloak", "v-once", "v-memo", "v-model", "v-bind", "v-on", "v-slot"];
|
|
3324
|
+
const directiveName = name.includes(":") ? name.slice(0, name.indexOf(":")) : name.includes(".") ? name.slice(0, name.indexOf(".")) : name;
|
|
3325
|
+
const suggestion = suggestName(directiveName, supported.map((candidate) => ({ name: candidate, display: candidate })));
|
|
3326
|
+
if (!suggestion) {
|
|
3327
|
+
return undefined;
|
|
3328
|
+
}
|
|
3329
|
+
if (suggestion.name === "v-bind" && name.includes(":")) {
|
|
3330
|
+
return `v-bind:${name.slice(name.indexOf(":") + 1)}`;
|
|
3331
|
+
}
|
|
3332
|
+
if (suggestion.name === "v-on" && name.includes(":")) {
|
|
3333
|
+
return `v-on:${name.slice(name.indexOf(":") + 1)}`;
|
|
3334
|
+
}
|
|
3335
|
+
return suggestion.display;
|
|
3336
|
+
}
|
|
2215
3337
|
function isDirectiveAttr(attr) {
|
|
2216
3338
|
return isSupportedDirectiveAttr(attr) || attr.name.startsWith("@") || attr.name.startsWith(":");
|
|
2217
3339
|
}
|
|
2218
3340
|
function isStructuralAttr(attr) {
|
|
2219
|
-
return attr.name === "v-if" || attr.name === "v-else-if" || attr.name === "v-else" || attr.name === "v-for";
|
|
3341
|
+
return attr.name === "v-if" || attr.name === "v-else-if" || attr.name === "v-else" || attr.name === "v-for" || attr.name === "v-once" || attr.name === "v-memo";
|
|
2220
3342
|
}
|
|
2221
3343
|
function isSlotDirectiveAttr(attr) {
|
|
2222
3344
|
return attr.name === "v-slot" || attr.name.startsWith("v-slot:") || attr.name.startsWith("#");
|
|
@@ -2227,19 +3349,55 @@ function isSupportedDirectiveAttr(attr) {
|
|
|
2227
3349
|
attr.name === "v-else" ||
|
|
2228
3350
|
attr.name === "v-for" ||
|
|
2229
3351
|
attr.name === "v-show" ||
|
|
3352
|
+
attr.name === "v-html" ||
|
|
3353
|
+
attr.name === "v-text" ||
|
|
3354
|
+
attr.name === "v-pre" ||
|
|
3355
|
+
attr.name === "v-cloak" ||
|
|
3356
|
+
attr.name === "v-once" ||
|
|
3357
|
+
attr.name === "v-memo" ||
|
|
2230
3358
|
Boolean(parseModelDirective(attr.name)) ||
|
|
2231
3359
|
isObjectBindAttr(attr) ||
|
|
2232
3360
|
isObjectOnAttr(attr) ||
|
|
3361
|
+
Boolean(getDynamicBindingArgument(attr.name)) ||
|
|
3362
|
+
Boolean(getDynamicEventArgument(attr.name)) ||
|
|
2233
3363
|
Boolean(parseEventDirective(attr.name)) ||
|
|
2234
3364
|
Boolean(getBindingName(attr.name)));
|
|
2235
3365
|
}
|
|
2236
3366
|
function isObjectBindAttr(attr) {
|
|
2237
|
-
return attr.name
|
|
3367
|
+
return Boolean(parseObjectBindDirective(attr.name));
|
|
3368
|
+
}
|
|
3369
|
+
function parseObjectBindDirective(name) {
|
|
3370
|
+
if (name === "v-bind") {
|
|
3371
|
+
return { modifiers: [] };
|
|
3372
|
+
}
|
|
3373
|
+
if (!name.startsWith("v-bind.")) {
|
|
3374
|
+
return undefined;
|
|
3375
|
+
}
|
|
3376
|
+
return { modifiers: name.slice("v-bind.".length).split(".").filter(Boolean) };
|
|
3377
|
+
}
|
|
3378
|
+
function getContentDirectiveAttr(node) {
|
|
3379
|
+
return getAttr(node, "v-html") ?? getAttr(node, "v-text");
|
|
3380
|
+
}
|
|
3381
|
+
function getAttr(node, name) {
|
|
3382
|
+
return node.attrs.find((attr) => attr.name === name);
|
|
2238
3383
|
}
|
|
2239
3384
|
function isObjectOnAttr(attr) {
|
|
2240
|
-
return attr.name
|
|
3385
|
+
return Boolean(parseObjectOnDirective(attr.name));
|
|
3386
|
+
}
|
|
3387
|
+
function parseObjectOnDirective(name) {
|
|
3388
|
+
if (name === "v-on") {
|
|
3389
|
+
return { modifiers: [] };
|
|
3390
|
+
}
|
|
3391
|
+
if (!name.startsWith("v-on.")) {
|
|
3392
|
+
return undefined;
|
|
3393
|
+
}
|
|
3394
|
+
return { modifiers: name.slice("v-on.".length).split(".").filter(Boolean) };
|
|
2241
3395
|
}
|
|
2242
3396
|
function parseEventDirective(name) {
|
|
3397
|
+
const dynamic = getDynamicEventArgument(name);
|
|
3398
|
+
if (dynamic) {
|
|
3399
|
+
return dynamic;
|
|
3400
|
+
}
|
|
2243
3401
|
const rawName = getEventName(name);
|
|
2244
3402
|
if (!rawName) {
|
|
2245
3403
|
return undefined;
|
|
@@ -2254,30 +3412,49 @@ function parseModelDirective(name) {
|
|
|
2254
3412
|
if (name === "v-model") {
|
|
2255
3413
|
return { modifiers: [] };
|
|
2256
3414
|
}
|
|
2257
|
-
if (!name.startsWith("v-model.")) {
|
|
3415
|
+
if (!name.startsWith("v-model.") && !name.startsWith("v-model:")) {
|
|
2258
3416
|
return undefined;
|
|
2259
3417
|
}
|
|
3418
|
+
if (name.startsWith("v-model:")) {
|
|
3419
|
+
const raw = name.slice("v-model:".length);
|
|
3420
|
+
const [argument = "", ...modifiers] = raw.split(".");
|
|
3421
|
+
return { argument, modifiers: modifiers.filter(Boolean) };
|
|
3422
|
+
}
|
|
2260
3423
|
return { modifiers: name.slice("v-model.".length).split(".").filter(Boolean) };
|
|
2261
3424
|
}
|
|
2262
3425
|
function validateModelModifiers(model, attr, context) {
|
|
2263
|
-
const supportedModifiers =
|
|
3426
|
+
const supportedModifiers = ["trim", "number", "lazy"];
|
|
3427
|
+
if (model.argument !== undefined && !model.argument) {
|
|
3428
|
+
throwTemplateError("v-model argument must not be empty", context, attr.loc);
|
|
3429
|
+
}
|
|
2264
3430
|
for (const modifier of model.modifiers) {
|
|
2265
|
-
if (!supportedModifiers.
|
|
2266
|
-
|
|
3431
|
+
if (!supportedModifiers.includes(modifier)) {
|
|
3432
|
+
const suggestion = suggestModifierName(modifier, supportedModifiers);
|
|
3433
|
+
const suggestionMessage = suggestion ? ` Did you mean .${suggestion}?` : "";
|
|
3434
|
+
throwTemplateError(`Unsupported v-model modifier .${modifier}.${suggestionMessage}`, context, attr.loc);
|
|
2267
3435
|
}
|
|
2268
3436
|
}
|
|
2269
3437
|
}
|
|
2270
|
-
|
|
3438
|
+
const modelValueProperty = "__mikuruModelValue";
|
|
3439
|
+
function modelElementValueExpression(targetExpression, modifiers) {
|
|
3440
|
+
const valueExpression = `(${quote(modelValueProperty)} in ${targetExpression} ? ${targetExpression}[${quote(modelValueProperty)}] : ${targetExpression}.getAttribute("value") ?? (${targetExpression}.tagName === "OPTION" ? (${targetExpression}.textContent ?? "") : "on"))`;
|
|
3441
|
+
return modifiers.includes("number") ? `Number(${valueExpression})` : valueExpression;
|
|
3442
|
+
}
|
|
3443
|
+
function modelAssignedValue(modelMode, modifiers, expression) {
|
|
2271
3444
|
if (modelMode === "checkbox") {
|
|
2272
|
-
|
|
3445
|
+
const valueExpression = modelElementValueExpression("$event.target", modifiers);
|
|
3446
|
+
return `(() => { const checked = $event.target.checked; const current = unwrap(${expression}); const value = ${valueExpression}; if (Array.isArray(current)) { const hasValue = current.some((item) => Object.is(item, value)); return checked ? (hasValue ? current : [...current, value]) : current.filter((item) => !Object.is(item, value)); } return checked; })()`;
|
|
2273
3447
|
}
|
|
2274
3448
|
if (modelMode === "radio") {
|
|
2275
|
-
|
|
2276
|
-
return modifiers.includes("number") ? `Number(${valueExpression})` : valueExpression;
|
|
3449
|
+
return modelElementValueExpression("$event.target", modifiers);
|
|
2277
3450
|
}
|
|
2278
3451
|
if (modelMode === "select-multiple") {
|
|
2279
|
-
const valueExpression = `Array.from($event.target.options).filter((option) => option.selected).map((option) =>
|
|
2280
|
-
return
|
|
3452
|
+
const valueExpression = `Array.from($event.target.options).filter((option) => option.selected).map((option) => ${modelElementValueExpression("option", modifiers)})`;
|
|
3453
|
+
return valueExpression;
|
|
3454
|
+
}
|
|
3455
|
+
if (modelMode === "select") {
|
|
3456
|
+
const valueExpression = `(() => { const option = $event.target.selectedOptions[0]; return option ? ${modelElementValueExpression("option", modifiers)} : ""; })()`;
|
|
3457
|
+
return valueExpression;
|
|
2281
3458
|
}
|
|
2282
3459
|
let valueExpression = "$event.target.value";
|
|
2283
3460
|
if (modifiers.includes("trim")) {
|
|
@@ -2289,10 +3466,12 @@ function modelAssignedValue(modelMode, modifiers) {
|
|
|
2289
3466
|
return valueExpression;
|
|
2290
3467
|
}
|
|
2291
3468
|
function validateEventModifiers(event, attr, context) {
|
|
2292
|
-
const supportedModifiers =
|
|
3469
|
+
const supportedModifiers = [...eventControlModifiers, ...eventOptionModifiers, ...eventSystemModifiers, ...eventMouseModifiers, ...eventKeyModifiers, "exact"];
|
|
2293
3470
|
for (const modifier of event.modifiers) {
|
|
2294
|
-
if (!supportedModifiers.
|
|
2295
|
-
|
|
3471
|
+
if (!supportedModifiers.includes(modifier)) {
|
|
3472
|
+
const suggestion = suggestModifierName(modifier, supportedModifiers);
|
|
3473
|
+
const suggestionMessage = suggestion ? ` Did you mean .${suggestion}?` : "";
|
|
3474
|
+
throwTemplateError(`Unsupported event modifier .${modifier}.${suggestionMessage}`, context, attr.loc);
|
|
2296
3475
|
}
|
|
2297
3476
|
}
|
|
2298
3477
|
if (event.modifiers.includes("passive") && event.modifiers.includes("prevent")) {
|
|
@@ -2300,12 +3479,30 @@ function validateEventModifiers(event, attr, context) {
|
|
|
2300
3479
|
}
|
|
2301
3480
|
}
|
|
2302
3481
|
function validateComponentEventModifiers(event, attr, context) {
|
|
3482
|
+
const supportedModifiers = ["once"];
|
|
2303
3483
|
for (const modifier of event.modifiers) {
|
|
2304
3484
|
if (modifier !== "once") {
|
|
2305
|
-
|
|
3485
|
+
const suggestion = suggestModifierName(modifier, supportedModifiers);
|
|
3486
|
+
const suggestionMessage = suggestion ? ` Did you mean .${suggestion}?` : "";
|
|
3487
|
+
throwTemplateError(`Event modifier .${modifier} is only supported on DOM events.${suggestionMessage}`, context, attr.loc);
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
function validateObjectOnModifiers(event, attr, context, target) {
|
|
3492
|
+
if (target === "component" && event.modifiers.length > 0) {
|
|
3493
|
+
throwTemplateError("Object v-on modifiers are only supported on native elements", context, attr.loc);
|
|
3494
|
+
}
|
|
3495
|
+
for (const modifier of event.modifiers) {
|
|
3496
|
+
if (!eventOptionModifiers.includes(modifier)) {
|
|
3497
|
+
const suggestion = suggestModifierName(modifier, eventOptionModifiers);
|
|
3498
|
+
const suggestionMessage = suggestion ? ` Did you mean .${suggestion}?` : "";
|
|
3499
|
+
throwTemplateError(`Object v-on modifier .${modifier} is not supported. Use .once, .capture, or .passive.${suggestionMessage}`, context, attr.loc);
|
|
2306
3500
|
}
|
|
2307
3501
|
}
|
|
2308
3502
|
}
|
|
3503
|
+
function suggestModifierName(name, supported) {
|
|
3504
|
+
return suggestName(name, supported.map((candidate) => ({ name: candidate, display: candidate })))?.name;
|
|
3505
|
+
}
|
|
2309
3506
|
function eventListenerOptions(event) {
|
|
2310
3507
|
const options = [
|
|
2311
3508
|
event.modifiers.includes("capture") ? "capture: true" : undefined,
|
|
@@ -2314,6 +3511,66 @@ function eventListenerOptions(event) {
|
|
|
2314
3511
|
].filter(Boolean);
|
|
2315
3512
|
return options.length ? `{ ${options.join(", ")} }` : undefined;
|
|
2316
3513
|
}
|
|
3514
|
+
const eventControlModifiers = ["prevent", "stop", "self"];
|
|
3515
|
+
const eventOptionModifiers = ["once", "capture", "passive"];
|
|
3516
|
+
const eventSystemModifiers = ["ctrl", "shift", "alt", "meta"];
|
|
3517
|
+
const eventMouseModifiers = ["left", "right", "middle"];
|
|
3518
|
+
const eventKeyModifiers = ["enter", "escape", "esc", "space", "tab", "delete", "backspace", "up", "down", "left", "right"];
|
|
3519
|
+
function eventModifierGuardExpression(event) {
|
|
3520
|
+
const checks = [];
|
|
3521
|
+
const mouseEvent = isMouseEventName(event.name);
|
|
3522
|
+
for (const modifier of event.modifiers) {
|
|
3523
|
+
if (eventSystemModifiers.includes(modifier)) {
|
|
3524
|
+
checks.push(`!$event.${modifier}Key`);
|
|
3525
|
+
continue;
|
|
3526
|
+
}
|
|
3527
|
+
const mouseExpression = mouseEvent ? eventMouseButtonExpression(modifier) : undefined;
|
|
3528
|
+
if (mouseExpression) {
|
|
3529
|
+
checks.push(`$event.button !== ${mouseExpression}`);
|
|
3530
|
+
continue;
|
|
3531
|
+
}
|
|
3532
|
+
const keyExpression = eventKeyExpression(modifier);
|
|
3533
|
+
if (keyExpression) {
|
|
3534
|
+
checks.push(`!${keyExpression}.includes($event.key)`);
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
if (event.modifiers.includes("exact")) {
|
|
3538
|
+
for (const modifier of eventSystemModifiers) {
|
|
3539
|
+
if (!event.modifiers.includes(modifier)) {
|
|
3540
|
+
checks.push(`$event.${modifier}Key`);
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
return checks.length ? checks.join(" || ") : undefined;
|
|
3545
|
+
}
|
|
3546
|
+
function isMouseEventName(name) {
|
|
3547
|
+
return !!name && /^(?:click|dblclick|auxclick|contextmenu|mousedown|mouseup|mousemove|mouseover|mouseout|mouseenter|mouseleave|pointerdown|pointerup|pointermove|pointerover|pointerout|pointerenter|pointerleave)$/.test(name);
|
|
3548
|
+
}
|
|
3549
|
+
function eventMouseButtonExpression(modifier) {
|
|
3550
|
+
const mouseButtons = {
|
|
3551
|
+
left: "0",
|
|
3552
|
+
middle: "1",
|
|
3553
|
+
right: "2"
|
|
3554
|
+
};
|
|
3555
|
+
return mouseButtons[modifier];
|
|
3556
|
+
}
|
|
3557
|
+
function eventKeyExpression(modifier) {
|
|
3558
|
+
const keyAliases = {
|
|
3559
|
+
enter: ["Enter"],
|
|
3560
|
+
escape: ["Escape"],
|
|
3561
|
+
esc: ["Escape"],
|
|
3562
|
+
space: [" ", "Spacebar"],
|
|
3563
|
+
tab: ["Tab"],
|
|
3564
|
+
delete: ["Delete"],
|
|
3565
|
+
backspace: ["Backspace"],
|
|
3566
|
+
up: ["ArrowUp", "Up"],
|
|
3567
|
+
down: ["ArrowDown", "Down"],
|
|
3568
|
+
left: ["ArrowLeft", "Left"],
|
|
3569
|
+
right: ["ArrowRight", "Right"]
|
|
3570
|
+
};
|
|
3571
|
+
const keys = keyAliases[modifier];
|
|
3572
|
+
return keys ? JSON.stringify(keys) : undefined;
|
|
3573
|
+
}
|
|
2317
3574
|
function componentEventHandlerExpression(event, handlerExpression, context) {
|
|
2318
3575
|
if (!event.modifiers.includes("once")) {
|
|
2319
3576
|
return handlerExpression;
|
|
@@ -2322,7 +3579,13 @@ function componentEventHandlerExpression(event, handlerExpression, context) {
|
|
|
2322
3579
|
const handlerVar = nextVar(context, "handler");
|
|
2323
3580
|
return `(() => { let ${calledVar} = false; const ${handlerVar} = ${handlerExpression}; return (...$args) => { if (${calledVar}) { return; } ${calledVar} = true; return ${handlerVar}(...$args); }; })()`;
|
|
2324
3581
|
}
|
|
3582
|
+
function componentEventPropRuntimeExpression(eventNameExpression) {
|
|
3583
|
+
return `"on" + ${eventNameExpression}.split(/[-:]/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join("")`;
|
|
3584
|
+
}
|
|
2325
3585
|
function getEventName(name) {
|
|
3586
|
+
if (name.startsWith("@[") || name.startsWith("v-on:[")) {
|
|
3587
|
+
return undefined;
|
|
3588
|
+
}
|
|
2326
3589
|
if (name.startsWith("@")) {
|
|
2327
3590
|
return name.slice(1);
|
|
2328
3591
|
}
|
|
@@ -2332,11 +3595,108 @@ function getEventName(name) {
|
|
|
2332
3595
|
return undefined;
|
|
2333
3596
|
}
|
|
2334
3597
|
function getBindingName(name) {
|
|
2335
|
-
|
|
2336
|
-
|
|
3598
|
+
const binding = parseBindDirective(name);
|
|
3599
|
+
if (!binding || binding.nameExpression) {
|
|
3600
|
+
return undefined;
|
|
3601
|
+
}
|
|
3602
|
+
return binding.name;
|
|
3603
|
+
}
|
|
3604
|
+
function getDynamicBindingArgument(name) {
|
|
3605
|
+
const binding = parseBindDirective(name);
|
|
3606
|
+
if (!binding?.nameExpression) {
|
|
3607
|
+
return undefined;
|
|
3608
|
+
}
|
|
3609
|
+
return { expression: binding.nameExpression };
|
|
3610
|
+
}
|
|
3611
|
+
function parseBindDirective(name) {
|
|
3612
|
+
const dynamic = parseDynamicArgument(name, [":", "v-bind:"]);
|
|
3613
|
+
if (dynamic) {
|
|
3614
|
+
return { nameExpression: dynamic.expression, modifiers: dynamic.modifiers };
|
|
3615
|
+
}
|
|
3616
|
+
const rawName = name.startsWith(":")
|
|
3617
|
+
? name.slice(1)
|
|
3618
|
+
: name.startsWith("v-bind:")
|
|
3619
|
+
? name.slice("v-bind:".length)
|
|
3620
|
+
: undefined;
|
|
3621
|
+
if (!rawName) {
|
|
3622
|
+
return undefined;
|
|
3623
|
+
}
|
|
3624
|
+
const [bindingName, ...modifiers] = rawName.split(".");
|
|
3625
|
+
if (!bindingName) {
|
|
3626
|
+
return undefined;
|
|
3627
|
+
}
|
|
3628
|
+
return { name: modifiers.includes("camel") ? camelize(bindingName) : bindingName, modifiers };
|
|
3629
|
+
}
|
|
3630
|
+
function validateBindModifiers(binding, attr, context, target) {
|
|
3631
|
+
const allowed = new Set(["camel", "prop", "attr"]);
|
|
3632
|
+
for (const modifier of binding.modifiers) {
|
|
3633
|
+
if (!allowed.has(modifier)) {
|
|
3634
|
+
throwTemplateError(`Unsupported v-bind modifier ".${modifier}". Use .camel, .prop, or .attr.`, context, attr.loc);
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
if (binding.modifiers.includes("prop") && binding.modifiers.includes("attr")) {
|
|
3638
|
+
throwTemplateError("v-bind modifiers .prop and .attr cannot be used together", context, attr.loc);
|
|
3639
|
+
}
|
|
3640
|
+
if (target === "component" && (binding.modifiers.includes("prop") || binding.modifiers.includes("attr"))) {
|
|
3641
|
+
throwTemplateError("v-bind .prop and .attr modifiers are only supported on native elements", context, attr.loc);
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
function validateObjectBindModifiers(binding, attr, context, target) {
|
|
3645
|
+
const allowed = new Set(["camel", "prop", "attr"]);
|
|
3646
|
+
for (const modifier of binding.modifiers) {
|
|
3647
|
+
if (!allowed.has(modifier)) {
|
|
3648
|
+
throwTemplateError(`Unsupported object v-bind modifier ".${modifier}". Use .camel, .prop, or .attr.`, context, attr.loc);
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
if (binding.modifiers.includes("prop") && binding.modifiers.includes("attr")) {
|
|
3652
|
+
throwTemplateError("Object v-bind modifiers .prop and .attr cannot be used together", context, attr.loc);
|
|
3653
|
+
}
|
|
3654
|
+
if (target === "component" && binding.modifiers.length > 0) {
|
|
3655
|
+
throwTemplateError("Object v-bind modifiers are only supported on native elements", context, attr.loc);
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
function bindOptionsExpression(binding) {
|
|
3659
|
+
if (binding.modifiers.includes("prop")) {
|
|
3660
|
+
return ", { property: true }";
|
|
3661
|
+
}
|
|
3662
|
+
if (binding.modifiers.includes("attr")) {
|
|
3663
|
+
return ", { attribute: true }";
|
|
3664
|
+
}
|
|
3665
|
+
return "";
|
|
3666
|
+
}
|
|
3667
|
+
function bindNameExpression(expression, binding) {
|
|
3668
|
+
return binding.modifiers.includes("camel") ? `(${expression}).replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase())` : expression;
|
|
3669
|
+
}
|
|
3670
|
+
function objectBindKeyExpression(keyExpression, binding) {
|
|
3671
|
+
return bindNameExpression(`String(${keyExpression})`, binding);
|
|
3672
|
+
}
|
|
3673
|
+
function camelize(value) {
|
|
3674
|
+
return value.replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
3675
|
+
}
|
|
3676
|
+
function getDynamicEventArgument(name) {
|
|
3677
|
+
const dynamic = parseDynamicArgument(name, ["@", "v-on:"]);
|
|
3678
|
+
if (!dynamic) {
|
|
3679
|
+
return undefined;
|
|
2337
3680
|
}
|
|
2338
|
-
|
|
2339
|
-
|
|
3681
|
+
return { nameExpression: dynamic.expression, modifiers: dynamic.modifiers };
|
|
3682
|
+
}
|
|
3683
|
+
function parseDynamicArgument(name, prefixes) {
|
|
3684
|
+
for (const prefix of prefixes) {
|
|
3685
|
+
if (!name.startsWith(`${prefix}[`)) {
|
|
3686
|
+
continue;
|
|
3687
|
+
}
|
|
3688
|
+
const argumentStart = prefix.length + 1;
|
|
3689
|
+
const argumentEnd = name.indexOf("]", argumentStart);
|
|
3690
|
+
if (argumentEnd === -1) {
|
|
3691
|
+
return undefined;
|
|
3692
|
+
}
|
|
3693
|
+
const expression = name.slice(argumentStart, argumentEnd).trim();
|
|
3694
|
+
if (!expression) {
|
|
3695
|
+
return undefined;
|
|
3696
|
+
}
|
|
3697
|
+
const rest = name.slice(argumentEnd + 1);
|
|
3698
|
+
const modifiers = rest.startsWith(".") ? rest.slice(1).split(".").filter(Boolean) : [];
|
|
3699
|
+
return { expression, modifiers };
|
|
2340
3700
|
}
|
|
2341
3701
|
return undefined;
|
|
2342
3702
|
}
|