@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
@@ -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}));`
@@ -1807,7 +1929,7 @@ var generate = _generate.default ?? _generate;
1807
1929
  function gen(node) {
1808
1930
  return generate(node, { concise: true }).code;
1809
1931
  }
1810
- function transformNestedJSX(node, used, onNested) {
1932
+ function transformNestedJSX(node, used, onNested, emit) {
1811
1933
  const clone = t.cloneNode(node, true);
1812
1934
  const file = t.file(t.program([t.expressionStatement(clone)]));
1813
1935
  let changed = false;
@@ -1820,9 +1942,12 @@ function transformNestedJSX(node, used, onNested) {
1820
1942
  traverse(file, {
1821
1943
  "JSXElement|JSXFragment"(path) {
1822
1944
  const jsx = path.node;
1823
- const nestedIR = buildIR(jsx, used, onNested);
1945
+ const nestedIR = buildIR(jsx, used, onNested, emit);
1824
1946
  onNested?.(nestedIR);
1825
- const { code, imports, templates } = imperativeBackend.emit(nestedIR, {});
1947
+ const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
1948
+ templateClone: emit?.templateClone,
1949
+ partialTemplates: emit?.partialTemplates ?? true
1950
+ });
1826
1951
  emitInto(path, inlineTemplates(code, templates), imports);
1827
1952
  },
1828
1953
  // Nested html templates compose: ${cond ? html`...` : html`...`},
@@ -1830,26 +1955,29 @@ function transformNestedJSX(node, used, onNested) {
1830
1955
  TaggedTemplateExpression(path) {
1831
1956
  const tag = path.node.tag;
1832
1957
  if (!t.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
1833
- const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
1958
+ const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested, emit });
1834
1959
  onNested?.(ir);
1835
- const { code, imports, templates } = imperativeBackend.emit(ir, {});
1960
+ const { code, imports, templates } = imperativeBackend.emit(ir, {
1961
+ templateClone: emit?.templateClone,
1962
+ partialTemplates: emit?.partialTemplates ?? true
1963
+ });
1836
1964
  emitInto(path, inlineTemplates(code, templates), imports);
1837
1965
  }
1838
1966
  });
1839
1967
  return changed ? file.program.body[0].expression : node;
1840
1968
  }
1841
- function genExpr(node, used, onNested) {
1842
- return gen(transformNestedJSX(node, used, onNested));
1969
+ function genExpr(node, used, onNested, emit) {
1970
+ return gen(transformNestedJSX(node, used, onNested, emit));
1843
1971
  }
1844
1972
  function isReactiveExpr(node) {
1845
1973
  return isReactiveShape(shapeOf(node));
1846
1974
  }
1847
- function buildIR(node, used = /* @__PURE__ */ new Set(), onNested) {
1975
+ function buildIR(node, used = /* @__PURE__ */ new Set(), onNested, emit) {
1848
1976
  const ir = buildJsxIR(node, {
1849
1977
  code: (n) => (
1850
1978
  // A member-expression tag (`<ns.Foo/>`) is not an Expression, so it cannot go
1851
1979
  // through the nested-JSX compile: it only ever needs printing.
1852
- n.type === "JSXMemberExpression" ? gen(n) : genExpr(n, used, onNested)
1980
+ n.type === "JSXMemberExpression" ? gen(n) : genExpr(n, used, onNested, emit)
1853
1981
  ),
1854
1982
  used
1855
1983
  });
@@ -1861,16 +1989,16 @@ var generate2 = _generate2.default ?? _generate2;
1861
1989
  function gen2(node) {
1862
1990
  return generate2(node, { concise: true }).code;
1863
1991
  }
1864
- function bindHole(expr, used, onNested) {
1992
+ function bindHole(expr, used, onNested, emit) {
1865
1993
  const hole = {
1866
- code: genExpr(expr, used, onNested),
1994
+ code: genExpr(expr, used, onNested, emit),
1867
1995
  reactive: isReactiveExpr(expr)
1868
1996
  };
1869
1997
  if (t2.isArrowFunctionExpression(expr) && !t2.isBlockStatement(expr.body)) {
1870
1998
  const body = expr.body;
1871
1999
  hole.arrow = {
1872
2000
  params: expr.params.map((p) => gen2(p)),
1873
- body: genExpr(body, used, onNested),
2001
+ body: genExpr(body, used, onNested, emit),
1874
2002
  bodyReactive: isReactiveExpr(body)
1875
2003
  };
1876
2004
  }
@@ -1879,7 +2007,7 @@ function bindHole(expr, used, onNested) {
1879
2007
  }
1880
2008
  function lowerHtmlTemplate(node, used, opts = {}) {
1881
2009
  const quasi = node.quasi;
1882
- const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
2010
+ const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested, opts.emit));
1883
2011
  return lowerTemplate(buildPieces(quasi), holes, used, opts);
1884
2012
  }
1885
2013
  function buildPieces(quasi) {
@@ -145,10 +145,64 @@ function isHole(node) {
145
145
  function isTemplateChild(node) {
146
146
  return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
147
147
  }
148
+ var PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
149
+ function disposition(prop) {
150
+ if (prop.kind === "spread") return null;
151
+ if (prop.name === "ref") return "ref";
152
+ if (prop.name === "use") return "use";
153
+ if (prop.kind === "event") return prop.event ? "event" : null;
154
+ if (prop.kind === "prop") return "property";
155
+ if (prop.kind !== "attr") return null;
156
+ const name = prop.name;
157
+ if (name.startsWith("on:")) return "nativeEvent";
158
+ if (name.startsWith("prop:")) return "property";
159
+ if (name.startsWith("attr:")) return "attr";
160
+ if (name.startsWith("bool:")) return "boolAttr";
161
+ if (name === "className" || name === "class") {
162
+ return isPlainLiteralAttr(prop) ? "serialize" : "class";
163
+ }
164
+ if (name === "classList") {
165
+ if (isPlainLiteralAttr(prop)) return "serialize";
166
+ return prop.reactive ? "classListDynamic" : "classList";
167
+ }
168
+ if (name === "style") {
169
+ if (isPlainLiteralAttr(prop)) return "serialize";
170
+ return prop.reactive ? "styleDynamic" : "style";
171
+ }
172
+ if (name.includes(":")) return null;
173
+ if (PROPERTY_ONLY.has(name)) return null;
174
+ if (PROPERTY_BINDS.has(name)) return "property";
175
+ if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
176
+ return "attr";
177
+ }
178
+ function effectiveProps(el) {
179
+ const order = [];
180
+ const byName = /* @__PURE__ */ new Map();
181
+ for (const prop of el.props) {
182
+ if (!byName.has(prop.name)) order.push(prop.name);
183
+ byName.set(prop.name, prop);
184
+ }
185
+ return order.map((name) => byName.get(name));
186
+ }
187
+ function propPlan(el) {
188
+ const custom = el.tag.includes("-");
189
+ const plan = { serialize: [], bindings: [] };
190
+ for (const prop of effectiveProps(el)) {
191
+ const kind = disposition(prop);
192
+ if (kind === null) return null;
193
+ if (kind === "serialize") {
194
+ plan.serialize.push(prop);
195
+ continue;
196
+ }
197
+ if (custom) return null;
198
+ plan.bindings.push({ prop, kind });
199
+ }
200
+ return plan;
201
+ }
148
202
  function isTemplateElement(node) {
149
203
  if (node.svg) return false;
150
204
  if (isRawTextElement(node.tag)) return false;
151
- if (!node.props.every((p) => isPlainLiteralAttr(p))) return false;
205
+ if (!propPlan(node)) return false;
152
206
  return node.children.every((c) => isTemplateChild(c) || isHole(c));
153
207
  }
154
208
  function containsHole(node) {
@@ -156,6 +210,15 @@ function containsHole(node) {
156
210
  const children = node.children;
157
211
  return children ? children.some(containsHole) : false;
158
212
  }
213
+ function containsBinding(node) {
214
+ if (node.kind !== "element") return false;
215
+ const plan = propPlan(node);
216
+ if (plan && plan.bindings.length > 0) return true;
217
+ return node.children.some(containsBinding);
218
+ }
219
+ function interesting(node) {
220
+ return containsHole(node) || containsBinding(node);
221
+ }
159
222
  function addressable(el) {
160
223
  for (let i = 0; i < el.children.length; i++) {
161
224
  const child = el.children[i];
@@ -169,9 +232,10 @@ function addressable(el) {
169
232
  function planPartialTemplate(root) {
170
233
  if (!isTemplateElement(root)) return null;
171
234
  if (!addressable(root)) return null;
172
- if (!root.children.some(containsHole)) return null;
235
+ if (!interesting(root)) return null;
173
236
  const steps = [];
174
237
  const holes = [];
238
+ const bindings = [];
175
239
  let refCount = 0;
176
240
  const declare = (expr) => {
177
241
  const ref = `_n$${refCount++}`;
@@ -179,11 +243,17 @@ function planPartialTemplate(root) {
179
243
  return ref;
180
244
  };
181
245
  walk(root, "_el$");
182
- return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
246
+ return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
183
247
  function walk(el, elementRef) {
248
+ const plan = propPlan(el);
249
+ if (plan) {
250
+ for (const binding of plan.bindings) {
251
+ bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
252
+ }
253
+ }
184
254
  let last = -1;
185
255
  el.children.forEach((child, i) => {
186
- if (containsHole(child)) last = i;
256
+ if (interesting(child)) last = i;
187
257
  });
188
258
  let previous = null;
189
259
  for (let i = 0; i <= last; i++) {
@@ -197,19 +267,21 @@ function planPartialTemplate(root) {
197
267
  continue;
198
268
  }
199
269
  const ref = declare(expr);
200
- if (child.kind === "element" && containsHole(child)) walk(child, ref);
270
+ if (child.kind === "element" && interesting(child)) walk(child, ref);
201
271
  previous = ref;
202
272
  }
203
273
  }
204
274
  }
205
275
  function serializeWithMarkers(el) {
206
- return serializeStatic(withSentinels(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
276
+ return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
207
277
  }
208
- function withSentinels(el) {
278
+ function templateTree(el) {
279
+ const plan = propPlan(el);
209
280
  return {
210
281
  ...el,
282
+ props: plan ? plan.serialize : [],
211
283
  children: el.children.map(
212
- (child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? withSentinels(child) : child
284
+ (child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
213
285
  )
214
286
  };
215
287
  }
@@ -482,6 +554,55 @@ function emitComponent(name, props, children, used, templates, at) {
482
554
  const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
483
555
  return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
484
556
  }
557
+ function bindingTarget(binding) {
558
+ const name = binding.prop.name;
559
+ if (binding.kind === "boolAttr") return name.slice("bool:".length);
560
+ if (name.startsWith("prop:")) return name.slice("prop:".length);
561
+ if (name.startsWith("attr:")) return name.slice("attr:".length);
562
+ return htmlAttrName(name);
563
+ }
564
+ function bindingStatement(binding, used) {
565
+ const { ref, prop, kind } = binding;
566
+ const value = propValue(prop);
567
+ switch (kind) {
568
+ case "class":
569
+ used.add("setClassName");
570
+ return `setClassName(${ref}, ${value});`;
571
+ case "classList":
572
+ used.add("setClassList");
573
+ return `setClassList(${ref}, ${value}, []);`;
574
+ case "classListDynamic":
575
+ used.add("setDynamicClassList");
576
+ return `setDynamicClassList(${ref}, ${value});`;
577
+ case "style":
578
+ used.add("setStyle");
579
+ return `setStyle(${ref}, ${value}, []);`;
580
+ case "styleDynamic":
581
+ used.add("setDynamicStyle");
582
+ return `setDynamicStyle(${ref}, ${value});`;
583
+ case "attr":
584
+ used.add("setDynamicAttribute");
585
+ return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
586
+ case "property":
587
+ used.add("setDynamicProperty");
588
+ return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
589
+ case "boolAttr":
590
+ used.add("setDynamicBoolAttribute");
591
+ return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
592
+ case "event":
593
+ used.add("addDelegatedEventListener");
594
+ return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
595
+ case "nativeEvent":
596
+ used.add("addNativeEventListener");
597
+ return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
598
+ case "ref":
599
+ used.add("handleRef");
600
+ return `handleRef(${value}, ${ref});`;
601
+ case "use":
602
+ used.add("applyUse");
603
+ return `applyUse(${ref}, ${value});`;
604
+ }
605
+ }
485
606
  function staticAttrAssignment(el, prop) {
486
607
  const name = htmlAttrName(prop.name);
487
608
  const value = prop.literal;
@@ -532,6 +653,7 @@ function emitNode(node, used, templates) {
532
653
  `const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
533
654
  ];
534
655
  for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
656
+ for (const binding of plan.bindings) lines.push(bindingStatement(binding, used));
535
657
  for (const hole of plan.holes) {
536
658
  lines.push(
537
659
  `insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`
@@ -1382,16 +1504,16 @@ var generate = import_generator.default.default ?? import_generator.default;
1382
1504
  function gen(node) {
1383
1505
  return generate(node, { concise: true }).code;
1384
1506
  }
1385
- function bindHole(expr, used, onNested) {
1507
+ function bindHole(expr, used, onNested, emit) {
1386
1508
  const hole = {
1387
- code: genExpr(expr, used, onNested),
1509
+ code: genExpr(expr, used, onNested, emit),
1388
1510
  reactive: isReactiveExpr(expr)
1389
1511
  };
1390
1512
  if (import_core.types.isArrowFunctionExpression(expr) && !import_core.types.isBlockStatement(expr.body)) {
1391
1513
  const body = expr.body;
1392
1514
  hole.arrow = {
1393
1515
  params: expr.params.map((p) => gen(p)),
1394
- body: genExpr(body, used, onNested),
1516
+ body: genExpr(body, used, onNested, emit),
1395
1517
  bodyReactive: isReactiveExpr(body)
1396
1518
  };
1397
1519
  }
@@ -1400,7 +1522,7 @@ function bindHole(expr, used, onNested) {
1400
1522
  }
1401
1523
  function lowerHtmlTemplate(node, used, opts = {}) {
1402
1524
  const quasi = node.quasi;
1403
- const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
1525
+ const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested, opts.emit));
1404
1526
  return lowerTemplate(buildPieces(quasi), holes, used, opts);
1405
1527
  }
1406
1528
  function buildPieces(quasi) {
@@ -1888,7 +2010,7 @@ var generate2 = import_generator2.default.default ?? import_generator2.default;
1888
2010
  function gen2(node) {
1889
2011
  return generate2(node, { concise: true }).code;
1890
2012
  }
1891
- function transformNestedJSX(node, used, onNested) {
2013
+ function transformNestedJSX(node, used, onNested, emit) {
1892
2014
  const clone = import_core2.types.cloneNode(node, true);
1893
2015
  const file = import_core2.types.file(import_core2.types.program([import_core2.types.expressionStatement(clone)]));
1894
2016
  let changed = false;
@@ -1901,9 +2023,12 @@ function transformNestedJSX(node, used, onNested) {
1901
2023
  (0, import_core2.traverse)(file, {
1902
2024
  "JSXElement|JSXFragment"(path) {
1903
2025
  const jsx = path.node;
1904
- const nestedIR = buildIR(jsx, used, onNested);
2026
+ const nestedIR = buildIR(jsx, used, onNested, emit);
1905
2027
  onNested?.(nestedIR);
1906
- const { code, imports, templates } = imperativeBackend.emit(nestedIR, {});
2028
+ const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
2029
+ templateClone: emit?.templateClone,
2030
+ partialTemplates: emit?.partialTemplates ?? true
2031
+ });
1907
2032
  emitInto(path, inlineTemplates(code, templates), imports);
1908
2033
  },
1909
2034
  // Nested html templates compose: ${cond ? html`...` : html`...`},
@@ -1911,16 +2036,19 @@ function transformNestedJSX(node, used, onNested) {
1911
2036
  TaggedTemplateExpression(path) {
1912
2037
  const tag = path.node.tag;
1913
2038
  if (!import_core2.types.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
1914
- const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
2039
+ const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested, emit });
1915
2040
  onNested?.(ir);
1916
- const { code, imports, templates } = imperativeBackend.emit(ir, {});
2041
+ const { code, imports, templates } = imperativeBackend.emit(ir, {
2042
+ templateClone: emit?.templateClone,
2043
+ partialTemplates: emit?.partialTemplates ?? true
2044
+ });
1917
2045
  emitInto(path, inlineTemplates(code, templates), imports);
1918
2046
  }
1919
2047
  });
1920
2048
  return changed ? file.program.body[0].expression : node;
1921
2049
  }
1922
- function genExpr(node, used, onNested) {
1923
- return gen2(transformNestedJSX(node, used, onNested));
2050
+ function genExpr(node, used, onNested, emit) {
2051
+ return gen2(transformNestedJSX(node, used, onNested, emit));
1924
2052
  }
1925
2053
  var SVG_TAGS2 = /* @__PURE__ */ new Set([
1926
2054
  "svg",
@@ -1939,12 +2067,12 @@ var SVG_TAGS2 = /* @__PURE__ */ new Set([
1939
2067
  function isReactiveExpr(node) {
1940
2068
  return isReactiveShape(shapeOf(node));
1941
2069
  }
1942
- function buildIR(node, used = /* @__PURE__ */ new Set(), onNested) {
2070
+ function buildIR(node, used = /* @__PURE__ */ new Set(), onNested, emit) {
1943
2071
  const ir = buildJsxIR(node, {
1944
2072
  code: (n) => (
1945
2073
  // A member-expression tag (`<ns.Foo/>`) is not an Expression, so it cannot go
1946
2074
  // through the nested-JSX compile: it only ever needs printing.
1947
- n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested)
2075
+ n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested, emit)
1948
2076
  ),
1949
2077
  used
1950
2078
  });
@@ -15,7 +15,7 @@
15
15
  import { types as t } from '@babel/core';
16
16
  import type { IRNode } from '../../ir/nodes.cjs';
17
17
  /** Stringify an expression, compiling any JSX nested inside it first. */
18
- export declare function genExpr(node: t.Expression, used: Set<string>, onNested?: NestedHook): string;
18
+ export declare function genExpr(node: t.Expression, used: Set<string>, onNested?: NestedHook, emit?: NestedEmitOptions): string;
19
19
  /**
20
20
  * Called with the IR of JSX (or a template) found inside an expression.
21
21
  *
@@ -25,6 +25,15 @@ export declare function genExpr(node: t.Expression, used: Set<string>, onNested?
25
25
  * reference to `Input` that nothing imported.
26
26
  */
27
27
  export type NestedHook = (ir: IRNode) => void;
28
+ /**
29
+ * Emit switches for JSX compiled inside an expression. The front-end passes its
30
+ * own, so a root inside a callback or a hole compiles the way a top-level root
31
+ * does; absent a front-end, partial templates stay on, as at the top level.
32
+ */
33
+ export interface NestedEmitOptions {
34
+ partialTemplates?: boolean;
35
+ templateClone?: boolean;
36
+ }
28
37
  export declare const SVG_TAGS: Set<string>;
29
38
  export declare function isReactiveExpr(node: t.Expression): boolean;
30
39
  /**
@@ -32,4 +41,4 @@ export declare function isReactiveExpr(node: t.Expression): boolean;
32
41
  * front-end supplies is how an expression becomes source, `genExpr`, which also
33
42
  * compiles any JSX or `` html`` `` nested inside it.
34
43
  */
35
- export declare function buildIR(node: t.JSXElement | t.JSXFragment, used?: Set<string>, onNested?: NestedHook): IRNode;
44
+ export declare function buildIR(node: t.JSXElement | t.JSXFragment, used?: Set<string>, onNested?: NestedHook, emit?: NestedEmitOptions): IRNode;
@@ -15,7 +15,7 @@
15
15
  import { types as t } from '@babel/core';
16
16
  import type { IRNode } from '../../ir/nodes.js';
17
17
  /** Stringify an expression, compiling any JSX nested inside it first. */
18
- export declare function genExpr(node: t.Expression, used: Set<string>, onNested?: NestedHook): string;
18
+ export declare function genExpr(node: t.Expression, used: Set<string>, onNested?: NestedHook, emit?: NestedEmitOptions): string;
19
19
  /**
20
20
  * Called with the IR of JSX (or a template) found inside an expression.
21
21
  *
@@ -25,6 +25,15 @@ export declare function genExpr(node: t.Expression, used: Set<string>, onNested?
25
25
  * reference to `Input` that nothing imported.
26
26
  */
27
27
  export type NestedHook = (ir: IRNode) => void;
28
+ /**
29
+ * Emit switches for JSX compiled inside an expression. The front-end passes its
30
+ * own, so a root inside a callback or a hole compiles the way a top-level root
31
+ * does; absent a front-end, partial templates stay on, as at the top level.
32
+ */
33
+ export interface NestedEmitOptions {
34
+ partialTemplates?: boolean;
35
+ templateClone?: boolean;
36
+ }
28
37
  export declare const SVG_TAGS: Set<string>;
29
38
  export declare function isReactiveExpr(node: t.Expression): boolean;
30
39
  /**
@@ -32,5 +41,5 @@ export declare function isReactiveExpr(node: t.Expression): boolean;
32
41
  * front-end supplies is how an expression becomes source, `genExpr`, which also
33
42
  * compiles any JSX or `` html`` `` nested inside it.
34
43
  */
35
- export declare function buildIR(node: t.JSXElement | t.JSXFragment, used?: Set<string>, onNested?: NestedHook): IRNode;
44
+ export declare function buildIR(node: t.JSXElement | t.JSXFragment, used?: Set<string>, onNested?: NestedHook, emit?: NestedEmitOptions): IRNode;
36
45
  //# sourceMappingURL=build-ir.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-ir.d.ts","sourceRoot":"","sources":["../../../src/frontend/babel/build-ir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,IAAI,CAAC,EAAsB,MAAM,aAAa,CAAC;AAI7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAwDhD,yEAAyE;AACzE,wBAAgB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,GAAG,MAAM,CAE5F;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;AAE9C,eAAO,MAAM,QAAQ,aAGnB,CAAC;AA2BH,wBAAgB,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,UAAU,GAAG,OAAO,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,WAAW,EAClC,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,EAC7B,QAAQ,CAAC,EAAE,UAAU,GACpB,MAAM,CAWR"}
1
+ {"version":3,"file":"build-ir.d.ts","sourceRoot":"","sources":["../../../src/frontend/babel/build-ir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,KAAK,IAAI,CAAC,EAAsB,MAAM,aAAa,CAAC;AAI7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA8DhD,yEAAyE;AACzE,wBAAgB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAEtH;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;AAE9C;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,eAAO,MAAM,QAAQ,aAGnB,CAAC;AA2BH,wBAAgB,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,UAAU,GAAG,OAAO,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,WAAW,EAClC,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,EAC7B,QAAQ,CAAC,EAAE,UAAU,EACrB,IAAI,CAAC,EAAE,iBAAiB,GACvB,MAAM,CAWR"}