@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
@@ -469,6 +469,7 @@ var INTRINSICS = {
469
469
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
470
470
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
471
471
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
472
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
472
473
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
473
474
  // name. Still worth having: no import to write, and when a shorter form is
474
475
  // designed, the change is this line rather than a compiler pass.
@@ -479,6 +480,19 @@ var INTRINSICS = {
479
480
  // dependency analysis must not guess at.
480
481
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
481
482
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
483
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
484
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
485
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
486
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
487
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
488
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
489
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
490
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
491
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
492
+ // handle as far as reads go: the same accessor with `set` on it.
493
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
494
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
495
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
482
496
  // An environment variable, public or server-only; the browser build refuses a secret.
483
497
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
484
498
  };
@@ -656,13 +670,16 @@ function emitComponent(name, props, children, used, templates, at) {
656
670
  used.add("createComponent");
657
671
  const spreads = props.filter((p) => p.kind === "spread");
658
672
  const normal = props.filter((p) => p.kind !== "spread");
659
- const entries = normal.map(
660
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
661
- );
673
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
662
674
  if (children.length > 0) {
663
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
675
+ bodies.push(["children", emitChildren(children, used, templates)]);
676
+ }
677
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
678
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
679
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
680
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
681
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
664
682
  }
665
- let obj = `{ ${entries.join(", ")} }`;
666
683
  if (spreads.length > 0) {
667
684
  used.add("mergeProps");
668
685
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -744,7 +761,8 @@ function emitNode(node, used, templates) {
744
761
  } else {
745
762
  used.add("spread");
746
763
  const svgFlag = node.svg ? ", isSVG: true" : "";
747
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
764
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
765
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
748
766
  parts.push(
749
767
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
750
768
  );
@@ -822,6 +840,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
822
840
  function escapeTemplate(s) {
823
841
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
824
842
  }
843
+ var CONTAINER = "container";
825
844
  function capitalize(s) {
826
845
  return s.charAt(0).toUpperCase() + s.slice(1);
827
846
  }
@@ -876,10 +895,6 @@ var Lowerer = class {
876
895
  if (hasElse) i = elseIdx;
877
896
  continue;
878
897
  }
879
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
880
- out.push(this.lowerEach(child));
881
- continue;
882
- }
883
898
  }
884
899
  const node = this.lowerNode(child);
885
900
  if (node) out.push(node);
@@ -921,13 +936,16 @@ var Lowerer = class {
921
936
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
922
937
  return node;
923
938
  }
939
+ const each = this.lowerEach(el);
940
+ const children = each ? [each] : this.lowerChildren(el.children);
941
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
924
942
  const at = this.tagAt(el);
925
943
  return {
926
944
  kind: "element",
927
945
  tag: el.tag,
928
946
  svg: el.namespace === "svg",
929
947
  props: this.lowerAttributes(el.attributes),
930
- children: this.lowerChildren(el.children),
948
+ children,
931
949
  static: false,
932
950
  ...at ? { at } : {}
933
951
  };
@@ -999,10 +1017,14 @@ var Lowerer = class {
999
1017
  const at = this.tagAt(el);
1000
1018
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
1001
1019
  }
1002
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1020
+ /**
1021
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1022
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1023
+ * it is. Null when the element has no `each`.
1024
+ */
1003
1025
  lowerEach(el) {
1004
1026
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
1005
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1027
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
1006
1028
  const each = this.hole(eachDir.hole);
1007
1029
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
1008
1030
  if (eachDir.key) {
@@ -1012,12 +1034,12 @@ var Lowerer = class {
1012
1034
  const arrow = this.itemArrow(el);
1013
1035
  let childrenCode;
1014
1036
  if (arrow) {
1015
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
1016
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
1017
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1037
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1038
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
1018
1039
  } else {
1019
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
1020
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1040
+ const content = this.lowerChildren(el.children);
1041
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1042
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
1021
1043
  }
1022
1044
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
1023
1045
  const at = this.tagAt(el);
@@ -1034,25 +1056,6 @@ var Lowerer = class {
1034
1056
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
1035
1057
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
1036
1058
  }
1037
- /** Same element (minus each/key) but with explicit IR children. */
1038
- rebuildWithChildren(el, children) {
1039
- const props = this.lowerAttributes(
1040
- el.attributes.filter((a) => a.kind !== "EachDirective")
1041
- );
1042
- if (el.kind === "Element") {
1043
- const at = this.tagAt(el);
1044
- return {
1045
- kind: "element",
1046
- tag: el.tag,
1047
- svg: el.namespace === "svg",
1048
- props,
1049
- children,
1050
- static: false,
1051
- ...at ? { at } : {}
1052
- };
1053
- }
1054
- return { kind: "component", name: el.tag, props, children };
1055
- }
1056
1059
  // --- attributes ---------------------------------------------------------
1057
1060
  lowerAttributes(attrs) {
1058
1061
  const props = [];
@@ -1464,6 +1467,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1464
1467
  "mouseup"
1465
1468
  ]);
1466
1469
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1470
+ var CONTAINER2 = "container";
1471
+ function directive(node, name) {
1472
+ if (node.type !== "JSXElement") return void 0;
1473
+ const opening = node.openingElement;
1474
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1475
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1476
+ }
1477
+ function directiveExpr(attr) {
1478
+ const v = attr.value;
1479
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1480
+ }
1481
+ function emitIR(ir, ctx) {
1482
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1483
+ for (const s of imports) ctx.used.add(s);
1484
+ return inlineTemplates(code, templates);
1485
+ }
1467
1486
  var posOf = (node, ctx) => {
1468
1487
  if (!ctx.sourceFile) return void 0;
1469
1488
  const at = node.loc?.start;
@@ -1572,7 +1591,16 @@ function buildProps(attrs, ctx) {
1572
1591
  }
1573
1592
  function buildChildren(children, ctx) {
1574
1593
  const out = [];
1575
- for (const child of children) {
1594
+ for (let i = 0; i < children.length; i++) {
1595
+ const child = children[i];
1596
+ if (directive(child, "if")) {
1597
+ let next = i + 1;
1598
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1599
+ const fallback = directive(children[next], "else") ? children[next] : null;
1600
+ out.push(buildIf(child, fallback, ctx));
1601
+ if (fallback) i = next;
1602
+ continue;
1603
+ }
1576
1604
  if (child.type === "JSXText") {
1577
1605
  const text = cleanTemplateText(child.value);
1578
1606
  if (text) out.push({ kind: "text", value: text });
@@ -1609,19 +1637,68 @@ function readLoad(attrs) {
1609
1637
  }
1610
1638
  return void 0;
1611
1639
  }
1640
+ function buildIf(node, fallback, ctx) {
1641
+ const cond = directiveExpr(directive(node, "if"));
1642
+ const props = [
1643
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1644
+ ];
1645
+ if (fallback) {
1646
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1647
+ }
1648
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1649
+ }
1650
+ function buildEach(node, ctx) {
1651
+ const attr = directive(node, "each");
1652
+ if (!attr) return null;
1653
+ const list = directiveExpr(attr);
1654
+ const props = [
1655
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1656
+ ];
1657
+ const key = directive(node, "key");
1658
+ if (key) {
1659
+ const keyExpr = directiveExpr(key);
1660
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1661
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1662
+ }
1663
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1664
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1665
+ let render;
1666
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1667
+ const fn = ctx.code(only);
1668
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1669
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1670
+ } else {
1671
+ const built = buildChildren(node.children, ctx);
1672
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1673
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1674
+ }
1675
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1676
+ }
1677
+ function buildElement(node, ctx, handled) {
1678
+ const skip = new Set(handled);
1679
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1680
+ const attributes = node.openingElement.attributes.filter(
1681
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1682
+ );
1683
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1684
+ const each = buildEach(node, ctx);
1685
+ const children = each ? [each] : buildChildren(node.children, ctx);
1686
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1687
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1688
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
1689
+ }
1612
1690
  function buildNode(node, ctx) {
1613
1691
  if (node.type === "JSXFragment") {
1614
1692
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1615
1693
  }
1694
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1616
1695
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1696
+ if (!component) return buildElement(node, ctx, []);
1617
1697
  const props = buildProps(node.openingElement.attributes, ctx);
1618
1698
  const children = buildChildren(node.children, ctx);
1619
1699
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1620
- if (component) {
1621
- const load = readLoad(node.openingElement.attributes);
1622
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1623
- }
1624
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1700
+ const load = readLoad(node.openingElement.attributes);
1701
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1625
1702
  }
1626
1703
  function buildJsxIR(node, ctx) {
1627
1704
  const ir = buildNode(node, ctx);
@@ -435,6 +435,7 @@ var INTRINSICS = {
435
435
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
436
436
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
437
437
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
438
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
438
439
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
439
440
  // name. Still worth having: no import to write, and when a shorter form is
440
441
  // designed, the change is this line rather than a compiler pass.
@@ -445,6 +446,19 @@ var INTRINSICS = {
445
446
  // dependency analysis must not guess at.
446
447
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
447
448
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
449
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
450
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
451
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
452
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
453
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
454
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
455
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
456
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
457
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
458
+ // handle as far as reads go: the same accessor with `set` on it.
459
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
460
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
461
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
448
462
  // An environment variable, public or server-only; the browser build refuses a secret.
449
463
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
450
464
  };
@@ -622,13 +636,16 @@ function emitComponent(name, props, children, used, templates, at) {
622
636
  used.add("createComponent");
623
637
  const spreads = props.filter((p) => p.kind === "spread");
624
638
  const normal = props.filter((p) => p.kind !== "spread");
625
- const entries = normal.map(
626
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
627
- );
639
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
628
640
  if (children.length > 0) {
629
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
641
+ bodies.push(["children", emitChildren(children, used, templates)]);
642
+ }
643
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
644
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
645
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
646
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
647
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
630
648
  }
631
- let obj = `{ ${entries.join(", ")} }`;
632
649
  if (spreads.length > 0) {
633
650
  used.add("mergeProps");
634
651
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -710,7 +727,8 @@ function emitNode(node, used, templates) {
710
727
  } else {
711
728
  used.add("spread");
712
729
  const svgFlag = node.svg ? ", isSVG: true" : "";
713
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
730
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
731
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
714
732
  parts.push(
715
733
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
716
734
  );
@@ -790,6 +808,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
790
808
  function escapeTemplate(s) {
791
809
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
792
810
  }
811
+ var CONTAINER = "container";
793
812
  function capitalize(s) {
794
813
  return s.charAt(0).toUpperCase() + s.slice(1);
795
814
  }
@@ -844,10 +863,6 @@ var Lowerer = class {
844
863
  if (hasElse) i = elseIdx;
845
864
  continue;
846
865
  }
847
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
848
- out.push(this.lowerEach(child));
849
- continue;
850
- }
851
866
  }
852
867
  const node = this.lowerNode(child);
853
868
  if (node) out.push(node);
@@ -889,13 +904,16 @@ var Lowerer = class {
889
904
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
890
905
  return node;
891
906
  }
907
+ const each = this.lowerEach(el);
908
+ const children = each ? [each] : this.lowerChildren(el.children);
909
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
892
910
  const at = this.tagAt(el);
893
911
  return {
894
912
  kind: "element",
895
913
  tag: el.tag,
896
914
  svg: el.namespace === "svg",
897
915
  props: this.lowerAttributes(el.attributes),
898
- children: this.lowerChildren(el.children),
916
+ children,
899
917
  static: false,
900
918
  ...at ? { at } : {}
901
919
  };
@@ -967,10 +985,14 @@ var Lowerer = class {
967
985
  const at = this.tagAt(el);
968
986
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
969
987
  }
970
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
988
+ /**
989
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
990
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
991
+ * it is. Null when the element has no `each`.
992
+ */
971
993
  lowerEach(el) {
972
994
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
973
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
995
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
974
996
  const each = this.hole(eachDir.hole);
975
997
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
976
998
  if (eachDir.key) {
@@ -980,12 +1002,12 @@ var Lowerer = class {
980
1002
  const arrow = this.itemArrow(el);
981
1003
  let childrenCode;
982
1004
  if (arrow) {
983
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
984
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
985
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1005
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1006
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
986
1007
  } else {
987
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
988
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1008
+ const content = this.lowerChildren(el.children);
1009
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1010
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
989
1011
  }
990
1012
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
991
1013
  const at = this.tagAt(el);
@@ -1002,25 +1024,6 @@ var Lowerer = class {
1002
1024
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
1003
1025
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
1004
1026
  }
1005
- /** Same element (minus each/key) but with explicit IR children. */
1006
- rebuildWithChildren(el, children) {
1007
- const props = this.lowerAttributes(
1008
- el.attributes.filter((a) => a.kind !== "EachDirective")
1009
- );
1010
- if (el.kind === "Element") {
1011
- const at = this.tagAt(el);
1012
- return {
1013
- kind: "element",
1014
- tag: el.tag,
1015
- svg: el.namespace === "svg",
1016
- props,
1017
- children,
1018
- static: false,
1019
- ...at ? { at } : {}
1020
- };
1021
- }
1022
- return { kind: "component", name: el.tag, props, children };
1023
- }
1024
1027
  // --- attributes ---------------------------------------------------------
1025
1028
  lowerAttributes(attrs) {
1026
1029
  const props = [];
@@ -1432,6 +1435,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1432
1435
  "mouseup"
1433
1436
  ]);
1434
1437
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1438
+ var CONTAINER2 = "container";
1439
+ function directive(node, name) {
1440
+ if (node.type !== "JSXElement") return void 0;
1441
+ const opening = node.openingElement;
1442
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1443
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1444
+ }
1445
+ function directiveExpr(attr) {
1446
+ const v = attr.value;
1447
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1448
+ }
1449
+ function emitIR(ir, ctx) {
1450
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1451
+ for (const s of imports) ctx.used.add(s);
1452
+ return inlineTemplates(code, templates);
1453
+ }
1435
1454
  var posOf = (node, ctx) => {
1436
1455
  if (!ctx.sourceFile) return void 0;
1437
1456
  const at = node.loc?.start;
@@ -1540,7 +1559,16 @@ function buildProps(attrs, ctx) {
1540
1559
  }
1541
1560
  function buildChildren(children, ctx) {
1542
1561
  const out = [];
1543
- for (const child of children) {
1562
+ for (let i = 0; i < children.length; i++) {
1563
+ const child = children[i];
1564
+ if (directive(child, "if")) {
1565
+ let next = i + 1;
1566
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1567
+ const fallback = directive(children[next], "else") ? children[next] : null;
1568
+ out.push(buildIf(child, fallback, ctx));
1569
+ if (fallback) i = next;
1570
+ continue;
1571
+ }
1544
1572
  if (child.type === "JSXText") {
1545
1573
  const text = cleanTemplateText(child.value);
1546
1574
  if (text) out.push({ kind: "text", value: text });
@@ -1577,19 +1605,68 @@ function readLoad(attrs) {
1577
1605
  }
1578
1606
  return void 0;
1579
1607
  }
1608
+ function buildIf(node, fallback, ctx) {
1609
+ const cond = directiveExpr(directive(node, "if"));
1610
+ const props = [
1611
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1612
+ ];
1613
+ if (fallback) {
1614
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1615
+ }
1616
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1617
+ }
1618
+ function buildEach(node, ctx) {
1619
+ const attr = directive(node, "each");
1620
+ if (!attr) return null;
1621
+ const list = directiveExpr(attr);
1622
+ const props = [
1623
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1624
+ ];
1625
+ const key = directive(node, "key");
1626
+ if (key) {
1627
+ const keyExpr = directiveExpr(key);
1628
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1629
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1630
+ }
1631
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1632
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1633
+ let render;
1634
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1635
+ const fn = ctx.code(only);
1636
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1637
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1638
+ } else {
1639
+ const built = buildChildren(node.children, ctx);
1640
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1641
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1642
+ }
1643
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1644
+ }
1645
+ function buildElement(node, ctx, handled) {
1646
+ const skip = new Set(handled);
1647
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1648
+ const attributes = node.openingElement.attributes.filter(
1649
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1650
+ );
1651
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1652
+ const each = buildEach(node, ctx);
1653
+ const children = each ? [each] : buildChildren(node.children, ctx);
1654
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1655
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1656
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
1657
+ }
1580
1658
  function buildNode(node, ctx) {
1581
1659
  if (node.type === "JSXFragment") {
1582
1660
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1583
1661
  }
1662
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1584
1663
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1664
+ if (!component) return buildElement(node, ctx, []);
1585
1665
  const props = buildProps(node.openingElement.attributes, ctx);
1586
1666
  const children = buildChildren(node.children, ctx);
1587
1667
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1588
- if (component) {
1589
- const load = readLoad(node.openingElement.attributes);
1590
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1591
- }
1592
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1668
+ const load = readLoad(node.openingElement.attributes);
1669
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1593
1670
  }
1594
1671
  function buildJsxIR(node, ctx) {
1595
1672
  const ir = buildNode(node, ctx);