@fluixi/compiler 1.0.0-alpha.92 → 1.0.0-alpha.93

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 (68) hide show
  1. package/dist/analyze/intrinsics.cjs +18 -0
  2. package/dist/analyze/intrinsics.d.ts.map +1 -1
  3. package/dist/analyze/intrinsics.js +18 -0
  4. package/dist/analyze/intrinsics.mjs +18 -0
  5. package/dist/analyze/reactive-bindings.cjs +18 -0
  6. package/dist/analyze/reactive-bindings.mjs +18 -0
  7. package/dist/analyze/static-graph.cjs +22 -4
  8. package/dist/analyze/static-graph.d.ts.map +1 -1
  9. package/dist/analyze/static-graph.js +6 -4
  10. package/dist/analyze/static-graph.mjs +22 -4
  11. package/dist/{babel-GI375YZ2.mjs → babel-RJHJQVEM.mjs} +4 -4
  12. package/dist/{chunk-QZ4CJPYP.mjs → chunk-56QSOVVL.mjs} +1 -1
  13. package/dist/{chunk-XJ5H2MXY.mjs → chunk-OD53TMER.mjs} +19 -1
  14. package/dist/{chunk-DWNWN5RH.mjs → chunk-R7PPFBCL.mjs} +1 -1
  15. package/dist/{chunk-VOUI6XPV.mjs → chunk-TJIH7TLX.mjs} +1 -1
  16. package/dist/{chunk-VC6X44FS.mjs → chunk-U7HCFLZ2.mjs} +153 -48
  17. package/dist/codegen/backends/imperative.cjs +24 -6
  18. package/dist/codegen/backends/imperative.d.ts.map +1 -1
  19. package/dist/codegen/backends/imperative.js +19 -4
  20. package/dist/codegen/backends/imperative.mjs +24 -6
  21. package/dist/frontend/babel/build-ir-lit.cjs +120 -43
  22. package/dist/frontend/babel/build-ir-lit.mjs +120 -43
  23. package/dist/frontend/babel/build-ir.cjs +120 -43
  24. package/dist/frontend/babel/build-ir.mjs +120 -43
  25. package/dist/frontend/babel/index.cjs +121 -44
  26. package/dist/frontend/babel/index.mjs +121 -44
  27. package/dist/frontend/babel/lower-template.cjs +120 -43
  28. package/dist/frontend/babel/lower-template.mjs +120 -43
  29. package/dist/frontend/babel/plugin.cjs +120 -43
  30. package/dist/frontend/babel/plugin.mjs +120 -43
  31. package/dist/index.cjs +41 -38
  32. package/dist/index.mjs +41 -38
  33. package/dist/integrations.cjs +176 -52
  34. package/dist/integrations.mjs +8 -8
  35. package/dist/lower/compile-template.cjs +41 -38
  36. package/dist/lower/compile-template.mjs +41 -38
  37. package/dist/lower/jsx.cjs +509 -6
  38. package/dist/lower/jsx.d.ts.map +1 -1
  39. package/dist/lower/jsx.js +114 -6
  40. package/dist/lower/jsx.mjs +509 -6
  41. package/dist/lower/template.cjs +40 -37
  42. package/dist/lower/template.d.ts.map +1 -1
  43. package/dist/lower/template.js +22 -30
  44. package/dist/lower/template.mjs +40 -37
  45. package/dist/resolve/builtins.cjs +1 -1
  46. package/dist/resolve/builtins.mjs +1 -1
  47. package/dist/resolve/component-globals.cjs +14 -0
  48. package/dist/resolve/component-globals.mjs +14 -0
  49. package/dist/resolve/write-globals.cjs +14 -0
  50. package/dist/resolve/write-globals.mjs +14 -0
  51. package/dist/{server-fns-BBZRZUPP.mjs → server-fns-ZFRDIQ2X.mjs} +4 -4
  52. package/dist/{server-functions-VEGLIFCE.mjs → server-functions-CTDXX67X.mjs} +3 -3
  53. package/dist/transform/index.cjs +170 -47
  54. package/dist/transform/index.mjs +170 -47
  55. package/dist/transform/intrinsics.cjs +18 -0
  56. package/dist/transform/intrinsics.mjs +18 -0
  57. package/dist/transform/source-locations.cjs +64 -3
  58. package/dist/transform/source-locations.d.cts +7 -2
  59. package/dist/transform/source-locations.d.ts +7 -2
  60. package/dist/transform/source-locations.d.ts.map +1 -1
  61. package/dist/transform/source-locations.js +68 -3
  62. package/dist/transform/source-locations.mjs +64 -3
  63. package/dist/transform/templates.cjs +169 -46
  64. package/dist/transform/templates.js +2 -2
  65. package/dist/transform/templates.mjs +169 -46
  66. package/dist/{transform-UWZ7ZGJ2.mjs → transform-QWWQDCXE.mjs} +7 -7
  67. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  68. package/package.json +9 -3
@@ -283,6 +283,7 @@ var INTRINSICS = {
283
283
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
284
284
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
285
285
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
286
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
286
287
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
287
288
  // name. Still worth having: no import to write, and when a shorter form is
288
289
  // designed, the change is this line rather than a compiler pass.
@@ -293,6 +294,19 @@ var INTRINSICS = {
293
294
  // dependency analysis must not guess at.
294
295
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
295
296
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
297
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
298
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
299
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
300
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
301
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
302
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
303
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
304
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
305
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
306
+ // handle as far as reads go: the same accessor with `set` on it.
307
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
308
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
309
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
296
310
  // An environment variable, public or server-only; the browser build refuses a secret.
297
311
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
298
312
  };
@@ -470,13 +484,16 @@ function emitComponent(name, props, children, used, templates, at) {
470
484
  used.add("createComponent");
471
485
  const spreads = props.filter((p) => p.kind === "spread");
472
486
  const normal = props.filter((p) => p.kind !== "spread");
473
- const entries = normal.map(
474
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
475
- );
487
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
476
488
  if (children.length > 0) {
477
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
489
+ bodies.push(["children", emitChildren(children, used, templates)]);
490
+ }
491
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
492
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
493
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
494
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
495
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
478
496
  }
479
- let obj = `{ ${entries.join(", ")} }`;
480
497
  if (spreads.length > 0) {
481
498
  used.add("mergeProps");
482
499
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -558,7 +575,8 @@ function emitNode(node, used, templates) {
558
575
  } else {
559
576
  used.add("spread");
560
577
  const svgFlag = node.svg ? ", isSVG: true" : "";
561
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
578
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
579
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
562
580
  parts.push(
563
581
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
564
582
  );
@@ -629,6 +647,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
629
647
  function escapeTemplate(s) {
630
648
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
631
649
  }
650
+ var CONTAINER = "container";
632
651
  function capitalize(s) {
633
652
  return s.charAt(0).toUpperCase() + s.slice(1);
634
653
  }
@@ -683,10 +702,6 @@ var Lowerer = class {
683
702
  if (hasElse) i = elseIdx;
684
703
  continue;
685
704
  }
686
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
687
- out.push(this.lowerEach(child));
688
- continue;
689
- }
690
705
  }
691
706
  const node = this.lowerNode(child);
692
707
  if (node) out.push(node);
@@ -728,13 +743,16 @@ var Lowerer = class {
728
743
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
729
744
  return node;
730
745
  }
746
+ const each = this.lowerEach(el);
747
+ const children = each ? [each] : this.lowerChildren(el.children);
748
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
731
749
  const at = this.tagAt(el);
732
750
  return {
733
751
  kind: "element",
734
752
  tag: el.tag,
735
753
  svg: el.namespace === "svg",
736
754
  props: this.lowerAttributes(el.attributes),
737
- children: this.lowerChildren(el.children),
755
+ children,
738
756
  static: false,
739
757
  ...at ? { at } : {}
740
758
  };
@@ -806,10 +824,14 @@ var Lowerer = class {
806
824
  const at = this.tagAt(el);
807
825
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
808
826
  }
809
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
827
+ /**
828
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
829
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
830
+ * it is. Null when the element has no `each`.
831
+ */
810
832
  lowerEach(el) {
811
833
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
812
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
834
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
813
835
  const each = this.hole(eachDir.hole);
814
836
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
815
837
  if (eachDir.key) {
@@ -819,12 +841,12 @@ var Lowerer = class {
819
841
  const arrow = this.itemArrow(el);
820
842
  let childrenCode;
821
843
  if (arrow) {
822
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
823
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
824
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
844
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
845
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
825
846
  } else {
826
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
827
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
847
+ const content = this.lowerChildren(el.children);
848
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
849
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
828
850
  }
829
851
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
830
852
  const at = this.tagAt(el);
@@ -841,25 +863,6 @@ var Lowerer = class {
841
863
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
842
864
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
843
865
  }
844
- /** Same element (minus each/key) but with explicit IR children. */
845
- rebuildWithChildren(el, children) {
846
- const props = this.lowerAttributes(
847
- el.attributes.filter((a) => a.kind !== "EachDirective")
848
- );
849
- if (el.kind === "Element") {
850
- const at = this.tagAt(el);
851
- return {
852
- kind: "element",
853
- tag: el.tag,
854
- svg: el.namespace === "svg",
855
- props,
856
- children,
857
- static: false,
858
- ...at ? { at } : {}
859
- };
860
- }
861
- return { kind: "component", name: el.tag, props, children };
862
- }
863
866
  // --- attributes ---------------------------------------------------------
864
867
  lowerAttributes(attrs) {
865
868
  const props = [];
@@ -1 +1 @@
1
- {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/lower/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAML,KAAK,KAAK,EAGX,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAsB,MAAM,gBAAgB,CAAC;AAM/E;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;IAClE,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,YAAY,CAAC;CACnB;AAmhBD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,KAAK,EAAE,EACf,KAAK,EAAE,YAAY,EAAE,EACrB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EACjB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAO,GACnH,MAAM,CAGR"}
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/lower/template.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAML,KAAK,KAAK,EAGX,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAsB,MAAM,gBAAgB,CAAC;AAM/E;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;IAClE,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,YAAY,CAAC;CACnB;AAwgBD;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,KAAK,EAAE,EACf,KAAK,EAAE,YAAY,EAAE,EACrB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,EACjB,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAO,GACnH,MAAM,CAGR"}
@@ -19,6 +19,8 @@ const DELEGATED = new Set([
19
19
  function escapeTemplate(s) {
20
20
  return s.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
21
21
  }
22
+ /** The tag that carries directives and renders only its content. Never a custom element: no dash. */
23
+ const CONTAINER = 'container';
22
24
  function capitalize(s) {
23
25
  return s.charAt(0).toUpperCase() + s.slice(1);
24
26
  }
@@ -88,10 +90,6 @@ class Lowerer {
88
90
  i = elseIdx;
89
91
  continue;
90
92
  }
91
- if (child.attributes.some((a) => a.kind === 'EachDirective')) {
92
- out.push(this.lowerEach(child));
93
- continue;
94
- }
95
93
  }
96
94
  const node = this.lowerNode(child);
97
95
  if (node)
@@ -137,13 +135,19 @@ class Lowerer {
137
135
  node.props.unshift({ name: 'component', kind: 'attr', expr: `() => ${paren(this.hole(isAttr.value.hole).code, 'arrowBody')}` });
138
136
  return node;
139
137
  }
138
+ // `each` repeats the element's content, never the element: `<ul each>` is one list.
139
+ const each = this.lowerEach(el);
140
+ const children = each ? [each] : this.lowerChildren(el.children);
141
+ // A `<container>` is only what it holds. It carries the directives and is never rendered.
142
+ if (el.tag === CONTAINER)
143
+ return children.length === 1 ? children[0] : { kind: 'fragment', children };
140
144
  const at = this.tagAt(el);
141
145
  return {
142
146
  kind: 'element',
143
147
  tag: el.tag,
144
148
  svg: el.namespace === 'svg',
145
149
  props: this.lowerAttributes(el.attributes),
146
- children: this.lowerChildren(el.children),
150
+ children,
147
151
  static: false,
148
152
  ...(at ? { at } : {}),
149
153
  };
@@ -233,11 +237,15 @@ class Lowerer {
233
237
  const at = this.tagAt(el);
234
238
  return { kind: 'component', name: 'Show', props, children: [child], ...(at ? { at } : {}), ...(cond.at ? { bindAt: cond.at } : {}) };
235
239
  }
236
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
240
+ /**
241
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
242
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
243
+ * it is. Null when the element has no `each`.
244
+ */
237
245
  lowerEach(el) {
238
246
  const eachDir = el.attributes.find((a) => a.kind === 'EachDirective');
239
247
  if (!eachDir || eachDir.kind !== 'EachDirective')
240
- return this.lowerNode(el);
248
+ return null;
241
249
  const each = this.hole(eachDir.hole);
242
250
  const props = [{ name: 'each', kind: 'attr', expr: each.code, reactive: each.reactive, ...(each.at ? { at: each.at } : {}) }];
243
251
  if (eachDir.key) {
@@ -248,17 +256,18 @@ class Lowerer {
248
256
  : this.hole(eachDir.key.hole).code;
249
257
  props.push({ name: 'by', kind: 'attr', expr: by });
250
258
  }
251
- // The element minus its each/key, repeated per item.
252
259
  const arrow = this.itemArrow(el);
253
260
  let childrenCode;
254
261
  if (arrow) {
255
- const bodyIR = { kind: 'expr', code: arrow.body, reactive: arrow.bodyReactive };
256
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
257
- childrenCode = `(${arrow.params.join(', ')}) => ${paren(this.emit(repeated), 'arrowBody')}`;
262
+ // A body that reads signals, `${(it) => it.label + count()}`, stays live: it is handed
263
+ // back as an accessor for the runtime to bind, as it was inside the repeated element.
264
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, 'arrowBody')}` : paren(arrow.body, 'arrowBody');
265
+ childrenCode = `(${arrow.params.join(', ')}) => ${body}`;
258
266
  }
259
267
  else {
260
- const repeated = this.stripAndLower(el, (a) => a.kind === 'EachDirective');
261
- childrenCode = `() => ${paren(this.emit(repeated), 'arrowBody')}`;
268
+ const content = this.lowerChildren(el.children);
269
+ const ir = content.length === 1 ? content[0] : { kind: 'fragment', children: content };
270
+ childrenCode = `() => ${paren(this.emit(ir), 'arrowBody')}`;
262
271
  }
263
272
  const children = [{ kind: 'expr', code: childrenCode, reactive: false }];
264
273
  const at = this.tagAt(el);
@@ -276,23 +285,6 @@ class Lowerer {
276
285
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
277
286
  return clone.kind === 'Element' ? this.lowerElement(clone) : this.lowerComponent(clone);
278
287
  }
279
- /** Same element (minus each/key) but with explicit IR children. */
280
- rebuildWithChildren(el, children) {
281
- const props = this.lowerAttributes(el.attributes.filter((a) => a.kind !== 'EachDirective'));
282
- if (el.kind === 'Element') {
283
- const at = this.tagAt(el);
284
- return {
285
- kind: 'element',
286
- tag: el.tag,
287
- svg: el.namespace === 'svg',
288
- props,
289
- children,
290
- static: false,
291
- ...(at ? { at } : {}),
292
- };
293
- }
294
- return { kind: 'component', name: el.tag, props, children };
295
- }
296
288
  // --- attributes ---------------------------------------------------------
297
289
  lowerAttributes(attrs) {
298
290
  const props = [];
@@ -261,6 +261,7 @@ var INTRINSICS = {
261
261
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
262
262
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
263
263
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
264
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
264
265
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
265
266
  // name. Still worth having: no import to write, and when a shorter form is
266
267
  // designed, the change is this line rather than a compiler pass.
@@ -271,6 +272,19 @@ var INTRINSICS = {
271
272
  // dependency analysis must not guess at.
272
273
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
273
274
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
275
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
276
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
277
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
278
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
279
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
280
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
281
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
282
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
283
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
284
+ // handle as far as reads go: the same accessor with `set` on it.
285
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
286
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
287
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
274
288
  // An environment variable, public or server-only; the browser build refuses a secret.
275
289
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
276
290
  };
@@ -448,13 +462,16 @@ function emitComponent(name, props, children, used, templates, at) {
448
462
  used.add("createComponent");
449
463
  const spreads = props.filter((p) => p.kind === "spread");
450
464
  const normal = props.filter((p) => p.kind !== "spread");
451
- const entries = normal.map(
452
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
453
- );
465
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
454
466
  if (children.length > 0) {
455
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
467
+ bodies.push(["children", emitChildren(children, used, templates)]);
468
+ }
469
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
470
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
471
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
472
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
473
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
456
474
  }
457
- let obj = `{ ${entries.join(", ")} }`;
458
475
  if (spreads.length > 0) {
459
476
  used.add("mergeProps");
460
477
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -536,7 +553,8 @@ function emitNode(node, used, templates) {
536
553
  } else {
537
554
  used.add("spread");
538
555
  const svgFlag = node.svg ? ", isSVG: true" : "";
539
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
556
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
557
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
540
558
  parts.push(
541
559
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
542
560
  );
@@ -607,6 +625,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
607
625
  function escapeTemplate(s) {
608
626
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
609
627
  }
628
+ var CONTAINER = "container";
610
629
  function capitalize(s) {
611
630
  return s.charAt(0).toUpperCase() + s.slice(1);
612
631
  }
@@ -661,10 +680,6 @@ var Lowerer = class {
661
680
  if (hasElse) i = elseIdx;
662
681
  continue;
663
682
  }
664
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
665
- out.push(this.lowerEach(child));
666
- continue;
667
- }
668
683
  }
669
684
  const node = this.lowerNode(child);
670
685
  if (node) out.push(node);
@@ -706,13 +721,16 @@ var Lowerer = class {
706
721
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
707
722
  return node;
708
723
  }
724
+ const each = this.lowerEach(el);
725
+ const children = each ? [each] : this.lowerChildren(el.children);
726
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
709
727
  const at = this.tagAt(el);
710
728
  return {
711
729
  kind: "element",
712
730
  tag: el.tag,
713
731
  svg: el.namespace === "svg",
714
732
  props: this.lowerAttributes(el.attributes),
715
- children: this.lowerChildren(el.children),
733
+ children,
716
734
  static: false,
717
735
  ...at ? { at } : {}
718
736
  };
@@ -784,10 +802,14 @@ var Lowerer = class {
784
802
  const at = this.tagAt(el);
785
803
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
786
804
  }
787
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
805
+ /**
806
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
807
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
808
+ * it is. Null when the element has no `each`.
809
+ */
788
810
  lowerEach(el) {
789
811
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
790
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
812
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
791
813
  const each = this.hole(eachDir.hole);
792
814
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
793
815
  if (eachDir.key) {
@@ -797,12 +819,12 @@ var Lowerer = class {
797
819
  const arrow = this.itemArrow(el);
798
820
  let childrenCode;
799
821
  if (arrow) {
800
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
801
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
802
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
822
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
823
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
803
824
  } else {
804
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
805
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
825
+ const content = this.lowerChildren(el.children);
826
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
827
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
806
828
  }
807
829
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
808
830
  const at = this.tagAt(el);
@@ -819,25 +841,6 @@ var Lowerer = class {
819
841
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
820
842
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
821
843
  }
822
- /** Same element (minus each/key) but with explicit IR children. */
823
- rebuildWithChildren(el, children) {
824
- const props = this.lowerAttributes(
825
- el.attributes.filter((a) => a.kind !== "EachDirective")
826
- );
827
- if (el.kind === "Element") {
828
- const at = this.tagAt(el);
829
- return {
830
- kind: "element",
831
- tag: el.tag,
832
- svg: el.namespace === "svg",
833
- props,
834
- children,
835
- static: false,
836
- ...at ? { at } : {}
837
- };
838
- }
839
- return { kind: "component", name: el.tag, props, children };
840
- }
841
844
  // --- attributes ---------------------------------------------------------
842
845
  lowerAttributes(attrs) {
843
846
  const props = [];
@@ -1,4 +1,4 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1,4 +1,4 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/resolve/builtins.ts
4
4
  var DOM_CONTROL_FLOW = [
@@ -124,6 +124,7 @@ var INTRINSICS = {
124
124
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
125
125
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
126
126
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
127
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
127
128
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
128
129
  // name. Still worth having: no import to write, and when a shorter form is
129
130
  // designed, the change is this line rather than a compiler pass.
@@ -134,6 +135,19 @@ var INTRINSICS = {
134
135
  // dependency analysis must not guess at.
135
136
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
136
137
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
138
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
139
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
140
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
141
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
142
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
143
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
144
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
145
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
146
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
147
+ // handle as far as reads go: the same accessor with `set` on it.
148
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
149
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
150
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
137
151
  // An environment variable, public or server-only; the browser build refuses a secret.
138
152
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
139
153
  };
@@ -98,6 +98,7 @@ var INTRINSICS = {
98
98
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
99
99
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
100
100
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
101
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
101
102
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
102
103
  // name. Still worth having: no import to write, and when a shorter form is
103
104
  // designed, the change is this line rather than a compiler pass.
@@ -108,6 +109,19 @@ var INTRINSICS = {
108
109
  // dependency analysis must not guess at.
109
110
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
110
111
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
112
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
113
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
114
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
115
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
116
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
117
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
118
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
119
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
120
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
121
+ // handle as far as reads go: the same accessor with `set` on it.
122
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
123
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
124
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
111
125
  // An environment variable, public or server-only; the browser build refuses a secret.
112
126
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
113
127
  };
@@ -128,6 +128,7 @@ var INTRINSICS = {
128
128
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
129
129
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
130
130
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
131
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
131
132
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
132
133
  // name. Still worth having: no import to write, and when a shorter form is
133
134
  // designed, the change is this line rather than a compiler pass.
@@ -138,6 +139,19 @@ var INTRINSICS = {
138
139
  // dependency analysis must not guess at.
139
140
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
140
141
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
142
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
143
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
144
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
145
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
146
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
147
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
148
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
149
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
150
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
151
+ // handle as far as reads go: the same accessor with `set` on it.
152
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
153
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
154
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
141
155
  // An environment variable, public or server-only; the browser build refuses a secret.
142
156
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
143
157
  };
@@ -102,6 +102,7 @@ var INTRINSICS = {
102
102
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
103
103
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
104
104
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
105
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
105
106
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
106
107
  // name. Still worth having: no import to write, and when a shorter form is
107
108
  // designed, the change is this line rather than a compiler pass.
@@ -112,6 +113,19 @@ var INTRINSICS = {
112
113
  // dependency analysis must not guess at.
113
114
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
114
115
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
116
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
117
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
118
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
119
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
120
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
121
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
122
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
123
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
124
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
125
+ // handle as far as reads go: the same accessor with `set` on it.
126
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
127
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
128
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
115
129
  // An environment variable, public or server-only; the browser build refuses a secret.
116
130
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
117
131
  };
@@ -1,11 +1,11 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  import {
3
3
  MagicString
4
- } from "./chunk-DWNWN5RH.mjs";
4
+ } from "./chunk-R7PPFBCL.mjs";
5
5
  import {
6
6
  serverFnId
7
- } from "./chunk-QZ4CJPYP.mjs";
8
- import "./chunk-VOUI6XPV.mjs";
7
+ } from "./chunk-56QSOVVL.mjs";
8
+ import "./chunk-TJIH7TLX.mjs";
9
9
 
10
10
  // src/transform/server-fns.ts
11
11
  import { parse } from "@babel/parser";
@@ -1,8 +1,8 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  import {
3
3
  serverFnId
4
- } from "./chunk-QZ4CJPYP.mjs";
5
- import "./chunk-VOUI6XPV.mjs";
4
+ } from "./chunk-56QSOVVL.mjs";
5
+ import "./chunk-TJIH7TLX.mjs";
6
6
 
7
7
  // src/frontend/babel/server-functions.ts
8
8
  import { transformSync } from "@babel/core";