@fluixi/compiler 1.0.0-alpha.104 → 1.0.0-alpha.106
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/dist/analyze/static-graph.cjs +1 -1
- package/dist/analyze/static-graph.mjs +1 -1
- package/dist/{babel-5SX36G76.mjs → babel-HJY4RVAV.mjs} +33 -20
- package/dist/{chunk-NWF66YTU.mjs → chunk-4XEOMQ57.mjs} +1 -1
- package/dist/{chunk-FMQPBKTW.mjs → chunk-JUFI6IR3.mjs} +1 -1
- package/dist/{chunk-NUET3BJB.mjs → chunk-LCQHL2VQ.mjs} +132 -10
- package/dist/{chunk-2O2YW5ZO.mjs → chunk-LGUZRHYN.mjs} +2 -2
- package/dist/{chunk-RBIYCE6M.mjs → chunk-TDN5MYWI.mjs} +1 -1
- package/dist/{chunk-ZDZKJQ5N.mjs → chunk-W7DYOMXJ.mjs} +2 -2
- package/dist/{chunk-DN33T5GJ.mjs → chunk-WHEDRMNR.mjs} +1 -1
- package/dist/codegen/backends/imperative.cjs +130 -8
- package/dist/codegen/backends/imperative.d.ts.map +1 -1
- package/dist/codegen/backends/imperative.js +62 -0
- package/dist/codegen/backends/imperative.mjs +130 -8
- package/dist/codegen/partial-template.cjs +80 -8
- package/dist/codegen/partial-template.d.cts +21 -4
- package/dist/codegen/partial-template.d.ts +21 -4
- package/dist/codegen/partial-template.d.ts.map +1 -1
- package/dist/codegen/partial-template.js +0 -0
- package/dist/codegen/partial-template.mjs +80 -8
- package/dist/codegen/serialize-static.cjs +1 -0
- package/dist/codegen/serialize-static.d.cts +8 -0
- package/dist/codegen/serialize-static.d.ts +8 -0
- package/dist/codegen/serialize-static.d.ts.map +1 -1
- package/dist/codegen/serialize-static.js +1 -1
- package/dist/codegen/serialize-static.mjs +1 -0
- package/dist/frontend/babel/build-ir-html.cjs +149 -21
- package/dist/frontend/babel/build-ir-html.d.cts +3 -1
- package/dist/frontend/babel/build-ir-html.d.ts +3 -1
- package/dist/frontend/babel/build-ir-html.d.ts.map +1 -1
- package/dist/frontend/babel/build-ir-html.mjs +149 -21
- package/dist/frontend/babel/build-ir.cjs +149 -21
- package/dist/frontend/babel/build-ir.d.cts +11 -2
- package/dist/frontend/babel/build-ir.d.ts +11 -2
- package/dist/frontend/babel/build-ir.d.ts.map +1 -1
- package/dist/frontend/babel/build-ir.js +15 -9
- package/dist/frontend/babel/build-ir.mjs +149 -21
- package/dist/frontend/babel/index.cjs +160 -25
- package/dist/frontend/babel/index.mjs +160 -25
- package/dist/frontend/babel/lower-template.cjs +149 -21
- package/dist/frontend/babel/lower-template.d.cts +2 -1
- package/dist/frontend/babel/lower-template.d.ts +2 -1
- package/dist/frontend/babel/lower-template.d.ts.map +1 -1
- package/dist/frontend/babel/lower-template.js +4 -4
- package/dist/frontend/babel/lower-template.mjs +149 -21
- package/dist/frontend/babel/plugin.cjs +158 -23
- package/dist/frontend/babel/plugin.js +9 -2
- package/dist/frontend/babel/plugin.mjs +158 -23
- package/dist/index.cjs +131 -9
- package/dist/index.mjs +131 -9
- package/dist/integrations.cjs +165 -33
- package/dist/integrations.mjs +11 -11
- package/dist/lower/compile-template.cjs +131 -9
- package/dist/lower/compile-template.mjs +131 -9
- package/dist/lower/jsx.cjs +130 -8
- package/dist/lower/jsx.mjs +130 -8
- package/dist/lower/template.cjs +130 -8
- package/dist/lower/template.mjs +130 -8
- package/dist/resolve/builtins.cjs +1 -1
- package/dist/resolve/builtins.mjs +1 -1
- package/dist/{server-fns-3Z3ZKJLB.mjs → server-fns-WZJK4BJE.mjs} +5 -5
- package/dist/{server-functions-DB7AHWE6.mjs → server-functions-4KTFFEO3.mjs} +3 -3
- package/dist/transform/index.cjs +135 -16
- package/dist/transform/index.mjs +135 -16
- package/dist/transform/templates.cjs +134 -15
- package/dist/transform/templates.d.ts.map +1 -1
- package/dist/transform/templates.js +4 -8
- package/dist/transform/templates.mjs +134 -15
- package/dist/{transform-VMK2URDB.mjs → transform-NO46NFQH.mjs} +11 -14
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +4 -4
|
@@ -178,6 +178,65 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
178
178
|
// Without one there is nothing to wrap, and a production build never has one.
|
|
179
179
|
return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, 'value')})`;
|
|
180
180
|
}
|
|
181
|
+
/** The attribute a binding writes: a forced name stripped, a plain name in its HTML spelling. */
|
|
182
|
+
function bindingTarget(binding) {
|
|
183
|
+
const name = binding.prop.name;
|
|
184
|
+
if (binding.kind === 'boolAttr')
|
|
185
|
+
return name.slice('bool:'.length);
|
|
186
|
+
if (name.startsWith('prop:'))
|
|
187
|
+
return name.slice('prop:'.length);
|
|
188
|
+
if (name.startsWith('attr:'))
|
|
189
|
+
return name.slice('attr:'.length);
|
|
190
|
+
return htmlAttrName(name);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* One attribute bound on a walked template node, through the same helper
|
|
194
|
+
* `spread` applies for that prop. A binding replaces the generic walk over
|
|
195
|
+
* every prop: what it does is decided here, once, instead of per element
|
|
196
|
+
* at runtime.
|
|
197
|
+
*/
|
|
198
|
+
function bindingStatement(binding, used) {
|
|
199
|
+
const { ref, prop, kind } = binding;
|
|
200
|
+
const value = propValue(prop);
|
|
201
|
+
switch (kind) {
|
|
202
|
+
case 'class':
|
|
203
|
+
used.add('setClassName');
|
|
204
|
+
return `setClassName(${ref}, ${value});`;
|
|
205
|
+
case 'classList':
|
|
206
|
+
used.add('setClassList');
|
|
207
|
+
return `setClassList(${ref}, ${value}, []);`;
|
|
208
|
+
case 'classListDynamic':
|
|
209
|
+
used.add('setDynamicClassList');
|
|
210
|
+
return `setDynamicClassList(${ref}, ${value});`;
|
|
211
|
+
case 'style':
|
|
212
|
+
used.add('setStyle');
|
|
213
|
+
return `setStyle(${ref}, ${value}, []);`;
|
|
214
|
+
case 'styleDynamic':
|
|
215
|
+
used.add('setDynamicStyle');
|
|
216
|
+
return `setDynamicStyle(${ref}, ${value});`;
|
|
217
|
+
case 'attr':
|
|
218
|
+
used.add('setDynamicAttribute');
|
|
219
|
+
return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
220
|
+
case 'property':
|
|
221
|
+
used.add('setDynamicProperty');
|
|
222
|
+
return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
223
|
+
case 'boolAttr':
|
|
224
|
+
used.add('setDynamicBoolAttribute');
|
|
225
|
+
return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
226
|
+
case 'event':
|
|
227
|
+
used.add('addDelegatedEventListener');
|
|
228
|
+
return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
|
|
229
|
+
case 'nativeEvent':
|
|
230
|
+
used.add('addNativeEventListener');
|
|
231
|
+
return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
|
|
232
|
+
case 'ref':
|
|
233
|
+
used.add('handleRef');
|
|
234
|
+
return `handleRef(${value}, ${ref});`;
|
|
235
|
+
case 'use':
|
|
236
|
+
used.add('applyUse');
|
|
237
|
+
return `applyUse(${ref}, ${value});`;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
181
240
|
/** `el.setAttribute("class", "card")` for a compile-time constant. */
|
|
182
241
|
function staticAttrAssignment(el, prop) {
|
|
183
242
|
const name = htmlAttrName(prop.name);
|
|
@@ -253,6 +312,9 @@ function emitNode(node, used, templates) {
|
|
|
253
312
|
// complete before any content goes in.
|
|
254
313
|
for (const step of plan.steps)
|
|
255
314
|
lines.push(`const ${step.ref} = ${step.expr};`);
|
|
315
|
+
// Attributes next, as spread applies props before children are inserted.
|
|
316
|
+
for (const binding of plan.bindings)
|
|
317
|
+
lines.push(bindingStatement(binding, used));
|
|
256
318
|
for (const hole of plan.holes) {
|
|
257
319
|
// The fourth argument is what the hole already holds, nothing on a fresh
|
|
258
320
|
// clone, the server's content when hydrating. `insert` reconciles against it,
|
|
@@ -104,10 +104,64 @@ function isHole(node) {
|
|
|
104
104
|
function isTemplateChild(node) {
|
|
105
105
|
return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
|
|
106
106
|
}
|
|
107
|
+
var PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
|
|
108
|
+
function disposition(prop) {
|
|
109
|
+
if (prop.kind === "spread") return null;
|
|
110
|
+
if (prop.name === "ref") return "ref";
|
|
111
|
+
if (prop.name === "use") return "use";
|
|
112
|
+
if (prop.kind === "event") return prop.event ? "event" : null;
|
|
113
|
+
if (prop.kind === "prop") return "property";
|
|
114
|
+
if (prop.kind !== "attr") return null;
|
|
115
|
+
const name = prop.name;
|
|
116
|
+
if (name.startsWith("on:")) return "nativeEvent";
|
|
117
|
+
if (name.startsWith("prop:")) return "property";
|
|
118
|
+
if (name.startsWith("attr:")) return "attr";
|
|
119
|
+
if (name.startsWith("bool:")) return "boolAttr";
|
|
120
|
+
if (name === "className" || name === "class") {
|
|
121
|
+
return isPlainLiteralAttr(prop) ? "serialize" : "class";
|
|
122
|
+
}
|
|
123
|
+
if (name === "classList") {
|
|
124
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
125
|
+
return prop.reactive ? "classListDynamic" : "classList";
|
|
126
|
+
}
|
|
127
|
+
if (name === "style") {
|
|
128
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
129
|
+
return prop.reactive ? "styleDynamic" : "style";
|
|
130
|
+
}
|
|
131
|
+
if (name.includes(":")) return null;
|
|
132
|
+
if (PROPERTY_ONLY.has(name)) return null;
|
|
133
|
+
if (PROPERTY_BINDS.has(name)) return "property";
|
|
134
|
+
if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
|
|
135
|
+
return "attr";
|
|
136
|
+
}
|
|
137
|
+
function effectiveProps(el) {
|
|
138
|
+
const order = [];
|
|
139
|
+
const byName = /* @__PURE__ */ new Map();
|
|
140
|
+
for (const prop of el.props) {
|
|
141
|
+
if (!byName.has(prop.name)) order.push(prop.name);
|
|
142
|
+
byName.set(prop.name, prop);
|
|
143
|
+
}
|
|
144
|
+
return order.map((name) => byName.get(name));
|
|
145
|
+
}
|
|
146
|
+
function propPlan(el) {
|
|
147
|
+
const custom = el.tag.includes("-");
|
|
148
|
+
const plan = { serialize: [], bindings: [] };
|
|
149
|
+
for (const prop of effectiveProps(el)) {
|
|
150
|
+
const kind = disposition(prop);
|
|
151
|
+
if (kind === null) return null;
|
|
152
|
+
if (kind === "serialize") {
|
|
153
|
+
plan.serialize.push(prop);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (custom) return null;
|
|
157
|
+
plan.bindings.push({ prop, kind });
|
|
158
|
+
}
|
|
159
|
+
return plan;
|
|
160
|
+
}
|
|
107
161
|
function isTemplateElement(node) {
|
|
108
162
|
if (node.svg) return false;
|
|
109
163
|
if (isRawTextElement(node.tag)) return false;
|
|
110
|
-
if (!node
|
|
164
|
+
if (!propPlan(node)) return false;
|
|
111
165
|
return node.children.every((c) => isTemplateChild(c) || isHole(c));
|
|
112
166
|
}
|
|
113
167
|
function containsHole(node) {
|
|
@@ -115,6 +169,15 @@ function containsHole(node) {
|
|
|
115
169
|
const children = node.children;
|
|
116
170
|
return children ? children.some(containsHole) : false;
|
|
117
171
|
}
|
|
172
|
+
function containsBinding(node) {
|
|
173
|
+
if (node.kind !== "element") return false;
|
|
174
|
+
const plan = propPlan(node);
|
|
175
|
+
if (plan && plan.bindings.length > 0) return true;
|
|
176
|
+
return node.children.some(containsBinding);
|
|
177
|
+
}
|
|
178
|
+
function interesting(node) {
|
|
179
|
+
return containsHole(node) || containsBinding(node);
|
|
180
|
+
}
|
|
118
181
|
function addressable(el) {
|
|
119
182
|
for (let i = 0; i < el.children.length; i++) {
|
|
120
183
|
const child = el.children[i];
|
|
@@ -128,9 +191,10 @@ function addressable(el) {
|
|
|
128
191
|
function planPartialTemplate(root) {
|
|
129
192
|
if (!isTemplateElement(root)) return null;
|
|
130
193
|
if (!addressable(root)) return null;
|
|
131
|
-
if (!root
|
|
194
|
+
if (!interesting(root)) return null;
|
|
132
195
|
const steps = [];
|
|
133
196
|
const holes = [];
|
|
197
|
+
const bindings = [];
|
|
134
198
|
let refCount = 0;
|
|
135
199
|
const declare = (expr) => {
|
|
136
200
|
const ref = `_n$${refCount++}`;
|
|
@@ -138,11 +202,17 @@ function planPartialTemplate(root) {
|
|
|
138
202
|
return ref;
|
|
139
203
|
};
|
|
140
204
|
walk(root, "_el$");
|
|
141
|
-
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
|
|
205
|
+
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
|
|
142
206
|
function walk(el, elementRef) {
|
|
207
|
+
const plan = propPlan(el);
|
|
208
|
+
if (plan) {
|
|
209
|
+
for (const binding of plan.bindings) {
|
|
210
|
+
bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
143
213
|
let last = -1;
|
|
144
214
|
el.children.forEach((child, i) => {
|
|
145
|
-
if (
|
|
215
|
+
if (interesting(child)) last = i;
|
|
146
216
|
});
|
|
147
217
|
let previous = null;
|
|
148
218
|
for (let i = 0; i <= last; i++) {
|
|
@@ -156,19 +226,21 @@ function planPartialTemplate(root) {
|
|
|
156
226
|
continue;
|
|
157
227
|
}
|
|
158
228
|
const ref = declare(expr);
|
|
159
|
-
if (child.kind === "element" &&
|
|
229
|
+
if (child.kind === "element" && interesting(child)) walk(child, ref);
|
|
160
230
|
previous = ref;
|
|
161
231
|
}
|
|
162
232
|
}
|
|
163
233
|
}
|
|
164
234
|
function serializeWithMarkers(el) {
|
|
165
|
-
return serializeStatic(
|
|
235
|
+
return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
|
|
166
236
|
}
|
|
167
|
-
function
|
|
237
|
+
function templateTree(el) {
|
|
238
|
+
const plan = propPlan(el);
|
|
168
239
|
return {
|
|
169
240
|
...el,
|
|
241
|
+
props: plan ? plan.serialize : [],
|
|
170
242
|
children: el.children.map(
|
|
171
|
-
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ?
|
|
243
|
+
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
|
|
172
244
|
)
|
|
173
245
|
};
|
|
174
246
|
}
|
|
@@ -441,6 +513,55 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
441
513
|
const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
|
|
442
514
|
return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
|
|
443
515
|
}
|
|
516
|
+
function bindingTarget(binding) {
|
|
517
|
+
const name = binding.prop.name;
|
|
518
|
+
if (binding.kind === "boolAttr") return name.slice("bool:".length);
|
|
519
|
+
if (name.startsWith("prop:")) return name.slice("prop:".length);
|
|
520
|
+
if (name.startsWith("attr:")) return name.slice("attr:".length);
|
|
521
|
+
return htmlAttrName(name);
|
|
522
|
+
}
|
|
523
|
+
function bindingStatement(binding, used) {
|
|
524
|
+
const { ref, prop, kind } = binding;
|
|
525
|
+
const value = propValue(prop);
|
|
526
|
+
switch (kind) {
|
|
527
|
+
case "class":
|
|
528
|
+
used.add("setClassName");
|
|
529
|
+
return `setClassName(${ref}, ${value});`;
|
|
530
|
+
case "classList":
|
|
531
|
+
used.add("setClassList");
|
|
532
|
+
return `setClassList(${ref}, ${value}, []);`;
|
|
533
|
+
case "classListDynamic":
|
|
534
|
+
used.add("setDynamicClassList");
|
|
535
|
+
return `setDynamicClassList(${ref}, ${value});`;
|
|
536
|
+
case "style":
|
|
537
|
+
used.add("setStyle");
|
|
538
|
+
return `setStyle(${ref}, ${value}, []);`;
|
|
539
|
+
case "styleDynamic":
|
|
540
|
+
used.add("setDynamicStyle");
|
|
541
|
+
return `setDynamicStyle(${ref}, ${value});`;
|
|
542
|
+
case "attr":
|
|
543
|
+
used.add("setDynamicAttribute");
|
|
544
|
+
return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
545
|
+
case "property":
|
|
546
|
+
used.add("setDynamicProperty");
|
|
547
|
+
return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
548
|
+
case "boolAttr":
|
|
549
|
+
used.add("setDynamicBoolAttribute");
|
|
550
|
+
return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
551
|
+
case "event":
|
|
552
|
+
used.add("addDelegatedEventListener");
|
|
553
|
+
return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
|
|
554
|
+
case "nativeEvent":
|
|
555
|
+
used.add("addNativeEventListener");
|
|
556
|
+
return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
|
|
557
|
+
case "ref":
|
|
558
|
+
used.add("handleRef");
|
|
559
|
+
return `handleRef(${value}, ${ref});`;
|
|
560
|
+
case "use":
|
|
561
|
+
used.add("applyUse");
|
|
562
|
+
return `applyUse(${ref}, ${value});`;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
444
565
|
function staticAttrAssignment(el, prop) {
|
|
445
566
|
const name = htmlAttrName(prop.name);
|
|
446
567
|
const value = prop.literal;
|
|
@@ -491,6 +612,7 @@ function emitNode(node, used, templates) {
|
|
|
491
612
|
`const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
|
|
492
613
|
];
|
|
493
614
|
for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
|
|
615
|
+
for (const binding of plan.bindings) lines.push(bindingStatement(binding, used));
|
|
494
616
|
for (const hole of plan.holes) {
|
|
495
617
|
lines.push(
|
|
496
618
|
`insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`
|
|
@@ -105,10 +105,64 @@ function isHole(node) {
|
|
|
105
105
|
function isTemplateChild(node) {
|
|
106
106
|
return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
|
|
107
107
|
}
|
|
108
|
+
var PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
|
|
109
|
+
function disposition(prop) {
|
|
110
|
+
if (prop.kind === "spread") return null;
|
|
111
|
+
if (prop.name === "ref") return "ref";
|
|
112
|
+
if (prop.name === "use") return "use";
|
|
113
|
+
if (prop.kind === "event") return prop.event ? "event" : null;
|
|
114
|
+
if (prop.kind === "prop") return "property";
|
|
115
|
+
if (prop.kind !== "attr") return null;
|
|
116
|
+
const name = prop.name;
|
|
117
|
+
if (name.startsWith("on:")) return "nativeEvent";
|
|
118
|
+
if (name.startsWith("prop:")) return "property";
|
|
119
|
+
if (name.startsWith("attr:")) return "attr";
|
|
120
|
+
if (name.startsWith("bool:")) return "boolAttr";
|
|
121
|
+
if (name === "className" || name === "class") {
|
|
122
|
+
return isPlainLiteralAttr(prop) ? "serialize" : "class";
|
|
123
|
+
}
|
|
124
|
+
if (name === "classList") {
|
|
125
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
126
|
+
return prop.reactive ? "classListDynamic" : "classList";
|
|
127
|
+
}
|
|
128
|
+
if (name === "style") {
|
|
129
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
130
|
+
return prop.reactive ? "styleDynamic" : "style";
|
|
131
|
+
}
|
|
132
|
+
if (name.includes(":")) return null;
|
|
133
|
+
if (PROPERTY_ONLY.has(name)) return null;
|
|
134
|
+
if (PROPERTY_BINDS.has(name)) return "property";
|
|
135
|
+
if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
|
|
136
|
+
return "attr";
|
|
137
|
+
}
|
|
138
|
+
function effectiveProps(el) {
|
|
139
|
+
const order = [];
|
|
140
|
+
const byName = /* @__PURE__ */ new Map();
|
|
141
|
+
for (const prop of el.props) {
|
|
142
|
+
if (!byName.has(prop.name)) order.push(prop.name);
|
|
143
|
+
byName.set(prop.name, prop);
|
|
144
|
+
}
|
|
145
|
+
return order.map((name) => byName.get(name));
|
|
146
|
+
}
|
|
147
|
+
function propPlan(el) {
|
|
148
|
+
const custom = el.tag.includes("-");
|
|
149
|
+
const plan = { serialize: [], bindings: [] };
|
|
150
|
+
for (const prop of effectiveProps(el)) {
|
|
151
|
+
const kind = disposition(prop);
|
|
152
|
+
if (kind === null) return null;
|
|
153
|
+
if (kind === "serialize") {
|
|
154
|
+
plan.serialize.push(prop);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (custom) return null;
|
|
158
|
+
plan.bindings.push({ prop, kind });
|
|
159
|
+
}
|
|
160
|
+
return plan;
|
|
161
|
+
}
|
|
108
162
|
function isTemplateElement(node) {
|
|
109
163
|
if (node.svg) return false;
|
|
110
164
|
if (isRawTextElement(node.tag)) return false;
|
|
111
|
-
if (!node
|
|
165
|
+
if (!propPlan(node)) return false;
|
|
112
166
|
return node.children.every((c) => isTemplateChild(c) || isHole(c));
|
|
113
167
|
}
|
|
114
168
|
function containsHole(node) {
|
|
@@ -116,6 +170,15 @@ function containsHole(node) {
|
|
|
116
170
|
const children = node.children;
|
|
117
171
|
return children ? children.some(containsHole) : false;
|
|
118
172
|
}
|
|
173
|
+
function containsBinding(node) {
|
|
174
|
+
if (node.kind !== "element") return false;
|
|
175
|
+
const plan = propPlan(node);
|
|
176
|
+
if (plan && plan.bindings.length > 0) return true;
|
|
177
|
+
return node.children.some(containsBinding);
|
|
178
|
+
}
|
|
179
|
+
function interesting(node) {
|
|
180
|
+
return containsHole(node) || containsBinding(node);
|
|
181
|
+
}
|
|
119
182
|
function addressable(el) {
|
|
120
183
|
for (let i = 0; i < el.children.length; i++) {
|
|
121
184
|
const child = el.children[i];
|
|
@@ -129,9 +192,10 @@ function addressable(el) {
|
|
|
129
192
|
function planPartialTemplate(root) {
|
|
130
193
|
if (!isTemplateElement(root)) return null;
|
|
131
194
|
if (!addressable(root)) return null;
|
|
132
|
-
if (!root
|
|
195
|
+
if (!interesting(root)) return null;
|
|
133
196
|
const steps = [];
|
|
134
197
|
const holes = [];
|
|
198
|
+
const bindings = [];
|
|
135
199
|
let refCount = 0;
|
|
136
200
|
const declare = (expr) => {
|
|
137
201
|
const ref = `_n$${refCount++}`;
|
|
@@ -139,11 +203,17 @@ function planPartialTemplate(root) {
|
|
|
139
203
|
return ref;
|
|
140
204
|
};
|
|
141
205
|
walk(root, "_el$");
|
|
142
|
-
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
|
|
206
|
+
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
|
|
143
207
|
function walk(el, elementRef) {
|
|
208
|
+
const plan = propPlan(el);
|
|
209
|
+
if (plan) {
|
|
210
|
+
for (const binding of plan.bindings) {
|
|
211
|
+
bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
144
214
|
let last = -1;
|
|
145
215
|
el.children.forEach((child, i) => {
|
|
146
|
-
if (
|
|
216
|
+
if (interesting(child)) last = i;
|
|
147
217
|
});
|
|
148
218
|
let previous = null;
|
|
149
219
|
for (let i = 0; i <= last; i++) {
|
|
@@ -157,19 +227,21 @@ function planPartialTemplate(root) {
|
|
|
157
227
|
continue;
|
|
158
228
|
}
|
|
159
229
|
const ref = declare(expr);
|
|
160
|
-
if (child.kind === "element" &&
|
|
230
|
+
if (child.kind === "element" && interesting(child)) walk(child, ref);
|
|
161
231
|
previous = ref;
|
|
162
232
|
}
|
|
163
233
|
}
|
|
164
234
|
}
|
|
165
235
|
function serializeWithMarkers(el) {
|
|
166
|
-
return serializeStatic(
|
|
236
|
+
return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
|
|
167
237
|
}
|
|
168
|
-
function
|
|
238
|
+
function templateTree(el) {
|
|
239
|
+
const plan = propPlan(el);
|
|
169
240
|
return {
|
|
170
241
|
...el,
|
|
242
|
+
props: plan ? plan.serialize : [],
|
|
171
243
|
children: el.children.map(
|
|
172
|
-
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ?
|
|
244
|
+
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
|
|
173
245
|
)
|
|
174
246
|
};
|
|
175
247
|
}
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cloning a subtree whose *structure* is static but which contains dynamic holes.
|
|
3
3
|
*
|
|
4
|
-
* The fully-static case is easy: serialize, clone, done. The common case is not static
|
|
4
|
+
* The fully-static case is easy: serialize, clone, done. The common case is not static,
|
|
5
5
|
* a card wrapper with one reactive value inside: and a single hole used to send the
|
|
6
6
|
* whole subtree back to being built an element at a time. This plans the middle ground:
|
|
7
7
|
* the structure becomes one template, and each hole is filled through a reference into
|
|
8
8
|
* the cloned tree.
|
|
9
9
|
*
|
|
10
|
+
* An element with dynamic attributes takes the same middle ground. Its static
|
|
11
|
+
* attributes serialize with the structure, and each attribute that can be bound one
|
|
12
|
+
* by one becomes a binding on the walked node, through the same setters `spread`
|
|
13
|
+
* applies internally. What cannot be bound that way (a real spread, a property-only
|
|
14
|
+
* name, a custom element's props, an SVG subtree) keeps the element out of the
|
|
15
|
+
* template entirely, so the imperative path builds it exactly as before.
|
|
16
|
+
*
|
|
10
17
|
* Three things make it correct in all three environments, fresh client render, server
|
|
11
18
|
* render, and hydration:
|
|
12
19
|
*
|
|
@@ -21,7 +28,7 @@
|
|
|
21
28
|
* - **Every reference is read before any insertion**, since inserting shifts what
|
|
22
29
|
* follows.
|
|
23
30
|
*/
|
|
24
|
-
import type { IRElement, IRNode } from '../ir/nodes.cjs';
|
|
31
|
+
import type { IRElement, IRNode, IRProp } from '../ir/nodes.cjs';
|
|
25
32
|
/**
|
|
26
33
|
* The comments bracketing a hole.
|
|
27
34
|
*
|
|
@@ -45,17 +52,27 @@ export interface TemplateHole {
|
|
|
45
52
|
endRef: string;
|
|
46
53
|
node: IRNode;
|
|
47
54
|
}
|
|
55
|
+
/** How one attribute is bound on a walked node: the runtime helper family it uses. */
|
|
56
|
+
export type BindingKind = 'class' | 'classList' | 'classListDynamic' | 'style' | 'styleDynamic' | 'attr' | 'property' | 'boolAttr' | 'event' | 'nativeEvent' | 'ref' | 'use';
|
|
57
|
+
/** One attribute bound at runtime on the element `ref` resolves to. */
|
|
58
|
+
export interface TemplateBinding {
|
|
59
|
+
ref: string;
|
|
60
|
+
prop: IRProp;
|
|
61
|
+
kind: BindingKind;
|
|
62
|
+
}
|
|
48
63
|
export interface PartialTemplate {
|
|
49
64
|
html: string;
|
|
50
65
|
tag: string;
|
|
51
66
|
/** Declarations to emit in order, before the inserts. */
|
|
52
67
|
steps: WalkStep[];
|
|
53
68
|
holes: TemplateHole[];
|
|
69
|
+
/** Attribute bindings to emit after the walk, before the inserts. */
|
|
70
|
+
bindings: TemplateBinding[];
|
|
54
71
|
}
|
|
55
72
|
/**
|
|
56
73
|
* Plan a partial template for `root`, or null when it does not qualify.
|
|
57
74
|
*
|
|
58
|
-
* Returns null for a subtree with no holes: that is the fully-static
|
|
59
|
-
* cheaper path of its own and must not be routed through this one.
|
|
75
|
+
* Returns null for a subtree with no holes and no bindings: that is the fully-static
|
|
76
|
+
* case, which has a cheaper path of its own and must not be routed through this one.
|
|
60
77
|
*/
|
|
61
78
|
export declare function planPartialTemplate(root: IRElement): PartialTemplate | null;
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cloning a subtree whose *structure* is static but which contains dynamic holes.
|
|
3
3
|
*
|
|
4
|
-
* The fully-static case is easy: serialize, clone, done. The common case is not static
|
|
4
|
+
* The fully-static case is easy: serialize, clone, done. The common case is not static,
|
|
5
5
|
* a card wrapper with one reactive value inside: and a single hole used to send the
|
|
6
6
|
* whole subtree back to being built an element at a time. This plans the middle ground:
|
|
7
7
|
* the structure becomes one template, and each hole is filled through a reference into
|
|
8
8
|
* the cloned tree.
|
|
9
9
|
*
|
|
10
|
+
* An element with dynamic attributes takes the same middle ground. Its static
|
|
11
|
+
* attributes serialize with the structure, and each attribute that can be bound one
|
|
12
|
+
* by one becomes a binding on the walked node, through the same setters `spread`
|
|
13
|
+
* applies internally. What cannot be bound that way (a real spread, a property-only
|
|
14
|
+
* name, a custom element's props, an SVG subtree) keeps the element out of the
|
|
15
|
+
* template entirely, so the imperative path builds it exactly as before.
|
|
16
|
+
*
|
|
10
17
|
* Three things make it correct in all three environments, fresh client render, server
|
|
11
18
|
* render, and hydration:
|
|
12
19
|
*
|
|
@@ -21,7 +28,7 @@
|
|
|
21
28
|
* - **Every reference is read before any insertion**, since inserting shifts what
|
|
22
29
|
* follows.
|
|
23
30
|
*/
|
|
24
|
-
import type { IRElement, IRNode } from '../ir/nodes.js';
|
|
31
|
+
import type { IRElement, IRNode, IRProp } from '../ir/nodes.js';
|
|
25
32
|
/**
|
|
26
33
|
* The comments bracketing a hole.
|
|
27
34
|
*
|
|
@@ -45,18 +52,28 @@ export interface TemplateHole {
|
|
|
45
52
|
endRef: string;
|
|
46
53
|
node: IRNode;
|
|
47
54
|
}
|
|
55
|
+
/** How one attribute is bound on a walked node: the runtime helper family it uses. */
|
|
56
|
+
export type BindingKind = 'class' | 'classList' | 'classListDynamic' | 'style' | 'styleDynamic' | 'attr' | 'property' | 'boolAttr' | 'event' | 'nativeEvent' | 'ref' | 'use';
|
|
57
|
+
/** One attribute bound at runtime on the element `ref` resolves to. */
|
|
58
|
+
export interface TemplateBinding {
|
|
59
|
+
ref: string;
|
|
60
|
+
prop: IRProp;
|
|
61
|
+
kind: BindingKind;
|
|
62
|
+
}
|
|
48
63
|
export interface PartialTemplate {
|
|
49
64
|
html: string;
|
|
50
65
|
tag: string;
|
|
51
66
|
/** Declarations to emit in order, before the inserts. */
|
|
52
67
|
steps: WalkStep[];
|
|
53
68
|
holes: TemplateHole[];
|
|
69
|
+
/** Attribute bindings to emit after the walk, before the inserts. */
|
|
70
|
+
bindings: TemplateBinding[];
|
|
54
71
|
}
|
|
55
72
|
/**
|
|
56
73
|
* Plan a partial template for `root`, or null when it does not qualify.
|
|
57
74
|
*
|
|
58
|
-
* Returns null for a subtree with no holes: that is the fully-static
|
|
59
|
-
* cheaper path of its own and must not be routed through this one.
|
|
75
|
+
* Returns null for a subtree with no holes and no bindings: that is the fully-static
|
|
76
|
+
* case, which has a cheaper path of its own and must not be routed through this one.
|
|
60
77
|
*/
|
|
61
78
|
export declare function planPartialTemplate(root: IRElement): PartialTemplate | null;
|
|
62
79
|
//# sourceMappingURL=partial-template.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partial-template.d.ts","sourceRoot":"","sources":["../../src/codegen/partial-template.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"partial-template.d.ts","sourceRoot":"","sources":["../../src/codegen/partial-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGhE;;;;;GAKG;AACH,eAAO,MAAM,UAAU,cAAc,CAAC;AACtC,eAAO,MAAM,QAAQ,eAAe,CAAC;AAErC,qEAAqE;AACrE,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sFAAsF;AACtF,MAAM,MAAM,WAAW,GACnB,OAAO,GACP,WAAW,GACX,kBAAkB,GAClB,OAAO,GACP,cAAc,GACd,MAAM,GACN,UAAU,GACV,UAAU,GACV,OAAO,GACP,aAAa,GACb,KAAK,GACL,KAAK,CAAC;AAEV,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,qEAAqE;IACrE,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAmJD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,GAAG,eAAe,GAAG,IAAI,CAqD3E"}
|
|
Binary file
|