@fluixi/compiler 1.0.0-alpha.105 → 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.
Files changed (71) hide show
  1. package/dist/analyze/static-graph.cjs +1 -1
  2. package/dist/analyze/static-graph.mjs +1 -1
  3. package/dist/{babel-USPHNEJF.mjs → babel-HJY4RVAV.mjs} +33 -20
  4. package/dist/{chunk-OQYQ7UTD.mjs → chunk-4XEOMQ57.mjs} +1 -1
  5. package/dist/{chunk-EK4OMGTU.mjs → chunk-JUFI6IR3.mjs} +1 -1
  6. package/dist/{chunk-5GAYDAAC.mjs → chunk-LCQHL2VQ.mjs} +132 -10
  7. package/dist/{chunk-XBAQME2V.mjs → chunk-LGUZRHYN.mjs} +2 -2
  8. package/dist/{chunk-O6OBEV4V.mjs → chunk-TDN5MYWI.mjs} +1 -1
  9. package/dist/{chunk-EQIIWIEU.mjs → chunk-W7DYOMXJ.mjs} +2 -2
  10. package/dist/{chunk-FMIIK5UM.mjs → chunk-WHEDRMNR.mjs} +1 -1
  11. package/dist/codegen/backends/imperative.cjs +130 -8
  12. package/dist/codegen/backends/imperative.d.ts.map +1 -1
  13. package/dist/codegen/backends/imperative.js +62 -0
  14. package/dist/codegen/backends/imperative.mjs +130 -8
  15. package/dist/codegen/partial-template.cjs +80 -8
  16. package/dist/codegen/partial-template.d.cts +21 -4
  17. package/dist/codegen/partial-template.d.ts +21 -4
  18. package/dist/codegen/partial-template.d.ts.map +1 -1
  19. package/dist/codegen/partial-template.js +0 -0
  20. package/dist/codegen/partial-template.mjs +80 -8
  21. package/dist/codegen/serialize-static.cjs +1 -0
  22. package/dist/codegen/serialize-static.d.cts +8 -0
  23. package/dist/codegen/serialize-static.d.ts +8 -0
  24. package/dist/codegen/serialize-static.d.ts.map +1 -1
  25. package/dist/codegen/serialize-static.js +1 -1
  26. package/dist/codegen/serialize-static.mjs +1 -0
  27. package/dist/frontend/babel/build-ir-html.cjs +149 -21
  28. package/dist/frontend/babel/build-ir-html.d.cts +3 -1
  29. package/dist/frontend/babel/build-ir-html.d.ts +3 -1
  30. package/dist/frontend/babel/build-ir-html.d.ts.map +1 -1
  31. package/dist/frontend/babel/build-ir-html.mjs +149 -21
  32. package/dist/frontend/babel/build-ir.cjs +149 -21
  33. package/dist/frontend/babel/build-ir.d.cts +11 -2
  34. package/dist/frontend/babel/build-ir.d.ts +11 -2
  35. package/dist/frontend/babel/build-ir.d.ts.map +1 -1
  36. package/dist/frontend/babel/build-ir.js +15 -9
  37. package/dist/frontend/babel/build-ir.mjs +149 -21
  38. package/dist/frontend/babel/index.cjs +160 -25
  39. package/dist/frontend/babel/index.mjs +160 -25
  40. package/dist/frontend/babel/lower-template.cjs +149 -21
  41. package/dist/frontend/babel/lower-template.d.cts +2 -1
  42. package/dist/frontend/babel/lower-template.d.ts +2 -1
  43. package/dist/frontend/babel/lower-template.d.ts.map +1 -1
  44. package/dist/frontend/babel/lower-template.js +4 -4
  45. package/dist/frontend/babel/lower-template.mjs +149 -21
  46. package/dist/frontend/babel/plugin.cjs +158 -23
  47. package/dist/frontend/babel/plugin.js +9 -2
  48. package/dist/frontend/babel/plugin.mjs +158 -23
  49. package/dist/index.cjs +131 -9
  50. package/dist/index.mjs +131 -9
  51. package/dist/integrations.cjs +165 -33
  52. package/dist/integrations.mjs +11 -11
  53. package/dist/lower/compile-template.cjs +131 -9
  54. package/dist/lower/compile-template.mjs +131 -9
  55. package/dist/lower/jsx.cjs +130 -8
  56. package/dist/lower/jsx.mjs +130 -8
  57. package/dist/lower/template.cjs +130 -8
  58. package/dist/lower/template.mjs +130 -8
  59. package/dist/resolve/builtins.cjs +1 -1
  60. package/dist/resolve/builtins.mjs +1 -1
  61. package/dist/{server-fns-7Y25MR3B.mjs → server-fns-WZJK4BJE.mjs} +5 -5
  62. package/dist/{server-functions-43CFKXY3.mjs → server-functions-4KTFFEO3.mjs} +3 -3
  63. package/dist/transform/index.cjs +135 -16
  64. package/dist/transform/index.mjs +135 -16
  65. package/dist/transform/templates.cjs +134 -15
  66. package/dist/transform/templates.d.ts.map +1 -1
  67. package/dist/transform/templates.js +4 -8
  68. package/dist/transform/templates.mjs +134 -15
  69. package/dist/{transform-YUDW7K4J.mjs → transform-NO46NFQH.mjs} +11 -14
  70. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  71. 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.props.every((p) => isPlainLiteralAttr(p))) return false;
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.children.some(containsHole)) return null;
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 (containsHole(child)) last = i;
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" && containsHole(child)) walk(child, ref);
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(withSentinels(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
325
+ return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
256
326
  }
257
- function withSentinels(el) {
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" ? withSentinels(child) : child
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;AAgCzE;;;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,CAAA;CAAO,GAClD,MAAM,CAIR"}
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.props.every((p) => isPlainLiteralAttr(p))) return false;
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.children.some(containsHole)) return null;
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 (containsHole(child)) last = i;
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" && containsHole(child)) walk(child, ref);
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(withSentinels(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
293
+ return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
224
294
  }
225
- function withSentinels(el) {
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" ? withSentinels(child) : child
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) {