@fluixi/compiler 1.0.0-alpha.92 → 1.0.0-alpha.94

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 (102) hide show
  1. package/dist/analyze/intrinsics.cjs +31 -1
  2. package/dist/analyze/intrinsics.d.cts +14 -0
  3. package/dist/analyze/intrinsics.d.ts +14 -0
  4. package/dist/analyze/intrinsics.d.ts.map +1 -1
  5. package/dist/analyze/intrinsics.js +35 -0
  6. package/dist/analyze/intrinsics.mjs +31 -1
  7. package/dist/analyze/reactive-bindings.cjs +18 -0
  8. package/dist/analyze/reactive-bindings.mjs +18 -0
  9. package/dist/analyze/static-graph.cjs +22 -4
  10. package/dist/analyze/static-graph.d.ts.map +1 -1
  11. package/dist/analyze/static-graph.js +6 -4
  12. package/dist/analyze/static-graph.mjs +22 -4
  13. package/dist/{babel-GI375YZ2.mjs → babel-RJFQVFJW.mjs} +4 -4
  14. package/dist/{chunk-QZ4CJPYP.mjs → chunk-DP2QDWWH.mjs} +1 -1
  15. package/dist/{chunk-DWNWN5RH.mjs → chunk-E2RWTIYE.mjs} +21 -15
  16. package/dist/{chunk-XJ5H2MXY.mjs → chunk-HCCBEJEZ.mjs} +31 -1
  17. package/dist/{chunk-VOUI6XPV.mjs → chunk-HIZU7WI2.mjs} +1 -1
  18. package/dist/{chunk-VC6X44FS.mjs → chunk-ZR2QHTEX.mjs} +505 -68
  19. package/dist/codegen/backends/imperative.cjs +24 -6
  20. package/dist/codegen/backends/imperative.d.ts.map +1 -1
  21. package/dist/codegen/backends/imperative.js +20 -4
  22. package/dist/codegen/backends/imperative.mjs +24 -6
  23. package/dist/frontend/babel/build-ir-lit.cjs +470 -63
  24. package/dist/frontend/babel/build-ir-lit.mjs +470 -63
  25. package/dist/frontend/babel/build-ir.cjs +470 -63
  26. package/dist/frontend/babel/build-ir.mjs +470 -63
  27. package/dist/frontend/babel/index.cjs +471 -64
  28. package/dist/frontend/babel/index.mjs +471 -64
  29. package/dist/frontend/babel/lower-template.cjs +470 -63
  30. package/dist/frontend/babel/lower-template.mjs +470 -63
  31. package/dist/frontend/babel/plugin.cjs +470 -63
  32. package/dist/frontend/babel/plugin.mjs +470 -63
  33. package/dist/index.cjs +360 -58
  34. package/dist/index.mjs +372 -58
  35. package/dist/integrations.cjs +1347 -98
  36. package/dist/integrations.d.ts.map +1 -1
  37. package/dist/integrations.js +44 -2
  38. package/dist/integrations.mjs +205 -19
  39. package/dist/lower/compile-template.cjs +354 -57
  40. package/dist/lower/compile-template.d.cts +13 -0
  41. package/dist/lower/compile-template.d.ts +13 -0
  42. package/dist/lower/compile-template.d.ts.map +1 -1
  43. package/dist/lower/compile-template.js +3 -0
  44. package/dist/lower/compile-template.mjs +366 -57
  45. package/dist/lower/entities.cjs +288 -0
  46. package/dist/lower/entities.d.cts +1 -0
  47. package/dist/lower/entities.d.ts +2 -0
  48. package/dist/lower/entities.d.ts.map +1 -0
  49. package/dist/lower/entities.js +54 -0
  50. package/dist/lower/entities.mjs +267 -0
  51. package/dist/lower/jsx.cjs +567 -7
  52. package/dist/lower/jsx.d.cts +8 -0
  53. package/dist/lower/jsx.d.ts +8 -0
  54. package/dist/lower/jsx.d.ts.map +1 -1
  55. package/dist/lower/jsx.js +205 -7
  56. package/dist/lower/jsx.mjs +567 -7
  57. package/dist/lower/template.cjs +332 -56
  58. package/dist/lower/template.d.ts.map +1 -1
  59. package/dist/lower/template.js +57 -49
  60. package/dist/lower/template.mjs +332 -56
  61. package/dist/resolve/builtins.cjs +1 -1
  62. package/dist/resolve/builtins.mjs +1 -1
  63. package/dist/resolve/component-globals.cjs +20 -1
  64. package/dist/resolve/component-globals.js +2 -2
  65. package/dist/resolve/component-globals.mjs +20 -1
  66. package/dist/resolve/write-globals.cjs +20 -1
  67. package/dist/resolve/write-globals.mjs +20 -1
  68. package/dist/{server-fns-BBZRZUPP.mjs → server-fns-JLSTKWPS.mjs} +4 -4
  69. package/dist/{server-functions-VEGLIFCE.mjs → server-functions-BGY3RD4R.mjs} +3 -3
  70. package/dist/transform/index.cjs +1123 -77
  71. package/dist/transform/index.mjs +1141 -91
  72. package/dist/transform/intrinsics.cjs +18 -0
  73. package/dist/transform/intrinsics.mjs +18 -0
  74. package/dist/transform/props.mjs +19 -14
  75. package/dist/transform/server-fns.mjs +19 -14
  76. package/dist/transform/source-locations.cjs +64 -3
  77. package/dist/transform/source-locations.d.cts +7 -2
  78. package/dist/transform/source-locations.d.ts +7 -2
  79. package/dist/transform/source-locations.d.ts.map +1 -1
  80. package/dist/transform/source-locations.js +68 -3
  81. package/dist/transform/source-locations.mjs +64 -3
  82. package/dist/transform/styles.cjs +588 -0
  83. package/dist/transform/styles.d.cts +211 -0
  84. package/dist/transform/styles.d.ts +212 -0
  85. package/dist/transform/styles.d.ts.map +1 -0
  86. package/dist/transform/styles.js +714 -0
  87. package/dist/transform/styles.mjs +653 -0
  88. package/dist/transform/templates.cjs +1122 -76
  89. package/dist/transform/templates.d.cts +13 -0
  90. package/dist/transform/templates.d.ts +13 -0
  91. package/dist/transform/templates.d.ts.map +1 -1
  92. package/dist/transform/templates.js +51 -14
  93. package/dist/transform/templates.mjs +1140 -90
  94. package/dist/{transform-UWZ7ZGJ2.mjs → transform-GBP6Y4UI.mjs} +603 -19
  95. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  96. package/dist/vite-styles.cjs +180 -0
  97. package/dist/vite-styles.d.cts +59 -0
  98. package/dist/vite-styles.d.ts +60 -0
  99. package/dist/vite-styles.d.ts.map +1 -0
  100. package/dist/vite-styles.js +198 -0
  101. package/dist/vite-styles.mjs +159 -0
  102. package/package.json +10 -3
@@ -268,6 +268,7 @@ var INTRINSICS = {
268
268
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
269
269
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
270
270
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
271
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
271
272
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
272
273
  // name. Still worth having: no import to write, and when a shorter form is
273
274
  // designed, the change is this line rather than a compiler pass.
@@ -278,6 +279,19 @@ var INTRINSICS = {
278
279
  // dependency analysis must not guess at.
279
280
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
280
281
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
282
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
283
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
284
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
285
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
286
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
287
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
288
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
289
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
290
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
291
+ // handle as far as reads go: the same accessor with `set` on it.
292
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
293
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
294
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
281
295
  // An environment variable, public or server-only; the browser build refuses a secret.
282
296
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
283
297
  };
@@ -455,13 +469,16 @@ function emitComponent(name, props, children, used, templates, at) {
455
469
  used.add("createComponent");
456
470
  const spreads = props.filter((p) => p.kind === "spread");
457
471
  const normal = props.filter((p) => p.kind !== "spread");
458
- const entries = normal.map(
459
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
460
- );
472
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
461
473
  if (children.length > 0) {
462
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
474
+ bodies.push(["children", emitChildren(children, used, templates)]);
475
+ }
476
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
477
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
478
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
479
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
480
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
463
481
  }
464
- let obj = `{ ${entries.join(", ")} }`;
465
482
  if (spreads.length > 0) {
466
483
  used.add("mergeProps");
467
484
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -543,7 +560,8 @@ function emitNode(node, used, templates) {
543
560
  } else {
544
561
  used.add("spread");
545
562
  const svgFlag = node.svg ? ", isSVG: true" : "";
546
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
563
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
564
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
547
565
  parts.push(
548
566
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
549
567
  );
@@ -596,6 +614,271 @@ function cleanTemplateText(value) {
596
614
  return out;
597
615
  }
598
616
 
617
+ // src/lower/entities.ts
618
+ var NAMED = {
619
+ quot: 34,
620
+ amp: 38,
621
+ apos: 39,
622
+ lt: 60,
623
+ gt: 62,
624
+ nbsp: 160,
625
+ iexcl: 161,
626
+ cent: 162,
627
+ pound: 163,
628
+ curren: 164,
629
+ yen: 165,
630
+ brvbar: 166,
631
+ sect: 167,
632
+ uml: 168,
633
+ copy: 169,
634
+ ordf: 170,
635
+ laquo: 171,
636
+ not: 172,
637
+ shy: 173,
638
+ reg: 174,
639
+ macr: 175,
640
+ deg: 176,
641
+ plusmn: 177,
642
+ sup2: 178,
643
+ sup3: 179,
644
+ acute: 180,
645
+ micro: 181,
646
+ para: 182,
647
+ middot: 183,
648
+ cedil: 184,
649
+ sup1: 185,
650
+ ordm: 186,
651
+ raquo: 187,
652
+ frac14: 188,
653
+ frac12: 189,
654
+ frac34: 190,
655
+ iquest: 191,
656
+ Agrave: 192,
657
+ Aacute: 193,
658
+ Acirc: 194,
659
+ Atilde: 195,
660
+ Auml: 196,
661
+ Aring: 197,
662
+ AElig: 198,
663
+ Ccedil: 199,
664
+ Egrave: 200,
665
+ Eacute: 201,
666
+ Ecirc: 202,
667
+ Euml: 203,
668
+ Igrave: 204,
669
+ Iacute: 205,
670
+ Icirc: 206,
671
+ Iuml: 207,
672
+ ETH: 208,
673
+ Ntilde: 209,
674
+ Ograve: 210,
675
+ Oacute: 211,
676
+ Ocirc: 212,
677
+ Otilde: 213,
678
+ Ouml: 214,
679
+ times: 215,
680
+ Oslash: 216,
681
+ Ugrave: 217,
682
+ Uacute: 218,
683
+ Ucirc: 219,
684
+ Uuml: 220,
685
+ Yacute: 221,
686
+ THORN: 222,
687
+ szlig: 223,
688
+ agrave: 224,
689
+ aacute: 225,
690
+ acirc: 226,
691
+ atilde: 227,
692
+ auml: 228,
693
+ aring: 229,
694
+ aelig: 230,
695
+ ccedil: 231,
696
+ egrave: 232,
697
+ eacute: 233,
698
+ ecirc: 234,
699
+ euml: 235,
700
+ igrave: 236,
701
+ iacute: 237,
702
+ icirc: 238,
703
+ iuml: 239,
704
+ eth: 240,
705
+ ntilde: 241,
706
+ ograve: 242,
707
+ oacute: 243,
708
+ ocirc: 244,
709
+ otilde: 245,
710
+ ouml: 246,
711
+ divide: 247,
712
+ oslash: 248,
713
+ ugrave: 249,
714
+ uacute: 250,
715
+ ucirc: 251,
716
+ uuml: 252,
717
+ yacute: 253,
718
+ thorn: 254,
719
+ yuml: 255,
720
+ OElig: 338,
721
+ oelig: 339,
722
+ Scaron: 352,
723
+ scaron: 353,
724
+ Yuml: 376,
725
+ fnof: 402,
726
+ circ: 710,
727
+ tilde: 732,
728
+ Alpha: 913,
729
+ Beta: 914,
730
+ Gamma: 915,
731
+ Delta: 916,
732
+ Epsilon: 917,
733
+ Zeta: 918,
734
+ Eta: 919,
735
+ Theta: 920,
736
+ Iota: 921,
737
+ Kappa: 922,
738
+ Lambda: 923,
739
+ Mu: 924,
740
+ Nu: 925,
741
+ Xi: 926,
742
+ Omicron: 927,
743
+ Pi: 928,
744
+ Rho: 929,
745
+ Sigma: 931,
746
+ Tau: 932,
747
+ Upsilon: 933,
748
+ Phi: 934,
749
+ Chi: 935,
750
+ Psi: 936,
751
+ Omega: 937,
752
+ alpha: 945,
753
+ beta: 946,
754
+ gamma: 947,
755
+ delta: 948,
756
+ epsilon: 949,
757
+ zeta: 950,
758
+ eta: 951,
759
+ theta: 952,
760
+ iota: 953,
761
+ kappa: 954,
762
+ lambda: 955,
763
+ mu: 956,
764
+ nu: 957,
765
+ xi: 958,
766
+ omicron: 959,
767
+ pi: 960,
768
+ rho: 961,
769
+ sigmaf: 962,
770
+ sigma: 963,
771
+ tau: 964,
772
+ upsilon: 965,
773
+ phi: 966,
774
+ chi: 967,
775
+ psi: 968,
776
+ omega: 969,
777
+ thetasym: 977,
778
+ upsih: 978,
779
+ piv: 982,
780
+ ensp: 8194,
781
+ emsp: 8195,
782
+ thinsp: 8201,
783
+ zwnj: 8204,
784
+ zwj: 8205,
785
+ lrm: 8206,
786
+ rlm: 8207,
787
+ ndash: 8211,
788
+ mdash: 8212,
789
+ lsquo: 8216,
790
+ rsquo: 8217,
791
+ sbquo: 8218,
792
+ ldquo: 8220,
793
+ rdquo: 8221,
794
+ bdquo: 8222,
795
+ dagger: 8224,
796
+ Dagger: 8225,
797
+ bull: 8226,
798
+ hellip: 8230,
799
+ permil: 8240,
800
+ prime: 8242,
801
+ Prime: 8243,
802
+ lsaquo: 8249,
803
+ rsaquo: 8250,
804
+ oline: 8254,
805
+ frasl: 8260,
806
+ euro: 8364,
807
+ image: 8465,
808
+ weierp: 8472,
809
+ real: 8476,
810
+ trade: 8482,
811
+ alefsym: 8501,
812
+ larr: 8592,
813
+ uarr: 8593,
814
+ rarr: 8594,
815
+ darr: 8595,
816
+ harr: 8596,
817
+ crarr: 8629,
818
+ lArr: 8656,
819
+ uArr: 8657,
820
+ rArr: 8658,
821
+ dArr: 8659,
822
+ hArr: 8660,
823
+ forall: 8704,
824
+ part: 8706,
825
+ exist: 8707,
826
+ empty: 8709,
827
+ nabla: 8711,
828
+ isin: 8712,
829
+ notin: 8713,
830
+ ni: 8715,
831
+ prod: 8719,
832
+ sum: 8721,
833
+ minus: 8722,
834
+ lowast: 8727,
835
+ radic: 8730,
836
+ prop: 8733,
837
+ infin: 8734,
838
+ ang: 8736,
839
+ and: 8743,
840
+ or: 8744,
841
+ cap: 8745,
842
+ cup: 8746,
843
+ int: 8747,
844
+ there4: 8756,
845
+ sim: 8764,
846
+ cong: 8773,
847
+ asymp: 8776,
848
+ ne: 8800,
849
+ equiv: 8801,
850
+ le: 8804,
851
+ ge: 8805,
852
+ sub: 8834,
853
+ sup: 8835,
854
+ nsub: 8836,
855
+ sube: 8838,
856
+ supe: 8839,
857
+ oplus: 8853,
858
+ otimes: 8855,
859
+ perp: 8869,
860
+ sdot: 8901,
861
+ lceil: 8968,
862
+ rceil: 8969,
863
+ lfloor: 8970,
864
+ rfloor: 8971,
865
+ lang: 9001,
866
+ rang: 9002,
867
+ loz: 9674,
868
+ spades: 9824,
869
+ clubs: 9827,
870
+ hearts: 9829,
871
+ diams: 9830
872
+ };
873
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
874
+ function decodeEntities(value) {
875
+ if (!value.includes("&")) return value;
876
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
877
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
878
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
879
+ });
880
+ }
881
+
599
882
  // src/lower/template.ts
600
883
  var DELEGATED = /* @__PURE__ */ new Set([
601
884
  "click",
@@ -614,6 +897,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
614
897
  function escapeTemplate(s) {
615
898
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
616
899
  }
900
+ var CONTAINER = "container";
617
901
  function capitalize(s) {
618
902
  return s.charAt(0).toUpperCase() + s.slice(1);
619
903
  }
@@ -668,10 +952,6 @@ var Lowerer = class {
668
952
  if (hasElse) i = elseIdx;
669
953
  continue;
670
954
  }
671
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
672
- out.push(this.lowerEach(child));
673
- continue;
674
- }
675
955
  }
676
956
  const node = this.lowerNode(child);
677
957
  if (node) out.push(node);
@@ -685,7 +965,7 @@ var Lowerer = class {
685
965
  switch (node.kind) {
686
966
  case "Text": {
687
967
  if (node.raw) return { kind: "text", value: node.value };
688
- const cleaned = cleanTemplateText(node.value);
968
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
689
969
  return cleaned ? { kind: "text", value: cleaned } : null;
690
970
  }
691
971
  case "Comment":
@@ -713,13 +993,16 @@ var Lowerer = class {
713
993
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
714
994
  return node;
715
995
  }
996
+ const each = this.lowerEach(el);
997
+ const children = each ? [each] : this.lowerChildren(el.children);
998
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
716
999
  const at = this.tagAt(el);
717
1000
  return {
718
1001
  kind: "element",
719
1002
  tag: el.tag,
720
1003
  svg: el.namespace === "svg",
721
- props: this.lowerAttributes(el.attributes),
722
- children: this.lowerChildren(el.children),
1004
+ props: this.lowerAttributes(el.attributes, true),
1005
+ children,
723
1006
  static: false,
724
1007
  ...at ? { at } : {}
725
1008
  };
@@ -791,10 +1074,14 @@ var Lowerer = class {
791
1074
  const at = this.tagAt(el);
792
1075
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
793
1076
  }
794
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1077
+ /**
1078
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1079
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1080
+ * it is. Null when the element has no `each`.
1081
+ */
795
1082
  lowerEach(el) {
796
1083
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
797
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1084
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
798
1085
  const each = this.hole(eachDir.hole);
799
1086
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
800
1087
  if (eachDir.key) {
@@ -804,12 +1091,12 @@ var Lowerer = class {
804
1091
  const arrow = this.itemArrow(el);
805
1092
  let childrenCode;
806
1093
  if (arrow) {
807
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
808
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
809
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1094
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1095
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
810
1096
  } else {
811
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
812
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1097
+ const content = this.lowerChildren(el.children);
1098
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1099
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
813
1100
  }
814
1101
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
815
1102
  const at = this.tagAt(el);
@@ -826,33 +1113,23 @@ var Lowerer = class {
826
1113
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
827
1114
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
828
1115
  }
829
- /** Same element (minus each/key) but with explicit IR children. */
830
- rebuildWithChildren(el, children) {
831
- const props = this.lowerAttributes(
832
- el.attributes.filter((a) => a.kind !== "EachDirective")
833
- );
834
- if (el.kind === "Element") {
835
- const at = this.tagAt(el);
836
- return {
837
- kind: "element",
838
- tag: el.tag,
839
- svg: el.namespace === "svg",
840
- props,
841
- children,
842
- static: false,
843
- ...at ? { at } : {}
844
- };
845
- }
846
- return { kind: "component", name: el.tag, props, children };
847
- }
848
1116
  // --- attributes ---------------------------------------------------------
849
- lowerAttributes(attrs) {
1117
+ /**
1118
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
1119
+ * A component gets the object as before, rebuilt whole when any value moves.
1120
+ */
1121
+ lowerAttributes(attrs, element = false) {
1122
+ let anyReactive = false;
1123
+ const entry = (name, h) => {
1124
+ anyReactive ||= h.reactive;
1125
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
1126
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
1127
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
1128
+ };
850
1129
  const props = [];
851
1130
  const classEntries = [];
852
- let classReactive = false;
853
1131
  let classAt;
854
1132
  const styleEntries = [];
855
- let styleReactive = false;
856
1133
  let styleAt;
857
1134
  const usePairs = [];
858
1135
  for (const a of attrs) {
@@ -892,15 +1169,13 @@ var Lowerer = class {
892
1169
  break;
893
1170
  case "ClassDirective": {
894
1171
  const h = this.hole(a.hole);
895
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
896
- classReactive = classReactive || h.reactive;
1172
+ classEntries.push(entry(a.name, h));
897
1173
  classAt ??= h.at;
898
1174
  break;
899
1175
  }
900
1176
  case "StyleDirective": {
901
1177
  const h = this.hole(a.hole);
902
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
903
- styleReactive = styleReactive || h.reactive;
1178
+ styleEntries.push(entry(a.name, h));
904
1179
  styleAt ??= h.at;
905
1180
  break;
906
1181
  }
@@ -920,18 +1195,19 @@ var Lowerer = class {
920
1195
  name: "classList",
921
1196
  kind: "attr",
922
1197
  expr: `{ ${classEntries.join(", ")} }`,
923
- reactive: classReactive,
1198
+ reactive: !element && anyReactive,
924
1199
  ...classAt ? { at: classAt } : {}
925
1200
  });
926
1201
  }
927
1202
  if (styleEntries.length > 0) {
928
- props.push({
929
- name: "style",
930
- kind: "attr",
931
- expr: `{ ${styleEntries.join(", ")} }`,
932
- reactive: styleReactive,
933
- ...styleAt ? { at: styleAt } : {}
934
- });
1203
+ const entries = `{ ${styleEntries.join(", ")} }`;
1204
+ const base = props.findIndex((p) => p.name === "style");
1205
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
1206
+ else {
1207
+ const prop = props[base];
1208
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1209
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1210
+ }
935
1211
  }
936
1212
  if (usePairs.length > 0) {
937
1213
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1020,7 +1296,7 @@ var Lowerer = class {
1020
1296
  return base;
1021
1297
  }
1022
1298
  if (v.kind === "static") {
1023
- base.literal = v.value;
1299
+ base.literal = decodeEntities(v.value);
1024
1300
  return base;
1025
1301
  }
1026
1302
  if (v.kind === "hole") {
@@ -1033,7 +1309,7 @@ var Lowerer = class {
1033
1309
  let reactive = false;
1034
1310
  let at;
1035
1311
  const body = v.parts.map((p) => {
1036
- if ("text" in p) return escapeTemplate(p.text);
1312
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1037
1313
  const h = this.hole(p.hole);
1038
1314
  reactive = reactive || h.reactive;
1039
1315
  at ??= h.at;
@@ -1214,6 +1490,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1214
1490
  "mouseup"
1215
1491
  ]);
1216
1492
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1493
+ var CONTAINER2 = "container";
1494
+ function directive(node, name) {
1495
+ if (node.type !== "JSXElement") return void 0;
1496
+ const opening = node.openingElement;
1497
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1498
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1499
+ }
1500
+ function directiveExpr(attr) {
1501
+ const v = attr.value;
1502
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1503
+ }
1504
+ function emitIR(ir, ctx) {
1505
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1506
+ for (const s of imports) ctx.used.add(s);
1507
+ return inlineTemplates(code, templates);
1508
+ }
1217
1509
  var posOf = (node, ctx) => {
1218
1510
  if (!ctx.sourceFile) return void 0;
1219
1511
  const at = node.loc?.start;
@@ -1291,9 +1583,12 @@ function attrExprCode(attr, ctx) {
1291
1583
  }
1292
1584
  return null;
1293
1585
  }
1294
- function buildProps(attrs, ctx) {
1586
+ function buildProps(attrs, ctx, element = false) {
1295
1587
  const props = [];
1296
1588
  const usePairs = [];
1589
+ const classEntries = [];
1590
+ const styleEntries = [];
1591
+ let styleAt;
1297
1592
  for (const a of attrs) {
1298
1593
  if (a.type === "JSXSpreadAttribute") {
1299
1594
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -1309,6 +1604,27 @@ function buildProps(attrs, ctx) {
1309
1604
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
1310
1605
  continue;
1311
1606
  }
1607
+ if (element && (ns === "class" || ns === "style")) {
1608
+ const key = JSON.stringify(a.name.name.name);
1609
+ const v = a.value;
1610
+ let value;
1611
+ if (v == null) value = "true";
1612
+ else if (isString(v)) value = JSON.stringify(literalOf(v));
1613
+ else if (v.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression") {
1614
+ value = keyValue(v.expression, ctx);
1615
+ if (ns === "style") styleAt ??= posOf(v.expression, ctx);
1616
+ } else continue;
1617
+ (ns === "class" ? classEntries : styleEntries).push(`${key}: ${value}`);
1618
+ continue;
1619
+ }
1620
+ if (element && a.name.type === "JSXIdentifier" && a.name.name === "style" && a.value?.type === "JSXExpressionContainer") {
1621
+ const entries = objectEntries(a.value.expression, ctx);
1622
+ if (entries) {
1623
+ styleEntries.unshift(...entries);
1624
+ styleAt = posOf(a.value.expression, ctx);
1625
+ continue;
1626
+ }
1627
+ }
1312
1628
  if (ns === "oncapture") {
1313
1629
  const evt = a.name.name.name.toLowerCase();
1314
1630
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -1317,12 +1633,49 @@ function buildProps(attrs, ctx) {
1317
1633
  }
1318
1634
  props.push(buildAttr(a, ctx));
1319
1635
  }
1636
+ if (classEntries.length > 0) props.push({ name: "classList", kind: "attr", expr: `{ ${classEntries.join(", ")} }`, reactive: false });
1637
+ if (styleEntries.length > 0) {
1638
+ const entries = `{ ${styleEntries.join(", ")} }`;
1639
+ const base = props.findIndex((p) => p.name === "style" && p.kind === "attr");
1640
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: false, ...styleAt ? { at: styleAt } : {} });
1641
+ else {
1642
+ const prop = props[base];
1643
+ const value = prop.literal !== void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1644
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1645
+ }
1646
+ }
1320
1647
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
1321
1648
  return props;
1322
1649
  }
1650
+ function keyValue(expr, ctx) {
1651
+ const code = ctx.code(expr);
1652
+ if (!isReactive(expr)) return code;
1653
+ const accessor = `() => ${paren(code, "arrowBody")}`;
1654
+ const at = posOf(expr, ctx);
1655
+ return at ? markHole(accessor, at.line, at.column) : accessor;
1656
+ }
1657
+ function objectEntries(expr, ctx) {
1658
+ if (expr?.type !== "ObjectExpression") return null;
1659
+ const entries = [];
1660
+ for (const p of expr.properties) {
1661
+ if (p.type !== "ObjectProperty") return null;
1662
+ const key = p.computed ? `[${ctx.code(p.key)}]` : p.key.type === "Identifier" ? JSON.stringify(p.key.name) : JSON.stringify(String(p.key.value));
1663
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
1664
+ }
1665
+ return entries;
1666
+ }
1323
1667
  function buildChildren(children, ctx) {
1324
1668
  const out = [];
1325
- for (const child of children) {
1669
+ for (let i = 0; i < children.length; i++) {
1670
+ const child = children[i];
1671
+ if (directive(child, "if")) {
1672
+ let next = i + 1;
1673
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1674
+ const fallback = directive(children[next], "else") ? children[next] : null;
1675
+ out.push(buildIf(child, fallback, ctx));
1676
+ if (fallback) i = next;
1677
+ continue;
1678
+ }
1326
1679
  if (child.type === "JSXText") {
1327
1680
  const text = cleanTemplateText(child.value);
1328
1681
  if (text) out.push({ kind: "text", value: text });
@@ -1338,6 +1691,7 @@ function buildChildren(children, ctx) {
1338
1691
  });
1339
1692
  }
1340
1693
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
1694
+ if (ctx.dropStyles?.has(child.start)) continue;
1341
1695
  out.push(buildNode(child, ctx));
1342
1696
  } else if (child.type === "JSXSpreadChild") {
1343
1697
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -1359,19 +1713,72 @@ function readLoad(attrs) {
1359
1713
  }
1360
1714
  return void 0;
1361
1715
  }
1716
+ function buildIf(node, fallback, ctx) {
1717
+ const cond = directiveExpr(directive(node, "if"));
1718
+ const props = [
1719
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1720
+ ];
1721
+ if (fallback) {
1722
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1723
+ }
1724
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1725
+ }
1726
+ function buildEach(node, ctx) {
1727
+ const attr = directive(node, "each");
1728
+ if (!attr) return null;
1729
+ const list = directiveExpr(attr);
1730
+ const props = [
1731
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1732
+ ];
1733
+ const key = directive(node, "key");
1734
+ if (key) {
1735
+ const keyExpr = directiveExpr(key);
1736
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1737
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1738
+ }
1739
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1740
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1741
+ let render;
1742
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1743
+ const fn = ctx.code(only);
1744
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1745
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1746
+ } else {
1747
+ const built = buildChildren(node.children, ctx);
1748
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1749
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1750
+ }
1751
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1752
+ }
1753
+ function buildElement(node, ctx, handled) {
1754
+ const skip = new Set(handled);
1755
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1756
+ const attributes = node.openingElement.attributes.filter(
1757
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1758
+ );
1759
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1760
+ const each = buildEach(node, ctx);
1761
+ const children = each ? [each] : buildChildren(node.children, ctx);
1762
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1763
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1764
+ const props = buildProps(attributes, ctx, true);
1765
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
1766
+ if (scope) props.unshift({ name: "data-fx-s", kind: "attr", literal: scope.value });
1767
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1768
+ }
1362
1769
  function buildNode(node, ctx) {
1363
1770
  if (node.type === "JSXFragment") {
1364
1771
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1365
1772
  }
1773
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1774
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
1366
1775
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1776
+ if (!component) return buildElement(node, ctx, []);
1367
1777
  const props = buildProps(node.openingElement.attributes, ctx);
1368
1778
  const children = buildChildren(node.children, ctx);
1369
1779
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1370
- if (component) {
1371
- const load = readLoad(node.openingElement.attributes);
1372
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1373
- }
1374
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1780
+ const load = readLoad(node.openingElement.attributes);
1781
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1375
1782
  }
1376
1783
  function buildJsxIR(node, ctx) {
1377
1784
  const ir = buildNode(node, ctx);