mikuru 1.0.14 → 1.0.16
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 +38 -1
- package/README.md +38 -7
- package/dist/compiler/analyzeTemplate.js +0 -3
- package/dist/compiler/analyzeTemplate.js.map +1 -1
- package/dist/compiler/generate.js +972 -73
- package/dist/compiler/generate.js.map +1 -1
- package/dist/router/index.d.ts +168 -7
- package/dist/router/index.js +610 -66
- package/dist/router/index.js.map +1 -1
- package/dist/runtime/dom.d.ts +4 -0
- package/dist/runtime/dom.js +39 -0
- package/dist/runtime/dom.js.map +1 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/index.js.map +1 -1
- package/package.json +26 -26
|
@@ -26,6 +26,68 @@ export function generate(descriptor, root) {
|
|
|
26
26
|
emit(context, 3, "cleanup();");
|
|
27
27
|
emit(context, 2, "}");
|
|
28
28
|
emit(context, 1, "};");
|
|
29
|
+
emit(context, 1, "const __mikuru_transitionFrame = (fn) => {");
|
|
30
|
+
emit(context, 2, "const raf = globalThis.requestAnimationFrame;");
|
|
31
|
+
emit(context, 2, "if (typeof raf === \"function\") { raf(() => raf(fn)); } else { setTimeout(fn, 0); }");
|
|
32
|
+
emit(context, 1, "};");
|
|
33
|
+
emit(context, 1, "const __mikuru_transitionDone = (element, cleanup) => {");
|
|
34
|
+
emit(context, 2, "let done = false;");
|
|
35
|
+
emit(context, 2, "const finish = () => {");
|
|
36
|
+
emit(context, 3, "if (done) { return; }");
|
|
37
|
+
emit(context, 3, "done = true;");
|
|
38
|
+
emit(context, 3, "element.removeEventListener(\"transitionend\", finish);");
|
|
39
|
+
emit(context, 3, "element.removeEventListener(\"animationend\", finish);");
|
|
40
|
+
emit(context, 3, "cleanup();");
|
|
41
|
+
emit(context, 2, "};");
|
|
42
|
+
emit(context, 2, "element.addEventListener(\"transitionend\", finish);");
|
|
43
|
+
emit(context, 2, "element.addEventListener(\"animationend\", finish);");
|
|
44
|
+
emit(context, 2, "setTimeout(finish, 50);");
|
|
45
|
+
emit(context, 1, "};");
|
|
46
|
+
emit(context, 1, "const __mikuru_applyTransitionEnter = (element, name) => {");
|
|
47
|
+
emit(context, 2, "const transition = typeof name === \"object\" ? name : { name };");
|
|
48
|
+
emit(context, 2, "if (!element || element.nodeType !== 1 || !transition.name) { return; }");
|
|
49
|
+
emit(context, 2, "const from = transition.enterFromClass || `${transition.name}-enter-from`;");
|
|
50
|
+
emit(context, 2, "const active = transition.enterActiveClass || `${transition.name}-enter-active`;");
|
|
51
|
+
emit(context, 2, "const to = transition.enterToClass || `${transition.name}-enter-to`;");
|
|
52
|
+
emit(context, 2, "element.classList.add(from, active);");
|
|
53
|
+
emit(context, 2, "__mikuru_transitionFrame(() => {");
|
|
54
|
+
emit(context, 3, "element.classList.remove(from);");
|
|
55
|
+
emit(context, 3, "element.classList.add(to);");
|
|
56
|
+
emit(context, 3, "__mikuru_transitionDone(element, () => element.classList.remove(active, to));");
|
|
57
|
+
emit(context, 2, "});");
|
|
58
|
+
emit(context, 1, "};");
|
|
59
|
+
emit(context, 1, "const __mikuru_removeNode = (node) => {");
|
|
60
|
+
emit(context, 2, "if (!node || !node.parentNode) { return; }");
|
|
61
|
+
emit(context, 2, "if (node.nodeType !== 1 || !node.__mikuru_transition || node.__mikuru_transitionLeaving) { node.remove(); return; }");
|
|
62
|
+
emit(context, 2, "node.__mikuru_transitionLeaving = true;");
|
|
63
|
+
emit(context, 2, "const transition = node.__mikuru_transition;");
|
|
64
|
+
emit(context, 2, "const from = transition.leaveFromClass || `${transition.name}-leave-from`;");
|
|
65
|
+
emit(context, 2, "const active = transition.leaveActiveClass || `${transition.name}-leave-active`;");
|
|
66
|
+
emit(context, 2, "const to = transition.leaveToClass || `${transition.name}-leave-to`;");
|
|
67
|
+
emit(context, 2, "node.classList.add(from, active);");
|
|
68
|
+
emit(context, 2, "__mikuru_transitionFrame(() => {");
|
|
69
|
+
emit(context, 3, "node.classList.remove(from);");
|
|
70
|
+
emit(context, 3, "node.classList.add(to);");
|
|
71
|
+
emit(context, 3, "__mikuru_transitionDone(node, () => { node.classList.remove(active, to); node.remove(); });");
|
|
72
|
+
emit(context, 2, "});");
|
|
73
|
+
emit(context, 1, "};");
|
|
74
|
+
emit(context, 1, "const __mikuru_setRef = (target, value, multiple = false) => {");
|
|
75
|
+
emit(context, 2, "if (typeof target === \"function\") {");
|
|
76
|
+
emit(context, 3, "target(value);");
|
|
77
|
+
emit(context, 3, "return () => target(null);");
|
|
78
|
+
emit(context, 2, "}");
|
|
79
|
+
emit(context, 2, "if (!target || typeof target !== \"object\" || !(\"value\" in target)) {");
|
|
80
|
+
emit(context, 3, "return () => {};");
|
|
81
|
+
emit(context, 2, "}");
|
|
82
|
+
emit(context, 2, "if (multiple) {");
|
|
83
|
+
emit(context, 3, "const list = Array.isArray(target.value) ? target.value : [];");
|
|
84
|
+
emit(context, 3, "if (target.value !== list) { target.value = list; }");
|
|
85
|
+
emit(context, 3, "list.push(value);");
|
|
86
|
+
emit(context, 3, "return () => { const index = list.indexOf(value); if (index >= 0) { list.splice(index, 1); } };");
|
|
87
|
+
emit(context, 2, "}");
|
|
88
|
+
emit(context, 2, "target.value = value;");
|
|
89
|
+
emit(context, 2, "return () => { if (target.value === value) { target.value = null; } };");
|
|
90
|
+
emit(context, 1, "};");
|
|
29
91
|
emit(context, 1, "// expose a lightweight registrar for runtime lifecycle and provide/inject helpers");
|
|
30
92
|
emit(context, 1, "const __mikuru_previousRegistrar = globalThis.__mikuru_currentRegistrar;");
|
|
31
93
|
emit(context, 1, "globalThis.__mikuru_currentRegistrar = {");
|
|
@@ -52,6 +114,9 @@ export function generate(descriptor, root) {
|
|
|
52
114
|
if (script.usesPropsAlias) {
|
|
53
115
|
emit(context, 1, "const __mikuru_props = props;");
|
|
54
116
|
}
|
|
117
|
+
if (script.usesAttrsAlias) {
|
|
118
|
+
emit(context, 1, "const __mikuru_attrs = props.__mikuru_attrs ?? {};");
|
|
119
|
+
}
|
|
55
120
|
if (script.usesEmitAlias) {
|
|
56
121
|
emit(context, 1, "const __mikuru_emit = (name, ...args) => {");
|
|
57
122
|
emit(context, 2, "const handlerName = \"on\" + String(name).split(/[-:]/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(\"\");");
|
|
@@ -73,12 +138,12 @@ export function generate(descriptor, root) {
|
|
|
73
138
|
emit(context, 2, "unmount() {");
|
|
74
139
|
emit(context, 3, "__mikuru_runCleanup(__mikuru_cleanup);");
|
|
75
140
|
emit(context, 3, "for (const cb of __mikuru_afterUnmount.splice(0).reverse()) { try { cb(); } catch (e) { setTimeout(() => { throw e; }); } }");
|
|
76
|
-
emit(context, 3,
|
|
141
|
+
emit(context, 3, `__mikuru_removeNode(${rootVar});`);
|
|
77
142
|
emit(context, 2, "}");
|
|
78
143
|
emit(context, 1, "};");
|
|
79
144
|
emit(context, 0, "}");
|
|
80
145
|
emit(context, 0, "");
|
|
81
|
-
emit(context, 0,
|
|
146
|
+
emit(context, 0, `const __mikuru_component = { mount${script.inheritAttrs ? "" : ", inheritAttrs: false"} };`);
|
|
82
147
|
emit(context, 0, "export default __mikuru_component;");
|
|
83
148
|
return `${context.lines.join("\n")}\n`;
|
|
84
149
|
}
|
|
@@ -110,6 +175,12 @@ function generateNode(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
|
110
175
|
if (orphanElseAttr) {
|
|
111
176
|
throwTemplateError(`${orphanElseAttr.name} must follow v-if or v-else-if`, context, orphanElseAttr.loc);
|
|
112
177
|
}
|
|
178
|
+
if (node.tag === "component") {
|
|
179
|
+
return generateDynamicComponent(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
180
|
+
}
|
|
181
|
+
if (node.tag === "Transition") {
|
|
182
|
+
return generateTransition(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
183
|
+
}
|
|
113
184
|
if (isComponentTag(node.tag)) {
|
|
114
185
|
return generateComponent(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
115
186
|
}
|
|
@@ -118,14 +189,64 @@ function generateNode(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
|
118
189
|
}
|
|
119
190
|
return generateElement(context, node, parentVar, cleanupVar, indent, beforeVar);
|
|
120
191
|
}
|
|
192
|
+
function generateTransition(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
193
|
+
validateTransitionAttributes(context, node);
|
|
194
|
+
const children = getTransitionChildren(context, node);
|
|
195
|
+
const transitionVar = nextVar(context, "transition");
|
|
196
|
+
emit(context, indent, `const ${transitionVar} = ${getTransitionOptionsExpression(context, node)};`);
|
|
197
|
+
const child = children[0];
|
|
198
|
+
if (getStringAttr(child, "v-if")) {
|
|
199
|
+
return generateIfChain(context, getTransitionBranches(context, children), parentVar, cleanupVar, indent, beforeVar, transitionVar);
|
|
200
|
+
}
|
|
201
|
+
if (child.tag === "component") {
|
|
202
|
+
return generateDynamicComponent(context, child, parentVar, cleanupVar, indent, beforeVar, transitionVar);
|
|
203
|
+
}
|
|
204
|
+
const childVar = generateNode(context, child, parentVar, cleanupVar, indent, beforeVar);
|
|
205
|
+
emitTransitionRegistration(context, childVar, transitionVar, indent);
|
|
206
|
+
return childVar;
|
|
207
|
+
}
|
|
121
208
|
function generateSlot(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
122
|
-
const slotVar = nextVar(context, "slot");
|
|
123
209
|
const slotCleanupVar = nextVar(context, "slotCleanup");
|
|
124
210
|
const slotCleanupResultVar = nextVar(context, "slotCleanup");
|
|
125
211
|
const slotFnVar = nextVar(context, "slot");
|
|
126
212
|
const slotPropsVar = nextVar(context, "slotProps");
|
|
127
|
-
const slotName =
|
|
128
|
-
|
|
213
|
+
const slotName = getSlotOutletNameExpression(node, context);
|
|
214
|
+
if (slotName.dynamic) {
|
|
215
|
+
const startVar = nextVar(context, "slotStart");
|
|
216
|
+
const endVar = nextVar(context, "slotEnd");
|
|
217
|
+
const slotFragmentVar = nextVar(context, "slot");
|
|
218
|
+
const slotNameVar = nextVar(context, "slotName");
|
|
219
|
+
const stopVar = nextVar(context, "stop");
|
|
220
|
+
emit(context, indent, `const ${startVar} = document.createComment("slot");`);
|
|
221
|
+
emit(context, indent, `const ${endVar} = document.createComment("/slot");`);
|
|
222
|
+
appendNode(context, parentVar, startVar, indent, beforeVar);
|
|
223
|
+
appendNode(context, parentVar, endVar, indent, beforeVar);
|
|
224
|
+
emit(context, indent, `const ${slotCleanupVar} = [];`);
|
|
225
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
226
|
+
emit(context, indent + 1, `__mikuru_runCleanup(${slotCleanupVar});`);
|
|
227
|
+
emitRemoveBetween(context, indent + 1, startVar, endVar);
|
|
228
|
+
emit(context, indent + 1, `const ${slotNameVar} = String(unwrap(${slotName.expression}) ?? "default");`);
|
|
229
|
+
emit(context, indent + 1, `const ${slotFnVar} = ${slotNameVar} === "default" ? props.slots?.default ?? props.children : props.slots?.[${slotNameVar}];`);
|
|
230
|
+
emitSlotOutletProps(context, node, slotPropsVar, indent + 1);
|
|
231
|
+
emit(context, indent + 1, `const ${slotFragmentVar} = document.createDocumentFragment();`);
|
|
232
|
+
emit(context, indent + 1, `if (${slotFnVar}) {`);
|
|
233
|
+
emit(context, indent + 2, `const ${slotCleanupResultVar} = ${slotFnVar}(${slotFragmentVar}, ${slotPropsVar});`);
|
|
234
|
+
emit(context, indent + 2, `if (${slotCleanupResultVar}) {`);
|
|
235
|
+
emit(context, indent + 3, `${slotCleanupVar}.push(${slotCleanupResultVar});`);
|
|
236
|
+
emit(context, indent + 2, "}");
|
|
237
|
+
emit(context, indent + 1, "} else {");
|
|
238
|
+
generateChildren(context, node.children, slotFragmentVar, slotCleanupVar, indent + 2);
|
|
239
|
+
emit(context, indent + 1, "}");
|
|
240
|
+
appendNode(context, parentVar, slotFragmentVar, indent + 1, endVar);
|
|
241
|
+
emit(context, indent, "});");
|
|
242
|
+
emit(context, indent, `${cleanupVar}.push(() => {`);
|
|
243
|
+
emit(context, indent + 1, `${stopVar}();`);
|
|
244
|
+
emit(context, indent + 1, `__mikuru_runCleanup(${slotCleanupVar});`);
|
|
245
|
+
emit(context, indent, "});");
|
|
246
|
+
return startVar;
|
|
247
|
+
}
|
|
248
|
+
const slotVar = nextVar(context, "slot");
|
|
249
|
+
emit(context, indent, `const ${slotFnVar} = ${slotName.name === "default" ? "props.slots?.default ?? props.children" : `props.slots?.[${quote(slotName.name)}]`};`);
|
|
129
250
|
emitSlotOutletProps(context, node, slotPropsVar, indent);
|
|
130
251
|
emit(context, indent, `const ${slotVar} = document.createDocumentFragment();`);
|
|
131
252
|
emit(context, indent, `const ${slotCleanupVar} = [];`);
|
|
@@ -143,20 +264,318 @@ function generateSlot(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
|
143
264
|
}
|
|
144
265
|
function generateComponent(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
145
266
|
const fragmentVar = nextVar(context, "fragment");
|
|
267
|
+
const attrsVar = nextVar(context, "attrs");
|
|
146
268
|
const propsVar = nextVar(context, "props");
|
|
147
269
|
const componentVar = nextVar(context, "component");
|
|
148
270
|
emit(context, indent, `const ${fragmentVar} = document.createDocumentFragment();`);
|
|
149
|
-
|
|
271
|
+
emitComponentAttrs(context, node, attrsVar, indent);
|
|
272
|
+
emitComponentProps(context, node, propsVar, attrsVar, indent);
|
|
150
273
|
emit(context, indent, `const ${componentVar} = ${node.tag}.mount(${fragmentVar}, ${propsVar});`);
|
|
151
274
|
if (context.scopeAttr) {
|
|
152
|
-
emit(context, indent, `if (${componentVar}.element
|
|
275
|
+
emit(context, indent, `if (${componentVar}.element?.nodeType === 1) {`);
|
|
153
276
|
emit(context, indent + 1, `${componentVar}.element.setAttribute(${quote(context.scopeAttr)}, "");`);
|
|
154
277
|
emit(context, indent, "}");
|
|
155
278
|
}
|
|
279
|
+
emit(context, indent, `if (${node.tag}.inheritAttrs !== false) {`);
|
|
280
|
+
emitComponentFallthrough(context, node, componentVar, cleanupVar, indent + 1);
|
|
281
|
+
emit(context, indent, "}");
|
|
282
|
+
emitComponentShow(context, node, componentVar, cleanupVar, indent);
|
|
156
283
|
emit(context, indent, `${cleanupVar}.push(() => ${componentVar}.unmount());`);
|
|
284
|
+
emitTemplateRef(context, node, componentVar, cleanupVar, indent);
|
|
157
285
|
appendNode(context, parentVar, fragmentVar, indent, beforeVar);
|
|
158
286
|
return `${componentVar}.element`;
|
|
159
287
|
}
|
|
288
|
+
function generateDynamicComponent(context, node, parentVar, cleanupVar, indent, beforeVar, transitionVar) {
|
|
289
|
+
const isAttr = node.attrs.find((attr) => getBindingName(attr.name) === "is");
|
|
290
|
+
if (!isAttr) {
|
|
291
|
+
throwTemplateError("Dynamic component requires :is to resolve to a component object", context, node.loc);
|
|
292
|
+
}
|
|
293
|
+
const expression = compileTemplateExpression(requireAttrValue(isAttr), isAttr.name, toExpressionContext(context, isAttr.valueLoc));
|
|
294
|
+
const dynamicNode = withoutAttrs(node, ["is", ":is", "v-bind:is"]);
|
|
295
|
+
const startVar = nextVar(context, "componentStart");
|
|
296
|
+
const endVar = nextVar(context, "componentEnd");
|
|
297
|
+
const branchCleanupVar = nextVar(context, "componentCleanup");
|
|
298
|
+
const componentTypeVar = nextVar(context, "componentType");
|
|
299
|
+
const currentTypeVar = nextVar(context, "currentComponent");
|
|
300
|
+
const currentInstanceVar = nextVar(context, "currentComponent");
|
|
301
|
+
const stopVar = nextVar(context, "stop");
|
|
302
|
+
emit(context, indent, `const ${startVar} = document.createComment("component");`);
|
|
303
|
+
emit(context, indent, `const ${endVar} = document.createComment("/component");`);
|
|
304
|
+
appendNode(context, parentVar, startVar, indent, beforeVar);
|
|
305
|
+
appendNode(context, parentVar, endVar, indent, beforeVar);
|
|
306
|
+
emit(context, indent, `const ${branchCleanupVar} = [];`);
|
|
307
|
+
emit(context, indent, `let ${currentTypeVar};`);
|
|
308
|
+
emit(context, indent, `let ${currentInstanceVar};`);
|
|
309
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
310
|
+
emit(context, indent + 1, `const ${componentTypeVar} = unwrap(${expression});`);
|
|
311
|
+
emit(context, indent + 1, `if (${componentTypeVar} === ${currentTypeVar}) { return; }`);
|
|
312
|
+
emit(context, indent + 1, `__mikuru_runCleanup(${branchCleanupVar});`);
|
|
313
|
+
emitRemoveBetween(context, indent + 1, startVar, endVar);
|
|
314
|
+
emit(context, indent + 1, `if (!${componentTypeVar}) {`);
|
|
315
|
+
emit(context, indent + 2, `${currentTypeVar} = ${componentTypeVar};`);
|
|
316
|
+
emit(context, indent + 2, `${currentInstanceVar} = null;`);
|
|
317
|
+
emit(context, indent + 2, "return;");
|
|
318
|
+
emit(context, indent + 1, "}");
|
|
319
|
+
emit(context, indent + 1, `if (typeof ${componentTypeVar} !== "object" || typeof ${componentTypeVar}.mount !== "function") {`);
|
|
320
|
+
emit(context, indent + 2, `throw new Error("Dynamic component :is must resolve to a component object with mount()");`);
|
|
321
|
+
emit(context, indent + 1, "}");
|
|
322
|
+
const fragmentVar = nextVar(context, "fragment");
|
|
323
|
+
const attrsVar = nextVar(context, "attrs");
|
|
324
|
+
const propsVar = nextVar(context, "props");
|
|
325
|
+
const componentVar = nextVar(context, "component");
|
|
326
|
+
emit(context, indent + 1, `const ${fragmentVar} = document.createDocumentFragment();`);
|
|
327
|
+
emitComponentAttrs(context, dynamicNode, attrsVar, indent + 1);
|
|
328
|
+
emitComponentProps(context, dynamicNode, propsVar, attrsVar, indent + 1);
|
|
329
|
+
emit(context, indent + 1, `const ${componentVar} = ${componentTypeVar}.mount(${fragmentVar}, ${propsVar});`);
|
|
330
|
+
if (context.scopeAttr) {
|
|
331
|
+
emit(context, indent + 1, `if (${componentVar}.element?.nodeType === 1) {`);
|
|
332
|
+
emit(context, indent + 2, `${componentVar}.element.setAttribute(${quote(context.scopeAttr)}, "");`);
|
|
333
|
+
emit(context, indent + 1, "}");
|
|
334
|
+
}
|
|
335
|
+
emit(context, indent + 1, `if (${componentTypeVar}.inheritAttrs !== false) {`);
|
|
336
|
+
emitComponentFallthrough(context, dynamicNode, componentVar, branchCleanupVar, indent + 2);
|
|
337
|
+
emit(context, indent + 1, "}");
|
|
338
|
+
emitComponentShow(context, dynamicNode, componentVar, branchCleanupVar, indent + 1);
|
|
339
|
+
emit(context, indent + 1, `${branchCleanupVar}.push(() => ${componentVar}.unmount());`);
|
|
340
|
+
emitTemplateRef(context, dynamicNode, componentVar, branchCleanupVar, indent + 1);
|
|
341
|
+
appendNode(context, parentVar, fragmentVar, indent + 1, endVar);
|
|
342
|
+
if (transitionVar) {
|
|
343
|
+
emitTransitionRegistration(context, `${componentVar}.element`, transitionVar, indent + 1);
|
|
344
|
+
}
|
|
345
|
+
emit(context, indent + 1, `${currentTypeVar} = ${componentTypeVar};`);
|
|
346
|
+
emit(context, indent + 1, `${currentInstanceVar} = ${componentVar};`);
|
|
347
|
+
emit(context, indent, "});");
|
|
348
|
+
emit(context, indent, `${cleanupVar}.push(() => {`);
|
|
349
|
+
emit(context, indent + 1, `${stopVar}();`);
|
|
350
|
+
emit(context, indent + 1, `__mikuru_runCleanup(${branchCleanupVar});`);
|
|
351
|
+
emit(context, indent, "});");
|
|
352
|
+
return startVar;
|
|
353
|
+
}
|
|
354
|
+
function emitComponentShow(context, node, componentVar, cleanupVar, indent) {
|
|
355
|
+
const showAttr = node.attrs.find((attr) => attr.name === "v-show");
|
|
356
|
+
if (!showAttr) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const expression = compileTemplateExpression(requireAttrValue(showAttr), showAttr.name, toExpressionContext(context, showAttr.valueLoc));
|
|
360
|
+
const elementVar = nextVar(context, "componentEl");
|
|
361
|
+
const baseDisplayVar = nextVar(context, "baseDisplay");
|
|
362
|
+
const stopVar = nextVar(context, "stop");
|
|
363
|
+
emit(context, indent, `const ${elementVar} = ${componentVar}.element;`);
|
|
364
|
+
emit(context, indent, `if (${elementVar}?.nodeType === 1) {`);
|
|
365
|
+
emit(context, indent + 1, `const ${baseDisplayVar} = ${elementVar}.style.display;`);
|
|
366
|
+
emit(context, indent + 1, `const ${stopVar} = effect(() => {`);
|
|
367
|
+
emit(context, indent + 2, `${elementVar}.style.display = unwrap(${expression}) ? ${baseDisplayVar} : "none";`);
|
|
368
|
+
emit(context, indent + 1, "});");
|
|
369
|
+
emit(context, indent + 1, `${cleanupVar}.push(${stopVar});`);
|
|
370
|
+
emit(context, indent, "}");
|
|
371
|
+
}
|
|
372
|
+
function emitComponentAttrs(context, node, attrsVar, indent) {
|
|
373
|
+
const objectBindExpressions = node.attrs
|
|
374
|
+
.filter((attr) => isObjectBindAttr(attr))
|
|
375
|
+
.map((attr) => compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc)));
|
|
376
|
+
const classParts = componentFallthroughExpressions(context, node, "class", objectBindExpressions);
|
|
377
|
+
const styleParts = componentFallthroughExpressions(context, node, "style", objectBindExpressions);
|
|
378
|
+
const directAttrs = componentDirectAttributeFallthroughs(context, node);
|
|
379
|
+
const baseVar = objectBindExpressions.length > 0 ? nextVar(context, "attrsBase") : attrsVar;
|
|
380
|
+
emit(context, indent, `const ${baseVar} = {`);
|
|
381
|
+
if (classParts.length > 0) {
|
|
382
|
+
emit(context, indent + 1, `get class() { return [${classParts.join(", ")}]; },`);
|
|
383
|
+
}
|
|
384
|
+
if (styleParts.length > 0) {
|
|
385
|
+
emit(context, indent + 1, `get style() { return [${styleParts.join(", ")}]; },`);
|
|
386
|
+
}
|
|
387
|
+
for (const attr of directAttrs) {
|
|
388
|
+
if (attr.dynamic) {
|
|
389
|
+
emit(context, indent + 1, `get ${quotePropertyName(attr.name)}() { return unwrap(${attr.expression}); },`);
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
emit(context, indent + 1, `${quotePropertyName(attr.name)}: ${attr.expression},`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
emit(context, indent, "};");
|
|
396
|
+
if (objectBindExpressions.length > 0) {
|
|
397
|
+
emitComponentAttrsProxy(context, attrsVar, baseVar, objectBindExpressions, indent);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function emitComponentAttrsProxy(context, attrsVar, baseVar, objectBindExpressions, indent) {
|
|
401
|
+
const keyVar = nextVar(context, "key");
|
|
402
|
+
const sourceVar = nextVar(context, "source");
|
|
403
|
+
const keysVar = nextVar(context, "keys");
|
|
404
|
+
const descriptorVar = nextVar(context, "descriptor");
|
|
405
|
+
emit(context, indent, `const ${attrsVar} = new Proxy(${baseVar}, {`);
|
|
406
|
+
emit(context, indent + 1, `get(target, ${keyVar}) {`);
|
|
407
|
+
emit(context, indent + 2, `if (typeof ${keyVar} === "symbol" || ${keyVar} in target) {`);
|
|
408
|
+
emit(context, indent + 3, `return target[${keyVar}];`);
|
|
409
|
+
emit(context, indent + 2, "}");
|
|
410
|
+
for (const expression of objectBindExpressions) {
|
|
411
|
+
emit(context, indent + 2, `{`);
|
|
412
|
+
emit(context, indent + 3, `const ${sourceVar} = unwrap(${expression}) ?? {};`);
|
|
413
|
+
emit(context, indent + 3, `if (${sourceVar} && typeof ${sourceVar} === "object" && ${keyVar} in ${sourceVar} && (${keyVar} === "class" || ${keyVar} === "style" || ${componentFallthroughAttributeNameExpression(keyVar)})) {`);
|
|
414
|
+
emit(context, indent + 4, `return unwrap(${sourceVar}[${keyVar}]);`);
|
|
415
|
+
emit(context, indent + 3, "}");
|
|
416
|
+
emit(context, indent + 2, `}`);
|
|
417
|
+
}
|
|
418
|
+
emit(context, indent + 2, "return undefined;");
|
|
419
|
+
emit(context, indent + 1, "},");
|
|
420
|
+
emit(context, indent + 1, "ownKeys(target) {");
|
|
421
|
+
emit(context, indent + 2, `const ${keysVar} = new Set(Reflect.ownKeys(target));`);
|
|
422
|
+
for (const expression of objectBindExpressions) {
|
|
423
|
+
emit(context, indent + 2, `{`);
|
|
424
|
+
emit(context, indent + 3, `const ${sourceVar} = unwrap(${expression}) ?? {};`);
|
|
425
|
+
emit(context, indent + 3, `if (${sourceVar} && typeof ${sourceVar} === "object") {`);
|
|
426
|
+
emit(context, indent + 4, `for (const ${keyVar} of Object.keys(${sourceVar})) {`);
|
|
427
|
+
emit(context, indent + 5, `if (${keyVar} === "class" || ${keyVar} === "style" || ${componentFallthroughAttributeNameExpression(keyVar)}) {`);
|
|
428
|
+
emit(context, indent + 6, `${keysVar}.add(${keyVar});`);
|
|
429
|
+
emit(context, indent + 5, "}");
|
|
430
|
+
emit(context, indent + 4, "}");
|
|
431
|
+
emit(context, indent + 3, "}");
|
|
432
|
+
emit(context, indent + 2, `}`);
|
|
433
|
+
}
|
|
434
|
+
emit(context, indent + 2, `return Array.from(${keysVar});`);
|
|
435
|
+
emit(context, indent + 1, "},");
|
|
436
|
+
emit(context, indent + 1, `getOwnPropertyDescriptor(target, ${keyVar}) {`);
|
|
437
|
+
emit(context, indent + 2, `const ${descriptorVar} = Reflect.getOwnPropertyDescriptor(target, ${keyVar});`);
|
|
438
|
+
emit(context, indent + 2, `if (${descriptorVar}) { return ${descriptorVar}; }`);
|
|
439
|
+
emit(context, indent + 2, `if (typeof ${keyVar} === "string" && this.get(target, ${keyVar}) !== undefined) {`);
|
|
440
|
+
emit(context, indent + 3, "return { enumerable: true, configurable: true };");
|
|
441
|
+
emit(context, indent + 2, "}");
|
|
442
|
+
emit(context, indent + 2, "return undefined;");
|
|
443
|
+
emit(context, indent + 1, "}");
|
|
444
|
+
emit(context, indent, "});");
|
|
445
|
+
}
|
|
446
|
+
function emitComponentFallthrough(context, node, componentVar, cleanupVar, indent) {
|
|
447
|
+
const objectBindExpressions = node.attrs
|
|
448
|
+
.filter((attr) => isObjectBindAttr(attr))
|
|
449
|
+
.map((attr) => compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc)));
|
|
450
|
+
const classParts = componentFallthroughExpressions(context, node, "class", objectBindExpressions);
|
|
451
|
+
const styleParts = componentFallthroughExpressions(context, node, "style", objectBindExpressions);
|
|
452
|
+
const directAttrs = componentDirectAttributeFallthroughs(context, node);
|
|
453
|
+
if (classParts.length === 0 && styleParts.length === 0 && directAttrs.length === 0 && objectBindExpressions.length === 0) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const elementVar = nextVar(context, "componentEl");
|
|
457
|
+
emit(context, indent, `const ${elementVar} = ${componentVar}.element;`);
|
|
458
|
+
emit(context, indent, `if (${elementVar}?.nodeType === 1) {`);
|
|
459
|
+
if (classParts.length > 0) {
|
|
460
|
+
emitComponentFallthroughAttribute(context, elementVar, cleanupVar, "class", classParts, indent + 1);
|
|
461
|
+
}
|
|
462
|
+
if (styleParts.length > 0) {
|
|
463
|
+
emitComponentFallthroughAttribute(context, elementVar, cleanupVar, "style", styleParts, indent + 1);
|
|
464
|
+
}
|
|
465
|
+
for (const attr of directAttrs) {
|
|
466
|
+
if (attr.dynamic) {
|
|
467
|
+
const stopVar = nextVar(context, "stop");
|
|
468
|
+
emit(context, indent + 1, `const ${stopVar} = effect(() => {`);
|
|
469
|
+
emit(context, indent + 2, `setAttribute(${elementVar}, ${quote(attr.name)}, unwrap(${attr.expression}));`);
|
|
470
|
+
emit(context, indent + 1, "});");
|
|
471
|
+
emit(context, indent + 1, `${cleanupVar}.push(${stopVar});`);
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
emit(context, indent + 1, `setAttribute(${elementVar}, ${quote(attr.name)}, ${attr.expression});`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
for (const expression of objectBindExpressions) {
|
|
478
|
+
emitComponentObjectAttributeFallthrough(context, elementVar, cleanupVar, expression, indent + 1);
|
|
479
|
+
}
|
|
480
|
+
emit(context, indent, "}");
|
|
481
|
+
}
|
|
482
|
+
function emitComponentFallthroughAttribute(context, elementVar, cleanupVar, attributeName, parts, indent) {
|
|
483
|
+
const baseVar = nextVar(context, `base${attributeName === "class" ? "Class" : "Style"}`);
|
|
484
|
+
const stopVar = nextVar(context, "stop");
|
|
485
|
+
emit(context, indent, `const ${baseVar} = ${elementVar}.getAttribute(${quote(attributeName)});`);
|
|
486
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
487
|
+
emit(context, indent + 1, `setAttribute(${elementVar}, ${quote(attributeName)}, [${baseVar}, ${parts.join(", ")}]);`);
|
|
488
|
+
emit(context, indent, "});");
|
|
489
|
+
emit(context, indent, `${cleanupVar}.push(${stopVar});`);
|
|
490
|
+
}
|
|
491
|
+
function componentFallthroughExpressions(context, node, attributeName, objectBindExpressions) {
|
|
492
|
+
return [
|
|
493
|
+
...node.attrs.flatMap((attr) => componentDirectFallthroughExpression(context, attr, attributeName)),
|
|
494
|
+
...objectBindExpressions.map((expression) => objectBindFallthroughExpression(expression, attributeName))
|
|
495
|
+
];
|
|
496
|
+
}
|
|
497
|
+
function componentDirectFallthroughExpression(context, attr, attributeName) {
|
|
498
|
+
if (attr.name === attributeName) {
|
|
499
|
+
return [quote(attr.value === true ? "" : attr.value)];
|
|
500
|
+
}
|
|
501
|
+
if (getBindingName(attr.name) === attributeName) {
|
|
502
|
+
return [compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc))];
|
|
503
|
+
}
|
|
504
|
+
return [];
|
|
505
|
+
}
|
|
506
|
+
function objectBindFallthroughExpression(expression, attributeName) {
|
|
507
|
+
const key = quote(attributeName);
|
|
508
|
+
return `(() => { const source = unwrap(${expression}) ?? {}; return source && typeof source === "object" && ${key} in source ? unwrap(source[${key}]) : null; })()`;
|
|
509
|
+
}
|
|
510
|
+
function componentDirectAttributeFallthroughs(context, node) {
|
|
511
|
+
return node.attrs.flatMap((attr) => {
|
|
512
|
+
if (attr.name === "class" || attr.name === "style") {
|
|
513
|
+
return [];
|
|
514
|
+
}
|
|
515
|
+
if (!isDirectiveAttr(attr) && isComponentFallthroughAttributeName(attr.name)) {
|
|
516
|
+
return [
|
|
517
|
+
{
|
|
518
|
+
dynamic: false,
|
|
519
|
+
expression: quote(attr.value === true ? true : attr.value),
|
|
520
|
+
name: attr.name
|
|
521
|
+
}
|
|
522
|
+
];
|
|
523
|
+
}
|
|
524
|
+
const bindingName = getBindingName(attr.name);
|
|
525
|
+
if (bindingName && bindingName !== "class" && bindingName !== "style" && isComponentFallthroughAttributeName(bindingName)) {
|
|
526
|
+
return [
|
|
527
|
+
{
|
|
528
|
+
dynamic: true,
|
|
529
|
+
expression: compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc)),
|
|
530
|
+
name: bindingName
|
|
531
|
+
}
|
|
532
|
+
];
|
|
533
|
+
}
|
|
534
|
+
return [];
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
function emitComponentObjectAttributeFallthrough(context, elementVar, cleanupVar, expression, indent) {
|
|
538
|
+
const previousVar = nextVar(context, "fallthroughKeys");
|
|
539
|
+
const stopVar = nextVar(context, "stop");
|
|
540
|
+
const sourceVar = nextVar(context, "source");
|
|
541
|
+
const nextVarName = nextVar(context, "nextKeys");
|
|
542
|
+
const keyVar = nextVar(context, "key");
|
|
543
|
+
const valueVar = nextVar(context, "value");
|
|
544
|
+
const staleKeyVar = nextVar(context, "staleKey");
|
|
545
|
+
emit(context, indent, `let ${previousVar} = new Set();`);
|
|
546
|
+
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
547
|
+
emit(context, indent + 1, `const ${sourceVar} = unwrap(${expression}) ?? {};`);
|
|
548
|
+
emit(context, indent + 1, `const ${nextVarName} = new Set();`);
|
|
549
|
+
emit(context, indent + 1, `if (${sourceVar} && typeof ${sourceVar} === "object") {`);
|
|
550
|
+
emit(context, indent + 2, `for (const [${keyVar}, ${valueVar}] of Object.entries(${sourceVar})) {`);
|
|
551
|
+
emit(context, indent + 3, `if (${keyVar} === "class" || ${keyVar} === "style" || !${componentFallthroughAttributeNameExpression(keyVar)}) { continue; }`);
|
|
552
|
+
emit(context, indent + 3, `${nextVarName}.add(${keyVar});`);
|
|
553
|
+
emit(context, indent + 3, `setAttribute(${elementVar}, ${keyVar}, unwrap(${valueVar}));`);
|
|
554
|
+
emit(context, indent + 2, "}");
|
|
555
|
+
emit(context, indent + 1, "}");
|
|
556
|
+
emit(context, indent + 1, `for (const ${staleKeyVar} of ${previousVar}) {`);
|
|
557
|
+
emit(context, indent + 2, `if (!${nextVarName}.has(${staleKeyVar})) {`);
|
|
558
|
+
emit(context, indent + 3, `setAttribute(${elementVar}, ${staleKeyVar}, null);`);
|
|
559
|
+
emit(context, indent + 2, "}");
|
|
560
|
+
emit(context, indent + 1, "}");
|
|
561
|
+
emit(context, indent + 1, `${previousVar} = ${nextVarName};`);
|
|
562
|
+
emit(context, indent, "});");
|
|
563
|
+
emit(context, indent, `${cleanupVar}.push(${stopVar});`);
|
|
564
|
+
}
|
|
565
|
+
function isComponentFallthroughAttributeName(name) {
|
|
566
|
+
return (name === "id" ||
|
|
567
|
+
name === "title" ||
|
|
568
|
+
name === "role" ||
|
|
569
|
+
name === "tabindex" ||
|
|
570
|
+
name === "lang" ||
|
|
571
|
+
name === "dir" ||
|
|
572
|
+
name === "hidden" ||
|
|
573
|
+
name.startsWith("aria-") ||
|
|
574
|
+
name.startsWith("data-"));
|
|
575
|
+
}
|
|
576
|
+
function componentFallthroughAttributeNameExpression(keyExpression) {
|
|
577
|
+
return `(${keyExpression} === "id" || ${keyExpression} === "title" || ${keyExpression} === "role" || ${keyExpression} === "tabindex" || ${keyExpression} === "lang" || ${keyExpression} === "dir" || ${keyExpression} === "hidden" || ${keyExpression}.startsWith("aria-") || ${keyExpression}.startsWith("data-"))`;
|
|
578
|
+
}
|
|
160
579
|
function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
161
580
|
const slotDirectiveAttr = node.attrs.find((attr) => isSlotDirectiveAttr(attr));
|
|
162
581
|
if (slotDirectiveAttr) {
|
|
@@ -169,26 +588,53 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
169
588
|
emit(context, indent, `${elementVar}.setAttribute(${quote(context.scopeAttr)}, "");`);
|
|
170
589
|
}
|
|
171
590
|
for (const attr of node.attrs) {
|
|
591
|
+
if (attr.name === "ref" || getBindingName(attr.name) === "ref") {
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
172
594
|
if (isDirectiveAttr(attr)) {
|
|
173
595
|
continue;
|
|
174
596
|
}
|
|
175
597
|
emit(context, indent, `setAttribute(${elementVar}, ${quote(attr.name)}, ${quote(attr.value === true ? "" : attr.value)});`);
|
|
176
598
|
}
|
|
599
|
+
emitTemplateRef(context, node, elementVar, cleanupVar, indent);
|
|
600
|
+
generateChildren(context, node.children, elementVar, cleanupVar, indent);
|
|
177
601
|
for (const attr of node.attrs) {
|
|
178
|
-
|
|
602
|
+
const modelDirective = parseModelDirective(attr.name);
|
|
603
|
+
if (modelDirective) {
|
|
604
|
+
validateModelModifiers(modelDirective, attr, context);
|
|
179
605
|
const expression = validateAssignableExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
180
606
|
const stopVar = nextVar(context, "stop");
|
|
181
607
|
const handlerVar = nextVar(context, "handler");
|
|
182
608
|
const inputType = getStaticAttrValue(node, "type")?.toLowerCase();
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
609
|
+
const multiple = node.tag === "select" && hasStaticBooleanAttr(node, "multiple");
|
|
610
|
+
const modelMode = node.tag === "input" && inputType === "checkbox"
|
|
611
|
+
? "checkbox"
|
|
612
|
+
: node.tag === "input" && inputType === "radio"
|
|
613
|
+
? "radio"
|
|
614
|
+
: node.tag === "select" && multiple
|
|
615
|
+
? "select-multiple"
|
|
616
|
+
: node.tag === "select"
|
|
617
|
+
? "select"
|
|
618
|
+
: "text";
|
|
619
|
+
const eventName = modelMode === "text" && !modelDirective.modifiers.includes("lazy") ? "input" : "change";
|
|
620
|
+
const propertyName = modelMode === "checkbox" || modelMode === "radio" ? "checked" : "value";
|
|
621
|
+
const renderedValue = modelMode === "checkbox"
|
|
622
|
+
? `Boolean(unwrap(${expression}))`
|
|
623
|
+
: modelMode === "radio"
|
|
624
|
+
? `(${modelDirective.modifiers.includes("number") ? `Number(${elementVar}.getAttribute("value") ?? "on")` : `(${elementVar}.getAttribute("value") ?? "on")`} === unwrap(${expression}))`
|
|
625
|
+
: modelMode === "select-multiple"
|
|
626
|
+
? `Array.from(${elementVar}.options).forEach((option) => { option.selected = (unwrap(${expression}) ?? []).map(String).includes(option.getAttribute("value") ?? option.textContent ?? ""); })`
|
|
627
|
+
: `String(unwrap(${expression}) ?? "")`;
|
|
628
|
+
const assignedValue = modelAssignedValue(modelMode, modelDirective.modifiers);
|
|
188
629
|
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
630
|
+
if (modelMode === "select-multiple") {
|
|
631
|
+
emit(context, indent + 1, renderedValue);
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
emit(context, indent + 1, `if (${elementVar}.${propertyName} !== ${renderedValue}) {`);
|
|
635
|
+
emit(context, indent + 2, `${elementVar}.${propertyName} = ${renderedValue};`);
|
|
636
|
+
emit(context, indent + 1, "}");
|
|
637
|
+
}
|
|
192
638
|
emit(context, indent, "});");
|
|
193
639
|
emit(context, indent, `${cleanupVar}.push(${stopVar});`);
|
|
194
640
|
emit(context, indent, `const ${handlerVar} = ($event) => {`);
|
|
@@ -208,7 +654,7 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
208
654
|
continue;
|
|
209
655
|
}
|
|
210
656
|
if (isObjectBindAttr(attr)) {
|
|
211
|
-
emitObjectBind(context, elementVar, attr, cleanupVar, indent);
|
|
657
|
+
emitObjectBind(context, node, elementVar, attr, cleanupVar, indent);
|
|
212
658
|
continue;
|
|
213
659
|
}
|
|
214
660
|
if (isObjectOnAttr(attr)) {
|
|
@@ -225,6 +671,9 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
225
671
|
const baseHandlerVar = nextVar(context, "handler");
|
|
226
672
|
emit(context, indent, `const ${baseHandlerVar} = ${handlerExpression};`);
|
|
227
673
|
emit(context, indent, `const ${handlerVar} = ($event) => {`);
|
|
674
|
+
if (event.modifiers.includes("self")) {
|
|
675
|
+
emit(context, indent + 1, `if ($event.target !== ${elementVar}) { return; }`);
|
|
676
|
+
}
|
|
228
677
|
if (event.modifiers.includes("prevent")) {
|
|
229
678
|
emit(context, indent + 1, "$event.preventDefault();");
|
|
230
679
|
}
|
|
@@ -237,12 +686,16 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
237
686
|
else {
|
|
238
687
|
emit(context, indent, `const ${handlerVar} = ${handlerExpression};`);
|
|
239
688
|
}
|
|
240
|
-
|
|
241
|
-
emit(context, indent, `${
|
|
689
|
+
const eventOptions = eventListenerOptions(event);
|
|
690
|
+
emit(context, indent, `${elementVar}.addEventListener(${quote(event.name)}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""});`);
|
|
691
|
+
emit(context, indent, `${cleanupVar}.push(() => ${elementVar}.removeEventListener(${quote(event.name)}, ${handlerVar}${eventOptions ? `, ${eventOptions}` : ""}));`);
|
|
242
692
|
continue;
|
|
243
693
|
}
|
|
244
694
|
const bindingName = getBindingName(attr.name);
|
|
245
695
|
if (bindingName) {
|
|
696
|
+
if (bindingName === "ref") {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
246
699
|
const expression = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
247
700
|
const stopVar = nextVar(context, "stop");
|
|
248
701
|
const valueExpression = bindingName === "class" && getStaticAttrValue(node, "class")
|
|
@@ -254,10 +707,36 @@ function generateElement(context, node, parentVar, cleanupVar, indent, beforeVar
|
|
|
254
707
|
emit(context, indent, `${cleanupVar}.push(${stopVar});`);
|
|
255
708
|
}
|
|
256
709
|
}
|
|
257
|
-
generateChildren(context, node.children, elementVar, cleanupVar, indent);
|
|
258
710
|
appendNode(context, parentVar, elementVar, indent, beforeVar);
|
|
259
711
|
return elementVar;
|
|
260
712
|
}
|
|
713
|
+
function emitTemplateRef(context, node, valueExpression, cleanupVar, indent) {
|
|
714
|
+
const refAttr = getTemplateRefAttr(node);
|
|
715
|
+
if (!refAttr) {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
const targetExpression = templateRefTargetExpression(context, refAttr);
|
|
719
|
+
const cleanupRefVar = nextVar(context, "cleanupRef");
|
|
720
|
+
const multiple = context.templateRefMode === "array";
|
|
721
|
+
emit(context, indent, `const ${cleanupRefVar} = __mikuru_setRef(${targetExpression}, ${valueExpression}, ${multiple ? "true" : "false"});`);
|
|
722
|
+
emit(context, indent, `${cleanupVar}.push(${cleanupRefVar});`);
|
|
723
|
+
}
|
|
724
|
+
function getTemplateRefAttr(node) {
|
|
725
|
+
return node.attrs.find((attr) => attr.name === "ref" || getBindingName(attr.name) === "ref");
|
|
726
|
+
}
|
|
727
|
+
function templateRefTargetExpression(context, attr) {
|
|
728
|
+
if (getBindingName(attr.name) === "ref") {
|
|
729
|
+
return compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
730
|
+
}
|
|
731
|
+
if (attr.value === true || !attr.value.trim()) {
|
|
732
|
+
throwTemplateError("Template ref requires a ref object name, for example ref=\"inputEl\"", context, attr.loc);
|
|
733
|
+
}
|
|
734
|
+
const name = attr.value.trim();
|
|
735
|
+
if (!isIdentifier(name)) {
|
|
736
|
+
throwTemplateError("Template ref must be a simple identifier that points to a ref object", context, attr.valueLoc ?? attr.loc);
|
|
737
|
+
}
|
|
738
|
+
return name;
|
|
739
|
+
}
|
|
261
740
|
function generateText(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
262
741
|
const textVar = nextVar(context, "text");
|
|
263
742
|
emit(context, indent, `const ${textVar} = document.createTextNode("");`);
|
|
@@ -274,7 +753,7 @@ function generateText(context, node, parentVar, cleanupVar, indent, beforeVar) {
|
|
|
274
753
|
appendNode(context, parentVar, textVar, indent, beforeVar);
|
|
275
754
|
return textVar;
|
|
276
755
|
}
|
|
277
|
-
function emitObjectBind(context, elementVar, attr, cleanupVar, indent) {
|
|
756
|
+
function emitObjectBind(context, node, elementVar, attr, cleanupVar, indent) {
|
|
278
757
|
const expression = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
279
758
|
const prevKeysVar = nextVar(context, "boundKeys");
|
|
280
759
|
const stopVar = nextVar(context, "stop");
|
|
@@ -283,6 +762,7 @@ function emitObjectBind(context, elementVar, attr, cleanupVar, indent) {
|
|
|
283
762
|
const keyVar = nextVar(context, "key");
|
|
284
763
|
const valueVar = nextVar(context, "value");
|
|
285
764
|
const staleKeyVar = nextVar(context, "key");
|
|
765
|
+
const staticClass = getStaticAttrValue(node, "class");
|
|
286
766
|
emit(context, indent, `const ${prevKeysVar} = new Set();`);
|
|
287
767
|
emit(context, indent, `const ${stopVar} = effect(() => {`);
|
|
288
768
|
emit(context, indent + 1, `const ${attrsVar} = unwrap(${expression}) ?? {};`);
|
|
@@ -290,12 +770,22 @@ function emitObjectBind(context, elementVar, attr, cleanupVar, indent) {
|
|
|
290
770
|
emit(context, indent + 1, `if (${attrsVar} && typeof ${attrsVar} === "object") {`);
|
|
291
771
|
emit(context, indent + 2, `for (const [${keyVar}, ${valueVar}] of Object.entries(${attrsVar})) {`);
|
|
292
772
|
emit(context, indent + 3, `${nextKeysVar}.add(${keyVar});`);
|
|
293
|
-
|
|
773
|
+
if (staticClass) {
|
|
774
|
+
emit(context, indent + 3, `setAttribute(${elementVar}, ${keyVar}, ${keyVar} === "class" ? [${quote(staticClass)}, unwrap(${valueVar})] : unwrap(${valueVar}));`);
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
emit(context, indent + 3, `setAttribute(${elementVar}, ${keyVar}, unwrap(${valueVar}));`);
|
|
778
|
+
}
|
|
294
779
|
emit(context, indent + 2, "}");
|
|
295
780
|
emit(context, indent + 1, "}");
|
|
296
781
|
emit(context, indent + 1, `for (const ${staleKeyVar} of ${prevKeysVar}) {`);
|
|
297
782
|
emit(context, indent + 2, `if (!${nextKeysVar}.has(${staleKeyVar})) {`);
|
|
298
|
-
|
|
783
|
+
if (staticClass) {
|
|
784
|
+
emit(context, indent + 3, `setAttribute(${elementVar}, ${staleKeyVar}, ${staleKeyVar} === "class" ? ${quote(staticClass)} : null);`);
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
emit(context, indent + 3, `setAttribute(${elementVar}, ${staleKeyVar}, null);`);
|
|
788
|
+
}
|
|
299
789
|
emit(context, indent + 2, "}");
|
|
300
790
|
emit(context, indent + 1, "}");
|
|
301
791
|
emit(context, indent + 1, `${prevKeysVar}.clear();`);
|
|
@@ -390,7 +880,7 @@ function generateChildren(context, children, parentVar, cleanupVar, indent, befo
|
|
|
390
880
|
index += 1;
|
|
391
881
|
}
|
|
392
882
|
}
|
|
393
|
-
function generateIfChain(context, branches, parentVar, cleanupVar, indent, beforeVar) {
|
|
883
|
+
function generateIfChain(context, branches, parentVar, cleanupVar, indent, beforeVar, transitionVar) {
|
|
394
884
|
const startVar = nextVar(context, "ifStart");
|
|
395
885
|
const endVar = nextVar(context, "ifEnd");
|
|
396
886
|
const branchCleanupVar = nextVar(context, "ifCleanup");
|
|
@@ -411,7 +901,10 @@ function generateIfChain(context, branches, parentVar, cleanupVar, indent, befor
|
|
|
411
901
|
const condition = compileTemplateExpression(branch.condition ?? "", branch.directive, toExpressionContext(context, getStringAttrLocation(branch.node, branch.directive)));
|
|
412
902
|
emit(context, indent + 1, `${branchIndex === 0 ? "if" : "else if"} (unwrap(${condition})) {`);
|
|
413
903
|
}
|
|
414
|
-
generateNode(context, withoutAttrs(branch.node, ["v-if", "v-else-if", "v-else"]), parentVar, branchCleanupVar, indent + 2, endVar);
|
|
904
|
+
const branchVar = generateNode(context, withoutAttrs(branch.node, ["v-if", "v-else-if", "v-else"]), parentVar, branchCleanupVar, indent + 2, endVar);
|
|
905
|
+
if (transitionVar) {
|
|
906
|
+
emitTransitionRegistration(context, branchVar, transitionVar, indent + 2);
|
|
907
|
+
}
|
|
415
908
|
emit(context, indent + 1, "}");
|
|
416
909
|
});
|
|
417
910
|
emit(context, indent, "});");
|
|
@@ -446,12 +939,16 @@ function generateFor(context, node, parentVar, cleanupVar, indent, expression, b
|
|
|
446
939
|
emit(context, indent + 1, `for (let ${indexVar} = 0; ${indexVar} < ${sourceVar}.length; ${indexVar} += 1) {`);
|
|
447
940
|
emit(context, indent + 2, `const ${itemName} = ${sourceVar}[${indexVar}];`);
|
|
448
941
|
emit(context, indent + 2, `const ${indexName} = ${indexVar};`);
|
|
449
|
-
|
|
942
|
+
withTemplateRefMode(context, "array", () => {
|
|
943
|
+
generateNode(context, withoutForAttrs(node), parentVar, branchCleanupVar, indent + 2, endVar);
|
|
944
|
+
});
|
|
450
945
|
emit(context, indent + 1, "}");
|
|
451
946
|
}
|
|
452
947
|
else {
|
|
453
948
|
emit(context, indent + 1, `for (const ${itemName} of unwrap(${compileTemplateExpression(sourceExpression, "v-for source", toExpressionContext(context, getStringAttrLocation(node, "v-for")))}) ?? []) {`);
|
|
454
|
-
|
|
949
|
+
withTemplateRefMode(context, "array", () => {
|
|
950
|
+
generateNode(context, withoutForAttrs(node), parentVar, branchCleanupVar, indent + 2, endVar);
|
|
951
|
+
});
|
|
455
952
|
emit(context, indent + 1, "}");
|
|
456
953
|
}
|
|
457
954
|
emit(context, indent, "});");
|
|
@@ -506,7 +1003,7 @@ function generateKeyedFor(context, node, parentVar, cleanupVar, indent, itemName
|
|
|
506
1003
|
if (indexName) {
|
|
507
1004
|
emit(context, indent + 4, `const ${indexName} = ${indexRefVar};`);
|
|
508
1005
|
}
|
|
509
|
-
const elementVar = generateNode(context, withoutForAttrs(node), parentVar, recordCleanupVar, indent + 4, endVar);
|
|
1006
|
+
const elementVar = withTemplateRefMode(context, "array", () => generateNode(context, withoutForAttrs(node), parentVar, recordCleanupVar, indent + 4, endVar));
|
|
510
1007
|
emit(context, indent + 4, `${recordVar} = { element: ${elementVar}, cleanups: ${recordCleanupVar}, item: ${itemRefVar}${indexName ? `, index: ${indexRefVar}` : ""} };`);
|
|
511
1008
|
emit(context, indent + 3, `}`);
|
|
512
1009
|
emit(context, indent + 2, `} else {`);
|
|
@@ -521,7 +1018,7 @@ function generateKeyedFor(context, node, parentVar, cleanupVar, indent, itemName
|
|
|
521
1018
|
emit(context, indent + 1, `for (const [${keyVar}, ${recordVar}] of ${recordsVar}) {`);
|
|
522
1019
|
emit(context, indent + 2, `if (!${nextRecordsVar}.has(${keyVar})) {`);
|
|
523
1020
|
emit(context, indent + 3, `__mikuru_runCleanup(${recordVar}.cleanups);`);
|
|
524
|
-
emit(context, indent + 3,
|
|
1021
|
+
emit(context, indent + 3, `__mikuru_removeNode(${recordVar}.element);`);
|
|
525
1022
|
emit(context, indent + 2, `}`);
|
|
526
1023
|
emit(context, indent + 1, `}`);
|
|
527
1024
|
emit(context, indent + 1, `${recordsVar}.clear();`);
|
|
@@ -544,10 +1041,91 @@ function emitRemoveBetween(context, indent, startVar, endVar) {
|
|
|
544
1041
|
emit(context, indent, `let ${currentVar} = ${startVar}.nextSibling;`);
|
|
545
1042
|
emit(context, indent, `while (${currentVar} && ${currentVar} !== ${endVar}) {`);
|
|
546
1043
|
emit(context, indent + 1, `const ${nextVarName} = ${currentVar}.nextSibling;`);
|
|
547
|
-
emit(context, indent + 1,
|
|
1044
|
+
emit(context, indent + 1, `__mikuru_removeNode(${currentVar});`);
|
|
548
1045
|
emit(context, indent + 1, `${currentVar} = ${nextVarName};`);
|
|
549
1046
|
emit(context, indent, "}");
|
|
550
1047
|
}
|
|
1048
|
+
function emitTransitionRegistration(context, nodeVar, transitionVar, indent) {
|
|
1049
|
+
emit(context, indent, `if (${nodeVar}?.nodeType === 1) {`);
|
|
1050
|
+
emit(context, indent + 1, `${nodeVar}.__mikuru_transition = ${transitionVar};`);
|
|
1051
|
+
emit(context, indent + 1, `${nodeVar}.__mikuru_transitionLeaving = false;`);
|
|
1052
|
+
emit(context, indent + 1, `__mikuru_applyTransitionEnter(${nodeVar}, ${transitionVar});`);
|
|
1053
|
+
emit(context, indent, "}");
|
|
1054
|
+
}
|
|
1055
|
+
function withTemplateRefMode(context, mode, callback) {
|
|
1056
|
+
const previousMode = context.templateRefMode;
|
|
1057
|
+
context.templateRefMode = mode;
|
|
1058
|
+
try {
|
|
1059
|
+
return callback();
|
|
1060
|
+
}
|
|
1061
|
+
finally {
|
|
1062
|
+
context.templateRefMode = previousMode;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
function splitTopLevel(source, delimiter) {
|
|
1066
|
+
const parts = [];
|
|
1067
|
+
let depth = 0;
|
|
1068
|
+
let quoteChar = "";
|
|
1069
|
+
let start = 0;
|
|
1070
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
1071
|
+
const char = source[index];
|
|
1072
|
+
const previous = source[index - 1];
|
|
1073
|
+
if (quoteChar) {
|
|
1074
|
+
if (char === quoteChar && previous !== "\\") {
|
|
1075
|
+
quoteChar = "";
|
|
1076
|
+
}
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
1080
|
+
quoteChar = char;
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
if (char === "{" || char === "[" || char === "(") {
|
|
1084
|
+
depth += 1;
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
if (char === "}" || char === "]" || char === ")") {
|
|
1088
|
+
depth -= 1;
|
|
1089
|
+
continue;
|
|
1090
|
+
}
|
|
1091
|
+
if (depth === 0 && char === delimiter) {
|
|
1092
|
+
parts.push(source.slice(start, index));
|
|
1093
|
+
start = index + 1;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
parts.push(source.slice(start));
|
|
1097
|
+
return parts;
|
|
1098
|
+
}
|
|
1099
|
+
function findTopLevelToken(source, token) {
|
|
1100
|
+
let depth = 0;
|
|
1101
|
+
let quoteChar = "";
|
|
1102
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
1103
|
+
const char = source[index];
|
|
1104
|
+
const previous = source[index - 1];
|
|
1105
|
+
if (quoteChar) {
|
|
1106
|
+
if (char === quoteChar && previous !== "\\") {
|
|
1107
|
+
quoteChar = "";
|
|
1108
|
+
}
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
1112
|
+
quoteChar = char;
|
|
1113
|
+
continue;
|
|
1114
|
+
}
|
|
1115
|
+
if (char === "{" || char === "[" || char === "(") {
|
|
1116
|
+
depth += 1;
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
if (char === "}" || char === "]" || char === ")") {
|
|
1120
|
+
depth -= 1;
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
if (depth === 0 && char === token) {
|
|
1124
|
+
return index;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
return -1;
|
|
1128
|
+
}
|
|
551
1129
|
function appendNode(context, parentVar, nodeVar, indent, beforeVar) {
|
|
552
1130
|
if (beforeVar) {
|
|
553
1131
|
emit(context, indent, `${parentVar}.insertBefore(${nodeVar}, ${beforeVar});`);
|
|
@@ -574,7 +1152,9 @@ function normalizeScript(descriptor) {
|
|
|
574
1152
|
const edits = [];
|
|
575
1153
|
const transformedMacroStarts = new Set();
|
|
576
1154
|
const emitsDeclarations = [];
|
|
1155
|
+
let inheritAttrs = true;
|
|
577
1156
|
let usesPropsAlias = false;
|
|
1157
|
+
let usesAttrsAlias = false;
|
|
578
1158
|
let usesEmitAlias = false;
|
|
579
1159
|
let ast;
|
|
580
1160
|
try {
|
|
@@ -596,17 +1176,26 @@ function normalizeScript(descriptor) {
|
|
|
596
1176
|
continue;
|
|
597
1177
|
}
|
|
598
1178
|
if (statement.type !== "VariableDeclaration") {
|
|
1179
|
+
if (isMacroCall(statement.expression, "defineOptions")) {
|
|
1180
|
+
inheritAttrs = parseDefineOptionsDeclaration(statement.expression, descriptor).inheritAttrs;
|
|
1181
|
+
edits.push({ start: statement.start, end: statement.end, replacement: "" });
|
|
1182
|
+
transformedMacroStarts.add(statement.expression?.start ?? statement.start);
|
|
1183
|
+
}
|
|
599
1184
|
continue;
|
|
600
1185
|
}
|
|
601
|
-
const macroDeclarations = (statement.declarations ?? []).filter((declaration) => isMacroCall(declaration.init, "defineProps") ||
|
|
1186
|
+
const macroDeclarations = (statement.declarations ?? []).filter((declaration) => isMacroCall(declaration.init, "defineProps") ||
|
|
1187
|
+
isMacroCall(declaration.init, "defineEmits") ||
|
|
1188
|
+
isMacroCall(declaration.init, "useAttrs"));
|
|
602
1189
|
if (macroDeclarations.length > 0 && (statement.declarations ?? []).length !== 1) {
|
|
603
|
-
throwUnsupportedMacro("defineProps() and
|
|
1190
|
+
throwUnsupportedMacro("defineProps(), defineEmits(), and useAttrs() cannot share a variable declaration with other bindings", macroDeclarations[0], descriptor);
|
|
604
1191
|
}
|
|
605
1192
|
if (macroDeclarations.length > 0 && statement.kind !== "const") {
|
|
606
|
-
throwUnsupportedMacro("defineProps() and
|
|
1193
|
+
throwUnsupportedMacro("defineProps(), defineEmits(), and useAttrs() must use const declarations", macroDeclarations[0], descriptor);
|
|
607
1194
|
}
|
|
608
1195
|
for (const declaration of statement.declarations ?? []) {
|
|
609
|
-
if (!isMacroCall(declaration.init, "defineProps") &&
|
|
1196
|
+
if (!isMacroCall(declaration.init, "defineProps") &&
|
|
1197
|
+
!isMacroCall(declaration.init, "defineEmits") &&
|
|
1198
|
+
!isMacroCall(declaration.init, "useAttrs")) {
|
|
610
1199
|
continue;
|
|
611
1200
|
}
|
|
612
1201
|
if (isMacroCall(declaration.init, "defineProps")) {
|
|
@@ -616,6 +1205,13 @@ function normalizeScript(descriptor) {
|
|
|
616
1205
|
usesPropsAlias = usesPropsAlias || replacement.includes("__mikuru_props");
|
|
617
1206
|
continue;
|
|
618
1207
|
}
|
|
1208
|
+
if (isMacroCall(declaration.init, "useAttrs")) {
|
|
1209
|
+
const replacement = transformUseAttrsDeclaration(declaration, descriptor);
|
|
1210
|
+
edits.push({ start: statement.start, end: statement.end, replacement });
|
|
1211
|
+
transformedMacroStarts.add(declaration.init?.start ?? declaration.start);
|
|
1212
|
+
usesAttrsAlias = true;
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
619
1215
|
const emitDeclaration = transformDefineEmitsDeclaration(declaration, descriptor);
|
|
620
1216
|
edits.push({ start: statement.start, end: statement.end, replacement: emitDeclaration.replacement });
|
|
621
1217
|
transformedMacroStarts.add(declaration.init?.start ?? declaration.start);
|
|
@@ -633,7 +1229,9 @@ function normalizeScript(descriptor) {
|
|
|
633
1229
|
imports,
|
|
634
1230
|
runtimeImports,
|
|
635
1231
|
body,
|
|
1232
|
+
inheritAttrs,
|
|
636
1233
|
usesPropsAlias,
|
|
1234
|
+
usesAttrsAlias,
|
|
637
1235
|
usesEmitAlias
|
|
638
1236
|
};
|
|
639
1237
|
}
|
|
@@ -718,6 +1316,14 @@ function transformDefineEmitsDeclaration(declaration, descriptor) {
|
|
|
718
1316
|
events: parseDefineEmitsDeclaration(declaration.init, descriptor)
|
|
719
1317
|
};
|
|
720
1318
|
}
|
|
1319
|
+
function transformUseAttrsDeclaration(declaration, descriptor) {
|
|
1320
|
+
validateNoMacroArguments(declaration.init, "useAttrs", descriptor);
|
|
1321
|
+
if (declaration.id?.type !== "Identifier" || !isIdentifier(declaration.id.name ?? "")) {
|
|
1322
|
+
throwUnsupportedMacro("useAttrs() only supports identifier bindings", declaration.id ?? declaration, descriptor);
|
|
1323
|
+
}
|
|
1324
|
+
const localName = declaration.id.name ?? "";
|
|
1325
|
+
return `const ${localName} = ${localName === "__mikuru_attrs" ? "props.__mikuru_attrs ?? {}" : "__mikuru_attrs"};`;
|
|
1326
|
+
}
|
|
721
1327
|
function applyScriptEdits(script, edits) {
|
|
722
1328
|
const ordered = [...edits].sort((left, right) => left.start - right.start);
|
|
723
1329
|
let output = "";
|
|
@@ -736,6 +1342,34 @@ function applyScriptEdits(script, edits) {
|
|
|
736
1342
|
function isMacroCall(node, name) {
|
|
737
1343
|
return node?.type === "CallExpression" && node.callee?.type === "Identifier" && node.callee.name === name;
|
|
738
1344
|
}
|
|
1345
|
+
function validateNoMacroArguments(node, name, descriptor) {
|
|
1346
|
+
const args = node?.arguments ?? [];
|
|
1347
|
+
if (args.length > 0) {
|
|
1348
|
+
throwUnsupportedMacro(`${name}() does not accept arguments`, args[0] ?? node ?? undefined, descriptor);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
function parseDefineOptionsDeclaration(node, descriptor) {
|
|
1352
|
+
const args = node?.arguments ?? [];
|
|
1353
|
+
if (args.length !== 1 || args[0]?.type !== "ObjectExpression") {
|
|
1354
|
+
throwUnsupportedMacro("defineOptions() only supports an object declaration argument", args[0] ?? node ?? undefined, descriptor);
|
|
1355
|
+
}
|
|
1356
|
+
let inheritAttrs = true;
|
|
1357
|
+
for (const property of args[0].properties ?? []) {
|
|
1358
|
+
if (property.type !== "Property" || property.computed) {
|
|
1359
|
+
throwUnsupportedMacro("Unsupported defineOptions() declaration entry", property, descriptor);
|
|
1360
|
+
}
|
|
1361
|
+
const keyName = getPropertyKeyName(property.key);
|
|
1362
|
+
if (keyName !== "inheritAttrs") {
|
|
1363
|
+
throwUnsupportedMacro(`Unsupported defineOptions() option "${keyName ?? ""}"`, property.key ?? property, descriptor);
|
|
1364
|
+
}
|
|
1365
|
+
const valueNode = property.value;
|
|
1366
|
+
if (valueNode?.type !== "Literal" || typeof valueNode.value !== "boolean") {
|
|
1367
|
+
throwUnsupportedMacro("defineOptions({ inheritAttrs }) must use a boolean literal", valueNode ?? property, descriptor);
|
|
1368
|
+
}
|
|
1369
|
+
inheritAttrs = valueNode.value;
|
|
1370
|
+
}
|
|
1371
|
+
return { inheritAttrs };
|
|
1372
|
+
}
|
|
739
1373
|
function validateDefinePropsDeclaration(node, descriptor) {
|
|
740
1374
|
const args = node?.arguments ?? [];
|
|
741
1375
|
if (args.length === 0) {
|
|
@@ -815,9 +1449,12 @@ function assertNoUnsupportedMacroCalls(node, transformedMacroStarts, descriptor)
|
|
|
815
1449
|
walkScriptNode(node, (candidate) => {
|
|
816
1450
|
if (candidate.type === "CallExpression" &&
|
|
817
1451
|
candidate.callee?.type === "Identifier" &&
|
|
818
|
-
(candidate.callee.name === "defineProps" ||
|
|
1452
|
+
(candidate.callee.name === "defineProps" ||
|
|
1453
|
+
candidate.callee.name === "defineEmits" ||
|
|
1454
|
+
candidate.callee.name === "useAttrs" ||
|
|
1455
|
+
candidate.callee.name === "defineOptions") &&
|
|
819
1456
|
!transformedMacroStarts.has(candidate.start)) {
|
|
820
|
-
throwUnsupportedMacro("defineProps() and
|
|
1457
|
+
throwUnsupportedMacro("defineProps(), defineEmits(), useAttrs(), and defineOptions() must be used in supported top-level declarations", candidate, descriptor);
|
|
821
1458
|
}
|
|
822
1459
|
});
|
|
823
1460
|
}
|
|
@@ -879,6 +1516,9 @@ function getStaticAttrValue(node, name) {
|
|
|
879
1516
|
}
|
|
880
1517
|
return attr.value;
|
|
881
1518
|
}
|
|
1519
|
+
function hasStaticBooleanAttr(node, name) {
|
|
1520
|
+
return node.attrs.some((candidate) => candidate.name === name && candidate.value === true);
|
|
1521
|
+
}
|
|
882
1522
|
function getKeyExpression(node) {
|
|
883
1523
|
return getStringAttr(node, ":key") ?? getStringAttr(node, "v-bind:key");
|
|
884
1524
|
}
|
|
@@ -925,18 +1565,19 @@ function throwTemplateError(message, context, location) {
|
|
|
925
1565
|
}
|
|
926
1566
|
throw new Error(message);
|
|
927
1567
|
}
|
|
928
|
-
function emitComponentProps(context, node, propsVar, indent) {
|
|
1568
|
+
function emitComponentProps(context, node, propsVar, attrsVar, indent) {
|
|
929
1569
|
const props = node.attrs
|
|
930
1570
|
.filter((attr) => !isStructuralAttr(attr))
|
|
931
1571
|
.flatMap((attr) => componentPropEntries(context, attr));
|
|
932
1572
|
const objectBindAttrs = node.attrs.filter((attr) => isObjectBindAttr(attr));
|
|
933
1573
|
const objectOnAttrs = node.attrs.filter((attr) => isObjectOnAttr(attr));
|
|
934
1574
|
const slots = collectComponentSlots(context, node);
|
|
935
|
-
const defaultSlot = slots.find((slot) => slot.name === "default");
|
|
1575
|
+
const defaultSlot = slots.find((slot) => !slot.nameExpression && slot.name === "default");
|
|
936
1576
|
const needsProxy = objectBindAttrs.length > 0 || objectOnAttrs.length > 0;
|
|
937
1577
|
const propsTargetVar = needsProxy ? nextVar(context, "propsBase") : propsVar;
|
|
938
1578
|
emit(context, indent, `const ${propsTargetVar} = {`);
|
|
939
1579
|
emit(context, indent + 1, "__mikuru_context,");
|
|
1580
|
+
emit(context, indent + 1, `__mikuru_attrs: ${attrsVar},`);
|
|
940
1581
|
for (const prop of props) {
|
|
941
1582
|
emit(context, indent + 1, `${prop},`);
|
|
942
1583
|
}
|
|
@@ -946,7 +1587,7 @@ function emitComponentProps(context, node, propsVar, indent) {
|
|
|
946
1587
|
if (slots.length > 0) {
|
|
947
1588
|
emit(context, indent + 1, "slots: {");
|
|
948
1589
|
for (const slot of slots) {
|
|
949
|
-
emitSlotFunction(context, quotePropertyName(slot.name), slot, indent + 2);
|
|
1590
|
+
emitSlotFunction(context, slot.nameExpression ? `[${slot.nameExpression}]` : quotePropertyName(slot.name), slot, indent + 2);
|
|
950
1591
|
}
|
|
951
1592
|
emit(context, indent + 1, "},");
|
|
952
1593
|
}
|
|
@@ -1010,9 +1651,24 @@ function emitSlotScopeBindings(context, slot, slotPropsVar, indent) {
|
|
|
1010
1651
|
emit(context, indent, `const ${binding.alias} = ${slotPropsVar};`);
|
|
1011
1652
|
continue;
|
|
1012
1653
|
}
|
|
1013
|
-
|
|
1654
|
+
if (binding.kind === "rest") {
|
|
1655
|
+
emit(context, indent, `const ${binding.alias} = { get value() { const rest = { ...${slotPropsVar} }; ${binding.exclude.map((key) => `delete rest[${quote(key)}];`).join(" ")} return rest; } };`);
|
|
1656
|
+
continue;
|
|
1657
|
+
}
|
|
1658
|
+
const valueExpression = slotScopePathExpression(slotPropsVar, binding.path);
|
|
1659
|
+
if (binding.defaultValue) {
|
|
1660
|
+
emit(context, indent, `const ${binding.alias} = { get value() { const value = ${valueExpression}; return value === undefined ? (${binding.defaultValue}) : value; } };`);
|
|
1661
|
+
continue;
|
|
1662
|
+
}
|
|
1663
|
+
emit(context, indent, `const ${binding.alias} = { get value() { return ${valueExpression}; } };`);
|
|
1014
1664
|
}
|
|
1015
1665
|
}
|
|
1666
|
+
function slotScopePathExpression(rootVar, path) {
|
|
1667
|
+
return path.reduce((expression, key, index) => {
|
|
1668
|
+
const property = /^[A-Za-z_$][\w$]*$/.test(key) ? `.${key}` : `[${quote(key)}]`;
|
|
1669
|
+
return `${expression}${index === 0 ? property : `?.${property.slice(1)}`}`;
|
|
1670
|
+
}, rootVar);
|
|
1671
|
+
}
|
|
1016
1672
|
function collectComponentSlots(context, node) {
|
|
1017
1673
|
const slots = [];
|
|
1018
1674
|
const usedNames = new Set();
|
|
@@ -1027,6 +1683,7 @@ function collectComponentSlots(context, node) {
|
|
|
1027
1683
|
usedNames.add(slotDirective.name);
|
|
1028
1684
|
slots.push({
|
|
1029
1685
|
name: slotDirective.name,
|
|
1686
|
+
nameExpression: slotDirective.nameExpression,
|
|
1030
1687
|
children: child.children,
|
|
1031
1688
|
scope: slotDirective.scope,
|
|
1032
1689
|
loc: child.loc,
|
|
@@ -1062,25 +1719,41 @@ function getSlotTemplateDirective(node, context) {
|
|
|
1062
1719
|
throwTemplateError("A slot template can only declare one slot target", context, slotAttrs[1]?.loc);
|
|
1063
1720
|
}
|
|
1064
1721
|
const attr = slotAttrs[0];
|
|
1065
|
-
const name = getSlotTemplateName(attr);
|
|
1722
|
+
const name = getSlotTemplateName(attr, context);
|
|
1066
1723
|
return {
|
|
1067
|
-
name,
|
|
1724
|
+
...name,
|
|
1068
1725
|
scope: attr.value,
|
|
1069
1726
|
loc: attr.loc,
|
|
1070
1727
|
scopeLoc: attr.valueLoc
|
|
1071
1728
|
};
|
|
1072
1729
|
}
|
|
1073
|
-
function getSlotTemplateName(attr) {
|
|
1730
|
+
function getSlotTemplateName(attr, context) {
|
|
1074
1731
|
if (attr.name === "v-slot") {
|
|
1075
|
-
return "default";
|
|
1732
|
+
return { name: "default" };
|
|
1076
1733
|
}
|
|
1077
1734
|
if (attr.name.startsWith("v-slot:")) {
|
|
1078
|
-
return attr.name.slice("v-slot:".length) || "default";
|
|
1735
|
+
return parseSlotTemplateName(attr.name.slice("v-slot:".length) || "default", attr, context);
|
|
1079
1736
|
}
|
|
1080
1737
|
if (attr.name.startsWith("#")) {
|
|
1081
|
-
return attr.name.slice(1) || "default";
|
|
1738
|
+
return parseSlotTemplateName(attr.name.slice(1) || "default", attr, context);
|
|
1739
|
+
}
|
|
1740
|
+
return { name: "default" };
|
|
1741
|
+
}
|
|
1742
|
+
function parseSlotTemplateName(rawName, attr, context) {
|
|
1743
|
+
const name = rawName.trim();
|
|
1744
|
+
const dynamicStart = name.indexOf("[");
|
|
1745
|
+
const dynamicEnd = name.lastIndexOf("]");
|
|
1746
|
+
if (dynamicStart >= 0 && dynamicEnd > dynamicStart) {
|
|
1747
|
+
const expression = name.slice(dynamicStart + 1, dynamicEnd).trim();
|
|
1748
|
+
if (!expression) {
|
|
1749
|
+
throwTemplateError("Dynamic slot name requires an expression", context, attr.loc);
|
|
1750
|
+
}
|
|
1751
|
+
return {
|
|
1752
|
+
name: `[${expression}]`,
|
|
1753
|
+
nameExpression: compileTemplateExpression(expression, attr.name, toExpressionContext(context, attr.loc))
|
|
1754
|
+
};
|
|
1082
1755
|
}
|
|
1083
|
-
return
|
|
1756
|
+
return { name };
|
|
1084
1757
|
}
|
|
1085
1758
|
function parseSlotScopeBindings(scope, context, location) {
|
|
1086
1759
|
if (scope === true || !scope.trim()) {
|
|
@@ -1097,22 +1770,90 @@ function parseSlotScopeBindings(scope, context, location) {
|
|
|
1097
1770
|
if (!body) {
|
|
1098
1771
|
return [];
|
|
1099
1772
|
}
|
|
1100
|
-
return body
|
|
1773
|
+
return parseSlotScopeObjectPattern(body, context, location, []);
|
|
1774
|
+
}
|
|
1775
|
+
function parseSlotScopeObjectPattern(body, context, location, pathPrefix) {
|
|
1776
|
+
const bindings = [];
|
|
1777
|
+
const excludedTopLevelKeys = [];
|
|
1778
|
+
for (const part of splitTopLevel(body, ",")) {
|
|
1101
1779
|
const sourcePart = part.trim();
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1780
|
+
if (!sourcePart) {
|
|
1781
|
+
continue;
|
|
1782
|
+
}
|
|
1783
|
+
if (sourcePart.startsWith("...")) {
|
|
1784
|
+
const alias = sourcePart.slice(3).trim();
|
|
1785
|
+
if (pathPrefix.length > 0) {
|
|
1786
|
+
throwTemplateError("Slot scope rest destructuring is only supported at the top level", context, location);
|
|
1787
|
+
}
|
|
1788
|
+
if (!isIdentifier(alias)) {
|
|
1789
|
+
throwTemplateError("Slot scope rest destructuring must use a simple identifier like ...rest", context, location);
|
|
1790
|
+
}
|
|
1791
|
+
bindings.push({ kind: "rest", alias, exclude: excludedTopLevelKeys });
|
|
1792
|
+
continue;
|
|
1105
1793
|
}
|
|
1794
|
+
const { left, right } = splitSlotScopeEntry(sourcePart, context, location);
|
|
1795
|
+
if (!isIdentifier(left)) {
|
|
1796
|
+
throwTemplateError(`Unsupported slot scope key "${left}". Use identifier keys in slot scope destructuring`, context, location);
|
|
1797
|
+
}
|
|
1798
|
+
if (pathPrefix.length === 0) {
|
|
1799
|
+
excludedTopLevelKeys.push(left);
|
|
1800
|
+
}
|
|
1801
|
+
const path = [...pathPrefix, left];
|
|
1802
|
+
if (right === undefined) {
|
|
1803
|
+
bindings.push(...slotScopeLeafBindings(left, path, context, location));
|
|
1804
|
+
continue;
|
|
1805
|
+
}
|
|
1806
|
+
const value = right.trim();
|
|
1807
|
+
if (value.startsWith("{") && value.endsWith("}")) {
|
|
1808
|
+
bindings.push(...parseSlotScopeObjectPattern(value.slice(1, -1), context, location, path));
|
|
1809
|
+
continue;
|
|
1810
|
+
}
|
|
1811
|
+
if (value.startsWith("[") || value.includes("{")) {
|
|
1812
|
+
throwTemplateError("Slot scope destructuring supports nested object patterns only; array and mixed patterns are not supported", context, location);
|
|
1813
|
+
}
|
|
1814
|
+
bindings.push(...slotScopeLeafBindings(value, path, context, location));
|
|
1815
|
+
}
|
|
1816
|
+
return bindings;
|
|
1817
|
+
}
|
|
1818
|
+
function splitSlotScopeEntry(source, context, location) {
|
|
1819
|
+
const colonIndex = findTopLevelToken(source, ":");
|
|
1820
|
+
if (colonIndex >= 0) {
|
|
1106
1821
|
return {
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
alias: match[2] ?? match[1]
|
|
1822
|
+
left: source.slice(0, colonIndex).trim(),
|
|
1823
|
+
right: source.slice(colonIndex + 1).trim()
|
|
1110
1824
|
};
|
|
1111
|
-
}
|
|
1825
|
+
}
|
|
1826
|
+
const equalsIndex = findTopLevelToken(source, "=");
|
|
1827
|
+
if (equalsIndex >= 0) {
|
|
1828
|
+
const left = source.slice(0, equalsIndex).trim();
|
|
1829
|
+
if (!isIdentifier(left)) {
|
|
1830
|
+
throwTemplateError("Slot scope default values can only be assigned to simple identifiers", context, location);
|
|
1831
|
+
}
|
|
1832
|
+
return { left, right: source };
|
|
1833
|
+
}
|
|
1834
|
+
return { left: source.trim() };
|
|
1835
|
+
}
|
|
1836
|
+
function slotScopeLeafBindings(source, path, context, location) {
|
|
1837
|
+
const equalsIndex = findTopLevelToken(source, "=");
|
|
1838
|
+
const localSource = equalsIndex >= 0 ? source.slice(0, equalsIndex).trim() : source.trim();
|
|
1839
|
+
const defaultSource = equalsIndex >= 0 ? source.slice(equalsIndex + 1).trim() : undefined;
|
|
1840
|
+
if (!isIdentifier(localSource)) {
|
|
1841
|
+
throwTemplateError(`Unsupported slot scope binding "${source}". Use identifiers, aliases, defaults, nested objects, or top-level ...rest`, context, location);
|
|
1842
|
+
}
|
|
1843
|
+
return [
|
|
1844
|
+
{
|
|
1845
|
+
kind: "property",
|
|
1846
|
+
path,
|
|
1847
|
+
alias: localSource,
|
|
1848
|
+
defaultValue: defaultSource
|
|
1849
|
+
? compileTemplateExpression(defaultSource, "slot scope default", toExpressionContext(context, location))
|
|
1850
|
+
: undefined
|
|
1851
|
+
}
|
|
1852
|
+
];
|
|
1112
1853
|
}
|
|
1113
1854
|
function emitSlotOutletProps(context, node, propsVar, indent) {
|
|
1114
1855
|
const entries = node.attrs
|
|
1115
|
-
.filter((attr) => attr.name !== "name")
|
|
1856
|
+
.filter((attr) => attr.name !== "name" && getBindingName(attr.name) !== "name")
|
|
1116
1857
|
.map((attr) => slotOutletPropEntry(context, attr));
|
|
1117
1858
|
emit(context, indent, `const ${propsVar} = {`);
|
|
1118
1859
|
for (const entry of entries) {
|
|
@@ -1123,9 +1864,6 @@ function emitSlotOutletProps(context, node, propsVar, indent) {
|
|
|
1123
1864
|
function slotOutletPropEntry(context, attr) {
|
|
1124
1865
|
const bindingName = getBindingName(attr.name);
|
|
1125
1866
|
if (bindingName) {
|
|
1126
|
-
if (bindingName === "name") {
|
|
1127
|
-
throwTemplateError("Dynamic slot names are not supported", context, attr.loc);
|
|
1128
|
-
}
|
|
1129
1867
|
const expression = compileTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
1130
1868
|
return `get ${quotePropertyName(bindingName)}() { return unwrap(${expression}); }`;
|
|
1131
1869
|
}
|
|
@@ -1134,35 +1872,50 @@ function slotOutletPropEntry(context, attr) {
|
|
|
1134
1872
|
}
|
|
1135
1873
|
return `${quotePropertyName(attr.name)}: ${quote(attr.value === true ? true : attr.value)}`;
|
|
1136
1874
|
}
|
|
1137
|
-
function
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1875
|
+
function getSlotOutletNameExpression(node, context) {
|
|
1876
|
+
const dynamicNameAttr = node.attrs.find((candidate) => getBindingName(candidate.name) === "name");
|
|
1877
|
+
if (dynamicNameAttr) {
|
|
1878
|
+
return {
|
|
1879
|
+
dynamic: true,
|
|
1880
|
+
expression: compileTemplateExpression(requireAttrValue(dynamicNameAttr), dynamicNameAttr.name, toExpressionContext(context, dynamicNameAttr.valueLoc))
|
|
1881
|
+
};
|
|
1141
1882
|
}
|
|
1142
|
-
return getStaticAttrValue(node, "name") ?? "default";
|
|
1883
|
+
return { dynamic: false, name: getStaticAttrValue(node, "name") ?? "default" };
|
|
1143
1884
|
}
|
|
1144
1885
|
function componentPropEntries(context, attr) {
|
|
1145
1886
|
if (isSlotDirectiveAttr(attr)) {
|
|
1146
1887
|
throwTemplateError("v-slot must be used on a <template> child in Mikuru", context, attr.loc);
|
|
1147
1888
|
}
|
|
1889
|
+
if (attr.name === "ref") {
|
|
1890
|
+
return [];
|
|
1891
|
+
}
|
|
1892
|
+
if (getBindingName(attr.name) === "ref") {
|
|
1893
|
+
return [];
|
|
1894
|
+
}
|
|
1148
1895
|
if (isObjectBindAttr(attr) || isObjectOnAttr(attr)) {
|
|
1149
1896
|
return [];
|
|
1150
1897
|
}
|
|
1151
|
-
|
|
1898
|
+
const modelDirective = parseModelDirective(attr.name);
|
|
1899
|
+
if (modelDirective) {
|
|
1900
|
+
validateModelModifiers(modelDirective, attr, context);
|
|
1152
1901
|
const expression = validateAssignableExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
1153
1902
|
const valueExpression = compileTemplateExpression(expression, attr.name, toExpressionContext(context, attr.valueLoc));
|
|
1154
|
-
|
|
1903
|
+
const entries = [
|
|
1155
1904
|
`get modelValue() { return unwrap(${valueExpression}); }`,
|
|
1156
1905
|
`onUpdateModelValue: ($value) => { ${expression}.value = $value; }`
|
|
1157
1906
|
];
|
|
1907
|
+
if (modelDirective.modifiers.length > 0) {
|
|
1908
|
+
entries.push(`modelModifiers: { ${modelDirective.modifiers.map((modifier) => `${quotePropertyName(modifier)}: true`).join(", ")} }`);
|
|
1909
|
+
}
|
|
1910
|
+
return entries;
|
|
1158
1911
|
}
|
|
1159
1912
|
const event = parseEventDirective(attr.name);
|
|
1160
1913
|
if (event) {
|
|
1161
|
-
|
|
1162
|
-
throwTemplateError("Event modifiers are not supported on component events yet", context, attr.loc);
|
|
1163
|
-
}
|
|
1914
|
+
validateComponentEventModifiers(event, attr, context);
|
|
1164
1915
|
const handler = validateTemplateExpression(requireAttrValue(attr), attr.name, toExpressionContext(context, attr.valueLoc));
|
|
1165
|
-
return [
|
|
1916
|
+
return [
|
|
1917
|
+
`${quotePropertyName(toComponentEventProp(event.name))}: ${componentEventHandlerExpression(event, eventHandlerExpression(handler, context, attr.valueLoc), context)}`
|
|
1918
|
+
];
|
|
1166
1919
|
}
|
|
1167
1920
|
const bindingName = getBindingName(attr.name);
|
|
1168
1921
|
if (bindingName) {
|
|
@@ -1170,13 +1923,94 @@ function componentPropEntries(context, attr) {
|
|
|
1170
1923
|
return [`get ${quotePropertyName(bindingName)}() { return unwrap(${expression}); }`];
|
|
1171
1924
|
}
|
|
1172
1925
|
if (attr.name === "v-show") {
|
|
1173
|
-
|
|
1926
|
+
return [];
|
|
1174
1927
|
}
|
|
1175
1928
|
return [`${quotePropertyName(attr.name)}: ${quote(attr.value === true ? true : attr.value)}`];
|
|
1176
1929
|
}
|
|
1177
1930
|
function hasMeaningfulTemplateChildren(children) {
|
|
1178
1931
|
return children.some((child) => child.type === "element" || child.parts.some((part) => part.value.trim()));
|
|
1179
1932
|
}
|
|
1933
|
+
function getTransitionChildren(context, node) {
|
|
1934
|
+
const meaningful = node.children.filter((child) => child.type === "element" || child.parts.some((part) => part.value.trim()));
|
|
1935
|
+
if (meaningful.length === 0 || meaningful.some((child) => child.type !== "element")) {
|
|
1936
|
+
throwTemplateError("<Transition> requires exactly one element/component child or one v-if chain", context, node.loc);
|
|
1937
|
+
}
|
|
1938
|
+
const children = meaningful;
|
|
1939
|
+
const first = children[0];
|
|
1940
|
+
if (children.length === 1) {
|
|
1941
|
+
return children;
|
|
1942
|
+
}
|
|
1943
|
+
if (!getStringAttr(first, "v-if")) {
|
|
1944
|
+
throwTemplateError("<Transition> requires exactly one element/component child or one v-if chain", context, node.loc);
|
|
1945
|
+
}
|
|
1946
|
+
for (const child of children.slice(1)) {
|
|
1947
|
+
if (!getStringAttr(child, "v-else-if") && !hasAttr(child, "v-else")) {
|
|
1948
|
+
throwTemplateError("<Transition> only accepts multiple children when they form a v-if chain", context, child.loc);
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
return children;
|
|
1952
|
+
}
|
|
1953
|
+
function getTransitionBranches(context, children) {
|
|
1954
|
+
return children.map((child, index) => {
|
|
1955
|
+
if (index === 0) {
|
|
1956
|
+
return { node: child, condition: getStringAttr(child, "v-if"), directive: "v-if" };
|
|
1957
|
+
}
|
|
1958
|
+
const elseIfExpression = getStringAttr(child, "v-else-if");
|
|
1959
|
+
if (elseIfExpression) {
|
|
1960
|
+
return { node: child, condition: elseIfExpression, directive: "v-else-if" };
|
|
1961
|
+
}
|
|
1962
|
+
validateElseAttribute(child, context);
|
|
1963
|
+
return { node: child, directive: "v-else" };
|
|
1964
|
+
});
|
|
1965
|
+
}
|
|
1966
|
+
function getTransitionOptionsExpression(context, node) {
|
|
1967
|
+
const entries = [`name: String(unwrap(${getTransitionAttrExpression(context, node, "name", "v")}) ?? "v")`];
|
|
1968
|
+
const classAttrs = [
|
|
1969
|
+
["enter-from-class", "enterFromClass"],
|
|
1970
|
+
["enter-active-class", "enterActiveClass"],
|
|
1971
|
+
["enter-to-class", "enterToClass"],
|
|
1972
|
+
["leave-from-class", "leaveFromClass"],
|
|
1973
|
+
["leave-active-class", "leaveActiveClass"],
|
|
1974
|
+
["leave-to-class", "leaveToClass"]
|
|
1975
|
+
];
|
|
1976
|
+
for (const [attrName, optionName] of classAttrs) {
|
|
1977
|
+
const expression = getTransitionAttrExpression(context, node, attrName);
|
|
1978
|
+
if (expression) {
|
|
1979
|
+
entries.push(`${optionName}: String(unwrap(${expression}) ?? "")`);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
return `{ ${entries.join(", ")} }`;
|
|
1983
|
+
}
|
|
1984
|
+
function getTransitionAttrExpression(context, node, name, fallback) {
|
|
1985
|
+
const dynamicAttr = node.attrs.find((attr) => getBindingName(attr.name) === name);
|
|
1986
|
+
if (dynamicAttr) {
|
|
1987
|
+
return compileTemplateExpression(requireAttrValue(dynamicAttr), dynamicAttr.name, toExpressionContext(context, dynamicAttr.valueLoc));
|
|
1988
|
+
}
|
|
1989
|
+
const staticValue = getStaticAttrValue(node, name);
|
|
1990
|
+
if (staticValue !== undefined) {
|
|
1991
|
+
return quote(staticValue);
|
|
1992
|
+
}
|
|
1993
|
+
return fallback === undefined ? undefined : quote(fallback);
|
|
1994
|
+
}
|
|
1995
|
+
function validateTransitionAttributes(context, node) {
|
|
1996
|
+
const supported = new Set([
|
|
1997
|
+
"name",
|
|
1998
|
+
"appear",
|
|
1999
|
+
"enter-from-class",
|
|
2000
|
+
"enter-active-class",
|
|
2001
|
+
"enter-to-class",
|
|
2002
|
+
"leave-from-class",
|
|
2003
|
+
"leave-active-class",
|
|
2004
|
+
"leave-to-class"
|
|
2005
|
+
]);
|
|
2006
|
+
for (const attr of node.attrs) {
|
|
2007
|
+
const name = getBindingName(attr.name) ?? attr.name;
|
|
2008
|
+
if (supported.has(name)) {
|
|
2009
|
+
continue;
|
|
2010
|
+
}
|
|
2011
|
+
throwTemplateError(`<Transition> only supports name, appear, and CSS class override attributes in v1`, context, attr.loc);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
1180
2014
|
function toComponentEventProp(eventName) {
|
|
1181
2015
|
return `on${eventName
|
|
1182
2016
|
.split(/[-:]/)
|
|
@@ -1206,7 +2040,7 @@ function isSupportedDirectiveAttr(attr) {
|
|
|
1206
2040
|
attr.name === "v-else" ||
|
|
1207
2041
|
attr.name === "v-for" ||
|
|
1208
2042
|
attr.name === "v-show" ||
|
|
1209
|
-
attr.name
|
|
2043
|
+
Boolean(parseModelDirective(attr.name)) ||
|
|
1210
2044
|
isObjectBindAttr(attr) ||
|
|
1211
2045
|
isObjectOnAttr(attr) ||
|
|
1212
2046
|
Boolean(parseEventDirective(attr.name)) ||
|
|
@@ -1229,12 +2063,77 @@ function parseEventDirective(name) {
|
|
|
1229
2063
|
modifiers
|
|
1230
2064
|
};
|
|
1231
2065
|
}
|
|
2066
|
+
function parseModelDirective(name) {
|
|
2067
|
+
if (name === "v-model") {
|
|
2068
|
+
return { modifiers: [] };
|
|
2069
|
+
}
|
|
2070
|
+
if (!name.startsWith("v-model.")) {
|
|
2071
|
+
return undefined;
|
|
2072
|
+
}
|
|
2073
|
+
return { modifiers: name.slice("v-model.".length).split(".").filter(Boolean) };
|
|
2074
|
+
}
|
|
2075
|
+
function validateModelModifiers(model, attr, context) {
|
|
2076
|
+
const supportedModifiers = new Set(["trim", "number", "lazy"]);
|
|
2077
|
+
for (const modifier of model.modifiers) {
|
|
2078
|
+
if (!supportedModifiers.has(modifier)) {
|
|
2079
|
+
throwTemplateError(`Unsupported v-model modifier .${modifier}`, context, attr.loc);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
function modelAssignedValue(modelMode, modifiers) {
|
|
2084
|
+
if (modelMode === "checkbox") {
|
|
2085
|
+
return "$event.target.checked";
|
|
2086
|
+
}
|
|
2087
|
+
if (modelMode === "radio") {
|
|
2088
|
+
const valueExpression = `($event.target.getAttribute("value") ?? "on")`;
|
|
2089
|
+
return modifiers.includes("number") ? `Number(${valueExpression})` : valueExpression;
|
|
2090
|
+
}
|
|
2091
|
+
if (modelMode === "select-multiple") {
|
|
2092
|
+
const valueExpression = `Array.from($event.target.options).filter((option) => option.selected).map((option) => option.getAttribute("value") ?? option.textContent ?? "")`;
|
|
2093
|
+
return modifiers.includes("number") ? `${valueExpression}.map((value) => Number(value))` : valueExpression;
|
|
2094
|
+
}
|
|
2095
|
+
let valueExpression = "$event.target.value";
|
|
2096
|
+
if (modifiers.includes("trim")) {
|
|
2097
|
+
valueExpression = `${valueExpression}.trim()`;
|
|
2098
|
+
}
|
|
2099
|
+
if (modifiers.includes("number")) {
|
|
2100
|
+
valueExpression = `Number(${valueExpression})`;
|
|
2101
|
+
}
|
|
2102
|
+
return valueExpression;
|
|
2103
|
+
}
|
|
1232
2104
|
function validateEventModifiers(event, attr, context) {
|
|
2105
|
+
const supportedModifiers = new Set(["prevent", "stop", "self", "once", "capture", "passive"]);
|
|
1233
2106
|
for (const modifier of event.modifiers) {
|
|
1234
|
-
if (modifier
|
|
2107
|
+
if (!supportedModifiers.has(modifier)) {
|
|
1235
2108
|
throwTemplateError(`Unsupported event modifier .${modifier}`, context, attr.loc);
|
|
1236
2109
|
}
|
|
1237
2110
|
}
|
|
2111
|
+
if (event.modifiers.includes("passive") && event.modifiers.includes("prevent")) {
|
|
2112
|
+
throwTemplateError("Event modifiers .passive and .prevent cannot be combined", context, attr.loc);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
function validateComponentEventModifiers(event, attr, context) {
|
|
2116
|
+
for (const modifier of event.modifiers) {
|
|
2117
|
+
if (modifier !== "once") {
|
|
2118
|
+
throwTemplateError(`Event modifier .${modifier} is only supported on DOM events`, context, attr.loc);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
function eventListenerOptions(event) {
|
|
2123
|
+
const options = [
|
|
2124
|
+
event.modifiers.includes("capture") ? "capture: true" : undefined,
|
|
2125
|
+
event.modifiers.includes("once") ? "once: true" : undefined,
|
|
2126
|
+
event.modifiers.includes("passive") ? "passive: true" : undefined
|
|
2127
|
+
].filter(Boolean);
|
|
2128
|
+
return options.length ? `{ ${options.join(", ")} }` : undefined;
|
|
2129
|
+
}
|
|
2130
|
+
function componentEventHandlerExpression(event, handlerExpression, context) {
|
|
2131
|
+
if (!event.modifiers.includes("once")) {
|
|
2132
|
+
return handlerExpression;
|
|
2133
|
+
}
|
|
2134
|
+
const calledVar = nextVar(context, "called");
|
|
2135
|
+
const handlerVar = nextVar(context, "handler");
|
|
2136
|
+
return `(() => { let ${calledVar} = false; const ${handlerVar} = ${handlerExpression}; return (...$args) => { if (${calledVar}) { return; } ${calledVar} = true; return ${handlerVar}(...$args); }; })()`;
|
|
1238
2137
|
}
|
|
1239
2138
|
function getEventName(name) {
|
|
1240
2139
|
if (name.startsWith("@")) {
|