@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
@@ -1,4 +1,4 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.105 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.106 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
@@ -1055,10 +1055,63 @@ function isHole(node) {
1055
1055
  function isTemplateChild(node) {
1056
1056
  return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
1057
1057
  }
1058
+ function disposition(prop) {
1059
+ if (prop.kind === "spread") return null;
1060
+ if (prop.name === "ref") return "ref";
1061
+ if (prop.name === "use") return "use";
1062
+ if (prop.kind === "event") return prop.event ? "event" : null;
1063
+ if (prop.kind === "prop") return "property";
1064
+ if (prop.kind !== "attr") return null;
1065
+ const name = prop.name;
1066
+ if (name.startsWith("on:")) return "nativeEvent";
1067
+ if (name.startsWith("prop:")) return "property";
1068
+ if (name.startsWith("attr:")) return "attr";
1069
+ if (name.startsWith("bool:")) return "boolAttr";
1070
+ if (name === "className" || name === "class") {
1071
+ return isPlainLiteralAttr(prop) ? "serialize" : "class";
1072
+ }
1073
+ if (name === "classList") {
1074
+ if (isPlainLiteralAttr(prop)) return "serialize";
1075
+ return prop.reactive ? "classListDynamic" : "classList";
1076
+ }
1077
+ if (name === "style") {
1078
+ if (isPlainLiteralAttr(prop)) return "serialize";
1079
+ return prop.reactive ? "styleDynamic" : "style";
1080
+ }
1081
+ if (name.includes(":")) return null;
1082
+ if (PROPERTY_ONLY.has(name)) return null;
1083
+ if (PROPERTY_BINDS.has(name)) return "property";
1084
+ if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
1085
+ return "attr";
1086
+ }
1087
+ function effectiveProps(el) {
1088
+ const order = [];
1089
+ const byName = /* @__PURE__ */ new Map();
1090
+ for (const prop of el.props) {
1091
+ if (!byName.has(prop.name)) order.push(prop.name);
1092
+ byName.set(prop.name, prop);
1093
+ }
1094
+ return order.map((name) => byName.get(name));
1095
+ }
1096
+ function propPlan(el) {
1097
+ const custom = el.tag.includes("-");
1098
+ const plan = { serialize: [], bindings: [] };
1099
+ for (const prop of effectiveProps(el)) {
1100
+ const kind = disposition(prop);
1101
+ if (kind === null) return null;
1102
+ if (kind === "serialize") {
1103
+ plan.serialize.push(prop);
1104
+ continue;
1105
+ }
1106
+ if (custom) return null;
1107
+ plan.bindings.push({ prop, kind });
1108
+ }
1109
+ return plan;
1110
+ }
1058
1111
  function isTemplateElement(node) {
1059
1112
  if (node.svg) return false;
1060
1113
  if (isRawTextElement(node.tag)) return false;
1061
- if (!node.props.every((p) => isPlainLiteralAttr(p))) return false;
1114
+ if (!propPlan(node)) return false;
1062
1115
  return node.children.every((c) => isTemplateChild(c) || isHole(c));
1063
1116
  }
1064
1117
  function containsHole(node) {
@@ -1066,6 +1119,15 @@ function containsHole(node) {
1066
1119
  const children = node.children;
1067
1120
  return children ? children.some(containsHole) : false;
1068
1121
  }
1122
+ function containsBinding(node) {
1123
+ if (node.kind !== "element") return false;
1124
+ const plan = propPlan(node);
1125
+ if (plan && plan.bindings.length > 0) return true;
1126
+ return node.children.some(containsBinding);
1127
+ }
1128
+ function interesting(node) {
1129
+ return containsHole(node) || containsBinding(node);
1130
+ }
1069
1131
  function addressable(el) {
1070
1132
  for (let i = 0; i < el.children.length; i++) {
1071
1133
  const child = el.children[i];
@@ -1079,9 +1141,10 @@ function addressable(el) {
1079
1141
  function planPartialTemplate(root) {
1080
1142
  if (!isTemplateElement(root)) return null;
1081
1143
  if (!addressable(root)) return null;
1082
- if (!root.children.some(containsHole)) return null;
1144
+ if (!interesting(root)) return null;
1083
1145
  const steps = [];
1084
1146
  const holes = [];
1147
+ const bindings = [];
1085
1148
  let refCount = 0;
1086
1149
  const declare2 = (expr) => {
1087
1150
  const ref = `_n$${refCount++}`;
@@ -1089,11 +1152,17 @@ function planPartialTemplate(root) {
1089
1152
  return ref;
1090
1153
  };
1091
1154
  walk8(root, "_el$");
1092
- return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
1155
+ return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
1093
1156
  function walk8(el, elementRef) {
1157
+ const plan = propPlan(el);
1158
+ if (plan) {
1159
+ for (const binding of plan.bindings) {
1160
+ bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
1161
+ }
1162
+ }
1094
1163
  let last = -1;
1095
1164
  el.children.forEach((child, i) => {
1096
- if (containsHole(child)) last = i;
1165
+ if (interesting(child)) last = i;
1097
1166
  });
1098
1167
  let previous = null;
1099
1168
  for (let i = 0; i <= last; i++) {
@@ -1107,29 +1176,32 @@ function planPartialTemplate(root) {
1107
1176
  continue;
1108
1177
  }
1109
1178
  const ref = declare2(expr);
1110
- if (child.kind === "element" && containsHole(child)) walk8(child, ref);
1179
+ if (child.kind === "element" && interesting(child)) walk8(child, ref);
1111
1180
  previous = ref;
1112
1181
  }
1113
1182
  }
1114
1183
  }
1115
1184
  function serializeWithMarkers(el) {
1116
- return serializeStatic(withSentinels(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
1185
+ return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
1117
1186
  }
1118
- function withSentinels(el) {
1187
+ function templateTree(el) {
1188
+ const plan = propPlan(el);
1119
1189
  return {
1120
1190
  ...el,
1191
+ props: plan ? plan.serialize : [],
1121
1192
  children: el.children.map(
1122
- (child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? withSentinels(child) : child
1193
+ (child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
1123
1194
  )
1124
1195
  };
1125
1196
  }
1126
- var HOLE_START, HOLE_END, SENTINEL;
1197
+ var HOLE_START, HOLE_END, PROPERTY_BINDS, SENTINEL;
1127
1198
  var init_partial_template = __esm({
1128
1199
  "src/codegen/partial-template.ts"() {
1129
1200
  "use strict";
1130
1201
  init_serialize_static();
1131
1202
  HOLE_START = "<!--fx-->";
1132
1203
  HOLE_END = "<!--fx/-->";
1204
+ PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
1133
1205
  SENTINEL = "\0fx-hole\0";
1134
1206
  }
1135
1207
  });
@@ -1693,6 +1765,55 @@ function emitComponent(name, props, children, used, templates, at) {
1693
1765
  const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
1694
1766
  return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
1695
1767
  }
1768
+ function bindingTarget(binding) {
1769
+ const name = binding.prop.name;
1770
+ if (binding.kind === "boolAttr") return name.slice("bool:".length);
1771
+ if (name.startsWith("prop:")) return name.slice("prop:".length);
1772
+ if (name.startsWith("attr:")) return name.slice("attr:".length);
1773
+ return htmlAttrName(name);
1774
+ }
1775
+ function bindingStatement(binding, used) {
1776
+ const { ref, prop, kind } = binding;
1777
+ const value = propValue(prop);
1778
+ switch (kind) {
1779
+ case "class":
1780
+ used.add("setClassName");
1781
+ return `setClassName(${ref}, ${value});`;
1782
+ case "classList":
1783
+ used.add("setClassList");
1784
+ return `setClassList(${ref}, ${value}, []);`;
1785
+ case "classListDynamic":
1786
+ used.add("setDynamicClassList");
1787
+ return `setDynamicClassList(${ref}, ${value});`;
1788
+ case "style":
1789
+ used.add("setStyle");
1790
+ return `setStyle(${ref}, ${value}, []);`;
1791
+ case "styleDynamic":
1792
+ used.add("setDynamicStyle");
1793
+ return `setDynamicStyle(${ref}, ${value});`;
1794
+ case "attr":
1795
+ used.add("setDynamicAttribute");
1796
+ return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
1797
+ case "property":
1798
+ used.add("setDynamicProperty");
1799
+ return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
1800
+ case "boolAttr":
1801
+ used.add("setDynamicBoolAttribute");
1802
+ return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
1803
+ case "event":
1804
+ used.add("addDelegatedEventListener");
1805
+ return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
1806
+ case "nativeEvent":
1807
+ used.add("addNativeEventListener");
1808
+ return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
1809
+ case "ref":
1810
+ used.add("handleRef");
1811
+ return `handleRef(${value}, ${ref});`;
1812
+ case "use":
1813
+ used.add("applyUse");
1814
+ return `applyUse(${ref}, ${value});`;
1815
+ }
1816
+ }
1696
1817
  function staticAttrAssignment(el, prop) {
1697
1818
  const name = htmlAttrName(prop.name);
1698
1819
  const value = prop.literal;
@@ -1742,6 +1863,7 @@ function emitNode(node, used, templates) {
1742
1863
  `const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
1743
1864
  ];
1744
1865
  for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
1866
+ for (const binding of plan.bindings) lines.push(bindingStatement(binding, used));
1745
1867
  for (const hole of plan.holes) {
1746
1868
  lines.push(
1747
1869
  `insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`
@@ -2581,16 +2703,16 @@ var init_template = __esm({
2581
2703
  function gen(node) {
2582
2704
  return generate(node, { concise: true }).code;
2583
2705
  }
2584
- function bindHole(expr, used, onNested) {
2706
+ function bindHole(expr, used, onNested, emit) {
2585
2707
  const hole = {
2586
- code: genExpr(expr, used, onNested),
2708
+ code: genExpr(expr, used, onNested, emit),
2587
2709
  reactive: isReactiveExpr(expr)
2588
2710
  };
2589
2711
  if (import_core.types.isArrowFunctionExpression(expr) && !import_core.types.isBlockStatement(expr.body)) {
2590
2712
  const body = expr.body;
2591
2713
  hole.arrow = {
2592
2714
  params: expr.params.map((p) => gen(p)),
2593
- body: genExpr(body, used, onNested),
2715
+ body: genExpr(body, used, onNested, emit),
2594
2716
  bodyReactive: isReactiveExpr(body)
2595
2717
  };
2596
2718
  }
@@ -2599,7 +2721,7 @@ function bindHole(expr, used, onNested) {
2599
2721
  }
2600
2722
  function lowerHtmlTemplate(node, used, opts = {}) {
2601
2723
  const quasi = node.quasi;
2602
- const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested));
2724
+ const holes = quasi.expressions.map((e) => bindHole(e, used, opts.onNested, opts.emit));
2603
2725
  return lowerTemplate(buildPieces(quasi), holes, used, opts);
2604
2726
  }
2605
2727
  function buildPieces(quasi) {
@@ -3135,7 +3257,7 @@ var init_jsx = __esm({
3135
3257
  function gen2(node) {
3136
3258
  return generate2(node, { concise: true }).code;
3137
3259
  }
3138
- function transformNestedJSX(node, used, onNested) {
3260
+ function transformNestedJSX(node, used, onNested, emit) {
3139
3261
  const clone = import_core2.types.cloneNode(node, true);
3140
3262
  const file = import_core2.types.file(import_core2.types.program([import_core2.types.expressionStatement(clone)]));
3141
3263
  let changed = false;
@@ -3148,9 +3270,12 @@ function transformNestedJSX(node, used, onNested) {
3148
3270
  (0, import_core2.traverse)(file, {
3149
3271
  "JSXElement|JSXFragment"(path) {
3150
3272
  const jsx = path.node;
3151
- const nestedIR = buildIR(jsx, used, onNested);
3273
+ const nestedIR = buildIR(jsx, used, onNested, emit);
3152
3274
  onNested?.(nestedIR);
3153
- const { code, imports, templates } = imperativeBackend.emit(nestedIR, {});
3275
+ const { code, imports, templates } = imperativeBackend.emit(nestedIR, {
3276
+ templateClone: emit?.templateClone,
3277
+ partialTemplates: emit?.partialTemplates ?? true
3278
+ });
3154
3279
  emitInto(path, inlineTemplates(code, templates), imports);
3155
3280
  },
3156
3281
  // Nested html templates compose: ${cond ? html`...` : html`...`},
@@ -3158,26 +3283,29 @@ function transformNestedJSX(node, used, onNested) {
3158
3283
  TaggedTemplateExpression(path) {
3159
3284
  const tag = path.node.tag;
3160
3285
  if (!import_core2.types.isIdentifier(tag) || tag.name !== "html" && tag.name !== "svg") return;
3161
- const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested });
3286
+ const ir = buildIRFromHtml(path.node, used, { svg: tag.name === "svg", onNested, emit });
3162
3287
  onNested?.(ir);
3163
- const { code, imports, templates } = imperativeBackend.emit(ir, {});
3288
+ const { code, imports, templates } = imperativeBackend.emit(ir, {
3289
+ templateClone: emit?.templateClone,
3290
+ partialTemplates: emit?.partialTemplates ?? true
3291
+ });
3164
3292
  emitInto(path, inlineTemplates(code, templates), imports);
3165
3293
  }
3166
3294
  });
3167
3295
  return changed ? file.program.body[0].expression : node;
3168
3296
  }
3169
- function genExpr(node, used, onNested) {
3170
- return gen2(transformNestedJSX(node, used, onNested));
3297
+ function genExpr(node, used, onNested, emit) {
3298
+ return gen2(transformNestedJSX(node, used, onNested, emit));
3171
3299
  }
3172
3300
  function isReactiveExpr(node) {
3173
3301
  return isReactiveShape(shapeOf(node));
3174
3302
  }
3175
- function buildIR(node, used = /* @__PURE__ */ new Set(), onNested) {
3303
+ function buildIR(node, used = /* @__PURE__ */ new Set(), onNested, emit) {
3176
3304
  const ir = buildJsxIR(node, {
3177
3305
  code: (n) => (
3178
3306
  // A member-expression tag (`<ns.Foo/>`) is not an Expression, so it cannot go
3179
3307
  // through the nested-JSX compile: it only ever needs printing.
3180
- n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested)
3308
+ n.type === "JSXMemberExpression" ? gen2(n) : genExpr(n, used, onNested, emit)
3181
3309
  ),
3182
3310
  used
3183
3311
  });
@@ -3250,7 +3378,10 @@ function adoptTemplates(code, templates, state) {
3250
3378
  }
3251
3379
  function emitViaIR(node, state) {
3252
3380
  const used = /* @__PURE__ */ new Set();
3253
- const ir = buildIR(node, used, nestedHook(state));
3381
+ const ir = buildIR(node, used, nestedHook(state), {
3382
+ partialTemplates: state.partialTemplates,
3383
+ templateClone: state.templateClone
3384
+ });
3254
3385
  resolveComponentSources(ir, {
3255
3386
  resolver: state.componentResolver,
3256
3387
  modules: {
@@ -3299,7 +3430,11 @@ function dedupeImports(imports) {
3299
3430
  function emitHtmlViaIR(node, state) {
3300
3431
  const used = /* @__PURE__ */ new Set();
3301
3432
  const svg = import_core3.types.isIdentifier(node.tag) && node.tag.name === "svg";
3302
- const ir = buildIRFromHtml(node, used, { svg, onNested: nestedHook(state) });
3433
+ const ir = buildIRFromHtml(node, used, {
3434
+ svg,
3435
+ onNested: nestedHook(state),
3436
+ emit: { partialTemplates: state.partialTemplates, templateClone: state.templateClone }
3437
+ });
3303
3438
  resolveComponentSources(ir, {
3304
3439
  resolver: state.componentResolver,
3305
3440
  modules: {
@@ -3694,7 +3829,7 @@ function createPreset(options = {}) {
3694
3829
  {
3695
3830
  runtime: "automatic",
3696
3831
  importSource: "@fluixi/jsx",
3697
- development: false,
3832
+ development: process.env.NODE_ENV === "development",
3698
3833
  detectReactivity: true,
3699
3834
  useHtmlTag: true,
3700
3835
  hoistStatics: true,
@@ -4488,11 +4623,8 @@ function transformTemplates(code, id, options = {}) {
4488
4623
  const hoisted = /* @__PURE__ */ new Map();
4489
4624
  const edits = [...props.edits, ...intrinsics.edits, ...sources.edits, ...styles?.edits ?? []];
4490
4625
  for (const node of found) {
4491
- const nested = found.some(
4492
- (o) => o !== node && o.start <= node.start && o.end >= node.end && o.end - o.start > node.end - node.start
4493
- );
4494
4626
  if (isJsx(node)) {
4495
- edits.push({ start: node.start, end: node.end, code: compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceName, styles) });
4627
+ edits.push({ start: node.start, end: node.end, code: compileJsx(code, node, edits, used, needs, hoisted, options, sourceName, styles) });
4496
4628
  continue;
4497
4629
  }
4498
4630
  const holes = node.quasi.expressions.map(
@@ -4519,7 +4651,7 @@ function transformTemplates(code, id, options = {}) {
4519
4651
  ...options.sourceLocations === true ? { sourceFile: sourceName, positionAt: positionsIn(code) } : {},
4520
4652
  // Same defaults as the plugin, so moving a build over is not a rendering change.
4521
4653
  templateClone: options.templateClone ?? true,
4522
- partialTemplates: nested ? false : options.partialTemplates ?? true,
4654
+ partialTemplates: options.partialTemplates ?? true,
4523
4655
  svg: templateTags.get(node.tag.name) === "svg"
4524
4656
  });
4525
4657
  for (const symbol of result.imports) used.add(symbol);
@@ -4591,7 +4723,7 @@ function transformTemplates(code, id, options = {}) {
4591
4723
  ...styles?.references.length ? { styleReferences: styles.references } : {}
4592
4724
  };
4593
4725
  }
4594
- function compileJsx(code, node, edits, nested, used, needs, hoisted, options, sourceFile, styles) {
4726
+ function compileJsx(code, node, edits, used, needs, hoisted, options, sourceFile, styles) {
4595
4727
  const ir = buildJsxIR(node, {
4596
4728
  code: (n) => slice(code, n.start, n.end, edits),
4597
4729
  used,
@@ -4616,7 +4748,7 @@ function compileJsx(code, node, edits, nested, used, needs, hoisted, options, so
4616
4748
  collapseStrategies(ir);
4617
4749
  const emitted = imperativeBackend.emit(ir, {
4618
4750
  templateClone: options.templateClone ?? true,
4619
- partialTemplates: nested ? false : options.partialTemplates ?? true
4751
+ partialTemplates: options.partialTemplates ?? true
4620
4752
  });
4621
4753
  for (const symbol of emitted.imports) used.add(symbol);
4622
4754
  collectNeeds(ir, needs);
@@ -1,17 +1,17 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.105 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.106 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  import {
3
3
  styleIds
4
- } from "./chunk-EQIIWIEU.mjs";
4
+ } from "./chunk-W7DYOMXJ.mjs";
5
5
  import {
6
6
  INTRINSICS,
7
7
  TAG_INTRINSICS,
8
8
  builtinComponentMap,
9
9
  createComponentResolver
10
- } from "./chunk-O6OBEV4V.mjs";
11
- import "./chunk-EK4OMGTU.mjs";
10
+ } from "./chunk-TDN5MYWI.mjs";
11
+ import "./chunk-JUFI6IR3.mjs";
12
12
  import {
13
13
  __require
14
- } from "./chunk-OQYQ7UTD.mjs";
14
+ } from "./chunk-4XEOMQ57.mjs";
15
15
 
16
16
  // src/resolve/write-globals.ts
17
17
  import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -432,7 +432,7 @@ import { dirname as dirname3, join as _join, resolve as resolvePath2 } from "nod
432
432
  async function createBabelPlugin(options = {}) {
433
433
  let pluginModule = null;
434
434
  try {
435
- pluginModule = await import("./babel-USPHNEJF.mjs");
435
+ pluginModule = await import("./babel-HJY4RVAV.mjs");
436
436
  } catch (e) {
437
437
  console.warn("Could not load the babel front-end:", e);
438
438
  }
@@ -521,7 +521,7 @@ function createVitePlugin(options = {}) {
521
521
  const entry = config?.command === "build" && config.build?.lib ? config.build.lib.entry : void 0;
522
522
  const first = typeof entry === "string" ? entry : Array.isArray(entry) ? entry[0] : entry && typeof entry === "object" ? Object.values(entry)[0] : void 0;
523
523
  if (typeof first === "string") componentStyles.setLibrary(dirname3(resolvePath2(config.root ?? process.cwd(), first)));
524
- if (componentStyles.active) ({ transformTemplates } = await import("./transform-YUDW7K4J.mjs"));
524
+ if (componentStyles.active) ({ transformTemplates } = await import("./transform-NO46NFQH.mjs"));
525
525
  },
526
526
  generateBundle() {
527
527
  for (const [fileName, source] of componentStyles.libraryFiles()) this.emitFile({ type: "asset", fileName, source });
@@ -639,7 +639,7 @@ function transformOptions(options, id) {
639
639
  }
640
640
  async function rewriteProps(code, id, options) {
641
641
  if (options.propsDestructure === false) return code;
642
- const { transformProps } = await import("./transform-YUDW7K4J.mjs");
642
+ const { transformProps } = await import("./transform-NO46NFQH.mjs");
643
643
  const result = transformProps(code, id);
644
644
  reportDiagnostics({ propsDiagnostics: result.diagnostics }, id);
645
645
  return result.rewritten > 0 ? result.code : code;
@@ -663,7 +663,7 @@ function reportDiagnostics(result, id) {
663
663
  }
664
664
  async function compileModule(code, id, options) {
665
665
  if (!options.babel) {
666
- const { transformTemplates } = await import("./transform-YUDW7K4J.mjs");
666
+ const { transformTemplates } = await import("./transform-NO46NFQH.mjs");
667
667
  const result2 = transformTemplates(code, id, transformOptions(options, id));
668
668
  reportDiagnostics(result2, id);
669
669
  return result2;
@@ -782,10 +782,10 @@ export { isServerFnRequest, handleServerFn } from '@fluixi/start/server-fn';
782
782
  if (!code.includes("use server")) return null;
783
783
  const args = { ssr: !!opts?.ssr, filename: id, root };
784
784
  if (options.babel) {
785
- const { transformServerFunctions } = await import("./server-functions-43CFKXY3.mjs");
785
+ const { transformServerFunctions } = await import("./server-functions-4KTFFEO3.mjs");
786
786
  return transformServerFunctions(code, args);
787
787
  }
788
- const { transformServerFns } = await import("./server-fns-7Y25MR3B.mjs");
788
+ const { transformServerFns } = await import("./server-fns-WZJK4BJE.mjs");
789
789
  return transformServerFns(code, args);
790
790
  }
791
791
  };
@@ -1,4 +1,4 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.105 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.106 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -382,10 +382,64 @@ function isHole(node) {
382
382
  function isTemplateChild(node) {
383
383
  return node.kind === "text" || node.kind === "element" && isTemplateElement(node);
384
384
  }
385
+ var PROPERTY_BINDS = /* @__PURE__ */ new Set(["indeterminate"]);
386
+ function disposition(prop) {
387
+ if (prop.kind === "spread") return null;
388
+ if (prop.name === "ref") return "ref";
389
+ if (prop.name === "use") return "use";
390
+ if (prop.kind === "event") return prop.event ? "event" : null;
391
+ if (prop.kind === "prop") return "property";
392
+ if (prop.kind !== "attr") return null;
393
+ const name = prop.name;
394
+ if (name.startsWith("on:")) return "nativeEvent";
395
+ if (name.startsWith("prop:")) return "property";
396
+ if (name.startsWith("attr:")) return "attr";
397
+ if (name.startsWith("bool:")) return "boolAttr";
398
+ if (name === "className" || name === "class") {
399
+ return isPlainLiteralAttr(prop) ? "serialize" : "class";
400
+ }
401
+ if (name === "classList") {
402
+ if (isPlainLiteralAttr(prop)) return "serialize";
403
+ return prop.reactive ? "classListDynamic" : "classList";
404
+ }
405
+ if (name === "style") {
406
+ if (isPlainLiteralAttr(prop)) return "serialize";
407
+ return prop.reactive ? "styleDynamic" : "style";
408
+ }
409
+ if (name.includes(":")) return null;
410
+ if (PROPERTY_ONLY.has(name)) return null;
411
+ if (PROPERTY_BINDS.has(name)) return "property";
412
+ if (prop.expr === void 0) return isPlainLiteralAttr(prop) ? "serialize" : null;
413
+ return "attr";
414
+ }
415
+ function effectiveProps(el) {
416
+ const order = [];
417
+ const byName = /* @__PURE__ */ new Map();
418
+ for (const prop of el.props) {
419
+ if (!byName.has(prop.name)) order.push(prop.name);
420
+ byName.set(prop.name, prop);
421
+ }
422
+ return order.map((name) => byName.get(name));
423
+ }
424
+ function propPlan(el) {
425
+ const custom = el.tag.includes("-");
426
+ const plan = { serialize: [], bindings: [] };
427
+ for (const prop of effectiveProps(el)) {
428
+ const kind = disposition(prop);
429
+ if (kind === null) return null;
430
+ if (kind === "serialize") {
431
+ plan.serialize.push(prop);
432
+ continue;
433
+ }
434
+ if (custom) return null;
435
+ plan.bindings.push({ prop, kind });
436
+ }
437
+ return plan;
438
+ }
385
439
  function isTemplateElement(node) {
386
440
  if (node.svg) return false;
387
441
  if (isRawTextElement(node.tag)) return false;
388
- if (!node.props.every((p) => isPlainLiteralAttr(p))) return false;
442
+ if (!propPlan(node)) return false;
389
443
  return node.children.every((c) => isTemplateChild(c) || isHole(c));
390
444
  }
391
445
  function containsHole(node) {
@@ -393,6 +447,15 @@ function containsHole(node) {
393
447
  const children = node.children;
394
448
  return children ? children.some(containsHole) : false;
395
449
  }
450
+ function containsBinding(node) {
451
+ if (node.kind !== "element") return false;
452
+ const plan = propPlan(node);
453
+ if (plan && plan.bindings.length > 0) return true;
454
+ return node.children.some(containsBinding);
455
+ }
456
+ function interesting(node) {
457
+ return containsHole(node) || containsBinding(node);
458
+ }
396
459
  function addressable(el) {
397
460
  for (let i = 0; i < el.children.length; i++) {
398
461
  const child = el.children[i];
@@ -406,9 +469,10 @@ function addressable(el) {
406
469
  function planPartialTemplate(root) {
407
470
  if (!isTemplateElement(root)) return null;
408
471
  if (!addressable(root)) return null;
409
- if (!root.children.some(containsHole)) return null;
472
+ if (!interesting(root)) return null;
410
473
  const steps = [];
411
474
  const holes = [];
475
+ const bindings = [];
412
476
  let refCount = 0;
413
477
  const declare = (expr) => {
414
478
  const ref = `_n$${refCount++}`;
@@ -416,11 +480,17 @@ function planPartialTemplate(root) {
416
480
  return ref;
417
481
  };
418
482
  walk2(root, "_el$");
419
- return { html: serializeWithMarkers(root), tag: root.tag, steps, holes };
483
+ return { html: serializeWithMarkers(root), tag: root.tag, steps, holes, bindings };
420
484
  function walk2(el, elementRef) {
485
+ const plan = propPlan(el);
486
+ if (plan) {
487
+ for (const binding of plan.bindings) {
488
+ bindings.push({ ref: elementRef, prop: binding.prop, kind: binding.kind });
489
+ }
490
+ }
421
491
  let last = -1;
422
492
  el.children.forEach((child, i) => {
423
- if (containsHole(child)) last = i;
493
+ if (interesting(child)) last = i;
424
494
  });
425
495
  let previous = null;
426
496
  for (let i = 0; i <= last; i++) {
@@ -434,19 +504,21 @@ function planPartialTemplate(root) {
434
504
  continue;
435
505
  }
436
506
  const ref = declare(expr);
437
- if (child.kind === "element" && containsHole(child)) walk2(child, ref);
507
+ if (child.kind === "element" && interesting(child)) walk2(child, ref);
438
508
  previous = ref;
439
509
  }
440
510
  }
441
511
  }
442
512
  function serializeWithMarkers(el) {
443
- return serializeStatic(withSentinels(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
513
+ return serializeStatic(templateTree(el)).split(SENTINEL).join(HOLE_START + HOLE_END);
444
514
  }
445
- function withSentinels(el) {
515
+ function templateTree(el) {
516
+ const plan = propPlan(el);
446
517
  return {
447
518
  ...el,
519
+ props: plan ? plan.serialize : [],
448
520
  children: el.children.map(
449
- (child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? withSentinels(child) : child
521
+ (child) => isHole(child) ? { kind: "text", value: SENTINEL } : child.kind === "element" ? templateTree(child) : child
450
522
  )
451
523
  };
452
524
  }
@@ -719,6 +791,55 @@ function emitComponent(name, props, children, used, templates, at) {
719
791
  const mark = at ? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), ` : "";
720
792
  return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, "value")})`;
721
793
  }
794
+ function bindingTarget(binding) {
795
+ const name = binding.prop.name;
796
+ if (binding.kind === "boolAttr") return name.slice("bool:".length);
797
+ if (name.startsWith("prop:")) return name.slice("prop:".length);
798
+ if (name.startsWith("attr:")) return name.slice("attr:".length);
799
+ return htmlAttrName(name);
800
+ }
801
+ function bindingStatement(binding, used) {
802
+ const { ref, prop, kind } = binding;
803
+ const value = propValue(prop);
804
+ switch (kind) {
805
+ case "class":
806
+ used.add("setClassName");
807
+ return `setClassName(${ref}, ${value});`;
808
+ case "classList":
809
+ used.add("setClassList");
810
+ return `setClassList(${ref}, ${value}, []);`;
811
+ case "classListDynamic":
812
+ used.add("setDynamicClassList");
813
+ return `setDynamicClassList(${ref}, ${value});`;
814
+ case "style":
815
+ used.add("setStyle");
816
+ return `setStyle(${ref}, ${value}, []);`;
817
+ case "styleDynamic":
818
+ used.add("setDynamicStyle");
819
+ return `setDynamicStyle(${ref}, ${value});`;
820
+ case "attr":
821
+ used.add("setDynamicAttribute");
822
+ return `setDynamicAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
823
+ case "property":
824
+ used.add("setDynamicProperty");
825
+ return `setDynamicProperty(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
826
+ case "boolAttr":
827
+ used.add("setDynamicBoolAttribute");
828
+ return `setDynamicBoolAttribute(${ref}, ${JSON.stringify(bindingTarget(binding))}, ${value});`;
829
+ case "event":
830
+ used.add("addDelegatedEventListener");
831
+ return `addDelegatedEventListener(${ref}, ${JSON.stringify(prop.event.name)}, ${value});`;
832
+ case "nativeEvent":
833
+ used.add("addNativeEventListener");
834
+ return `addNativeEventListener(${ref}, ${JSON.stringify(prop.name.slice(3))}, ${value});`;
835
+ case "ref":
836
+ used.add("handleRef");
837
+ return `handleRef(${value}, ${ref});`;
838
+ case "use":
839
+ used.add("applyUse");
840
+ return `applyUse(${ref}, ${value});`;
841
+ }
842
+ }
722
843
  function staticAttrAssignment(el, prop) {
723
844
  const name = htmlAttrName(prop.name);
724
845
  const value = prop.literal;
@@ -769,6 +890,7 @@ function emitNode(node, used, templates) {
769
890
  `const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ""});`
770
891
  ];
771
892
  for (const step of plan.steps) lines.push(`const ${step.ref} = ${step.expr};`);
893
+ for (const binding of plan.bindings) lines.push(bindingStatement(binding, used));
772
894
  for (const hole of plan.holes) {
773
895
  lines.push(
774
896
  `insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ${paren(emitNode(hole.node, used, templates), "arrowBody")}), ${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`