@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
@@ -1519,6 +1519,7 @@ var INTRINSICS = {
1519
1519
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
1520
1520
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
1521
1521
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
1522
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
1522
1523
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
1523
1524
  // name. Still worth having: no import to write, and when a shorter form is
1524
1525
  // designed, the change is this line rather than a compiler pass.
@@ -1529,6 +1530,19 @@ var INTRINSICS = {
1529
1530
  // dependency analysis must not guess at.
1530
1531
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
1531
1532
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
1533
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
1534
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
1535
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
1536
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
1537
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
1538
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
1539
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
1540
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
1541
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
1542
+ // handle as far as reads go: the same accessor with `set` on it.
1543
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
1544
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
1545
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
1532
1546
  // An environment variable, public or server-only; the browser build refuses a secret.
1533
1547
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
1534
1548
  };
@@ -1541,8 +1555,11 @@ var RUNTIME_PRIMITIVES = {
1541
1555
  createMemo: "memo-accessor",
1542
1556
  createEffect: "effect",
1543
1557
  createRenderEffect: "effect",
1558
+ renderEffect: "effect",
1559
+ computed: "effect",
1544
1560
  createStore: "reactive-object",
1545
1561
  resource: "resource-handle",
1562
+ cachedResource: "resource-handle",
1546
1563
  createResource: "reactive-object",
1547
1564
  createSelector: "memo-accessor",
1548
1565
  createDeferred: "memo-accessor"
@@ -1551,6 +1568,7 @@ var PRIMITIVE_MODULES = [
1551
1568
  "@fluixi/reactive",
1552
1569
  "@fluixi/reactive/signal",
1553
1570
  "@fluixi/reactive/store",
1571
+ "@fluixi/reactive/cached-resource",
1554
1572
  "@fluixi/core"
1555
1573
  ];
1556
1574
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n2) => n2.slice(1)).join("|")})\\s*\\(`);
@@ -1686,6 +1704,7 @@ function resolveReactiveBindings(program) {
1686
1704
  // src/transform/source-locations.ts
1687
1705
  var SOURCE_MARKER = "__fx_source";
1688
1706
  var COMPONENT_MARKER = "__fx_component";
1707
+ var CLASS_MARKER = "__fx_class";
1689
1708
  var HOLE_MARKER = "__fx_hole";
1690
1709
  var markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
1691
1710
  var PROPS_MARKER = "__fx_props";
@@ -1703,11 +1722,12 @@ var RUNTIME_PRIMITIVES2 = /* @__PURE__ */ new Set([
1703
1722
  "createStore",
1704
1723
  "createResource"
1705
1724
  ]);
1706
- function mayHaveReactiveCall(code) {
1725
+ function mayHaveSourceMarks(code) {
1707
1726
  for (const name of RUNTIME_PRIMITIVES2) {
1708
1727
  if (code.includes(name)) return true;
1709
1728
  }
1710
- return code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store");
1729
+ if (code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store")) return true;
1730
+ return /\bclass\b[^{;]*\bextends\b/.test(code);
1711
1731
  }
1712
1732
  var SINGLE_STATEMENT_PARENTS = /* @__PURE__ */ new Set([
1713
1733
  "IfStatement",
@@ -1726,6 +1746,7 @@ function isLoneBranch(statement, parent) {
1726
1746
  function isExport(node) {
1727
1747
  return node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration";
1728
1748
  }
1749
+ var CLASS_FIELDS = /* @__PURE__ */ new Set(["PropertyDefinition", "ClassProperty", "ClassPrivateProperty", "AccessorProperty"]);
1729
1750
  function isStatement(node) {
1730
1751
  return node.type.endsWith("Statement") || node.type === "VariableDeclaration";
1731
1752
  }
@@ -1746,6 +1767,14 @@ function walk3(node, visit, stack = []) {
1746
1767
  }
1747
1768
  function nameFor(stack) {
1748
1769
  const declarator = stack[stack.length - 1];
1770
+ if (declarator && CLASS_FIELDS.has(declarator.type)) {
1771
+ const key = declarator.key;
1772
+ if (key?.type === "Identifier" || key?.type === "PrivateIdentifier" || key?.type === "PrivateName") {
1773
+ const name = key.name ?? key.id?.name;
1774
+ return name;
1775
+ }
1776
+ return void 0;
1777
+ }
1749
1778
  if (declarator?.type !== "VariableDeclarator") return void 0;
1750
1779
  const id = declarator.id;
1751
1780
  if (id?.type === "Identifier") return id.name;
@@ -1794,6 +1823,17 @@ function collectSourceEdits(program, filename) {
1794
1823
  });
1795
1824
  }
1796
1825
  }
1826
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.superClass && !node.declare) {
1827
+ const at = node.loc?.start;
1828
+ const body = node.body;
1829
+ if (at && body) {
1830
+ edits.push({
1831
+ start: body.start + 1,
1832
+ end: body.start + 1,
1833
+ code: `static{globalThis.${CLASS_MARKER}?.(this,${JSON.stringify(filename)},${at.line},${at.column});}`
1834
+ });
1835
+ }
1836
+ }
1797
1837
  if (node.type !== "CallExpression") return;
1798
1838
  const callee = node.callee;
1799
1839
  if (callee?.type !== "Identifier") return;
@@ -1803,6 +1843,26 @@ function collectSourceEdits(program, filename) {
1803
1843
  if (!intrinsic && !imported) return;
1804
1844
  const position = node.loc?.start;
1805
1845
  if (!position) return;
1846
+ for (let i = stack.length - 1; i >= 0; i--) {
1847
+ const up = stack[i];
1848
+ if (isStatement(up)) break;
1849
+ if (CLASS_FIELDS.has(up.type)) {
1850
+ if (claimed.has(node.start)) return;
1851
+ claimed.add(node.start);
1852
+ const label2 = nameFor(stack);
1853
+ const own = !up.static;
1854
+ const args2 = [
1855
+ JSON.stringify(filename),
1856
+ String(position.line),
1857
+ String(position.column),
1858
+ label2 ? JSON.stringify(label2) : own ? "undefined" : void 0,
1859
+ own ? "this" : void 0
1860
+ ].filter((a) => a !== void 0).join(",");
1861
+ edits.push({ start: node.start, end: node.start, code: `(globalThis.${SOURCE_MARKER}?.(${args2}),` });
1862
+ edits.push({ start: node.end, end: node.end, code: ")" });
1863
+ return;
1864
+ }
1865
+ }
1806
1866
  let statement;
1807
1867
  let parent;
1808
1868
  let index = -1;
@@ -2008,13 +2068,16 @@ function emitComponent(name, props, children, used, templates, at) {
2008
2068
  used.add("createComponent");
2009
2069
  const spreads = props.filter((p) => p.kind === "spread");
2010
2070
  const normal = props.filter((p) => p.kind !== "spread");
2011
- const entries = normal.map(
2012
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
2013
- );
2071
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
2014
2072
  if (children.length > 0) {
2015
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
2073
+ bodies.push(["children", emitChildren(children, used, templates)]);
2074
+ }
2075
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
2076
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
2077
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
2078
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
2079
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
2016
2080
  }
2017
- let obj = `{ ${entries.join(", ")} }`;
2018
2081
  if (spreads.length > 0) {
2019
2082
  used.add("mergeProps");
2020
2083
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -2096,7 +2159,8 @@ function emitNode(node, used, templates) {
2096
2159
  } else {
2097
2160
  used.add("spread");
2098
2161
  const svgFlag = node.svg ? ", isSVG: true" : "";
2099
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
2162
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
2163
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
2100
2164
  parts.push(
2101
2165
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
2102
2166
  );
@@ -2229,6 +2293,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
2229
2293
  function escapeTemplate(s) {
2230
2294
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
2231
2295
  }
2296
+ var CONTAINER = "container";
2232
2297
  function capitalize(s) {
2233
2298
  return s.charAt(0).toUpperCase() + s.slice(1);
2234
2299
  }
@@ -2283,10 +2348,6 @@ var Lowerer = class {
2283
2348
  if (hasElse) i = elseIdx;
2284
2349
  continue;
2285
2350
  }
2286
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
2287
- out.push(this.lowerEach(child));
2288
- continue;
2289
- }
2290
2351
  }
2291
2352
  const node = this.lowerNode(child);
2292
2353
  if (node) out.push(node);
@@ -2328,13 +2389,16 @@ var Lowerer = class {
2328
2389
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
2329
2390
  return node;
2330
2391
  }
2392
+ const each = this.lowerEach(el);
2393
+ const children = each ? [each] : this.lowerChildren(el.children);
2394
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
2331
2395
  const at = this.tagAt(el);
2332
2396
  return {
2333
2397
  kind: "element",
2334
2398
  tag: el.tag,
2335
2399
  svg: el.namespace === "svg",
2336
2400
  props: this.lowerAttributes(el.attributes),
2337
- children: this.lowerChildren(el.children),
2401
+ children,
2338
2402
  static: false,
2339
2403
  ...at ? { at } : {}
2340
2404
  };
@@ -2406,10 +2470,14 @@ var Lowerer = class {
2406
2470
  const at = this.tagAt(el);
2407
2471
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
2408
2472
  }
2409
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
2473
+ /**
2474
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
2475
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
2476
+ * it is. Null when the element has no `each`.
2477
+ */
2410
2478
  lowerEach(el) {
2411
2479
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
2412
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
2480
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
2413
2481
  const each = this.hole(eachDir.hole);
2414
2482
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
2415
2483
  if (eachDir.key) {
@@ -2419,12 +2487,12 @@ var Lowerer = class {
2419
2487
  const arrow = this.itemArrow(el);
2420
2488
  let childrenCode;
2421
2489
  if (arrow) {
2422
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
2423
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
2424
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
2490
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
2491
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
2425
2492
  } else {
2426
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
2427
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
2493
+ const content = this.lowerChildren(el.children);
2494
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
2495
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
2428
2496
  }
2429
2497
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
2430
2498
  const at = this.tagAt(el);
@@ -2441,25 +2509,6 @@ var Lowerer = class {
2441
2509
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
2442
2510
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
2443
2511
  }
2444
- /** Same element (minus each/key) but with explicit IR children. */
2445
- rebuildWithChildren(el, children) {
2446
- const props = this.lowerAttributes(
2447
- el.attributes.filter((a) => a.kind !== "EachDirective")
2448
- );
2449
- if (el.kind === "Element") {
2450
- const at = this.tagAt(el);
2451
- return {
2452
- kind: "element",
2453
- tag: el.tag,
2454
- svg: el.namespace === "svg",
2455
- props,
2456
- children,
2457
- static: false,
2458
- ...at ? { at } : {}
2459
- };
2460
- }
2461
- return { kind: "component", name: el.tag, props, children };
2462
- }
2463
2512
  // --- attributes ---------------------------------------------------------
2464
2513
  lowerAttributes(attrs) {
2465
2514
  const props = [];
@@ -2778,6 +2827,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
2778
2827
  "mouseup"
2779
2828
  ]);
2780
2829
  var isReactive = (node) => isReactiveShape(shapeOf(node));
2830
+ var CONTAINER2 = "container";
2831
+ function directive(node, name) {
2832
+ if (node.type !== "JSXElement") return void 0;
2833
+ const opening = node.openingElement;
2834
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
2835
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
2836
+ }
2837
+ function directiveExpr(attr) {
2838
+ const v = attr.value;
2839
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
2840
+ }
2841
+ function emitIR(ir, ctx) {
2842
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
2843
+ for (const s of imports) ctx.used.add(s);
2844
+ return inlineTemplates(code, templates);
2845
+ }
2781
2846
  var posOf = (node, ctx) => {
2782
2847
  if (!ctx.sourceFile) return void 0;
2783
2848
  const at = node.loc?.start;
@@ -2886,7 +2951,16 @@ function buildProps(attrs, ctx) {
2886
2951
  }
2887
2952
  function buildChildren(children, ctx) {
2888
2953
  const out = [];
2889
- for (const child of children) {
2954
+ for (let i = 0; i < children.length; i++) {
2955
+ const child = children[i];
2956
+ if (directive(child, "if")) {
2957
+ let next = i + 1;
2958
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
2959
+ const fallback = directive(children[next], "else") ? children[next] : null;
2960
+ out.push(buildIf(child, fallback, ctx));
2961
+ if (fallback) i = next;
2962
+ continue;
2963
+ }
2890
2964
  if (child.type === "JSXText") {
2891
2965
  const text = cleanTemplateText(child.value);
2892
2966
  if (text) out.push({ kind: "text", value: text });
@@ -2923,19 +2997,68 @@ function readLoad(attrs) {
2923
2997
  }
2924
2998
  return void 0;
2925
2999
  }
3000
+ function buildIf(node, fallback, ctx) {
3001
+ const cond = directiveExpr(directive(node, "if"));
3002
+ const props = [
3003
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
3004
+ ];
3005
+ if (fallback) {
3006
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
3007
+ }
3008
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
3009
+ }
3010
+ function buildEach(node, ctx) {
3011
+ const attr = directive(node, "each");
3012
+ if (!attr) return null;
3013
+ const list = directiveExpr(attr);
3014
+ const props = [
3015
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
3016
+ ];
3017
+ const key = directive(node, "key");
3018
+ if (key) {
3019
+ const keyExpr = directiveExpr(key);
3020
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
3021
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
3022
+ }
3023
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
3024
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
3025
+ let render;
3026
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
3027
+ const fn = ctx.code(only);
3028
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
3029
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
3030
+ } else {
3031
+ const built = buildChildren(node.children, ctx);
3032
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
3033
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
3034
+ }
3035
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
3036
+ }
3037
+ function buildElement(node, ctx, handled) {
3038
+ const skip = new Set(handled);
3039
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
3040
+ const attributes = node.openingElement.attributes.filter(
3041
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
3042
+ );
3043
+ const { tag } = tagInfo(node.openingElement.name, ctx);
3044
+ const each = buildEach(node, ctx);
3045
+ const children = each ? [each] : buildChildren(node.children, ctx);
3046
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
3047
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
3048
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
3049
+ }
2926
3050
  function buildNode(node, ctx) {
2927
3051
  if (node.type === "JSXFragment") {
2928
3052
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
2929
3053
  }
3054
+ if (directive(node, "if")) return buildIf(node, null, ctx);
2930
3055
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
3056
+ if (!component) return buildElement(node, ctx, []);
2931
3057
  const props = buildProps(node.openingElement.attributes, ctx);
2932
3058
  const children = buildChildren(node.children, ctx);
2933
3059
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
2934
- if (component) {
2935
- const load = readLoad(node.openingElement.attributes);
2936
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
2937
- }
2938
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
3060
+ const load = readLoad(node.openingElement.attributes);
3061
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
2939
3062
  }
2940
3063
  function buildJsxIR(node, ctx) {
2941
3064
  const ir = buildNode(node, ctx);
@@ -3580,7 +3703,7 @@ function transformTemplates(code, id, options = {}) {
3580
3703
  const wantsJsx = format !== "lit";
3581
3704
  const mayHaveTemplate = wantsLit && (code.includes(`${litTag}\``) || code.includes("svg`"));
3582
3705
  const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
3583
- const wantsSources = options.sourceLocations === true && mayHaveReactiveCall(code);
3706
+ const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
3584
3707
  const sourceName = relativeToRoot(id, options.sourceRoot);
3585
3708
  if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources) return null;
3586
3709
  const ast = parse2(code, {
@@ -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
  import {
3
3
  analyzeStatic,
4
4
  buildJsxIR,
@@ -10,11 +10,11 @@ import {
10
10
  isDeferred,
11
11
  isReactiveShape,
12
12
  lowerTemplate,
13
- mayHaveReactiveCall,
13
+ mayHaveSourceMarks,
14
14
  resolveComponentSources,
15
15
  resolveReactiveBindings,
16
16
  shapeOf
17
- } from "./chunk-VC6X44FS.mjs";
17
+ } from "./chunk-U7HCFLZ2.mjs";
18
18
  import {
19
19
  CORE_CONTROL_FLOW,
20
20
  DOM_CONTROL_FLOW,
@@ -23,11 +23,11 @@ import {
23
23
  createComponentResolver,
24
24
  isIntrinsicName,
25
25
  mayHaveIntrinsic
26
- } from "./chunk-XJ5H2MXY.mjs";
26
+ } from "./chunk-OD53TMER.mjs";
27
27
  import {
28
28
  MagicString
29
- } from "./chunk-DWNWN5RH.mjs";
30
- import "./chunk-VOUI6XPV.mjs";
29
+ } from "./chunk-R7PPFBCL.mjs";
30
+ import "./chunk-TJIH7TLX.mjs";
31
31
 
32
32
  // src/transform/templates.ts
33
33
  import { parse as parse2 } from "@babel/parser";
@@ -720,7 +720,7 @@ function transformTemplates(code, id, options = {}) {
720
720
  const wantsJsx = format !== "lit";
721
721
  const mayHaveTemplate = wantsLit && (code.includes(`${litTag}\``) || code.includes("svg`"));
722
722
  const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
723
- const wantsSources = options.sourceLocations === true && mayHaveReactiveCall(code);
723
+ const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
724
724
  const sourceName = relativeToRoot(id, options.sourceRoot);
725
725
  if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources) return null;
726
726
  const ast = parse2(code, {