@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
@@ -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/transform/templates.ts
4
4
  import { parse as parse2 } from "@babel/parser";
@@ -1521,6 +1521,7 @@ var INTRINSICS = {
1521
1521
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
1522
1522
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
1523
1523
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
1524
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
1524
1525
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
1525
1526
  // name. Still worth having: no import to write, and when a shorter form is
1526
1527
  // designed, the change is this line rather than a compiler pass.
@@ -1531,6 +1532,19 @@ var INTRINSICS = {
1531
1532
  // dependency analysis must not guess at.
1532
1533
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
1533
1534
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
1535
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
1536
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
1537
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
1538
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
1539
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
1540
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
1541
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
1542
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
1543
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
1544
+ // handle as far as reads go: the same accessor with `set` on it.
1545
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
1546
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
1547
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
1534
1548
  // An environment variable, public or server-only; the browser build refuses a secret.
1535
1549
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
1536
1550
  };
@@ -1543,8 +1557,11 @@ var RUNTIME_PRIMITIVES = {
1543
1557
  createMemo: "memo-accessor",
1544
1558
  createEffect: "effect",
1545
1559
  createRenderEffect: "effect",
1560
+ renderEffect: "effect",
1561
+ computed: "effect",
1546
1562
  createStore: "reactive-object",
1547
1563
  resource: "resource-handle",
1564
+ cachedResource: "resource-handle",
1548
1565
  createResource: "reactive-object",
1549
1566
  createSelector: "memo-accessor",
1550
1567
  createDeferred: "memo-accessor"
@@ -1553,6 +1570,7 @@ var PRIMITIVE_MODULES = [
1553
1570
  "@fluixi/reactive",
1554
1571
  "@fluixi/reactive/signal",
1555
1572
  "@fluixi/reactive/store",
1573
+ "@fluixi/reactive/cached-resource",
1556
1574
  "@fluixi/core"
1557
1575
  ];
1558
1576
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n2) => n2.slice(1)).join("|")})\\s*\\(`);
@@ -1688,6 +1706,7 @@ function resolveReactiveBindings(program) {
1688
1706
  // src/transform/source-locations.ts
1689
1707
  var SOURCE_MARKER = "__fx_source";
1690
1708
  var COMPONENT_MARKER = "__fx_component";
1709
+ var CLASS_MARKER = "__fx_class";
1691
1710
  var HOLE_MARKER = "__fx_hole";
1692
1711
  var markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
1693
1712
  var PROPS_MARKER = "__fx_props";
@@ -1705,11 +1724,12 @@ var RUNTIME_PRIMITIVES2 = /* @__PURE__ */ new Set([
1705
1724
  "createStore",
1706
1725
  "createResource"
1707
1726
  ]);
1708
- function mayHaveReactiveCall(code) {
1727
+ function mayHaveSourceMarks(code) {
1709
1728
  for (const name of RUNTIME_PRIMITIVES2) {
1710
1729
  if (code.includes(name)) return true;
1711
1730
  }
1712
- return code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store");
1731
+ if (code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store")) return true;
1732
+ return /\bclass\b[^{;]*\bextends\b/.test(code);
1713
1733
  }
1714
1734
  var SINGLE_STATEMENT_PARENTS = /* @__PURE__ */ new Set([
1715
1735
  "IfStatement",
@@ -1728,6 +1748,7 @@ function isLoneBranch(statement, parent) {
1728
1748
  function isExport(node) {
1729
1749
  return node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration";
1730
1750
  }
1751
+ var CLASS_FIELDS = /* @__PURE__ */ new Set(["PropertyDefinition", "ClassProperty", "ClassPrivateProperty", "AccessorProperty"]);
1731
1752
  function isStatement(node) {
1732
1753
  return node.type.endsWith("Statement") || node.type === "VariableDeclaration";
1733
1754
  }
@@ -1748,6 +1769,14 @@ function walk3(node, visit, stack = []) {
1748
1769
  }
1749
1770
  function nameFor(stack) {
1750
1771
  const declarator = stack[stack.length - 1];
1772
+ if (declarator && CLASS_FIELDS.has(declarator.type)) {
1773
+ const key = declarator.key;
1774
+ if (key?.type === "Identifier" || key?.type === "PrivateIdentifier" || key?.type === "PrivateName") {
1775
+ const name = key.name ?? key.id?.name;
1776
+ return name;
1777
+ }
1778
+ return void 0;
1779
+ }
1751
1780
  if (declarator?.type !== "VariableDeclarator") return void 0;
1752
1781
  const id = declarator.id;
1753
1782
  if (id?.type === "Identifier") return id.name;
@@ -1796,6 +1825,17 @@ function collectSourceEdits(program, filename) {
1796
1825
  });
1797
1826
  }
1798
1827
  }
1828
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.superClass && !node.declare) {
1829
+ const at = node.loc?.start;
1830
+ const body = node.body;
1831
+ if (at && body) {
1832
+ edits.push({
1833
+ start: body.start + 1,
1834
+ end: body.start + 1,
1835
+ code: `static{globalThis.${CLASS_MARKER}?.(this,${JSON.stringify(filename)},${at.line},${at.column});}`
1836
+ });
1837
+ }
1838
+ }
1799
1839
  if (node.type !== "CallExpression") return;
1800
1840
  const callee = node.callee;
1801
1841
  if (callee?.type !== "Identifier") return;
@@ -1805,6 +1845,26 @@ function collectSourceEdits(program, filename) {
1805
1845
  if (!intrinsic && !imported) return;
1806
1846
  const position = node.loc?.start;
1807
1847
  if (!position) return;
1848
+ for (let i = stack.length - 1; i >= 0; i--) {
1849
+ const up = stack[i];
1850
+ if (isStatement(up)) break;
1851
+ if (CLASS_FIELDS.has(up.type)) {
1852
+ if (claimed.has(node.start)) return;
1853
+ claimed.add(node.start);
1854
+ const label2 = nameFor(stack);
1855
+ const own = !up.static;
1856
+ const args2 = [
1857
+ JSON.stringify(filename),
1858
+ String(position.line),
1859
+ String(position.column),
1860
+ label2 ? JSON.stringify(label2) : own ? "undefined" : void 0,
1861
+ own ? "this" : void 0
1862
+ ].filter((a) => a !== void 0).join(",");
1863
+ edits.push({ start: node.start, end: node.start, code: `(globalThis.${SOURCE_MARKER}?.(${args2}),` });
1864
+ edits.push({ start: node.end, end: node.end, code: ")" });
1865
+ return;
1866
+ }
1867
+ }
1808
1868
  let statement;
1809
1869
  let parent;
1810
1870
  let index = -1;
@@ -2010,13 +2070,16 @@ function emitComponent(name, props, children, used, templates, at) {
2010
2070
  used.add("createComponent");
2011
2071
  const spreads = props.filter((p) => p.kind === "spread");
2012
2072
  const normal = props.filter((p) => p.kind !== "spread");
2013
- const entries = normal.map(
2014
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
2015
- );
2073
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
2016
2074
  if (children.length > 0) {
2017
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
2075
+ bodies.push(["children", emitChildren(children, used, templates)]);
2076
+ }
2077
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
2078
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
2079
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
2080
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
2081
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
2018
2082
  }
2019
- let obj = `{ ${entries.join(", ")} }`;
2020
2083
  if (spreads.length > 0) {
2021
2084
  used.add("mergeProps");
2022
2085
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -2098,7 +2161,8 @@ function emitNode(node, used, templates) {
2098
2161
  } else {
2099
2162
  used.add("spread");
2100
2163
  const svgFlag = node.svg ? ", isSVG: true" : "";
2101
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
2164
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
2165
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
2102
2166
  parts.push(
2103
2167
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
2104
2168
  );
@@ -2231,6 +2295,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
2231
2295
  function escapeTemplate(s) {
2232
2296
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
2233
2297
  }
2298
+ var CONTAINER = "container";
2234
2299
  function capitalize(s) {
2235
2300
  return s.charAt(0).toUpperCase() + s.slice(1);
2236
2301
  }
@@ -2285,10 +2350,6 @@ var Lowerer = class {
2285
2350
  if (hasElse) i = elseIdx;
2286
2351
  continue;
2287
2352
  }
2288
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
2289
- out.push(this.lowerEach(child));
2290
- continue;
2291
- }
2292
2353
  }
2293
2354
  const node = this.lowerNode(child);
2294
2355
  if (node) out.push(node);
@@ -2330,13 +2391,16 @@ var Lowerer = class {
2330
2391
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
2331
2392
  return node;
2332
2393
  }
2394
+ const each = this.lowerEach(el);
2395
+ const children = each ? [each] : this.lowerChildren(el.children);
2396
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
2333
2397
  const at = this.tagAt(el);
2334
2398
  return {
2335
2399
  kind: "element",
2336
2400
  tag: el.tag,
2337
2401
  svg: el.namespace === "svg",
2338
2402
  props: this.lowerAttributes(el.attributes),
2339
- children: this.lowerChildren(el.children),
2403
+ children,
2340
2404
  static: false,
2341
2405
  ...at ? { at } : {}
2342
2406
  };
@@ -2408,10 +2472,14 @@ var Lowerer = class {
2408
2472
  const at = this.tagAt(el);
2409
2473
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
2410
2474
  }
2411
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
2475
+ /**
2476
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
2477
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
2478
+ * it is. Null when the element has no `each`.
2479
+ */
2412
2480
  lowerEach(el) {
2413
2481
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
2414
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
2482
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
2415
2483
  const each = this.hole(eachDir.hole);
2416
2484
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
2417
2485
  if (eachDir.key) {
@@ -2421,12 +2489,12 @@ var Lowerer = class {
2421
2489
  const arrow = this.itemArrow(el);
2422
2490
  let childrenCode;
2423
2491
  if (arrow) {
2424
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
2425
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
2426
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
2492
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
2493
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
2427
2494
  } else {
2428
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
2429
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
2495
+ const content = this.lowerChildren(el.children);
2496
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
2497
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
2430
2498
  }
2431
2499
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
2432
2500
  const at = this.tagAt(el);
@@ -2443,25 +2511,6 @@ var Lowerer = class {
2443
2511
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
2444
2512
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
2445
2513
  }
2446
- /** Same element (minus each/key) but with explicit IR children. */
2447
- rebuildWithChildren(el, children) {
2448
- const props = this.lowerAttributes(
2449
- el.attributes.filter((a) => a.kind !== "EachDirective")
2450
- );
2451
- if (el.kind === "Element") {
2452
- const at = this.tagAt(el);
2453
- return {
2454
- kind: "element",
2455
- tag: el.tag,
2456
- svg: el.namespace === "svg",
2457
- props,
2458
- children,
2459
- static: false,
2460
- ...at ? { at } : {}
2461
- };
2462
- }
2463
- return { kind: "component", name: el.tag, props, children };
2464
- }
2465
2514
  // --- attributes ---------------------------------------------------------
2466
2515
  lowerAttributes(attrs) {
2467
2516
  const props = [];
@@ -2780,6 +2829,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
2780
2829
  "mouseup"
2781
2830
  ]);
2782
2831
  var isReactive = (node) => isReactiveShape(shapeOf(node));
2832
+ var CONTAINER2 = "container";
2833
+ function directive(node, name) {
2834
+ if (node.type !== "JSXElement") return void 0;
2835
+ const opening = node.openingElement;
2836
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
2837
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
2838
+ }
2839
+ function directiveExpr(attr) {
2840
+ const v = attr.value;
2841
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
2842
+ }
2843
+ function emitIR(ir, ctx) {
2844
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
2845
+ for (const s of imports) ctx.used.add(s);
2846
+ return inlineTemplates(code, templates);
2847
+ }
2783
2848
  var posOf = (node, ctx) => {
2784
2849
  if (!ctx.sourceFile) return void 0;
2785
2850
  const at = node.loc?.start;
@@ -2888,7 +2953,16 @@ function buildProps(attrs, ctx) {
2888
2953
  }
2889
2954
  function buildChildren(children, ctx) {
2890
2955
  const out = [];
2891
- for (const child of children) {
2956
+ for (let i = 0; i < children.length; i++) {
2957
+ const child = children[i];
2958
+ if (directive(child, "if")) {
2959
+ let next = i + 1;
2960
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
2961
+ const fallback = directive(children[next], "else") ? children[next] : null;
2962
+ out.push(buildIf(child, fallback, ctx));
2963
+ if (fallback) i = next;
2964
+ continue;
2965
+ }
2892
2966
  if (child.type === "JSXText") {
2893
2967
  const text = cleanTemplateText(child.value);
2894
2968
  if (text) out.push({ kind: "text", value: text });
@@ -2925,19 +2999,68 @@ function readLoad(attrs) {
2925
2999
  }
2926
3000
  return void 0;
2927
3001
  }
3002
+ function buildIf(node, fallback, ctx) {
3003
+ const cond = directiveExpr(directive(node, "if"));
3004
+ const props = [
3005
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
3006
+ ];
3007
+ if (fallback) {
3008
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
3009
+ }
3010
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
3011
+ }
3012
+ function buildEach(node, ctx) {
3013
+ const attr = directive(node, "each");
3014
+ if (!attr) return null;
3015
+ const list = directiveExpr(attr);
3016
+ const props = [
3017
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
3018
+ ];
3019
+ const key = directive(node, "key");
3020
+ if (key) {
3021
+ const keyExpr = directiveExpr(key);
3022
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
3023
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
3024
+ }
3025
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
3026
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
3027
+ let render;
3028
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
3029
+ const fn = ctx.code(only);
3030
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
3031
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
3032
+ } else {
3033
+ const built = buildChildren(node.children, ctx);
3034
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
3035
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
3036
+ }
3037
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
3038
+ }
3039
+ function buildElement(node, ctx, handled) {
3040
+ const skip = new Set(handled);
3041
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
3042
+ const attributes = node.openingElement.attributes.filter(
3043
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
3044
+ );
3045
+ const { tag } = tagInfo(node.openingElement.name, ctx);
3046
+ const each = buildEach(node, ctx);
3047
+ const children = each ? [each] : buildChildren(node.children, ctx);
3048
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
3049
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
3050
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
3051
+ }
2928
3052
  function buildNode(node, ctx) {
2929
3053
  if (node.type === "JSXFragment") {
2930
3054
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
2931
3055
  }
3056
+ if (directive(node, "if")) return buildIf(node, null, ctx);
2932
3057
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
3058
+ if (!component) return buildElement(node, ctx, []);
2933
3059
  const props = buildProps(node.openingElement.attributes, ctx);
2934
3060
  const children = buildChildren(node.children, ctx);
2935
3061
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
2936
- if (component) {
2937
- const load = readLoad(node.openingElement.attributes);
2938
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
2939
- }
2940
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
3062
+ const load = readLoad(node.openingElement.attributes);
3063
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
2941
3064
  }
2942
3065
  function buildJsxIR(node, ctx) {
2943
3066
  const ir = buildNode(node, ctx);
@@ -3605,7 +3728,7 @@ function transformTemplates(code, id, options = {}) {
3605
3728
  const wantsJsx = format !== "lit";
3606
3729
  const mayHaveTemplate = wantsLit && (code.includes(`${litTag}\``) || code.includes("svg`"));
3607
3730
  const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
3608
- const wantsSources = options.sourceLocations === true && mayHaveReactiveCall(code);
3731
+ const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
3609
3732
  const sourceName = relativeToRoot(id, options.sourceRoot);
3610
3733
  if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources) return null;
3611
3734
  const ast = parse2(code, {
@@ -34,6 +34,7 @@ var INTRINSICS = {
34
34
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
35
35
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
36
36
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
37
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
37
38
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
38
39
  // name. Still worth having: no import to write, and when a shorter form is
39
40
  // designed, the change is this line rather than a compiler pass.
@@ -44,6 +45,19 @@ var INTRINSICS = {
44
45
  // dependency analysis must not guess at.
45
46
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
46
47
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
48
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
49
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
50
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
51
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
52
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
53
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
54
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
55
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
56
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
57
+ // handle as far as reads go: the same accessor with `set` on it.
58
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
59
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
60
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
47
61
  // An environment variable, public or server-only; the browser build refuses a secret.
48
62
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
49
63
  };
@@ -56,8 +70,11 @@ var RUNTIME_PRIMITIVES = {
56
70
  createMemo: "memo-accessor",
57
71
  createEffect: "effect",
58
72
  createRenderEffect: "effect",
73
+ renderEffect: "effect",
74
+ computed: "effect",
59
75
  createStore: "reactive-object",
60
76
  resource: "resource-handle",
77
+ cachedResource: "resource-handle",
61
78
  createResource: "reactive-object",
62
79
  createSelector: "memo-accessor",
63
80
  createDeferred: "memo-accessor"
@@ -66,6 +83,7 @@ var PRIMITIVE_MODULES = [
66
83
  "@fluixi/reactive",
67
84
  "@fluixi/reactive/signal",
68
85
  "@fluixi/reactive/store",
86
+ "@fluixi/reactive/cached-resource",
69
87
  "@fluixi/core"
70
88
  ];
71
89
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
@@ -6,6 +6,7 @@ var INTRINSICS = {
6
6
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
7
7
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
8
8
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
9
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
9
10
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
10
11
  // name. Still worth having: no import to write, and when a shorter form is
11
12
  // designed, the change is this line rather than a compiler pass.
@@ -16,6 +17,19 @@ var INTRINSICS = {
16
17
  // dependency analysis must not guess at.
17
18
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
18
19
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
20
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
21
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
22
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
23
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
24
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
25
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
26
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
27
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
28
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
29
+ // handle as far as reads go: the same accessor with `set` on it.
30
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
31
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
32
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
19
33
  // An environment variable, public or server-only; the browser build refuses a secret.
20
34
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
21
35
  };
@@ -28,8 +42,11 @@ var RUNTIME_PRIMITIVES = {
28
42
  createMemo: "memo-accessor",
29
43
  createEffect: "effect",
30
44
  createRenderEffect: "effect",
45
+ renderEffect: "effect",
46
+ computed: "effect",
31
47
  createStore: "reactive-object",
32
48
  resource: "resource-handle",
49
+ cachedResource: "resource-handle",
33
50
  createResource: "reactive-object",
34
51
  createSelector: "memo-accessor",
35
52
  createDeferred: "memo-accessor"
@@ -38,6 +55,7 @@ var PRIMITIVE_MODULES = [
38
55
  "@fluixi/reactive",
39
56
  "@fluixi/reactive/signal",
40
57
  "@fluixi/reactive/store",
58
+ "@fluixi/reactive/cached-resource",
41
59
  "@fluixi/core"
42
60
  ];
43
61
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/transform/source-locations.ts
21
21
  var source_locations_exports = {};
22
22
  __export(source_locations_exports, {
23
+ CLASS_MARKER: () => CLASS_MARKER,
23
24
  COMPONENT_MARKER: () => COMPONENT_MARKER,
24
25
  HOLE_MARKER: () => HOLE_MARKER,
25
26
  PROPS_MARKER: () => PROPS_MARKER,
@@ -27,7 +28,7 @@ __export(source_locations_exports, {
27
28
  collectSourceEdits: () => collectSourceEdits,
28
29
  markHole: () => markHole,
29
30
  markProps: () => markProps,
30
- mayHaveReactiveCall: () => mayHaveReactiveCall
31
+ mayHaveSourceMarks: () => mayHaveSourceMarks
31
32
  });
32
33
  module.exports = __toCommonJS(source_locations_exports);
33
34
 
@@ -39,6 +40,7 @@ var INTRINSICS = {
39
40
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
40
41
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
41
42
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
43
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
42
44
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
43
45
  // name. Still worth having: no import to write, and when a shorter form is
44
46
  // designed, the change is this line rather than a compiler pass.
@@ -49,6 +51,19 @@ var INTRINSICS = {
49
51
  // dependency analysis must not guess at.
50
52
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
51
53
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
54
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
55
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
56
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
57
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
58
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
59
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
60
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
61
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
62
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
63
+ // handle as far as reads go: the same accessor with `set` on it.
64
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
65
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
66
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
52
67
  // An environment variable, public or server-only; the browser build refuses a secret.
53
68
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
54
69
  };
@@ -61,8 +76,11 @@ var RUNTIME_PRIMITIVES = {
61
76
  createMemo: "memo-accessor",
62
77
  createEffect: "effect",
63
78
  createRenderEffect: "effect",
79
+ renderEffect: "effect",
80
+ computed: "effect",
64
81
  createStore: "reactive-object",
65
82
  resource: "resource-handle",
83
+ cachedResource: "resource-handle",
66
84
  createResource: "reactive-object",
67
85
  createSelector: "memo-accessor",
68
86
  createDeferred: "memo-accessor"
@@ -71,6 +89,7 @@ var PRIMITIVE_MODULES = [
71
89
  "@fluixi/reactive",
72
90
  "@fluixi/reactive/signal",
73
91
  "@fluixi/reactive/store",
92
+ "@fluixi/reactive/cached-resource",
74
93
  "@fluixi/core"
75
94
  ];
76
95
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
@@ -174,6 +193,7 @@ function resolveReactiveBindings(program) {
174
193
  // src/transform/source-locations.ts
175
194
  var SOURCE_MARKER = "__fx_source";
176
195
  var COMPONENT_MARKER = "__fx_component";
196
+ var CLASS_MARKER = "__fx_class";
177
197
  var HOLE_MARKER = "__fx_hole";
178
198
  var markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
179
199
  var PROPS_MARKER = "__fx_props";
@@ -191,11 +211,12 @@ var RUNTIME_PRIMITIVES2 = /* @__PURE__ */ new Set([
191
211
  "createStore",
192
212
  "createResource"
193
213
  ]);
194
- function mayHaveReactiveCall(code) {
214
+ function mayHaveSourceMarks(code) {
195
215
  for (const name of RUNTIME_PRIMITIVES2) {
196
216
  if (code.includes(name)) return true;
197
217
  }
198
- return code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store");
218
+ if (code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store")) return true;
219
+ return /\bclass\b[^{;]*\bextends\b/.test(code);
199
220
  }
200
221
  var SINGLE_STATEMENT_PARENTS = /* @__PURE__ */ new Set([
201
222
  "IfStatement",
@@ -214,6 +235,7 @@ function isLoneBranch(statement, parent) {
214
235
  function isExport(node) {
215
236
  return node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration";
216
237
  }
238
+ var CLASS_FIELDS = /* @__PURE__ */ new Set(["PropertyDefinition", "ClassProperty", "ClassPrivateProperty", "AccessorProperty"]);
217
239
  function isStatement(node) {
218
240
  return node.type.endsWith("Statement") || node.type === "VariableDeclaration";
219
241
  }
@@ -234,6 +256,14 @@ function walk2(node, visit, stack = []) {
234
256
  }
235
257
  function nameFor(stack) {
236
258
  const declarator = stack[stack.length - 1];
259
+ if (declarator && CLASS_FIELDS.has(declarator.type)) {
260
+ const key = declarator.key;
261
+ if (key?.type === "Identifier" || key?.type === "PrivateIdentifier" || key?.type === "PrivateName") {
262
+ const name = key.name ?? key.id?.name;
263
+ return name;
264
+ }
265
+ return void 0;
266
+ }
237
267
  if (declarator?.type !== "VariableDeclarator") return void 0;
238
268
  const id = declarator.id;
239
269
  if (id?.type === "Identifier") return id.name;
@@ -282,6 +312,17 @@ function collectSourceEdits(program, filename) {
282
312
  });
283
313
  }
284
314
  }
315
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.superClass && !node.declare) {
316
+ const at = node.loc?.start;
317
+ const body = node.body;
318
+ if (at && body) {
319
+ edits.push({
320
+ start: body.start + 1,
321
+ end: body.start + 1,
322
+ code: `static{globalThis.${CLASS_MARKER}?.(this,${JSON.stringify(filename)},${at.line},${at.column});}`
323
+ });
324
+ }
325
+ }
285
326
  if (node.type !== "CallExpression") return;
286
327
  const callee = node.callee;
287
328
  if (callee?.type !== "Identifier") return;
@@ -291,6 +332,26 @@ function collectSourceEdits(program, filename) {
291
332
  if (!intrinsic && !imported) return;
292
333
  const position = node.loc?.start;
293
334
  if (!position) return;
335
+ for (let i = stack.length - 1; i >= 0; i--) {
336
+ const up = stack[i];
337
+ if (isStatement(up)) break;
338
+ if (CLASS_FIELDS.has(up.type)) {
339
+ if (claimed.has(node.start)) return;
340
+ claimed.add(node.start);
341
+ const label2 = nameFor(stack);
342
+ const own = !up.static;
343
+ const args2 = [
344
+ JSON.stringify(filename),
345
+ String(position.line),
346
+ String(position.column),
347
+ label2 ? JSON.stringify(label2) : own ? "undefined" : void 0,
348
+ own ? "this" : void 0
349
+ ].filter((a) => a !== void 0).join(",");
350
+ edits.push({ start: node.start, end: node.start, code: `(globalThis.${SOURCE_MARKER}?.(${args2}),` });
351
+ edits.push({ start: node.end, end: node.end, code: ")" });
352
+ return;
353
+ }
354
+ }
294
355
  let statement;
295
356
  let parent;
296
357
  let index = -1;
@@ -12,6 +12,11 @@ export interface SourceLocationsResult {
12
12
  export declare const SOURCE_MARKER = "__fx_source";
13
13
  /** Where a component was declared. Keyed by name, since that is all the runtime carries. */
14
14
  export declare const COMPONENT_MARKER = "__fx_component";
15
+ /**
16
+ * Where a class that extends another was declared, keyed by the class itself: a custom
17
+ * element's definition, which the runtime can only reach through `element.constructor`.
18
+ */
19
+ export declare const CLASS_MARKER = "__fx_class";
15
20
  /**
16
21
  * Where one reactive hole was written. This one wraps: the position has to travel with the
17
22
  * accessor to wherever the dom wires it up. A template is emitted as one overwrite, so the
@@ -38,6 +43,6 @@ export declare const markHole: (accessor: string, line: number, column: number,
38
43
  export declare const PROPS_MARKER = "__fx_props";
39
44
  /** `_({ get a() {…} }, {"a":[9,25]})`, or the object untouched when nothing is watching. */
40
45
  export declare const markProps: (object: string, at: Record<string, [number, number]>) => string;
41
- /** Cheap enough to run before parsing. */
42
- export declare function mayHaveReactiveCall(code: string): boolean;
46
+ /** Whether a module has anything to mark: a primitive call or a subclass. Cheap enough to run before parsing. */
47
+ export declare function mayHaveSourceMarks(code: string): boolean;
43
48
  export declare function collectSourceEdits(program: unknown, filename: string): SourceLocationsResult;