@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
@@ -363,10 +363,64 @@ function isHole(node) {
363
363
  function isTemplateChild(node) {
364
364
  return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
365
365
  }
366
+ var PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
367
+ function disposition(prop) {
368
+ if (prop.kind === "spread") return null;
369
+ if (prop.name === "ref") return "ref";
370
+ if (prop.name === "use") return "use";
371
+ if (prop.kind === "event") return prop.event ? "event" : null;
372
+ if (prop.kind === "prop") return "property";
373
+ if (prop.kind !== "attr") return null;
374
+ const name = prop.name;
375
+ if (name.startsWith("on:")) return "nativeEvent";
376
+ if (name.startsWith("prop:")) return "property";
377
+ if (name.startsWith("attr:")) return "attr";
378
+ if (name.startsWith("bool:")) return "boolAttr";
379
+ if (name === "className" || name === "class") {
380
+ return isPlainLiteralAttr(prop) ? "serialize" : "class";
381
+ }
382
+ if (name === "classList") {
383
+ if (isPlainLiteralAttr(prop)) return "serialize";
384
+ return prop.reactive ? "classListDynamic" : "classList";
385
+ }
386
+ if (name === "style") {
387
+ if (isPlainLiteralAttr(prop)) return "serialize";
388
+ return prop.reactive ? "styleDynamic" : "style";
389
+ }
390
+ if (name.includes(":")) return null;
391
+ if (PROPERTY_ONLY.has(name)) return null;
392
+ if (PROPERTY_BINDS.has(name)) return "property";
393
+ if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
394
+ return "attr";
395
+ }
396
+ function effectiveProps(el) {
397
+ const order = [];
398
+ const byName = /* @__PURE__ */ new Map();
399
+ for (const prop of el.props) {
400
+ if (!byName.has(prop.name)) order.push(prop.name);
401
+ byName.set(prop.name, prop);
402
+ }
403
+ return order.map((name) => byName.get(name));
404
+ }
405
+ function propPlan(el) {
406
+ const custom = el.tag.includes("-");
407
+ const plan = { serialize: [], bindings: [] };
408
+ for (const prop of effectiveProps(el)) {
409
+ const kind = disposition(prop);
410
+ if (kind === null) return null;
411
+ if (kind === "serialize") {
412
+ plan.serialize.push(prop);
413
+ continue;
414
+ }
415
+ if (custom) return null;
416
+ plan.bindings.push({ prop, kind });
417
+ }
418
+ return plan;
419
+ }
366
420
  function isTemplateElement(node) {
367
421
  if (node.svg) return false;
368
422
  if (isRawTextElement(node.tag)) return false;
369
- if (!node.props.every((p) => isPlainLiteralAttr(p))) return false;
423
+ if (!propPlan(node)) return false;
370
424
  return node.children.every((c) => isTemplateChild(c) || isHole(c));
371
425
  }
372
426
  function containsHole(node) {
@@ -374,6 +428,15 @@ function containsHole(node) {
374
428
  const children = node.children;
375
429
  return children ? children.some(containsHole) : false;
376
430
  }
431
+ function containsBinding(node) {
432
+ if (node.kind !== "element") return false;
433
+ const plan = propPlan(node);
434
+ if (plan && plan.bindings.length > 0) return true;
435
+ return node.children.some(containsBinding);
436
+ }
437
+ function interesting(node) {
438
+ return containsHole(node) || containsBinding(node);
439
+ }
377
440
  function addressable(el) {
378
441
  for (let i = 0; i < el.children.length; i++) {
379
442
  const child = el.children[i];
@@ -387,9 +450,10 @@ function addressable(el) {
387
450
  function planPartialTemplate(root) {
388
451
  if (!isTemplateElement(root)) return null;
389
452
  if (!addressable(root)) return null;
390
- if (!root.children.some(containsHole)) return null;
453
+ if (!interesting(root)) return null;
391
454
  const steps = [];
392
455
  const holes = [];
456
+ const bindings = [];
393
457
  let refCount = 0;
394
458
  const declare2 = (expr) => {
395
459
  const ref = `_n$${refCount++}`;
@@ -397,11 +461,17 @@ function planPartialTemplate(root) {
397
461
  return ref;
398
462
  };
399
463
  walk2(root, "_el$");
400
- return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
464
+ return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
401
465
  function walk2(el, elementRef) {
466
+ const plan = propPlan(el);
467
+ if (plan) {
468
+ for (const binding of plan.bindings) {
469
+ bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
470
+ }
471
+ }
402
472
  let last = -1;
403
473
  el.children.forEach((child, i) => {
404
- if (containsHole(child)) last = i;
474
+ if (interesting(child)) last = i;
405
475
  });
406
476
  let previous = null;
407
477
  for (let i = 0; i <= last; i++) {
@@ -415,19 +485,21 @@ function planPartialTemplate(root) {
415
485
  continue;
416
486
  }
417
487
  const ref = declare2(expr);
418
- if (child.kind === "element" && containsHole(child)) walk2(child, ref);
488
+ if (child.kind === "element" && interesting(child)) walk2(child, ref);
419
489
  previous = ref;
420
490
  }
421
491
  }
422
492
  }
423
493
  function serializeWithMarkers(el) {
424
- return serializeStatic(withSentinels(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
494
+ return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
425
495
  }
426
- function withSentinels(el) {
496
+ function templateTree(el) {
497
+ const plan = propPlan(el);
427
498
  return {
428
499
  ...el,
500
+ props: plan ? plan.serialize : [],
429
501
  children: el.children.map(
430
- (child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? withSentinels(child) : child
502
+ (child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
431
503
  )
432
504
  };
433
505
  }
@@ -700,6 +772,55 @@ function emitComponent(name, props, children, used, templates, at) {
700
772
  const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
701
773
  return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
702
774
  }
775
+ function bindingTarget(binding) {
776
+ const name = binding.prop.name;
777
+ if (binding.kind === "boolAttr") return name.slice("bool:".length);
778
+ if (name.startsWith("prop:")) return name.slice("prop:".length);
779
+ if (name.startsWith("attr:")) return name.slice("attr:".length);
780
+ return htmlAttrName(name);
781
+ }
782
+ function bindingStatement(binding, used) {
783
+ const { ref, prop, kind } = binding;
784
+ const value = propValue(prop);
785
+ switch (kind) {
786
+ case "class":
787
+ used.add("setClassName");
788
+ return `setClassName(${ref}, ${value});`;
789
+ case "classList":
790
+ used.add("setClassList");
791
+ return `setClassList(${ref}, ${value}, []);`;
792
+ case "classListDynamic":
793
+ used.add("setDynamicClassList");
794
+ return `setDynamicClassList(${ref}, ${value});`;
795
+ case "style":
796
+ used.add("setStyle");
797
+ return `setStyle(${ref}, ${value}, []);`;
798
+ case "styleDynamic":
799
+ used.add("setDynamicStyle");
800
+ return `setDynamicStyle(${ref}, ${value});`;
801
+ case "attr":
802
+ used.add("setDynamicAttribute");
803
+ return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
804
+ case "property":
805
+ used.add("setDynamicProperty");
806
+ return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
807
+ case "boolAttr":
808
+ used.add("setDynamicBoolAttribute");
809
+ return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
810
+ case "event":
811
+ used.add("addDelegatedEventListener");
812
+ return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
813
+ case "nativeEvent":
814
+ used.add("addNativeEventListener");
815
+ return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
816
+ case "ref":
817
+ used.add("handleRef");
818
+ return `handleRef(${value}, ${ref});`;
819
+ case "use":
820
+ used.add("applyUse");
821
+ return `applyUse(${ref}, ${value});`;
822
+ }
823
+ }
703
824
  function staticAttrAssignment(el, prop) {
704
825
  const name = htmlAttrName(prop.name);
705
826
  const value = prop.literal;
@@ -750,6 +871,7 @@ function emitNode(node, used, templates) {
750
871
  `const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
751
872
  ];
752
873
  for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
874
+ for (const binding of plan.bindings) lines.push(bindingStatement(binding, used));
753
875
  for (const hole of plan.holes) {
754
876
  lines.push(
755
877
  `insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`
@@ -1551,16 +1673,16 @@ var generate = import_generator.default.default ?? import_generator.default;
1551
1673
  function gen(node) {
1552
1674
  return generate(node, { concise: true }).code;
1553
1675
  }
1554
- function bindHole(expr, used, onNested) {
1676
+ function bindHole(expr, used, onNested, emit) {
1555
1677
  const hole = {
1556
- code: genExpr(expr, used, onNested),
1678
+ code: genExpr(expr, used, onNested, emit),
1557
1679
  reactive: isReactiveExpr(expr)
1558
1680
  };
1559
1681
  if (import_core.types.isArrowFunctionExpression(expr) && !import_core.types.isBlockStatement(expr.body)) {
1560
1682
  const body = expr.body;
1561
1683
  hole.arrow = {
1562
1684
  params: expr.params.map((p) => gen(p)),
1563
- body: genExpr(body, used, onNested),
1685
+ body: genExpr(body, used, onNested, emit),
1564
1686
  bodyReactive: isReactiveExpr(body)
1565
1687
  };
1566
1688
  }
@@ -1569,7 +1691,7 @@ function bindHole(expr, used, onNested) {
1569
1691
  }
1570
1692
  function lowerHtmlTemplate(node, used, opts = {}) {
1571
1693
  const quasi = node.quasi;
1572
- const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
1694
+ const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested, opts.emit));
1573
1695
  return lowerTemplate(buildPieces(quasi), holes, used, opts);
1574
1696
  }
1575
1697
  function buildPieces(quasi) {
@@ -2057,7 +2179,7 @@ var generate2 = import_generator2.default.default ?? import_generator2.default;
2057
2179
  function gen2(node) {
2058
2180
  return generate2(node, { concise: true }).code;
2059
2181
  }
2060
- function transformNestedJSX(node, used, onNested) {
2182
+ function transformNestedJSX(node, used, onNested, emit) {
2061
2183
  const clone = import_core2.types.cloneNode(node, true);
2062
2184
  const file = import_core2.types.file(import_core2.types.program([import_core2.types.expressionStatement(clone)]));
2063
2185
  let changed = false;
@@ -2070,9 +2192,12 @@ function transformNestedJSX(node, used, onNested) {
2070
2192
  (0, import_core2.traverse)(file, {
2071
2193
  "JSXElement|JSXFragment"(path) {
2072
2194
  const jsx = path.node;
2073
- const nestedIR = buildIR(jsx, used, onNested);
2195
+ const nestedIR = buildIR(jsx, used, onNested, emit);
2074
2196
  onNested?.(nestedIR);
2075
- const { code, imports, templates } = imperativeBackend.emit(nestedIR, {});
2197
+ const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
2198
+ templateClone: emit?.templateClone,
2199
+ partialTemplates: emit?.partialTemplates ?? true
2200
+ });
2076
2201
  emitInto(path, inlineTemplates(code, templates), imports);
2077
2202
  },
2078
2203
  // Nested html templates compose: ${cond ? html`...` : html`...`},
@@ -2080,26 +2205,29 @@ function transformNestedJSX(node, used, onNested) {
2080
2205
  TaggedTemplateExpression(path) {
2081
2206
  const tag = path.node.tag;
2082
2207
  if (!import_core2.types.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
2083
- const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
2208
+ const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested, emit });
2084
2209
  onNested?.(ir);
2085
- const { code, imports, templates } = imperativeBackend.emit(ir, {});
2210
+ const { code, imports, templates } = imperativeBackend.emit(ir, {
2211
+ templateClone: emit?.templateClone,
2212
+ partialTemplates: emit?.partialTemplates ?? true
2213
+ });
2086
2214
  emitInto(path, inlineTemplates(code, templates), imports);
2087
2215
  }
2088
2216
  });
2089
2217
  return changed ? file.program.body[0].expression : node;
2090
2218
  }
2091
- function genExpr(node, used, onNested) {
2092
- return gen2(transformNestedJSX(node, used, onNested));
2219
+ function genExpr(node, used, onNested, emit) {
2220
+ return gen2(transformNestedJSX(node, used, onNested, emit));
2093
2221
  }
2094
2222
  function isReactiveExpr(node) {
2095
2223
  return isReactiveShape(shapeOf(node));
2096
2224
  }
2097
- function buildIR(node, used = /* @__PURE__ */ new Set(), onNested) {
2225
+ function buildIR(node, used = /* @__PURE__ */ new Set(), onNested, emit) {
2098
2226
  const ir = buildJsxIR(node, {
2099
2227
  code: (n) => (
2100
2228
  // A member-expression tag (`<ns.Foo/>`) is not an Expression, so it cannot go
2101
2229
  // through the nested-JSX compile: it only ever needs printing.
2102
- n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested)
2230
+ n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested, emit)
2103
2231
  ),
2104
2232
  used
2105
2233
  });
@@ -2434,7 +2562,10 @@ function adoptTemplates(code, templates, state) {
2434
2562
  }
2435
2563
  function emitViaIR(node, state) {
2436
2564
  const used = /* @__PURE__ */ new Set();
2437
- const ir = buildIR(node, used, nestedHook(state));
2565
+ const ir = buildIR(node, used, nestedHook(state), {
2566
+ partialTemplates: state.partialTemplates,
2567
+ templateClone: state.templateClone
2568
+ });
2438
2569
  resolveComponentSources(ir, {
2439
2570
  resolver: state.componentResolver,
2440
2571
  modules: {
@@ -2483,7 +2614,11 @@ function dedupeImports(imports) {
2483
2614
  function emitHtmlViaIR(node, state) {
2484
2615
  const used = /* @__PURE__ */ new Set();
2485
2616
  const svg = import_core3.types.isIdentifier(node.tag) && node.tag.name === "svg";
2486
- const ir = buildIRFromHtml(node, used, { svg, onNested: nestedHook(state) });
2617
+ const ir = buildIRFromHtml(node, used, {
2618
+ svg,
2619
+ onNested: nestedHook(state),
2620
+ emit: { partialTemplates: state.partialTemplates, templateClone: state.templateClone }
2621
+ });
2487
2622
  resolveComponentSources(ir, {
2488
2623
  resolver: state.componentResolver,
2489
2624
  modules: {
@@ -323,7 +323,10 @@ function emitViaIR(node, state) {
323
323
  // `used` collects runtime symbols emitted for JSX nested inside expressions
324
324
  // (render-props, ternaries, fallback={<p/>}, ...) so we import them too.
325
325
  const used = new Set();
326
- const ir = buildIR(node, used, nestedHook(state));
326
+ const ir = buildIR(node, used, nestedHook(state), {
327
+ partialTemplates: state.partialTemplates,
328
+ templateClone: state.templateClone,
329
+ });
327
330
  // The same passes the html path runs. Without them `codegen: 'ir'` silently skipped
328
331
  // resolution entirely: a resolved tag compiled to a reference with no import.
329
332
  resolveComponentSources(ir, {
@@ -386,7 +389,11 @@ function dedupeImports(imports) {
386
389
  function emitHtmlViaIR(node, state) {
387
390
  const used = new Set();
388
391
  const svg = t.isIdentifier(node.tag) && node.tag.name === 'svg';
389
- const ir = buildIRFromHtml(node, used, { svg, onNested: nestedHook(state) });
392
+ const ir = buildIRFromHtml(node, used, {
393
+ svg,
394
+ onNested: nestedHook(state),
395
+ emit: { partialTemplates: state.partialTemplates, templateClone: state.templateClone },
396
+ });
390
397
  // Resolve pass: annotate each component reference with where it comes from, before
391
398
  // codegen. Bindings are not checked here: Program.exit knows the whole file's scope
392
399
  // and drops any injected import whose name turns out to be bound.
@@ -329,10 +329,64 @@ function isHole(node) {
329
329
  function isTemplateChild(node) {
330
330
  return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
331
331
  }
332
+ var PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
333
+ function disposition(prop) {
334
+ if (prop.kind === "spread") return null;
335
+ if (prop.name === "ref") return "ref";
336
+ if (prop.name === "use") return "use";
337
+ if (prop.kind === "event") return prop.event ? "event" : null;
338
+ if (prop.kind === "prop") return "property";
339
+ if (prop.kind !== "attr") return null;
340
+ const name = prop.name;
341
+ if (name.startsWith("on:")) return "nativeEvent";
342
+ if (name.startsWith("prop:")) return "property";
343
+ if (name.startsWith("attr:")) return "attr";
344
+ if (name.startsWith("bool:")) return "boolAttr";
345
+ if (name === "className" || name === "class") {
346
+ return isPlainLiteralAttr(prop) ? "serialize" : "class";
347
+ }
348
+ if (name === "classList") {
349
+ if (isPlainLiteralAttr(prop)) return "serialize";
350
+ return prop.reactive ? "classListDynamic" : "classList";
351
+ }
352
+ if (name === "style") {
353
+ if (isPlainLiteralAttr(prop)) return "serialize";
354
+ return prop.reactive ? "styleDynamic" : "style";
355
+ }
356
+ if (name.includes(":")) return null;
357
+ if (PROPERTY_ONLY.has(name)) return null;
358
+ if (PROPERTY_BINDS.has(name)) return "property";
359
+ if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
360
+ return "attr";
361
+ }
362
+ function effectiveProps(el) {
363
+ const order = [];
364
+ const byName = /* @__PURE__ */ new Map();
365
+ for (const prop of el.props) {
366
+ if (!byName.has(prop.name)) order.push(prop.name);
367
+ byName.set(prop.name, prop);
368
+ }
369
+ return order.map((name) => byName.get(name));
370
+ }
371
+ function propPlan(el) {
372
+ const custom = el.tag.includes("-");
373
+ const plan = { serialize: [], bindings: [] };
374
+ for (const prop of effectiveProps(el)) {
375
+ const kind = disposition(prop);
376
+ if (kind === null) return null;
377
+ if (kind === "serialize") {
378
+ plan.serialize.push(prop);
379
+ continue;
380
+ }
381
+ if (custom) return null;
382
+ plan.bindings.push({ prop, kind });
383
+ }
384
+ return plan;
385
+ }
332
386
  function isTemplateElement(node) {
333
387
  if (node.svg) return false;
334
388
  if (isRawTextElement(node.tag)) return false;
335
- if (!node.props.every((p) => isPlainLiteralAttr(p))) return false;
389
+ if (!propPlan(node)) return false;
336
390
  return node.children.every((c) => isTemplateChild(c) || isHole(c));
337
391
  }
338
392
  function containsHole(node) {
@@ -340,6 +394,15 @@ function containsHole(node) {
340
394
  const children = node.children;
341
395
  return children ? children.some(containsHole) : false;
342
396
  }
397
+ function containsBinding(node) {
398
+ if (node.kind !== "element") return false;
399
+ const plan = propPlan(node);
400
+ if (plan && plan.bindings.length > 0) return true;
401
+ return node.children.some(containsBinding);
402
+ }
403
+ function interesting(node) {
404
+ return containsHole(node) || containsBinding(node);
405
+ }
343
406
  function addressable(el) {
344
407
  for (let i = 0; i < el.children.length; i++) {
345
408
  const child = el.children[i];
@@ -353,9 +416,10 @@ function addressable(el) {
353
416
  function planPartialTemplate(root) {
354
417
  if (!isTemplateElement(root)) return null;
355
418
  if (!addressable(root)) return null;
356
- if (!root.children.some(containsHole)) return null;
419
+ if (!interesting(root)) return null;
357
420
  const steps = [];
358
421
  const holes = [];
422
+ const bindings = [];
359
423
  let refCount = 0;
360
424
  const declare2 = (expr) => {
361
425
  const ref = `_n$${refCount++}`;
@@ -363,11 +427,17 @@ function planPartialTemplate(root) {
363
427
  return ref;
364
428
  };
365
429
  walk2(root, "_el$");
366
- return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
430
+ return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
367
431
  function walk2(el, elementRef) {
432
+ const plan = propPlan(el);
433
+ if (plan) {
434
+ for (const binding of plan.bindings) {
435
+ bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
436
+ }
437
+ }
368
438
  let last = -1;
369
439
  el.children.forEach((child, i) => {
370
- if (containsHole(child)) last = i;
440
+ if (interesting(child)) last = i;
371
441
  });
372
442
  let previous = null;
373
443
  for (let i = 0; i <= last; i++) {
@@ -381,19 +451,21 @@ function planPartialTemplate(root) {
381
451
  continue;
382
452
  }
383
453
  const ref = declare2(expr);
384
- if (child.kind === "element" && containsHole(child)) walk2(child, ref);
454
+ if (child.kind === "element" && interesting(child)) walk2(child, ref);
385
455
  previous = ref;
386
456
  }
387
457
  }
388
458
  }
389
459
  function serializeWithMarkers(el) {
390
- return serializeStatic(withSentinels(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
460
+ return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
391
461
  }
392
- function withSentinels(el) {
462
+ function templateTree(el) {
463
+ const plan = propPlan(el);
393
464
  return {
394
465
  ...el,
466
+ props: plan ? plan.serialize : [],
395
467
  children: el.children.map(
396
- (child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? withSentinels(child) : child
468
+ (child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
397
469
  )
398
470
  };
399
471
  }
@@ -666,6 +738,55 @@ function emitComponent(name, props, children, used, templates, at) {
666
738
  const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
667
739
  return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
668
740
  }
741
+ function bindingTarget(binding) {
742
+ const name = binding.prop.name;
743
+ if (binding.kind === "boolAttr") return name.slice("bool:".length);
744
+ if (name.startsWith("prop:")) return name.slice("prop:".length);
745
+ if (name.startsWith("attr:")) return name.slice("attr:".length);
746
+ return htmlAttrName(name);
747
+ }
748
+ function bindingStatement(binding, used) {
749
+ const { ref, prop, kind } = binding;
750
+ const value = propValue(prop);
751
+ switch (kind) {
752
+ case "class":
753
+ used.add("setClassName");
754
+ return `setClassName(${ref}, ${value});`;
755
+ case "classList":
756
+ used.add("setClassList");
757
+ return `setClassList(${ref}, ${value}, []);`;
758
+ case "classListDynamic":
759
+ used.add("setDynamicClassList");
760
+ return `setDynamicClassList(${ref}, ${value});`;
761
+ case "style":
762
+ used.add("setStyle");
763
+ return `setStyle(${ref}, ${value}, []);`;
764
+ case "styleDynamic":
765
+ used.add("setDynamicStyle");
766
+ return `setDynamicStyle(${ref}, ${value});`;
767
+ case "attr":
768
+ used.add("setDynamicAttribute");
769
+ return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
770
+ case "property":
771
+ used.add("setDynamicProperty");
772
+ return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
773
+ case "boolAttr":
774
+ used.add("setDynamicBoolAttribute");
775
+ return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
776
+ case "event":
777
+ used.add("addDelegatedEventListener");
778
+ return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
779
+ case "nativeEvent":
780
+ used.add("addNativeEventListener");
781
+ return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
782
+ case "ref":
783
+ used.add("handleRef");
784
+ return `handleRef(${value}, ${ref});`;
785
+ case "use":
786
+ used.add("applyUse");
787
+ return `applyUse(${ref}, ${value});`;
788
+ }
789
+ }
669
790
  function staticAttrAssignment(el, prop) {
670
791
  const name = htmlAttrName(prop.name);
671
792
  const value = prop.literal;
@@ -716,6 +837,7 @@ function emitNode(node, used, templates) {
716
837
  `const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
717
838
  ];
718
839
  for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
840
+ for (const binding of plan.bindings) lines.push(bindingStatement(binding, used));
719
841
  for (const hole of plan.holes) {
720
842
  lines.push(
721
843
  `insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`
@@ -1519,16 +1641,16 @@ var generate = _generate.default ?? _generate;
1519
1641
  function gen(node) {
1520
1642
  return generate(node, { concise: true }).code;
1521
1643
  }
1522
- function bindHole(expr, used, onNested) {
1644
+ function bindHole(expr, used, onNested, emit) {
1523
1645
  const hole = {
1524
- code: genExpr(expr, used, onNested),
1646
+ code: genExpr(expr, used, onNested, emit),
1525
1647
  reactive: isReactiveExpr(expr)
1526
1648
  };
1527
1649
  if (t.isArrowFunctionExpression(expr) && !t.isBlockStatement(expr.body)) {
1528
1650
  const body = expr.body;
1529
1651
  hole.arrow = {
1530
1652
  params: expr.params.map((p) => gen(p)),
1531
- body: genExpr(body, used, onNested),
1653
+ body: genExpr(body, used, onNested, emit),
1532
1654
  bodyReactive: isReactiveExpr(body)
1533
1655
  };
1534
1656
  }
@@ -1537,7 +1659,7 @@ function bindHole(expr, used, onNested) {
1537
1659
  }
1538
1660
  function lowerHtmlTemplate(node, used, opts = {}) {
1539
1661
  const quasi = node.quasi;
1540
- const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
1662
+ const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested, opts.emit));
1541
1663
  return lowerTemplate(buildPieces(quasi), holes, used, opts);
1542
1664
  }
1543
1665
  function buildPieces(quasi) {
@@ -2025,7 +2147,7 @@ var generate2 = _generate2.default ?? _generate2;
2025
2147
  function gen2(node) {
2026
2148
  return generate2(node, { concise: true }).code;
2027
2149
  }
2028
- function transformNestedJSX(node, used, onNested) {
2150
+ function transformNestedJSX(node, used, onNested, emit) {
2029
2151
  const clone = t2.cloneNode(node, true);
2030
2152
  const file = t2.file(t2.program([t2.expressionStatement(clone)]));
2031
2153
  let changed = false;
@@ -2038,9 +2160,12 @@ function transformNestedJSX(node, used, onNested) {
2038
2160
  traverse(file, {
2039
2161
  "JSXElement|JSXFragment"(path) {
2040
2162
  const jsx = path.node;
2041
- const nestedIR = buildIR(jsx, used, onNested);
2163
+ const nestedIR = buildIR(jsx, used, onNested, emit);
2042
2164
  onNested?.(nestedIR);
2043
- const { code, imports, templates } = imperativeBackend.emit(nestedIR, {});
2165
+ const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
2166
+ templateClone: emit?.templateClone,
2167
+ partialTemplates: emit?.partialTemplates ?? true
2168
+ });
2044
2169
  emitInto(path, inlineTemplates(code, templates), imports);
2045
2170
  },
2046
2171
  // Nested html templates compose: ${cond ? html`...` : html`...`},
@@ -2048,26 +2173,29 @@ function transformNestedJSX(node, used, onNested) {
2048
2173
  TaggedTemplateExpression(path) {
2049
2174
  const tag = path.node.tag;
2050
2175
  if (!t2.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
2051
- const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
2176
+ const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested, emit });
2052
2177
  onNested?.(ir);
2053
- const { code, imports, templates } = imperativeBackend.emit(ir, {});
2178
+ const { code, imports, templates } = imperativeBackend.emit(ir, {
2179
+ templateClone: emit?.templateClone,
2180
+ partialTemplates: emit?.partialTemplates ?? true
2181
+ });
2054
2182
  emitInto(path, inlineTemplates(code, templates), imports);
2055
2183
  }
2056
2184
  });
2057
2185
  return changed ? file.program.body[0].expression : node;
2058
2186
  }
2059
- function genExpr(node, used, onNested) {
2060
- return gen2(transformNestedJSX(node, used, onNested));
2187
+ function genExpr(node, used, onNested, emit) {
2188
+ return gen2(transformNestedJSX(node, used, onNested, emit));
2061
2189
  }
2062
2190
  function isReactiveExpr(node) {
2063
2191
  return isReactiveShape(shapeOf(node));
2064
2192
  }
2065
- function buildIR(node, used = /* @__PURE__ */ new Set(), onNested) {
2193
+ function buildIR(node, used = /* @__PURE__ */ new Set(), onNested, emit) {
2066
2194
  const ir = buildJsxIR(node, {
2067
2195
  code: (n) => (
2068
2196
  // A member-expression tag (`<ns.Foo/>`) is not an Expression, so it cannot go
2069
2197
  // through the nested-JSX compile: it only ever needs printing.
2070
- n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested)
2198
+ n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested, emit)
2071
2199
  ),
2072
2200
  used
2073
2201
  });
@@ -2402,7 +2530,10 @@ function adoptTemplates(code, templates, state) {
2402
2530
  }
2403
2531
  function emitViaIR(node, state) {
2404
2532
  const used = /* @__PURE__ */ new Set();
2405
- const ir = buildIR(node, used, nestedHook(state));
2533
+ const ir = buildIR(node, used, nestedHook(state), {
2534
+ partialTemplates: state.partialTemplates,
2535
+ templateClone: state.templateClone
2536
+ });
2406
2537
  resolveComponentSources(ir, {
2407
2538
  resolver: state.componentResolver,
2408
2539
  modules: {
@@ -2451,7 +2582,11 @@ function dedupeImports(imports) {
2451
2582
  function emitHtmlViaIR(node, state) {
2452
2583
  const used = /* @__PURE__ */ new Set();
2453
2584
  const svg = t3.isIdentifier(node.tag) && node.tag.name === "svg";
2454
- const ir = buildIRFromHtml(node, used, { svg, onNested: nestedHook(state) });
2585
+ const ir = buildIRFromHtml(node, used, {
2586
+ svg,
2587
+ onNested: nestedHook(state),
2588
+ emit: { partialTemplates: state.partialTemplates, templateClone: state.templateClone }
2589
+ });
2455
2590
  resolveComponentSources(ir, {
2456
2591
  resolver: state.componentResolver,
2457
2592
  modules: {