@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
|
@@ -194,10 +194,64 @@ function isHole(node) {
|
|
|
194
194
|
function isTemplateChild(node) {
|
|
195
195
|
return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
|
|
196
196
|
}
|
|
197
|
+
var PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
|
|
198
|
+
function disposition(prop) {
|
|
199
|
+
if (prop.kind === "spread") return null;
|
|
200
|
+
if (prop.name === "ref") return "ref";
|
|
201
|
+
if (prop.name === "use") return "use";
|
|
202
|
+
if (prop.kind === "event") return prop.event ? "event" : null;
|
|
203
|
+
if (prop.kind === "prop") return "property";
|
|
204
|
+
if (prop.kind !== "attr") return null;
|
|
205
|
+
const name = prop.name;
|
|
206
|
+
if (name.startsWith("on:")) return "nativeEvent";
|
|
207
|
+
if (name.startsWith("prop:")) return "property";
|
|
208
|
+
if (name.startsWith("attr:")) return "attr";
|
|
209
|
+
if (name.startsWith("bool:")) return "boolAttr";
|
|
210
|
+
if (name === "className" || name === "class") {
|
|
211
|
+
return isPlainLiteralAttr(prop) ? "serialize" : "class";
|
|
212
|
+
}
|
|
213
|
+
if (name === "classList") {
|
|
214
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
215
|
+
return prop.reactive ? "classListDynamic" : "classList";
|
|
216
|
+
}
|
|
217
|
+
if (name === "style") {
|
|
218
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
219
|
+
return prop.reactive ? "styleDynamic" : "style";
|
|
220
|
+
}
|
|
221
|
+
if (name.includes(":")) return null;
|
|
222
|
+
if (PROPERTY_ONLY.has(name)) return null;
|
|
223
|
+
if (PROPERTY_BINDS.has(name)) return "property";
|
|
224
|
+
if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
|
|
225
|
+
return "attr";
|
|
226
|
+
}
|
|
227
|
+
function effectiveProps(el) {
|
|
228
|
+
const order = [];
|
|
229
|
+
const byName = /* @__PURE__ */ new Map();
|
|
230
|
+
for (const prop of el.props) {
|
|
231
|
+
if (!byName.has(prop.name)) order.push(prop.name);
|
|
232
|
+
byName.set(prop.name, prop);
|
|
233
|
+
}
|
|
234
|
+
return order.map((name) => byName.get(name));
|
|
235
|
+
}
|
|
236
|
+
function propPlan(el) {
|
|
237
|
+
const custom = el.tag.includes("-");
|
|
238
|
+
const plan = { serialize: [], bindings: [] };
|
|
239
|
+
for (const prop of effectiveProps(el)) {
|
|
240
|
+
const kind = disposition(prop);
|
|
241
|
+
if (kind === null) return null;
|
|
242
|
+
if (kind === "serialize") {
|
|
243
|
+
plan.serialize.push(prop);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (custom) return null;
|
|
247
|
+
plan.bindings.push({ prop, kind });
|
|
248
|
+
}
|
|
249
|
+
return plan;
|
|
250
|
+
}
|
|
197
251
|
function isTemplateElement(node) {
|
|
198
252
|
if (node.svg) return false;
|
|
199
253
|
if (isRawTextElement(node.tag)) return false;
|
|
200
|
-
if (!node
|
|
254
|
+
if (!propPlan(node)) return false;
|
|
201
255
|
return node.children.every((c) => isTemplateChild(c) || isHole(c));
|
|
202
256
|
}
|
|
203
257
|
function containsHole(node) {
|
|
@@ -205,6 +259,15 @@ function containsHole(node) {
|
|
|
205
259
|
const children = node.children;
|
|
206
260
|
return children ? children.some(containsHole) : false;
|
|
207
261
|
}
|
|
262
|
+
function containsBinding(node) {
|
|
263
|
+
if (node.kind !== "element") return false;
|
|
264
|
+
const plan = propPlan(node);
|
|
265
|
+
if (plan && plan.bindings.length > 0) return true;
|
|
266
|
+
return node.children.some(containsBinding);
|
|
267
|
+
}
|
|
268
|
+
function interesting(node) {
|
|
269
|
+
return containsHole(node) || containsBinding(node);
|
|
270
|
+
}
|
|
208
271
|
function addressable(el) {
|
|
209
272
|
for (let i = 0; i < el.children.length; i++) {
|
|
210
273
|
const child = el.children[i];
|
|
@@ -218,9 +281,10 @@ function addressable(el) {
|
|
|
218
281
|
function planPartialTemplate(root) {
|
|
219
282
|
if (!isTemplateElement(root)) return null;
|
|
220
283
|
if (!addressable(root)) return null;
|
|
221
|
-
if (!root
|
|
284
|
+
if (!interesting(root)) return null;
|
|
222
285
|
const steps = [];
|
|
223
286
|
const holes = [];
|
|
287
|
+
const bindings = [];
|
|
224
288
|
let refCount = 0;
|
|
225
289
|
const declare = (expr) => {
|
|
226
290
|
const ref = `_n$${refCount++}`;
|
|
@@ -228,11 +292,17 @@ function planPartialTemplate(root) {
|
|
|
228
292
|
return ref;
|
|
229
293
|
};
|
|
230
294
|
walk(root, "_el$");
|
|
231
|
-
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
|
|
295
|
+
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
|
|
232
296
|
function walk(el, elementRef) {
|
|
297
|
+
const plan = propPlan(el);
|
|
298
|
+
if (plan) {
|
|
299
|
+
for (const binding of plan.bindings) {
|
|
300
|
+
bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
233
303
|
let last = -1;
|
|
234
304
|
el.children.forEach((child, i) => {
|
|
235
|
-
if (
|
|
305
|
+
if (interesting(child)) last = i;
|
|
236
306
|
});
|
|
237
307
|
let previous = null;
|
|
238
308
|
for (let i = 0; i <= last; i++) {
|
|
@@ -246,19 +316,21 @@ function planPartialTemplate(root) {
|
|
|
246
316
|
continue;
|
|
247
317
|
}
|
|
248
318
|
const ref = declare(expr);
|
|
249
|
-
if (child.kind === "element" &&
|
|
319
|
+
if (child.kind === "element" && interesting(child)) walk(child, ref);
|
|
250
320
|
previous = ref;
|
|
251
321
|
}
|
|
252
322
|
}
|
|
253
323
|
}
|
|
254
324
|
function serializeWithMarkers(el) {
|
|
255
|
-
return serializeStatic(
|
|
325
|
+
return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
|
|
256
326
|
}
|
|
257
|
-
function
|
|
327
|
+
function templateTree(el) {
|
|
328
|
+
const plan = propPlan(el);
|
|
258
329
|
return {
|
|
259
330
|
...el,
|
|
331
|
+
props: plan ? plan.serialize : [],
|
|
260
332
|
children: el.children.map(
|
|
261
|
-
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ?
|
|
333
|
+
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
|
|
262
334
|
)
|
|
263
335
|
};
|
|
264
336
|
}
|
|
@@ -531,6 +603,55 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
531
603
|
const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
|
|
532
604
|
return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
|
|
533
605
|
}
|
|
606
|
+
function bindingTarget(binding) {
|
|
607
|
+
const name = binding.prop.name;
|
|
608
|
+
if (binding.kind === "boolAttr") return name.slice("bool:".length);
|
|
609
|
+
if (name.startsWith("prop:")) return name.slice("prop:".length);
|
|
610
|
+
if (name.startsWith("attr:")) return name.slice("attr:".length);
|
|
611
|
+
return htmlAttrName(name);
|
|
612
|
+
}
|
|
613
|
+
function bindingStatement(binding, used) {
|
|
614
|
+
const { ref, prop, kind } = binding;
|
|
615
|
+
const value = propValue(prop);
|
|
616
|
+
switch (kind) {
|
|
617
|
+
case "class":
|
|
618
|
+
used.add("setClassName");
|
|
619
|
+
return `setClassName(${ref}, ${value});`;
|
|
620
|
+
case "classList":
|
|
621
|
+
used.add("setClassList");
|
|
622
|
+
return `setClassList(${ref}, ${value}, []);`;
|
|
623
|
+
case "classListDynamic":
|
|
624
|
+
used.add("setDynamicClassList");
|
|
625
|
+
return `setDynamicClassList(${ref}, ${value});`;
|
|
626
|
+
case "style":
|
|
627
|
+
used.add("setStyle");
|
|
628
|
+
return `setStyle(${ref}, ${value}, []);`;
|
|
629
|
+
case "styleDynamic":
|
|
630
|
+
used.add("setDynamicStyle");
|
|
631
|
+
return `setDynamicStyle(${ref}, ${value});`;
|
|
632
|
+
case "attr":
|
|
633
|
+
used.add("setDynamicAttribute");
|
|
634
|
+
return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
635
|
+
case "property":
|
|
636
|
+
used.add("setDynamicProperty");
|
|
637
|
+
return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
638
|
+
case "boolAttr":
|
|
639
|
+
used.add("setDynamicBoolAttribute");
|
|
640
|
+
return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
641
|
+
case "event":
|
|
642
|
+
used.add("addDelegatedEventListener");
|
|
643
|
+
return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
|
|
644
|
+
case "nativeEvent":
|
|
645
|
+
used.add("addNativeEventListener");
|
|
646
|
+
return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
|
|
647
|
+
case "ref":
|
|
648
|
+
used.add("handleRef");
|
|
649
|
+
return `handleRef(${value}, ${ref});`;
|
|
650
|
+
case "use":
|
|
651
|
+
used.add("applyUse");
|
|
652
|
+
return `applyUse(${ref}, ${value});`;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
534
655
|
function staticAttrAssignment(el, prop) {
|
|
535
656
|
const name = htmlAttrName(prop.name);
|
|
536
657
|
const value = prop.literal;
|
|
@@ -581,6 +702,7 @@ function emitNode(node, used, templates) {
|
|
|
581
702
|
`const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
|
|
582
703
|
];
|
|
583
704
|
for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
|
|
705
|
+
for (const binding of plan.bindings) lines.push(bindingStatement(binding, used));
|
|
584
706
|
for (const hole of plan.holes) {
|
|
585
707
|
lines.push(
|
|
586
708
|
`insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`
|
|
@@ -1846,7 +1968,7 @@ var generate = import_generator.default.default ?? import_generator.default;
|
|
|
1846
1968
|
function gen(node) {
|
|
1847
1969
|
return generate(node, { concise: true }).code;
|
|
1848
1970
|
}
|
|
1849
|
-
function transformNestedJSX(node, used, onNested) {
|
|
1971
|
+
function transformNestedJSX(node, used, onNested, emit) {
|
|
1850
1972
|
const clone = import_core.types.cloneNode(node, true);
|
|
1851
1973
|
const file = import_core.types.file(import_core.types.program([import_core.types.expressionStatement(clone)]));
|
|
1852
1974
|
let changed = false;
|
|
@@ -1859,9 +1981,12 @@ function transformNestedJSX(node, used, onNested) {
|
|
|
1859
1981
|
(0, import_core.traverse)(file, {
|
|
1860
1982
|
"JSXElement|JSXFragment"(path) {
|
|
1861
1983
|
const jsx = path.node;
|
|
1862
|
-
const nestedIR = buildIR(jsx, used, onNested);
|
|
1984
|
+
const nestedIR = buildIR(jsx, used, onNested, emit);
|
|
1863
1985
|
onNested?.(nestedIR);
|
|
1864
|
-
const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
|
|
1986
|
+
const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
|
|
1987
|
+
templateClone: emit?.templateClone,
|
|
1988
|
+
partialTemplates: emit?.partialTemplates ?? true
|
|
1989
|
+
});
|
|
1865
1990
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
1866
1991
|
},
|
|
1867
1992
|
// Nested html templates compose: ${cond ? html`...` : html`...`},
|
|
@@ -1869,26 +1994,29 @@ function transformNestedJSX(node, used, onNested) {
|
|
|
1869
1994
|
TaggedTemplateExpression(path) {
|
|
1870
1995
|
const tag = path.node.tag;
|
|
1871
1996
|
if (!import_core.types.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
|
|
1872
|
-
const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
|
|
1997
|
+
const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested, emit });
|
|
1873
1998
|
onNested?.(ir);
|
|
1874
|
-
const { code, imports, templates } = imperativeBackend.emit(ir, {
|
|
1999
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {
|
|
2000
|
+
templateClone: emit?.templateClone,
|
|
2001
|
+
partialTemplates: emit?.partialTemplates ?? true
|
|
2002
|
+
});
|
|
1875
2003
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
1876
2004
|
}
|
|
1877
2005
|
});
|
|
1878
2006
|
return changed ? file.program.body[0].expression : node;
|
|
1879
2007
|
}
|
|
1880
|
-
function genExpr(node, used, onNested) {
|
|
1881
|
-
return gen(transformNestedJSX(node, used, onNested));
|
|
2008
|
+
function genExpr(node, used, onNested, emit) {
|
|
2009
|
+
return gen(transformNestedJSX(node, used, onNested, emit));
|
|
1882
2010
|
}
|
|
1883
2011
|
function isReactiveExpr(node) {
|
|
1884
2012
|
return isReactiveShape(shapeOf(node));
|
|
1885
2013
|
}
|
|
1886
|
-
function buildIR(node, used = /* @__PURE__ */ new Set(), onNested) {
|
|
2014
|
+
function buildIR(node, used = /* @__PURE__ */ new Set(), onNested, emit) {
|
|
1887
2015
|
const ir = buildJsxIR(node, {
|
|
1888
2016
|
code: (n) => (
|
|
1889
2017
|
// A member-expression tag (`<ns.Foo/>`) is not an Expression, so it cannot go
|
|
1890
2018
|
// through the nested-JSX compile: it only ever needs printing.
|
|
1891
|
-
n.type === "JSXMemberExpression" ? gen(n) : genExpr(n, used, onNested)
|
|
2019
|
+
n.type === "JSXMemberExpression" ? gen(n) : genExpr(n, used, onNested, emit)
|
|
1892
2020
|
),
|
|
1893
2021
|
used
|
|
1894
2022
|
});
|
|
@@ -1900,16 +2028,16 @@ var generate2 = import_generator2.default.default ?? import_generator2.default;
|
|
|
1900
2028
|
function gen2(node) {
|
|
1901
2029
|
return generate2(node, { concise: true }).code;
|
|
1902
2030
|
}
|
|
1903
|
-
function bindHole(expr, used, onNested) {
|
|
2031
|
+
function bindHole(expr, used, onNested, emit) {
|
|
1904
2032
|
const hole = {
|
|
1905
|
-
code: genExpr(expr, used, onNested),
|
|
2033
|
+
code: genExpr(expr, used, onNested, emit),
|
|
1906
2034
|
reactive: isReactiveExpr(expr)
|
|
1907
2035
|
};
|
|
1908
2036
|
if (import_core2.types.isArrowFunctionExpression(expr) && !import_core2.types.isBlockStatement(expr.body)) {
|
|
1909
2037
|
const body = expr.body;
|
|
1910
2038
|
hole.arrow = {
|
|
1911
2039
|
params: expr.params.map((p) => gen2(p)),
|
|
1912
|
-
body: genExpr(body, used, onNested),
|
|
2040
|
+
body: genExpr(body, used, onNested, emit),
|
|
1913
2041
|
bodyReactive: isReactiveExpr(body)
|
|
1914
2042
|
};
|
|
1915
2043
|
}
|
|
@@ -1918,7 +2046,7 @@ function bindHole(expr, used, onNested) {
|
|
|
1918
2046
|
}
|
|
1919
2047
|
function lowerHtmlTemplate(node, used, opts = {}) {
|
|
1920
2048
|
const quasi = node.quasi;
|
|
1921
|
-
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
|
|
2049
|
+
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested, opts.emit));
|
|
1922
2050
|
return lowerTemplate(buildPieces(quasi), holes, used, opts);
|
|
1923
2051
|
}
|
|
1924
2052
|
function buildPieces(quasi) {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { types as t } from '@babel/core';
|
|
14
14
|
import type { IRNode } from '../../ir/nodes.cjs';
|
|
15
|
-
import { type NestedHook } from './build-ir.cjs';
|
|
15
|
+
import { type NestedEmitOptions, type NestedHook } from './build-ir.cjs';
|
|
16
16
|
/**
|
|
17
17
|
* Build IR from an `` html`...` `` / `` svg`...` `` TaggedTemplateExpression.
|
|
18
18
|
* `used` collects runtime symbols emitted for anything nested inside a hole.
|
|
@@ -20,4 +20,5 @@ import { type NestedHook } from './build-ir.cjs';
|
|
|
20
20
|
export declare function lowerHtmlTemplate(node: t.TaggedTemplateExpression, used: Set<string>, opts?: {
|
|
21
21
|
svg?: boolean;
|
|
22
22
|
onNested?: NestedHook;
|
|
23
|
+
emit?: NestedEmitOptions;
|
|
23
24
|
}): IRNode;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { types as t } from '@babel/core';
|
|
14
14
|
import type { IRNode } from '../../ir/nodes.js';
|
|
15
|
-
import { type NestedHook } from './build-ir.js';
|
|
15
|
+
import { type NestedEmitOptions, type NestedHook } from './build-ir.js';
|
|
16
16
|
/**
|
|
17
17
|
* Build IR from an `` html`...` `` / `` svg`...` `` TaggedTemplateExpression.
|
|
18
18
|
* `used` collects runtime symbols emitted for anything nested inside a hole.
|
|
@@ -20,5 +20,6 @@ import { type NestedHook } from './build-ir.js';
|
|
|
20
20
|
export declare function lowerHtmlTemplate(node: t.TaggedTemplateExpression, used: Set<string>, opts?: {
|
|
21
21
|
svg?: boolean;
|
|
22
22
|
onNested?: NestedHook;
|
|
23
|
+
emit?: NestedEmitOptions;
|
|
23
24
|
}): IRNode;
|
|
24
25
|
//# sourceMappingURL=lower-template.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lower-template.d.ts","sourceRoot":"","sources":["../../../src/frontend/babel/lower-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAA2B,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"lower-template.d.ts","sourceRoot":"","sources":["../../../src/frontend/babel/lower-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,KAAK,IAAI,CAAC,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAA2B,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAgCjG;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,CAAC,CAAC,wBAAwB,EAChC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EACjB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;IAAC,IAAI,CAAC,EAAE,iBAAiB,CAAA;CAAO,GAC5E,MAAM,CAIR"}
|
|
@@ -19,9 +19,9 @@ function gen(node) {
|
|
|
19
19
|
return generate(node, { concise: true }).code;
|
|
20
20
|
}
|
|
21
21
|
/** Describe one `${...}` for the lowering. */
|
|
22
|
-
function bindHole(expr, used, onNested) {
|
|
22
|
+
function bindHole(expr, used, onNested, emit) {
|
|
23
23
|
const hole = {
|
|
24
|
-
code: genExpr(expr, used, onNested),
|
|
24
|
+
code: genExpr(expr, used, onNested, emit),
|
|
25
25
|
reactive: isReactiveExpr(expr),
|
|
26
26
|
};
|
|
27
27
|
// `${item => <li>${item}</li>}`: `each` lowers this into For's render
|
|
@@ -31,7 +31,7 @@ function bindHole(expr, used, onNested) {
|
|
|
31
31
|
const body = expr.body;
|
|
32
32
|
hole.arrow = {
|
|
33
33
|
params: expr.params.map((p) => gen(p)),
|
|
34
|
-
body: genExpr(body, used, onNested),
|
|
34
|
+
body: genExpr(body, used, onNested, emit),
|
|
35
35
|
bodyReactive: isReactiveExpr(body),
|
|
36
36
|
};
|
|
37
37
|
}
|
|
@@ -46,7 +46,7 @@ function bindHole(expr, used, onNested) {
|
|
|
46
46
|
*/
|
|
47
47
|
export function lowerHtmlTemplate(node, used, opts = {}) {
|
|
48
48
|
const quasi = node.quasi;
|
|
49
|
-
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
|
|
49
|
+
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested, opts.emit));
|
|
50
50
|
return lowerTemplate(buildPieces(quasi), holes, used, opts);
|
|
51
51
|
}
|
|
52
52
|
/** Turn a Babel TemplateLiteral into position-carrying parser pieces. */
|
|
@@ -162,10 +162,64 @@ function isHole(node) {
|
|
|
162
162
|
function isTemplateChild(node) {
|
|
163
163
|
return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
|
|
164
164
|
}
|
|
165
|
+
var PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
|
|
166
|
+
function disposition(prop) {
|
|
167
|
+
if (prop.kind === "spread") return null;
|
|
168
|
+
if (prop.name === "ref") return "ref";
|
|
169
|
+
if (prop.name === "use") return "use";
|
|
170
|
+
if (prop.kind === "event") return prop.event ? "event" : null;
|
|
171
|
+
if (prop.kind === "prop") return "property";
|
|
172
|
+
if (prop.kind !== "attr") return null;
|
|
173
|
+
const name = prop.name;
|
|
174
|
+
if (name.startsWith("on:")) return "nativeEvent";
|
|
175
|
+
if (name.startsWith("prop:")) return "property";
|
|
176
|
+
if (name.startsWith("attr:")) return "attr";
|
|
177
|
+
if (name.startsWith("bool:")) return "boolAttr";
|
|
178
|
+
if (name === "className" || name === "class") {
|
|
179
|
+
return isPlainLiteralAttr(prop) ? "serialize" : "class";
|
|
180
|
+
}
|
|
181
|
+
if (name === "classList") {
|
|
182
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
183
|
+
return prop.reactive ? "classListDynamic" : "classList";
|
|
184
|
+
}
|
|
185
|
+
if (name === "style") {
|
|
186
|
+
if (isPlainLiteralAttr(prop)) return "serialize";
|
|
187
|
+
return prop.reactive ? "styleDynamic" : "style";
|
|
188
|
+
}
|
|
189
|
+
if (name.includes(":")) return null;
|
|
190
|
+
if (PROPERTY_ONLY.has(name)) return null;
|
|
191
|
+
if (PROPERTY_BINDS.has(name)) return "property";
|
|
192
|
+
if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
|
|
193
|
+
return "attr";
|
|
194
|
+
}
|
|
195
|
+
function effectiveProps(el) {
|
|
196
|
+
const order = [];
|
|
197
|
+
const byName = /* @__PURE__ */ new Map();
|
|
198
|
+
for (const prop of el.props) {
|
|
199
|
+
if (!byName.has(prop.name)) order.push(prop.name);
|
|
200
|
+
byName.set(prop.name, prop);
|
|
201
|
+
}
|
|
202
|
+
return order.map((name) => byName.get(name));
|
|
203
|
+
}
|
|
204
|
+
function propPlan(el) {
|
|
205
|
+
const custom = el.tag.includes("-");
|
|
206
|
+
const plan = { serialize: [], bindings: [] };
|
|
207
|
+
for (const prop of effectiveProps(el)) {
|
|
208
|
+
const kind = disposition(prop);
|
|
209
|
+
if (kind === null) return null;
|
|
210
|
+
if (kind === "serialize") {
|
|
211
|
+
plan.serialize.push(prop);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (custom) return null;
|
|
215
|
+
plan.bindings.push({ prop, kind });
|
|
216
|
+
}
|
|
217
|
+
return plan;
|
|
218
|
+
}
|
|
165
219
|
function isTemplateElement(node) {
|
|
166
220
|
if (node.svg) return false;
|
|
167
221
|
if (isRawTextElement(node.tag)) return false;
|
|
168
|
-
if (!node
|
|
222
|
+
if (!propPlan(node)) return false;
|
|
169
223
|
return node.children.every((c) => isTemplateChild(c) || isHole(c));
|
|
170
224
|
}
|
|
171
225
|
function containsHole(node) {
|
|
@@ -173,6 +227,15 @@ function containsHole(node) {
|
|
|
173
227
|
const children = node.children;
|
|
174
228
|
return children ? children.some(containsHole) : false;
|
|
175
229
|
}
|
|
230
|
+
function containsBinding(node) {
|
|
231
|
+
if (node.kind !== "element") return false;
|
|
232
|
+
const plan = propPlan(node);
|
|
233
|
+
if (plan && plan.bindings.length > 0) return true;
|
|
234
|
+
return node.children.some(containsBinding);
|
|
235
|
+
}
|
|
236
|
+
function interesting(node) {
|
|
237
|
+
return containsHole(node) || containsBinding(node);
|
|
238
|
+
}
|
|
176
239
|
function addressable(el) {
|
|
177
240
|
for (let i = 0; i < el.children.length; i++) {
|
|
178
241
|
const child = el.children[i];
|
|
@@ -186,9 +249,10 @@ function addressable(el) {
|
|
|
186
249
|
function planPartialTemplate(root) {
|
|
187
250
|
if (!isTemplateElement(root)) return null;
|
|
188
251
|
if (!addressable(root)) return null;
|
|
189
|
-
if (!root
|
|
252
|
+
if (!interesting(root)) return null;
|
|
190
253
|
const steps = [];
|
|
191
254
|
const holes = [];
|
|
255
|
+
const bindings = [];
|
|
192
256
|
let refCount = 0;
|
|
193
257
|
const declare = (expr) => {
|
|
194
258
|
const ref = `_n$${refCount++}`;
|
|
@@ -196,11 +260,17 @@ function planPartialTemplate(root) {
|
|
|
196
260
|
return ref;
|
|
197
261
|
};
|
|
198
262
|
walk(root, "_el$");
|
|
199
|
-
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
|
|
263
|
+
return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
|
|
200
264
|
function walk(el, elementRef) {
|
|
265
|
+
const plan = propPlan(el);
|
|
266
|
+
if (plan) {
|
|
267
|
+
for (const binding of plan.bindings) {
|
|
268
|
+
bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
201
271
|
let last = -1;
|
|
202
272
|
el.children.forEach((child, i) => {
|
|
203
|
-
if (
|
|
273
|
+
if (interesting(child)) last = i;
|
|
204
274
|
});
|
|
205
275
|
let previous = null;
|
|
206
276
|
for (let i = 0; i <= last; i++) {
|
|
@@ -214,19 +284,21 @@ function planPartialTemplate(root) {
|
|
|
214
284
|
continue;
|
|
215
285
|
}
|
|
216
286
|
const ref = declare(expr);
|
|
217
|
-
if (child.kind === "element" &&
|
|
287
|
+
if (child.kind === "element" && interesting(child)) walk(child, ref);
|
|
218
288
|
previous = ref;
|
|
219
289
|
}
|
|
220
290
|
}
|
|
221
291
|
}
|
|
222
292
|
function serializeWithMarkers(el) {
|
|
223
|
-
return serializeStatic(
|
|
293
|
+
return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
|
|
224
294
|
}
|
|
225
|
-
function
|
|
295
|
+
function templateTree(el) {
|
|
296
|
+
const plan = propPlan(el);
|
|
226
297
|
return {
|
|
227
298
|
...el,
|
|
299
|
+
props: plan ? plan.serialize : [],
|
|
228
300
|
children: el.children.map(
|
|
229
|
-
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ?
|
|
301
|
+
(child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
|
|
230
302
|
)
|
|
231
303
|
};
|
|
232
304
|
}
|
|
@@ -499,6 +571,55 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
499
571
|
const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
|
|
500
572
|
return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
|
|
501
573
|
}
|
|
574
|
+
function bindingTarget(binding) {
|
|
575
|
+
const name = binding.prop.name;
|
|
576
|
+
if (binding.kind === "boolAttr") return name.slice("bool:".length);
|
|
577
|
+
if (name.startsWith("prop:")) return name.slice("prop:".length);
|
|
578
|
+
if (name.startsWith("attr:")) return name.slice("attr:".length);
|
|
579
|
+
return htmlAttrName(name);
|
|
580
|
+
}
|
|
581
|
+
function bindingStatement(binding, used) {
|
|
582
|
+
const { ref, prop, kind } = binding;
|
|
583
|
+
const value = propValue(prop);
|
|
584
|
+
switch (kind) {
|
|
585
|
+
case "class":
|
|
586
|
+
used.add("setClassName");
|
|
587
|
+
return `setClassName(${ref}, ${value});`;
|
|
588
|
+
case "classList":
|
|
589
|
+
used.add("setClassList");
|
|
590
|
+
return `setClassList(${ref}, ${value}, []);`;
|
|
591
|
+
case "classListDynamic":
|
|
592
|
+
used.add("setDynamicClassList");
|
|
593
|
+
return `setDynamicClassList(${ref}, ${value});`;
|
|
594
|
+
case "style":
|
|
595
|
+
used.add("setStyle");
|
|
596
|
+
return `setStyle(${ref}, ${value}, []);`;
|
|
597
|
+
case "styleDynamic":
|
|
598
|
+
used.add("setDynamicStyle");
|
|
599
|
+
return `setDynamicStyle(${ref}, ${value});`;
|
|
600
|
+
case "attr":
|
|
601
|
+
used.add("setDynamicAttribute");
|
|
602
|
+
return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
603
|
+
case "property":
|
|
604
|
+
used.add("setDynamicProperty");
|
|
605
|
+
return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
606
|
+
case "boolAttr":
|
|
607
|
+
used.add("setDynamicBoolAttribute");
|
|
608
|
+
return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
|
|
609
|
+
case "event":
|
|
610
|
+
used.add("addDelegatedEventListener");
|
|
611
|
+
return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
|
|
612
|
+
case "nativeEvent":
|
|
613
|
+
used.add("addNativeEventListener");
|
|
614
|
+
return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
|
|
615
|
+
case "ref":
|
|
616
|
+
used.add("handleRef");
|
|
617
|
+
return `handleRef(${value}, ${ref});`;
|
|
618
|
+
case "use":
|
|
619
|
+
used.add("applyUse");
|
|
620
|
+
return `applyUse(${ref}, ${value});`;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
502
623
|
function staticAttrAssignment(el, prop) {
|
|
503
624
|
const name = htmlAttrName(prop.name);
|
|
504
625
|
const value = prop.literal;
|
|
@@ -549,6 +670,7 @@ function emitNode(node, used, templates) {
|
|
|
549
670
|
`const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
|
|
550
671
|
];
|
|
551
672
|
for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
|
|
673
|
+
for (const binding of plan.bindings) lines.push(bindingStatement(binding, used));
|
|
552
674
|
for (const hole of plan.holes) {
|
|
553
675
|
lines.push(
|
|
554
676
|
`insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`
|
|
@@ -1814,7 +1936,7 @@ var generate = _generate.default ?? _generate;
|
|
|
1814
1936
|
function gen(node) {
|
|
1815
1937
|
return generate(node, { concise: true }).code;
|
|
1816
1938
|
}
|
|
1817
|
-
function transformNestedJSX(node, used, onNested) {
|
|
1939
|
+
function transformNestedJSX(node, used, onNested, emit) {
|
|
1818
1940
|
const clone = t.cloneNode(node, true);
|
|
1819
1941
|
const file = t.file(t.program([t.expressionStatement(clone)]));
|
|
1820
1942
|
let changed = false;
|
|
@@ -1827,9 +1949,12 @@ function transformNestedJSX(node, used, onNested) {
|
|
|
1827
1949
|
traverse(file, {
|
|
1828
1950
|
"JSXElement|JSXFragment"(path) {
|
|
1829
1951
|
const jsx = path.node;
|
|
1830
|
-
const nestedIR = buildIR(jsx, used, onNested);
|
|
1952
|
+
const nestedIR = buildIR(jsx, used, onNested, emit);
|
|
1831
1953
|
onNested?.(nestedIR);
|
|
1832
|
-
const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
|
|
1954
|
+
const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
|
|
1955
|
+
templateClone: emit?.templateClone,
|
|
1956
|
+
partialTemplates: emit?.partialTemplates ?? true
|
|
1957
|
+
});
|
|
1833
1958
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
1834
1959
|
},
|
|
1835
1960
|
// Nested html templates compose: ${cond ? html`...` : html`...`},
|
|
@@ -1837,26 +1962,29 @@ function transformNestedJSX(node, used, onNested) {
|
|
|
1837
1962
|
TaggedTemplateExpression(path) {
|
|
1838
1963
|
const tag = path.node.tag;
|
|
1839
1964
|
if (!t.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
|
|
1840
|
-
const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
|
|
1965
|
+
const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested, emit });
|
|
1841
1966
|
onNested?.(ir);
|
|
1842
|
-
const { code, imports, templates } = imperativeBackend.emit(ir, {
|
|
1967
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {
|
|
1968
|
+
templateClone: emit?.templateClone,
|
|
1969
|
+
partialTemplates: emit?.partialTemplates ?? true
|
|
1970
|
+
});
|
|
1843
1971
|
emitInto(path, inlineTemplates(code, templates), imports);
|
|
1844
1972
|
}
|
|
1845
1973
|
});
|
|
1846
1974
|
return changed ? file.program.body[0].expression : node;
|
|
1847
1975
|
}
|
|
1848
|
-
function genExpr(node, used, onNested) {
|
|
1849
|
-
return gen(transformNestedJSX(node, used, onNested));
|
|
1976
|
+
function genExpr(node, used, onNested, emit) {
|
|
1977
|
+
return gen(transformNestedJSX(node, used, onNested, emit));
|
|
1850
1978
|
}
|
|
1851
1979
|
function isReactiveExpr(node) {
|
|
1852
1980
|
return isReactiveShape(shapeOf(node));
|
|
1853
1981
|
}
|
|
1854
|
-
function buildIR(node, used = /* @__PURE__ */ new Set(), onNested) {
|
|
1982
|
+
function buildIR(node, used = /* @__PURE__ */ new Set(), onNested, emit) {
|
|
1855
1983
|
const ir = buildJsxIR(node, {
|
|
1856
1984
|
code: (n) => (
|
|
1857
1985
|
// A member-expression tag (`<ns.Foo/>`) is not an Expression, so it cannot go
|
|
1858
1986
|
// through the nested-JSX compile: it only ever needs printing.
|
|
1859
|
-
n.type === "JSXMemberExpression" ? gen(n) : genExpr(n, used, onNested)
|
|
1987
|
+
n.type === "JSXMemberExpression" ? gen(n) : genExpr(n, used, onNested, emit)
|
|
1860
1988
|
),
|
|
1861
1989
|
used
|
|
1862
1990
|
});
|
|
@@ -1868,16 +1996,16 @@ var generate2 = _generate2.default ?? _generate2;
|
|
|
1868
1996
|
function gen2(node) {
|
|
1869
1997
|
return generate2(node, { concise: true }).code;
|
|
1870
1998
|
}
|
|
1871
|
-
function bindHole(expr, used, onNested) {
|
|
1999
|
+
function bindHole(expr, used, onNested, emit) {
|
|
1872
2000
|
const hole = {
|
|
1873
|
-
code: genExpr(expr, used, onNested),
|
|
2001
|
+
code: genExpr(expr, used, onNested, emit),
|
|
1874
2002
|
reactive: isReactiveExpr(expr)
|
|
1875
2003
|
};
|
|
1876
2004
|
if (t2.isArrowFunctionExpression(expr) && !t2.isBlockStatement(expr.body)) {
|
|
1877
2005
|
const body = expr.body;
|
|
1878
2006
|
hole.arrow = {
|
|
1879
2007
|
params: expr.params.map((p) => gen2(p)),
|
|
1880
|
-
body: genExpr(body, used, onNested),
|
|
2008
|
+
body: genExpr(body, used, onNested, emit),
|
|
1881
2009
|
bodyReactive: isReactiveExpr(body)
|
|
1882
2010
|
};
|
|
1883
2011
|
}
|
|
@@ -1886,7 +2014,7 @@ function bindHole(expr, used, onNested) {
|
|
|
1886
2014
|
}
|
|
1887
2015
|
function lowerHtmlTemplate(node, used, opts = {}) {
|
|
1888
2016
|
const quasi = node.quasi;
|
|
1889
|
-
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
|
|
2017
|
+
const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested, opts.emit));
|
|
1890
2018
|
return lowerTemplate(buildPieces(quasi), holes, used, opts);
|
|
1891
2019
|
}
|
|
1892
2020
|
function buildPieces(quasi) {
|