@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
  "use strict";
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
@@ -489,6 +489,7 @@ var INTRINSICS = {
489
489
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
490
490
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
491
491
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
492
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
492
493
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
493
494
  // name. Still worth having: no import to write, and when a shorter form is
494
495
  // designed, the change is this line rather than a compiler pass.
@@ -499,6 +500,19 @@ var INTRINSICS = {
499
500
  // dependency analysis must not guess at.
500
501
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
501
502
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
503
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
504
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
505
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
506
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
507
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
508
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
509
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
510
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
511
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
512
+ // handle as far as reads go: the same accessor with `set` on it.
513
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
514
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
515
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
502
516
  // An environment variable, public or server-only; the browser build refuses a secret.
503
517
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
504
518
  };
@@ -511,8 +525,11 @@ var RUNTIME_PRIMITIVES = {
511
525
  createMemo: "memo-accessor",
512
526
  createEffect: "effect",
513
527
  createRenderEffect: "effect",
528
+ renderEffect: "effect",
529
+ computed: "effect",
514
530
  createStore: "reactive-object",
515
531
  resource: "resource-handle",
532
+ cachedResource: "resource-handle",
516
533
  createResource: "reactive-object",
517
534
  createSelector: "memo-accessor",
518
535
  createDeferred: "memo-accessor"
@@ -521,6 +538,7 @@ var PRIMITIVE_MODULES = [
521
538
  "@fluixi/reactive",
522
539
  "@fluixi/reactive/signal",
523
540
  "@fluixi/reactive/store",
541
+ "@fluixi/reactive/cached-resource",
524
542
  "@fluixi/core"
525
543
  ];
526
544
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
@@ -656,6 +674,7 @@ function resolveReactiveBindings(program) {
656
674
  // src/transform/source-locations.ts
657
675
  var SOURCE_MARKER = "__fx_source";
658
676
  var COMPONENT_MARKER = "__fx_component";
677
+ var CLASS_MARKER = "__fx_class";
659
678
  var HOLE_MARKER = "__fx_hole";
660
679
  var markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
661
680
  var PROPS_MARKER = "__fx_props";
@@ -673,11 +692,12 @@ var RUNTIME_PRIMITIVES2 = /* @__PURE__ */ new Set([
673
692
  "createStore",
674
693
  "createResource"
675
694
  ]);
676
- function mayHaveReactiveCall(code) {
695
+ function mayHaveSourceMarks(code) {
677
696
  for (const name of RUNTIME_PRIMITIVES2) {
678
697
  if (code.includes(name)) return true;
679
698
  }
680
- return code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store");
699
+ if (code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store")) return true;
700
+ return /\bclass\b[^{;]*\bextends\b/.test(code);
681
701
  }
682
702
  var SINGLE_STATEMENT_PARENTS = /* @__PURE__ */ new Set([
683
703
  "IfStatement",
@@ -696,6 +716,7 @@ function isLoneBranch(statement, parent) {
696
716
  function isExport(node) {
697
717
  return node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration";
698
718
  }
719
+ var CLASS_FIELDS = /* @__PURE__ */ new Set(["PropertyDefinition", "ClassProperty", "ClassPrivateProperty", "AccessorProperty"]);
699
720
  function isStatement(node) {
700
721
  return node.type.endsWith("Statement") || node.type === "VariableDeclaration";
701
722
  }
@@ -716,6 +737,14 @@ function walk3(node, visit, stack = []) {
716
737
  }
717
738
  function nameFor(stack) {
718
739
  const declarator = stack[stack.length - 1];
740
+ if (declarator && CLASS_FIELDS.has(declarator.type)) {
741
+ const key = declarator.key;
742
+ if (key?.type === "Identifier" || key?.type === "PrivateIdentifier" || key?.type === "PrivateName") {
743
+ const name = key.name ?? key.id?.name;
744
+ return name;
745
+ }
746
+ return void 0;
747
+ }
719
748
  if (declarator?.type !== "VariableDeclarator") return void 0;
720
749
  const id = declarator.id;
721
750
  if (id?.type === "Identifier") return id.name;
@@ -764,6 +793,17 @@ function collectSourceEdits(program, filename) {
764
793
  });
765
794
  }
766
795
  }
796
+ if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.superClass && !node.declare) {
797
+ const at = node.loc?.start;
798
+ const body = node.body;
799
+ if (at && body) {
800
+ edits.push({
801
+ start: body.start + 1,
802
+ end: body.start + 1,
803
+ code: `static{globalThis.${CLASS_MARKER}?.(this,${JSON.stringify(filename)},${at.line},${at.column});}`
804
+ });
805
+ }
806
+ }
767
807
  if (node.type !== "CallExpression") return;
768
808
  const callee = node.callee;
769
809
  if (callee?.type !== "Identifier") return;
@@ -773,6 +813,26 @@ function collectSourceEdits(program, filename) {
773
813
  if (!intrinsic && !imported) return;
774
814
  const position = node.loc?.start;
775
815
  if (!position) return;
816
+ for (let i = stack.length - 1; i >= 0; i--) {
817
+ const up = stack[i];
818
+ if (isStatement(up)) break;
819
+ if (CLASS_FIELDS.has(up.type)) {
820
+ if (claimed.has(node.start)) return;
821
+ claimed.add(node.start);
822
+ const label2 = nameFor(stack);
823
+ const own = !up.static;
824
+ const args2 = [
825
+ JSON.stringify(filename),
826
+ String(position.line),
827
+ String(position.column),
828
+ label2 ? JSON.stringify(label2) : own ? "undefined" : void 0,
829
+ own ? "this" : void 0
830
+ ].filter((a) => a !== void 0).join(",");
831
+ edits.push({ start: node.start, end: node.start, code: `(globalThis.${SOURCE_MARKER}?.(${args2}),` });
832
+ edits.push({ start: node.end, end: node.end, code: ")" });
833
+ return;
834
+ }
835
+ }
776
836
  let statement;
777
837
  let parent;
778
838
  let index = -1;
@@ -978,13 +1038,16 @@ function emitComponent(name, props, children, used, templates, at) {
978
1038
  used.add("createComponent");
979
1039
  const spreads = props.filter((p) => p.kind === "spread");
980
1040
  const normal = props.filter((p) => p.kind !== "spread");
981
- const entries = normal.map(
982
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
983
- );
1041
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
984
1042
  if (children.length > 0) {
985
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
1043
+ bodies.push(["children", emitChildren(children, used, templates)]);
1044
+ }
1045
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
1046
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
1047
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
1048
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
1049
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
986
1050
  }
987
- let obj = `{ ${entries.join(", ")} }`;
988
1051
  if (spreads.length > 0) {
989
1052
  used.add("mergeProps");
990
1053
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -1066,7 +1129,8 @@ function emitNode(node, used, templates) {
1066
1129
  } else {
1067
1130
  used.add("spread");
1068
1131
  const svgFlag = node.svg ? ", isSVG: true" : "";
1069
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
1132
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
1133
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
1070
1134
  parts.push(
1071
1135
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
1072
1136
  );
@@ -1197,6 +1261,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
1197
1261
  function escapeTemplate(s) {
1198
1262
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
1199
1263
  }
1264
+ var CONTAINER = "container";
1200
1265
  function capitalize(s) {
1201
1266
  return s.charAt(0).toUpperCase() + s.slice(1);
1202
1267
  }
@@ -1251,10 +1316,6 @@ var Lowerer = class {
1251
1316
  if (hasElse) i = elseIdx;
1252
1317
  continue;
1253
1318
  }
1254
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
1255
- out.push(this.lowerEach(child));
1256
- continue;
1257
- }
1258
1319
  }
1259
1320
  const node = this.lowerNode(child);
1260
1321
  if (node) out.push(node);
@@ -1296,13 +1357,16 @@ var Lowerer = class {
1296
1357
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
1297
1358
  return node;
1298
1359
  }
1360
+ const each = this.lowerEach(el);
1361
+ const children = each ? [each] : this.lowerChildren(el.children);
1362
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
1299
1363
  const at = this.tagAt(el);
1300
1364
  return {
1301
1365
  kind: "element",
1302
1366
  tag: el.tag,
1303
1367
  svg: el.namespace === "svg",
1304
1368
  props: this.lowerAttributes(el.attributes),
1305
- children: this.lowerChildren(el.children),
1369
+ children,
1306
1370
  static: false,
1307
1371
  ...at ? { at } : {}
1308
1372
  };
@@ -1374,10 +1438,14 @@ var Lowerer = class {
1374
1438
  const at = this.tagAt(el);
1375
1439
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
1376
1440
  }
1377
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1441
+ /**
1442
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1443
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1444
+ * it is. Null when the element has no `each`.
1445
+ */
1378
1446
  lowerEach(el) {
1379
1447
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
1380
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1448
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
1381
1449
  const each = this.hole(eachDir.hole);
1382
1450
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
1383
1451
  if (eachDir.key) {
@@ -1387,12 +1455,12 @@ var Lowerer = class {
1387
1455
  const arrow = this.itemArrow(el);
1388
1456
  let childrenCode;
1389
1457
  if (arrow) {
1390
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
1391
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
1392
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1458
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1459
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
1393
1460
  } else {
1394
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
1395
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1461
+ const content = this.lowerChildren(el.children);
1462
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1463
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
1396
1464
  }
1397
1465
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
1398
1466
  const at = this.tagAt(el);
@@ -1409,25 +1477,6 @@ var Lowerer = class {
1409
1477
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
1410
1478
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
1411
1479
  }
1412
- /** Same element (minus each/key) but with explicit IR children. */
1413
- rebuildWithChildren(el, children) {
1414
- const props = this.lowerAttributes(
1415
- el.attributes.filter((a) => a.kind !== "EachDirective")
1416
- );
1417
- if (el.kind === "Element") {
1418
- const at = this.tagAt(el);
1419
- return {
1420
- kind: "element",
1421
- tag: el.tag,
1422
- svg: el.namespace === "svg",
1423
- props,
1424
- children,
1425
- static: false,
1426
- ...at ? { at } : {}
1427
- };
1428
- }
1429
- return { kind: "component", name: el.tag, props, children };
1430
- }
1431
1480
  // --- attributes ---------------------------------------------------------
1432
1481
  lowerAttributes(attrs) {
1433
1482
  const props = [];
@@ -1746,6 +1795,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1746
1795
  "mouseup"
1747
1796
  ]);
1748
1797
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1798
+ var CONTAINER2 = "container";
1799
+ function directive(node, name) {
1800
+ if (node.type !== "JSXElement") return void 0;
1801
+ const opening = node.openingElement;
1802
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1803
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1804
+ }
1805
+ function directiveExpr(attr) {
1806
+ const v = attr.value;
1807
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1808
+ }
1809
+ function emitIR(ir, ctx) {
1810
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1811
+ for (const s of imports) ctx.used.add(s);
1812
+ return inlineTemplates(code, templates);
1813
+ }
1749
1814
  var posOf = (node, ctx) => {
1750
1815
  if (!ctx.sourceFile) return void 0;
1751
1816
  const at = node.loc?.start;
@@ -1854,7 +1919,16 @@ function buildProps(attrs, ctx) {
1854
1919
  }
1855
1920
  function buildChildren(children, ctx) {
1856
1921
  const out = [];
1857
- for (const child of children) {
1922
+ for (let i = 0; i < children.length; i++) {
1923
+ const child = children[i];
1924
+ if (directive(child, "if")) {
1925
+ let next = i + 1;
1926
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1927
+ const fallback = directive(children[next], "else") ? children[next] : null;
1928
+ out.push(buildIf(child, fallback, ctx));
1929
+ if (fallback) i = next;
1930
+ continue;
1931
+ }
1858
1932
  if (child.type === "JSXText") {
1859
1933
  const text = cleanTemplateText(child.value);
1860
1934
  if (text) out.push({ kind: "text", value: text });
@@ -1891,19 +1965,68 @@ function readLoad(attrs) {
1891
1965
  }
1892
1966
  return void 0;
1893
1967
  }
1968
+ function buildIf(node, fallback, ctx) {
1969
+ const cond = directiveExpr(directive(node, "if"));
1970
+ const props = [
1971
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1972
+ ];
1973
+ if (fallback) {
1974
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1975
+ }
1976
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1977
+ }
1978
+ function buildEach(node, ctx) {
1979
+ const attr = directive(node, "each");
1980
+ if (!attr) return null;
1981
+ const list = directiveExpr(attr);
1982
+ const props = [
1983
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1984
+ ];
1985
+ const key = directive(node, "key");
1986
+ if (key) {
1987
+ const keyExpr = directiveExpr(key);
1988
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1989
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1990
+ }
1991
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1992
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1993
+ let render;
1994
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1995
+ const fn = ctx.code(only);
1996
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1997
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1998
+ } else {
1999
+ const built = buildChildren(node.children, ctx);
2000
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
2001
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
2002
+ }
2003
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
2004
+ }
2005
+ function buildElement(node, ctx, handled) {
2006
+ const skip = new Set(handled);
2007
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
2008
+ const attributes = node.openingElement.attributes.filter(
2009
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
2010
+ );
2011
+ const { tag } = tagInfo(node.openingElement.name, ctx);
2012
+ const each = buildEach(node, ctx);
2013
+ const children = each ? [each] : buildChildren(node.children, ctx);
2014
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
2015
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
2016
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
2017
+ }
1894
2018
  function buildNode(node, ctx) {
1895
2019
  if (node.type === "JSXFragment") {
1896
2020
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1897
2021
  }
2022
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1898
2023
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
2024
+ if (!component) return buildElement(node, ctx, []);
1899
2025
  const props = buildProps(node.openingElement.attributes, ctx);
1900
2026
  const children = buildChildren(node.children, ctx);
1901
2027
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1902
- if (component) {
1903
- const load = readLoad(node.openingElement.attributes);
1904
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1905
- }
1906
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
2028
+ const load = readLoad(node.openingElement.attributes);
2029
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1907
2030
  }
1908
2031
  function buildJsxIR(node, ctx) {
1909
2032
  const ir = buildNode(node, ctx);
@@ -2572,7 +2695,7 @@ function transformTemplates(code, id, options = {}) {
2572
2695
  const wantsJsx = format !== "lit";
2573
2696
  const mayHaveTemplate = wantsLit && (code.includes(`${litTag}\``) || code.includes("svg`"));
2574
2697
  const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
2575
- const wantsSources = options.sourceLocations === true && mayHaveReactiveCall(code);
2698
+ const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
2576
2699
  const sourceName = relativeToRoot(id, options.sourceRoot);
2577
2700
  if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources) return null;
2578
2701
  const ast = (0, import_parser2.parse)(code, {