@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
@@ -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
  );
@@ -600,6 +618,271 @@ function cleanTemplateText(value) {
600
618
  return out;
601
619
  }
602
620
 
621
+ // src/lower/entities.ts
622
+ var NAMED = {
623
+ quot: 34,
624
+ amp: 38,
625
+ apos: 39,
626
+ lt: 60,
627
+ gt: 62,
628
+ nbsp: 160,
629
+ iexcl: 161,
630
+ cent: 162,
631
+ pound: 163,
632
+ curren: 164,
633
+ yen: 165,
634
+ brvbar: 166,
635
+ sect: 167,
636
+ uml: 168,
637
+ copy: 169,
638
+ ordf: 170,
639
+ laquo: 171,
640
+ not: 172,
641
+ shy: 173,
642
+ reg: 174,
643
+ macr: 175,
644
+ deg: 176,
645
+ plusmn: 177,
646
+ sup2: 178,
647
+ sup3: 179,
648
+ acute: 180,
649
+ micro: 181,
650
+ para: 182,
651
+ middot: 183,
652
+ cedil: 184,
653
+ sup1: 185,
654
+ ordm: 186,
655
+ raquo: 187,
656
+ frac14: 188,
657
+ frac12: 189,
658
+ frac34: 190,
659
+ iquest: 191,
660
+ Agrave: 192,
661
+ Aacute: 193,
662
+ Acirc: 194,
663
+ Atilde: 195,
664
+ Auml: 196,
665
+ Aring: 197,
666
+ AElig: 198,
667
+ Ccedil: 199,
668
+ Egrave: 200,
669
+ Eacute: 201,
670
+ Ecirc: 202,
671
+ Euml: 203,
672
+ Igrave: 204,
673
+ Iacute: 205,
674
+ Icirc: 206,
675
+ Iuml: 207,
676
+ ETH: 208,
677
+ Ntilde: 209,
678
+ Ograve: 210,
679
+ Oacute: 211,
680
+ Ocirc: 212,
681
+ Otilde: 213,
682
+ Ouml: 214,
683
+ times: 215,
684
+ Oslash: 216,
685
+ Ugrave: 217,
686
+ Uacute: 218,
687
+ Ucirc: 219,
688
+ Uuml: 220,
689
+ Yacute: 221,
690
+ THORN: 222,
691
+ szlig: 223,
692
+ agrave: 224,
693
+ aacute: 225,
694
+ acirc: 226,
695
+ atilde: 227,
696
+ auml: 228,
697
+ aring: 229,
698
+ aelig: 230,
699
+ ccedil: 231,
700
+ egrave: 232,
701
+ eacute: 233,
702
+ ecirc: 234,
703
+ euml: 235,
704
+ igrave: 236,
705
+ iacute: 237,
706
+ icirc: 238,
707
+ iuml: 239,
708
+ eth: 240,
709
+ ntilde: 241,
710
+ ograve: 242,
711
+ oacute: 243,
712
+ ocirc: 244,
713
+ otilde: 245,
714
+ ouml: 246,
715
+ divide: 247,
716
+ oslash: 248,
717
+ ugrave: 249,
718
+ uacute: 250,
719
+ ucirc: 251,
720
+ uuml: 252,
721
+ yacute: 253,
722
+ thorn: 254,
723
+ yuml: 255,
724
+ OElig: 338,
725
+ oelig: 339,
726
+ Scaron: 352,
727
+ scaron: 353,
728
+ Yuml: 376,
729
+ fnof: 402,
730
+ circ: 710,
731
+ tilde: 732,
732
+ Alpha: 913,
733
+ Beta: 914,
734
+ Gamma: 915,
735
+ Delta: 916,
736
+ Epsilon: 917,
737
+ Zeta: 918,
738
+ Eta: 919,
739
+ Theta: 920,
740
+ Iota: 921,
741
+ Kappa: 922,
742
+ Lambda: 923,
743
+ Mu: 924,
744
+ Nu: 925,
745
+ Xi: 926,
746
+ Omicron: 927,
747
+ Pi: 928,
748
+ Rho: 929,
749
+ Sigma: 931,
750
+ Tau: 932,
751
+ Upsilon: 933,
752
+ Phi: 934,
753
+ Chi: 935,
754
+ Psi: 936,
755
+ Omega: 937,
756
+ alpha: 945,
757
+ beta: 946,
758
+ gamma: 947,
759
+ delta: 948,
760
+ epsilon: 949,
761
+ zeta: 950,
762
+ eta: 951,
763
+ theta: 952,
764
+ iota: 953,
765
+ kappa: 954,
766
+ lambda: 955,
767
+ mu: 956,
768
+ nu: 957,
769
+ xi: 958,
770
+ omicron: 959,
771
+ pi: 960,
772
+ rho: 961,
773
+ sigmaf: 962,
774
+ sigma: 963,
775
+ tau: 964,
776
+ upsilon: 965,
777
+ phi: 966,
778
+ chi: 967,
779
+ psi: 968,
780
+ omega: 969,
781
+ thetasym: 977,
782
+ upsih: 978,
783
+ piv: 982,
784
+ ensp: 8194,
785
+ emsp: 8195,
786
+ thinsp: 8201,
787
+ zwnj: 8204,
788
+ zwj: 8205,
789
+ lrm: 8206,
790
+ rlm: 8207,
791
+ ndash: 8211,
792
+ mdash: 8212,
793
+ lsquo: 8216,
794
+ rsquo: 8217,
795
+ sbquo: 8218,
796
+ ldquo: 8220,
797
+ rdquo: 8221,
798
+ bdquo: 8222,
799
+ dagger: 8224,
800
+ Dagger: 8225,
801
+ bull: 8226,
802
+ hellip: 8230,
803
+ permil: 8240,
804
+ prime: 8242,
805
+ Prime: 8243,
806
+ lsaquo: 8249,
807
+ rsaquo: 8250,
808
+ oline: 8254,
809
+ frasl: 8260,
810
+ euro: 8364,
811
+ image: 8465,
812
+ weierp: 8472,
813
+ real: 8476,
814
+ trade: 8482,
815
+ alefsym: 8501,
816
+ larr: 8592,
817
+ uarr: 8593,
818
+ rarr: 8594,
819
+ darr: 8595,
820
+ harr: 8596,
821
+ crarr: 8629,
822
+ lArr: 8656,
823
+ uArr: 8657,
824
+ rArr: 8658,
825
+ dArr: 8659,
826
+ hArr: 8660,
827
+ forall: 8704,
828
+ part: 8706,
829
+ exist: 8707,
830
+ empty: 8709,
831
+ nabla: 8711,
832
+ isin: 8712,
833
+ notin: 8713,
834
+ ni: 8715,
835
+ prod: 8719,
836
+ sum: 8721,
837
+ minus: 8722,
838
+ lowast: 8727,
839
+ radic: 8730,
840
+ prop: 8733,
841
+ infin: 8734,
842
+ ang: 8736,
843
+ and: 8743,
844
+ or: 8744,
845
+ cap: 8745,
846
+ cup: 8746,
847
+ int: 8747,
848
+ there4: 8756,
849
+ sim: 8764,
850
+ cong: 8773,
851
+ asymp: 8776,
852
+ ne: 8800,
853
+ equiv: 8801,
854
+ le: 8804,
855
+ ge: 8805,
856
+ sub: 8834,
857
+ sup: 8835,
858
+ nsub: 8836,
859
+ sube: 8838,
860
+ supe: 8839,
861
+ oplus: 8853,
862
+ otimes: 8855,
863
+ perp: 8869,
864
+ sdot: 8901,
865
+ lceil: 8968,
866
+ rceil: 8969,
867
+ lfloor: 8970,
868
+ rfloor: 8971,
869
+ lang: 9001,
870
+ rang: 9002,
871
+ loz: 9674,
872
+ spades: 9824,
873
+ clubs: 9827,
874
+ hearts: 9829,
875
+ diams: 9830
876
+ };
877
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
878
+ function decodeEntities(value) {
879
+ if (!value.includes("&")) return value;
880
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
881
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
882
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
883
+ });
884
+ }
885
+
603
886
  // src/lower/template.ts
604
887
  var DELEGATED = /* @__PURE__ */ new Set([
605
888
  "click",
@@ -618,6 +901,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
618
901
  function escapeTemplate(s) {
619
902
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
620
903
  }
904
+ var CONTAINER = "container";
621
905
  function capitalize(s) {
622
906
  return s.charAt(0).toUpperCase() + s.slice(1);
623
907
  }
@@ -672,10 +956,6 @@ var Lowerer = class {
672
956
  if (hasElse) i = elseIdx;
673
957
  continue;
674
958
  }
675
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
676
- out.push(this.lowerEach(child));
677
- continue;
678
- }
679
959
  }
680
960
  const node = this.lowerNode(child);
681
961
  if (node) out.push(node);
@@ -689,7 +969,7 @@ var Lowerer = class {
689
969
  switch (node.kind) {
690
970
  case "Text": {
691
971
  if (node.raw) return { kind: "text", value: node.value };
692
- const cleaned = cleanTemplateText(node.value);
972
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
693
973
  return cleaned ? { kind: "text", value: cleaned } : null;
694
974
  }
695
975
  case "Comment":
@@ -717,13 +997,16 @@ var Lowerer = class {
717
997
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
718
998
  return node;
719
999
  }
1000
+ const each = this.lowerEach(el);
1001
+ const children = each ? [each] : this.lowerChildren(el.children);
1002
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
720
1003
  const at = this.tagAt(el);
721
1004
  return {
722
1005
  kind: "element",
723
1006
  tag: el.tag,
724
1007
  svg: el.namespace === "svg",
725
- props: this.lowerAttributes(el.attributes),
726
- children: this.lowerChildren(el.children),
1008
+ props: this.lowerAttributes(el.attributes, true),
1009
+ children,
727
1010
  static: false,
728
1011
  ...at ? { at } : {}
729
1012
  };
@@ -795,10 +1078,14 @@ var Lowerer = class {
795
1078
  const at = this.tagAt(el);
796
1079
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
797
1080
  }
798
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1081
+ /**
1082
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1083
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1084
+ * it is. Null when the element has no `each`.
1085
+ */
799
1086
  lowerEach(el) {
800
1087
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
801
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1088
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
802
1089
  const each = this.hole(eachDir.hole);
803
1090
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
804
1091
  if (eachDir.key) {
@@ -808,12 +1095,12 @@ var Lowerer = class {
808
1095
  const arrow = this.itemArrow(el);
809
1096
  let childrenCode;
810
1097
  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")}`;
1098
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1099
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
814
1100
  } else {
815
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
816
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1101
+ const content = this.lowerChildren(el.children);
1102
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1103
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
817
1104
  }
818
1105
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
819
1106
  const at = this.tagAt(el);
@@ -830,33 +1117,23 @@ var Lowerer = class {
830
1117
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
831
1118
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
832
1119
  }
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
1120
  // --- attributes ---------------------------------------------------------
853
- lowerAttributes(attrs) {
1121
+ /**
1122
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
1123
+ * A component gets the object as before, rebuilt whole when any value moves.
1124
+ */
1125
+ lowerAttributes(attrs, element = false) {
1126
+ let anyReactive = false;
1127
+ const entry = (name, h) => {
1128
+ anyReactive ||= h.reactive;
1129
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
1130
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
1131
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
1132
+ };
854
1133
  const props = [];
855
1134
  const classEntries = [];
856
- let classReactive = false;
857
1135
  let classAt;
858
1136
  const styleEntries = [];
859
- let styleReactive = false;
860
1137
  let styleAt;
861
1138
  const usePairs = [];
862
1139
  for (const a of attrs) {
@@ -896,15 +1173,13 @@ var Lowerer = class {
896
1173
  break;
897
1174
  case "ClassDirective": {
898
1175
  const h = this.hole(a.hole);
899
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
900
- classReactive = classReactive || h.reactive;
1176
+ classEntries.push(entry(a.name, h));
901
1177
  classAt ??= h.at;
902
1178
  break;
903
1179
  }
904
1180
  case "StyleDirective": {
905
1181
  const h = this.hole(a.hole);
906
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
907
- styleReactive = styleReactive || h.reactive;
1182
+ styleEntries.push(entry(a.name, h));
908
1183
  styleAt ??= h.at;
909
1184
  break;
910
1185
  }
@@ -924,18 +1199,19 @@ var Lowerer = class {
924
1199
  name: "classList",
925
1200
  kind: "attr",
926
1201
  expr: `{ ${classEntries.join(", ")} }`,
927
- reactive: classReactive,
1202
+ reactive: !element && anyReactive,
928
1203
  ...classAt ? { at: classAt } : {}
929
1204
  });
930
1205
  }
931
1206
  if (styleEntries.length > 0) {
932
- props.push({
933
- name: "style",
934
- kind: "attr",
935
- expr: `{ ${styleEntries.join(", ")} }`,
936
- reactive: styleReactive,
937
- ...styleAt ? { at: styleAt } : {}
938
- });
1207
+ const entries = `{ ${styleEntries.join(", ")} }`;
1208
+ const base = props.findIndex((p) => p.name === "style");
1209
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
1210
+ else {
1211
+ const prop = props[base];
1212
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1213
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1214
+ }
939
1215
  }
940
1216
  if (usePairs.length > 0) {
941
1217
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1024,7 +1300,7 @@ var Lowerer = class {
1024
1300
  return base;
1025
1301
  }
1026
1302
  if (v.kind === "static") {
1027
- base.literal = v.value;
1303
+ base.literal = decodeEntities(v.value);
1028
1304
  return base;
1029
1305
  }
1030
1306
  if (v.kind === "hole") {
@@ -1037,7 +1313,7 @@ var Lowerer = class {
1037
1313
  let reactive = false;
1038
1314
  let at;
1039
1315
  const body = v.parts.map((p) => {
1040
- if ("text" in p) return escapeTemplate(p.text);
1316
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1041
1317
  const h = this.hole(p.hole);
1042
1318
  reactive = reactive || h.reactive;
1043
1319
  at ??= h.at;
@@ -1260,6 +1536,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1260
1536
  "mouseup"
1261
1537
  ]);
1262
1538
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1539
+ var CONTAINER2 = "container";
1540
+ function directive(node, name) {
1541
+ if (node.type !== "JSXElement") return void 0;
1542
+ const opening = node.openingElement;
1543
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1544
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1545
+ }
1546
+ function directiveExpr(attr) {
1547
+ const v = attr.value;
1548
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1549
+ }
1550
+ function emitIR(ir, ctx) {
1551
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1552
+ for (const s of imports) ctx.used.add(s);
1553
+ return inlineTemplates(code, templates);
1554
+ }
1263
1555
  var posOf = (node, ctx) => {
1264
1556
  if (!ctx.sourceFile) return void 0;
1265
1557
  const at = node.loc?.start;
@@ -1337,9 +1629,12 @@ function attrExprCode(attr, ctx) {
1337
1629
  }
1338
1630
  return null;
1339
1631
  }
1340
- function buildProps(attrs, ctx) {
1632
+ function buildProps(attrs, ctx, element = false) {
1341
1633
  const props = [];
1342
1634
  const usePairs = [];
1635
+ const classEntries = [];
1636
+ const styleEntries = [];
1637
+ let styleAt;
1343
1638
  for (const a of attrs) {
1344
1639
  if (a.type === "JSXSpreadAttribute") {
1345
1640
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -1355,6 +1650,27 @@ function buildProps(attrs, ctx) {
1355
1650
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
1356
1651
  continue;
1357
1652
  }
1653
+ if (element && (ns === "class" || ns === "style")) {
1654
+ const key = JSON.stringify(a.name.name.name);
1655
+ const v = a.value;
1656
+ let value;
1657
+ if (v == null) value = "true";
1658
+ else if (isString(v)) value = JSON.stringify(literalOf(v));
1659
+ else if (v.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression") {
1660
+ value = keyValue(v.expression, ctx);
1661
+ if (ns === "style") styleAt ??= posOf(v.expression, ctx);
1662
+ } else continue;
1663
+ (ns === "class" ? classEntries : styleEntries).push(`${key}: ${value}`);
1664
+ continue;
1665
+ }
1666
+ if (element && a.name.type === "JSXIdentifier" && a.name.name === "style" && a.value?.type === "JSXExpressionContainer") {
1667
+ const entries = objectEntries(a.value.expression, ctx);
1668
+ if (entries) {
1669
+ styleEntries.unshift(...entries);
1670
+ styleAt = posOf(a.value.expression, ctx);
1671
+ continue;
1672
+ }
1673
+ }
1358
1674
  if (ns === "oncapture") {
1359
1675
  const evt = a.name.name.name.toLowerCase();
1360
1676
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -1363,12 +1679,49 @@ function buildProps(attrs, ctx) {
1363
1679
  }
1364
1680
  props.push(buildAttr(a, ctx));
1365
1681
  }
1682
+ if (classEntries.length > 0) props.push({ name: "classList", kind: "attr", expr: `{ ${classEntries.join(", ")} }`, reactive: false });
1683
+ if (styleEntries.length > 0) {
1684
+ const entries = `{ ${styleEntries.join(", ")} }`;
1685
+ const base = props.findIndex((p) => p.name === "style" && p.kind === "attr");
1686
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: false, ...styleAt ? { at: styleAt } : {} });
1687
+ else {
1688
+ const prop = props[base];
1689
+ const value = prop.literal !== void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1690
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1691
+ }
1692
+ }
1366
1693
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
1367
1694
  return props;
1368
1695
  }
1696
+ function keyValue(expr, ctx) {
1697
+ const code = ctx.code(expr);
1698
+ if (!isReactive(expr)) return code;
1699
+ const accessor = `() => ${paren(code, "arrowBody")}`;
1700
+ const at = posOf(expr, ctx);
1701
+ return at ? markHole(accessor, at.line, at.column) : accessor;
1702
+ }
1703
+ function objectEntries(expr, ctx) {
1704
+ if (expr?.type !== "ObjectExpression") return null;
1705
+ const entries = [];
1706
+ for (const p of expr.properties) {
1707
+ if (p.type !== "ObjectProperty") return null;
1708
+ const key = p.computed ? `[${ctx.code(p.key)}]` : p.key.type === "Identifier" ? JSON.stringify(p.key.name) : JSON.stringify(String(p.key.value));
1709
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
1710
+ }
1711
+ return entries;
1712
+ }
1369
1713
  function buildChildren(children, ctx) {
1370
1714
  const out = [];
1371
- for (const child of children) {
1715
+ for (let i = 0; i < children.length; i++) {
1716
+ const child = children[i];
1717
+ if (directive(child, "if")) {
1718
+ let next = i + 1;
1719
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1720
+ const fallback = directive(children[next], "else") ? children[next] : null;
1721
+ out.push(buildIf(child, fallback, ctx));
1722
+ if (fallback) i = next;
1723
+ continue;
1724
+ }
1372
1725
  if (child.type === "JSXText") {
1373
1726
  const text = cleanTemplateText(child.value);
1374
1727
  if (text) out.push({ kind: "text", value: text });
@@ -1384,6 +1737,7 @@ function buildChildren(children, ctx) {
1384
1737
  });
1385
1738
  }
1386
1739
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
1740
+ if (ctx.dropStyles?.has(child.start)) continue;
1387
1741
  out.push(buildNode(child, ctx));
1388
1742
  } else if (child.type === "JSXSpreadChild") {
1389
1743
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -1405,19 +1759,72 @@ function readLoad(attrs) {
1405
1759
  }
1406
1760
  return void 0;
1407
1761
  }
1762
+ function buildIf(node, fallback, ctx) {
1763
+ const cond = directiveExpr(directive(node, "if"));
1764
+ const props = [
1765
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1766
+ ];
1767
+ if (fallback) {
1768
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1769
+ }
1770
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1771
+ }
1772
+ function buildEach(node, ctx) {
1773
+ const attr = directive(node, "each");
1774
+ if (!attr) return null;
1775
+ const list = directiveExpr(attr);
1776
+ const props = [
1777
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1778
+ ];
1779
+ const key = directive(node, "key");
1780
+ if (key) {
1781
+ const keyExpr = directiveExpr(key);
1782
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1783
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1784
+ }
1785
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1786
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1787
+ let render;
1788
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1789
+ const fn = ctx.code(only);
1790
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1791
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1792
+ } else {
1793
+ const built = buildChildren(node.children, ctx);
1794
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1795
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1796
+ }
1797
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1798
+ }
1799
+ function buildElement(node, ctx, handled) {
1800
+ const skip = new Set(handled);
1801
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1802
+ const attributes = node.openingElement.attributes.filter(
1803
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1804
+ );
1805
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1806
+ const each = buildEach(node, ctx);
1807
+ const children = each ? [each] : buildChildren(node.children, ctx);
1808
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1809
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1810
+ const props = buildProps(attributes, ctx, true);
1811
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
1812
+ if (scope) props.unshift({ name: "data-fx-s", kind: "attr", literal: scope.value });
1813
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1814
+ }
1408
1815
  function buildNode(node, ctx) {
1409
1816
  if (node.type === "JSXFragment") {
1410
1817
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1411
1818
  }
1819
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1820
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
1412
1821
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1822
+ if (!component) return buildElement(node, ctx, []);
1413
1823
  const props = buildProps(node.openingElement.attributes, ctx);
1414
1824
  const children = buildChildren(node.children, ctx);
1415
1825
  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 } : {} };
1826
+ const load = readLoad(node.openingElement.attributes);
1827
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1421
1828
  }
1422
1829
  function buildJsxIR(node, ctx) {
1423
1830
  const ir = buildNode(node, ctx);