@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
@@ -251,6 +251,7 @@ var INTRINSICS = {
251
251
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
252
252
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
253
253
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
254
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
254
255
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
255
256
  // name. Still worth having: no import to write, and when a shorter form is
256
257
  // designed, the change is this line rather than a compiler pass.
@@ -261,6 +262,19 @@ var INTRINSICS = {
261
262
  // dependency analysis must not guess at.
262
263
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
263
264
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
265
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
266
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
267
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
268
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
269
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
270
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
271
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
272
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
273
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
274
+ // handle as far as reads go: the same accessor with `set` on it.
275
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
276
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
277
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
264
278
  // An environment variable, public or server-only; the browser build refuses a secret.
265
279
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
266
280
  };
@@ -438,13 +452,16 @@ function emitComponent(name, props, children, used, templates, at) {
438
452
  used.add("createComponent");
439
453
  const spreads = props.filter((p) => p.kind === "spread");
440
454
  const normal = props.filter((p) => p.kind !== "spread");
441
- const entries = normal.map(
442
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
443
- );
455
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
444
456
  if (children.length > 0) {
445
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
457
+ bodies.push(["children", emitChildren(children, used, templates)]);
458
+ }
459
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
460
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
461
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
462
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
463
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
446
464
  }
447
- let obj = `{ ${entries.join(", ")} }`;
448
465
  if (spreads.length > 0) {
449
466
  used.add("mergeProps");
450
467
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -526,7 +543,8 @@ function emitNode(node, used, templates) {
526
543
  } else {
527
544
  used.add("spread");
528
545
  const svgFlag = node.svg ? ", isSVG: true" : "";
529
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
546
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
547
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
530
548
  parts.push(
531
549
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
532
550
  );
@@ -653,6 +671,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
653
671
  function escapeTemplate(s) {
654
672
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
655
673
  }
674
+ var CONTAINER = "container";
656
675
  function capitalize(s) {
657
676
  return s.charAt(0).toUpperCase() + s.slice(1);
658
677
  }
@@ -707,10 +726,6 @@ var Lowerer = class {
707
726
  if (hasElse) i = elseIdx;
708
727
  continue;
709
728
  }
710
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
711
- out.push(this.lowerEach(child));
712
- continue;
713
- }
714
729
  }
715
730
  const node = this.lowerNode(child);
716
731
  if (node) out.push(node);
@@ -752,13 +767,16 @@ var Lowerer = class {
752
767
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
753
768
  return node;
754
769
  }
770
+ const each = this.lowerEach(el);
771
+ const children = each ? [each] : this.lowerChildren(el.children);
772
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
755
773
  const at = this.tagAt(el);
756
774
  return {
757
775
  kind: "element",
758
776
  tag: el.tag,
759
777
  svg: el.namespace === "svg",
760
778
  props: this.lowerAttributes(el.attributes),
761
- children: this.lowerChildren(el.children),
779
+ children,
762
780
  static: false,
763
781
  ...at ? { at } : {}
764
782
  };
@@ -830,10 +848,14 @@ var Lowerer = class {
830
848
  const at = this.tagAt(el);
831
849
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
832
850
  }
833
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
851
+ /**
852
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
853
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
854
+ * it is. Null when the element has no `each`.
855
+ */
834
856
  lowerEach(el) {
835
857
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
836
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
858
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
837
859
  const each = this.hole(eachDir.hole);
838
860
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
839
861
  if (eachDir.key) {
@@ -843,12 +865,12 @@ var Lowerer = class {
843
865
  const arrow = this.itemArrow(el);
844
866
  let childrenCode;
845
867
  if (arrow) {
846
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
847
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
848
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
868
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
869
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
849
870
  } else {
850
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
851
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
871
+ const content = this.lowerChildren(el.children);
872
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
873
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
852
874
  }
853
875
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
854
876
  const at = this.tagAt(el);
@@ -865,25 +887,6 @@ var Lowerer = class {
865
887
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
866
888
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
867
889
  }
868
- /** Same element (minus each/key) but with explicit IR children. */
869
- rebuildWithChildren(el, children) {
870
- const props = this.lowerAttributes(
871
- el.attributes.filter((a) => a.kind !== "EachDirective")
872
- );
873
- if (el.kind === "Element") {
874
- const at = this.tagAt(el);
875
- return {
876
- kind: "element",
877
- tag: el.tag,
878
- svg: el.namespace === "svg",
879
- props,
880
- children,
881
- static: false,
882
- ...at ? { at } : {}
883
- };
884
- }
885
- return { kind: "component", name: el.tag, props, children };
886
- }
887
890
  // --- attributes ---------------------------------------------------------
888
891
  lowerAttributes(attrs) {
889
892
  const props = [];
@@ -1295,6 +1298,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1295
1298
  "mouseup"
1296
1299
  ]);
1297
1300
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1301
+ var CONTAINER2 = "container";
1302
+ function directive(node, name) {
1303
+ if (node.type !== "JSXElement") return void 0;
1304
+ const opening = node.openingElement;
1305
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1306
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1307
+ }
1308
+ function directiveExpr(attr) {
1309
+ const v = attr.value;
1310
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1311
+ }
1312
+ function emitIR(ir, ctx) {
1313
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1314
+ for (const s of imports) ctx.used.add(s);
1315
+ return inlineTemplates(code, templates);
1316
+ }
1298
1317
  var posOf = (node, ctx) => {
1299
1318
  if (!ctx.sourceFile) return void 0;
1300
1319
  const at = node.loc?.start;
@@ -1403,7 +1422,16 @@ function buildProps(attrs, ctx) {
1403
1422
  }
1404
1423
  function buildChildren(children, ctx) {
1405
1424
  const out = [];
1406
- for (const child of children) {
1425
+ for (let i = 0; i < children.length; i++) {
1426
+ const child = children[i];
1427
+ if (directive(child, "if")) {
1428
+ let next = i + 1;
1429
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1430
+ const fallback = directive(children[next], "else") ? children[next] : null;
1431
+ out.push(buildIf(child, fallback, ctx));
1432
+ if (fallback) i = next;
1433
+ continue;
1434
+ }
1407
1435
  if (child.type === "JSXText") {
1408
1436
  const text = cleanTemplateText(child.value);
1409
1437
  if (text) out.push({ kind: "text", value: text });
@@ -1440,19 +1468,68 @@ function readLoad(attrs) {
1440
1468
  }
1441
1469
  return void 0;
1442
1470
  }
1471
+ function buildIf(node, fallback, ctx) {
1472
+ const cond = directiveExpr(directive(node, "if"));
1473
+ const props = [
1474
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1475
+ ];
1476
+ if (fallback) {
1477
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1478
+ }
1479
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1480
+ }
1481
+ function buildEach(node, ctx) {
1482
+ const attr = directive(node, "each");
1483
+ if (!attr) return null;
1484
+ const list = directiveExpr(attr);
1485
+ const props = [
1486
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1487
+ ];
1488
+ const key = directive(node, "key");
1489
+ if (key) {
1490
+ const keyExpr = directiveExpr(key);
1491
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1492
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1493
+ }
1494
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1495
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1496
+ let render;
1497
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1498
+ const fn = ctx.code(only);
1499
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1500
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1501
+ } else {
1502
+ const built = buildChildren(node.children, ctx);
1503
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1504
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1505
+ }
1506
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1507
+ }
1508
+ function buildElement(node, ctx, handled) {
1509
+ const skip = new Set(handled);
1510
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1511
+ const attributes = node.openingElement.attributes.filter(
1512
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1513
+ );
1514
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1515
+ const each = buildEach(node, ctx);
1516
+ const children = each ? [each] : buildChildren(node.children, ctx);
1517
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1518
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1519
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
1520
+ }
1443
1521
  function buildNode(node, ctx) {
1444
1522
  if (node.type === "JSXFragment") {
1445
1523
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1446
1524
  }
1525
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1447
1526
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1527
+ if (!component) return buildElement(node, ctx, []);
1448
1528
  const props = buildProps(node.openingElement.attributes, ctx);
1449
1529
  const children = buildChildren(node.children, ctx);
1450
1530
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1451
- if (component) {
1452
- const load = readLoad(node.openingElement.attributes);
1453
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1454
- }
1455
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1531
+ const load = readLoad(node.openingElement.attributes);
1532
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1456
1533
  }
1457
1534
  function buildJsxIR(node, ctx) {
1458
1535
  const ir = buildNode(node, ctx);
@@ -214,6 +214,7 @@ var INTRINSICS = {
214
214
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
215
215
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
216
216
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
217
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
217
218
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
218
219
  // name. Still worth having: no import to write, and when a shorter form is
219
220
  // designed, the change is this line rather than a compiler pass.
@@ -224,6 +225,19 @@ var INTRINSICS = {
224
225
  // dependency analysis must not guess at.
225
226
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
226
227
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
228
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
229
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
230
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
231
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
232
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
233
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
234
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
235
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
236
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
237
+ // handle as far as reads go: the same accessor with `set` on it.
238
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
239
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
240
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
227
241
  // An environment variable, public or server-only; the browser build refuses a secret.
228
242
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
229
243
  };
@@ -401,13 +415,16 @@ function emitComponent(name, props, children, used, templates, at) {
401
415
  used.add("createComponent");
402
416
  const spreads = props.filter((p) => p.kind === "spread");
403
417
  const normal = props.filter((p) => p.kind !== "spread");
404
- const entries = normal.map(
405
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
406
- );
418
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
407
419
  if (children.length > 0) {
408
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
420
+ bodies.push(["children", emitChildren(children, used, templates)]);
421
+ }
422
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
423
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
424
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
425
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
426
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
409
427
  }
410
- let obj = `{ ${entries.join(", ")} }`;
411
428
  if (spreads.length > 0) {
412
429
  used.add("mergeProps");
413
430
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -489,7 +506,8 @@ function emitNode(node, used, templates) {
489
506
  } else {
490
507
  used.add("spread");
491
508
  const svgFlag = node.svg ? ", isSVG: true" : "";
492
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
509
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
510
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
493
511
  parts.push(
494
512
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
495
513
  );
@@ -618,6 +636,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
618
636
  function escapeTemplate(s) {
619
637
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
620
638
  }
639
+ var CONTAINER = "container";
621
640
  function capitalize(s) {
622
641
  return s.charAt(0).toUpperCase() + s.slice(1);
623
642
  }
@@ -672,10 +691,6 @@ var Lowerer = class {
672
691
  if (hasElse) i = elseIdx;
673
692
  continue;
674
693
  }
675
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
676
- out.push(this.lowerEach(child));
677
- continue;
678
- }
679
694
  }
680
695
  const node = this.lowerNode(child);
681
696
  if (node) out.push(node);
@@ -717,13 +732,16 @@ var Lowerer = class {
717
732
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
718
733
  return node;
719
734
  }
735
+ const each = this.lowerEach(el);
736
+ const children = each ? [each] : this.lowerChildren(el.children);
737
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
720
738
  const at = this.tagAt(el);
721
739
  return {
722
740
  kind: "element",
723
741
  tag: el.tag,
724
742
  svg: el.namespace === "svg",
725
743
  props: this.lowerAttributes(el.attributes),
726
- children: this.lowerChildren(el.children),
744
+ children,
727
745
  static: false,
728
746
  ...at ? { at } : {}
729
747
  };
@@ -795,10 +813,14 @@ var Lowerer = class {
795
813
  const at = this.tagAt(el);
796
814
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
797
815
  }
798
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
816
+ /**
817
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
818
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
819
+ * it is. Null when the element has no `each`.
820
+ */
799
821
  lowerEach(el) {
800
822
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
801
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
823
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
802
824
  const each = this.hole(eachDir.hole);
803
825
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
804
826
  if (eachDir.key) {
@@ -808,12 +830,12 @@ var Lowerer = class {
808
830
  const arrow = this.itemArrow(el);
809
831
  let childrenCode;
810
832
  if (arrow) {
811
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
812
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
813
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
833
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
834
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
814
835
  } else {
815
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
816
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
836
+ const content = this.lowerChildren(el.children);
837
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
838
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
817
839
  }
818
840
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
819
841
  const at = this.tagAt(el);
@@ -830,25 +852,6 @@ var Lowerer = class {
830
852
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
831
853
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
832
854
  }
833
- /** Same element (minus each/key) but with explicit IR children. */
834
- rebuildWithChildren(el, children) {
835
- const props = this.lowerAttributes(
836
- el.attributes.filter((a) => a.kind !== "EachDirective")
837
- );
838
- if (el.kind === "Element") {
839
- const at = this.tagAt(el);
840
- return {
841
- kind: "element",
842
- tag: el.tag,
843
- svg: el.namespace === "svg",
844
- props,
845
- children,
846
- static: false,
847
- ...at ? { at } : {}
848
- };
849
- }
850
- return { kind: "component", name: el.tag, props, children };
851
- }
852
855
  // --- attributes ---------------------------------------------------------
853
856
  lowerAttributes(attrs) {
854
857
  const props = [];
@@ -1260,6 +1263,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1260
1263
  "mouseup"
1261
1264
  ]);
1262
1265
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1266
+ var CONTAINER2 = "container";
1267
+ function directive(node, name) {
1268
+ if (node.type !== "JSXElement") return void 0;
1269
+ const opening = node.openingElement;
1270
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1271
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1272
+ }
1273
+ function directiveExpr(attr) {
1274
+ const v = attr.value;
1275
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1276
+ }
1277
+ function emitIR(ir, ctx) {
1278
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1279
+ for (const s of imports) ctx.used.add(s);
1280
+ return inlineTemplates(code, templates);
1281
+ }
1263
1282
  var posOf = (node, ctx) => {
1264
1283
  if (!ctx.sourceFile) return void 0;
1265
1284
  const at = node.loc?.start;
@@ -1368,7 +1387,16 @@ function buildProps(attrs, ctx) {
1368
1387
  }
1369
1388
  function buildChildren(children, ctx) {
1370
1389
  const out = [];
1371
- for (const child of children) {
1390
+ for (let i = 0; i < children.length; i++) {
1391
+ const child = children[i];
1392
+ if (directive(child, "if")) {
1393
+ let next = i + 1;
1394
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1395
+ const fallback = directive(children[next], "else") ? children[next] : null;
1396
+ out.push(buildIf(child, fallback, ctx));
1397
+ if (fallback) i = next;
1398
+ continue;
1399
+ }
1372
1400
  if (child.type === "JSXText") {
1373
1401
  const text = cleanTemplateText(child.value);
1374
1402
  if (text) out.push({ kind: "text", value: text });
@@ -1405,19 +1433,68 @@ function readLoad(attrs) {
1405
1433
  }
1406
1434
  return void 0;
1407
1435
  }
1436
+ function buildIf(node, fallback, ctx) {
1437
+ const cond = directiveExpr(directive(node, "if"));
1438
+ const props = [
1439
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1440
+ ];
1441
+ if (fallback) {
1442
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1443
+ }
1444
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1445
+ }
1446
+ function buildEach(node, ctx) {
1447
+ const attr = directive(node, "each");
1448
+ if (!attr) return null;
1449
+ const list = directiveExpr(attr);
1450
+ const props = [
1451
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1452
+ ];
1453
+ const key = directive(node, "key");
1454
+ if (key) {
1455
+ const keyExpr = directiveExpr(key);
1456
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1457
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1458
+ }
1459
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1460
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1461
+ let render;
1462
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1463
+ const fn = ctx.code(only);
1464
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1465
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1466
+ } else {
1467
+ const built = buildChildren(node.children, ctx);
1468
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1469
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1470
+ }
1471
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1472
+ }
1473
+ function buildElement(node, ctx, handled) {
1474
+ const skip = new Set(handled);
1475
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1476
+ const attributes = node.openingElement.attributes.filter(
1477
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1478
+ );
1479
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1480
+ const each = buildEach(node, ctx);
1481
+ const children = each ? [each] : buildChildren(node.children, ctx);
1482
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1483
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1484
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
1485
+ }
1408
1486
  function buildNode(node, ctx) {
1409
1487
  if (node.type === "JSXFragment") {
1410
1488
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1411
1489
  }
1490
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1412
1491
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1492
+ if (!component) return buildElement(node, ctx, []);
1413
1493
  const props = buildProps(node.openingElement.attributes, ctx);
1414
1494
  const children = buildChildren(node.children, ctx);
1415
1495
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1416
- if (component) {
1417
- const load = readLoad(node.openingElement.attributes);
1418
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1419
- }
1420
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1496
+ const load = readLoad(node.openingElement.attributes);
1497
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1421
1498
  }
1422
1499
  function buildJsxIR(node, ctx) {
1423
1500
  const ir = buildNode(node, ctx);