@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
@@ -251,6 +251,7 @@ var INTRINSICS = {
251
251
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
252
252
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
253
253
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
254
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
254
255
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
255
256
  // name. Still worth having: no import to write, and when a shorter form is
256
257
  // designed, the change is this line rather than a compiler pass.
@@ -261,6 +262,19 @@ var INTRINSICS = {
261
262
  // dependency analysis must not guess at.
262
263
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
263
264
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
265
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
266
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
267
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
268
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
269
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
270
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
271
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
272
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
273
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
274
+ // handle as far as reads go: the same accessor with `set` on it.
275
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
276
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
277
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
264
278
  // An environment variable, public or server-only; the browser build refuses a secret.
265
279
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
266
280
  };
@@ -438,13 +452,16 @@ function emitComponent(name, props, children, used, templates, at) {
438
452
  used.add("createComponent");
439
453
  const spreads = props.filter((p) => p.kind === "spread");
440
454
  const normal = props.filter((p) => p.kind !== "spread");
441
- const entries = normal.map(
442
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
443
- );
455
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
444
456
  if (children.length > 0) {
445
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
457
+ bodies.push(["children", emitChildren(children, used, templates)]);
458
+ }
459
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
460
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
461
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
462
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
463
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
446
464
  }
447
- let obj = `{ ${entries.join(", ")} }`;
448
465
  if (spreads.length > 0) {
449
466
  used.add("mergeProps");
450
467
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -526,7 +543,8 @@ function emitNode(node, used, templates) {
526
543
  } else {
527
544
  used.add("spread");
528
545
  const svgFlag = node.svg ? ", isSVG: true" : "";
529
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
546
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
547
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
530
548
  parts.push(
531
549
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
532
550
  );
@@ -635,6 +653,271 @@ function cleanTemplateText(value) {
635
653
  return out;
636
654
  }
637
655
 
656
+ // src/lower/entities.ts
657
+ var NAMED = {
658
+ quot: 34,
659
+ amp: 38,
660
+ apos: 39,
661
+ lt: 60,
662
+ gt: 62,
663
+ nbsp: 160,
664
+ iexcl: 161,
665
+ cent: 162,
666
+ pound: 163,
667
+ curren: 164,
668
+ yen: 165,
669
+ brvbar: 166,
670
+ sect: 167,
671
+ uml: 168,
672
+ copy: 169,
673
+ ordf: 170,
674
+ laquo: 171,
675
+ not: 172,
676
+ shy: 173,
677
+ reg: 174,
678
+ macr: 175,
679
+ deg: 176,
680
+ plusmn: 177,
681
+ sup2: 178,
682
+ sup3: 179,
683
+ acute: 180,
684
+ micro: 181,
685
+ para: 182,
686
+ middot: 183,
687
+ cedil: 184,
688
+ sup1: 185,
689
+ ordm: 186,
690
+ raquo: 187,
691
+ frac14: 188,
692
+ frac12: 189,
693
+ frac34: 190,
694
+ iquest: 191,
695
+ Agrave: 192,
696
+ Aacute: 193,
697
+ Acirc: 194,
698
+ Atilde: 195,
699
+ Auml: 196,
700
+ Aring: 197,
701
+ AElig: 198,
702
+ Ccedil: 199,
703
+ Egrave: 200,
704
+ Eacute: 201,
705
+ Ecirc: 202,
706
+ Euml: 203,
707
+ Igrave: 204,
708
+ Iacute: 205,
709
+ Icirc: 206,
710
+ Iuml: 207,
711
+ ETH: 208,
712
+ Ntilde: 209,
713
+ Ograve: 210,
714
+ Oacute: 211,
715
+ Ocirc: 212,
716
+ Otilde: 213,
717
+ Ouml: 214,
718
+ times: 215,
719
+ Oslash: 216,
720
+ Ugrave: 217,
721
+ Uacute: 218,
722
+ Ucirc: 219,
723
+ Uuml: 220,
724
+ Yacute: 221,
725
+ THORN: 222,
726
+ szlig: 223,
727
+ agrave: 224,
728
+ aacute: 225,
729
+ acirc: 226,
730
+ atilde: 227,
731
+ auml: 228,
732
+ aring: 229,
733
+ aelig: 230,
734
+ ccedil: 231,
735
+ egrave: 232,
736
+ eacute: 233,
737
+ ecirc: 234,
738
+ euml: 235,
739
+ igrave: 236,
740
+ iacute: 237,
741
+ icirc: 238,
742
+ iuml: 239,
743
+ eth: 240,
744
+ ntilde: 241,
745
+ ograve: 242,
746
+ oacute: 243,
747
+ ocirc: 244,
748
+ otilde: 245,
749
+ ouml: 246,
750
+ divide: 247,
751
+ oslash: 248,
752
+ ugrave: 249,
753
+ uacute: 250,
754
+ ucirc: 251,
755
+ uuml: 252,
756
+ yacute: 253,
757
+ thorn: 254,
758
+ yuml: 255,
759
+ OElig: 338,
760
+ oelig: 339,
761
+ Scaron: 352,
762
+ scaron: 353,
763
+ Yuml: 376,
764
+ fnof: 402,
765
+ circ: 710,
766
+ tilde: 732,
767
+ Alpha: 913,
768
+ Beta: 914,
769
+ Gamma: 915,
770
+ Delta: 916,
771
+ Epsilon: 917,
772
+ Zeta: 918,
773
+ Eta: 919,
774
+ Theta: 920,
775
+ Iota: 921,
776
+ Kappa: 922,
777
+ Lambda: 923,
778
+ Mu: 924,
779
+ Nu: 925,
780
+ Xi: 926,
781
+ Omicron: 927,
782
+ Pi: 928,
783
+ Rho: 929,
784
+ Sigma: 931,
785
+ Tau: 932,
786
+ Upsilon: 933,
787
+ Phi: 934,
788
+ Chi: 935,
789
+ Psi: 936,
790
+ Omega: 937,
791
+ alpha: 945,
792
+ beta: 946,
793
+ gamma: 947,
794
+ delta: 948,
795
+ epsilon: 949,
796
+ zeta: 950,
797
+ eta: 951,
798
+ theta: 952,
799
+ iota: 953,
800
+ kappa: 954,
801
+ lambda: 955,
802
+ mu: 956,
803
+ nu: 957,
804
+ xi: 958,
805
+ omicron: 959,
806
+ pi: 960,
807
+ rho: 961,
808
+ sigmaf: 962,
809
+ sigma: 963,
810
+ tau: 964,
811
+ upsilon: 965,
812
+ phi: 966,
813
+ chi: 967,
814
+ psi: 968,
815
+ omega: 969,
816
+ thetasym: 977,
817
+ upsih: 978,
818
+ piv: 982,
819
+ ensp: 8194,
820
+ emsp: 8195,
821
+ thinsp: 8201,
822
+ zwnj: 8204,
823
+ zwj: 8205,
824
+ lrm: 8206,
825
+ rlm: 8207,
826
+ ndash: 8211,
827
+ mdash: 8212,
828
+ lsquo: 8216,
829
+ rsquo: 8217,
830
+ sbquo: 8218,
831
+ ldquo: 8220,
832
+ rdquo: 8221,
833
+ bdquo: 8222,
834
+ dagger: 8224,
835
+ Dagger: 8225,
836
+ bull: 8226,
837
+ hellip: 8230,
838
+ permil: 8240,
839
+ prime: 8242,
840
+ Prime: 8243,
841
+ lsaquo: 8249,
842
+ rsaquo: 8250,
843
+ oline: 8254,
844
+ frasl: 8260,
845
+ euro: 8364,
846
+ image: 8465,
847
+ weierp: 8472,
848
+ real: 8476,
849
+ trade: 8482,
850
+ alefsym: 8501,
851
+ larr: 8592,
852
+ uarr: 8593,
853
+ rarr: 8594,
854
+ darr: 8595,
855
+ harr: 8596,
856
+ crarr: 8629,
857
+ lArr: 8656,
858
+ uArr: 8657,
859
+ rArr: 8658,
860
+ dArr: 8659,
861
+ hArr: 8660,
862
+ forall: 8704,
863
+ part: 8706,
864
+ exist: 8707,
865
+ empty: 8709,
866
+ nabla: 8711,
867
+ isin: 8712,
868
+ notin: 8713,
869
+ ni: 8715,
870
+ prod: 8719,
871
+ sum: 8721,
872
+ minus: 8722,
873
+ lowast: 8727,
874
+ radic: 8730,
875
+ prop: 8733,
876
+ infin: 8734,
877
+ ang: 8736,
878
+ and: 8743,
879
+ or: 8744,
880
+ cap: 8745,
881
+ cup: 8746,
882
+ int: 8747,
883
+ there4: 8756,
884
+ sim: 8764,
885
+ cong: 8773,
886
+ asymp: 8776,
887
+ ne: 8800,
888
+ equiv: 8801,
889
+ le: 8804,
890
+ ge: 8805,
891
+ sub: 8834,
892
+ sup: 8835,
893
+ nsub: 8836,
894
+ sube: 8838,
895
+ supe: 8839,
896
+ oplus: 8853,
897
+ otimes: 8855,
898
+ perp: 8869,
899
+ sdot: 8901,
900
+ lceil: 8968,
901
+ rceil: 8969,
902
+ lfloor: 8970,
903
+ rfloor: 8971,
904
+ lang: 9001,
905
+ rang: 9002,
906
+ loz: 9674,
907
+ spades: 9824,
908
+ clubs: 9827,
909
+ hearts: 9829,
910
+ diams: 9830
911
+ };
912
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
913
+ function decodeEntities(value) {
914
+ if (!value.includes("&")) return value;
915
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
916
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
917
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
918
+ });
919
+ }
920
+
638
921
  // src/lower/template.ts
639
922
  var DELEGATED = /* @__PURE__ */ new Set([
640
923
  "click",
@@ -653,6 +936,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
653
936
  function escapeTemplate(s) {
654
937
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
655
938
  }
939
+ var CONTAINER = "container";
656
940
  function capitalize(s) {
657
941
  return s.charAt(0).toUpperCase() + s.slice(1);
658
942
  }
@@ -707,10 +991,6 @@ var Lowerer = class {
707
991
  if (hasElse) i = elseIdx;
708
992
  continue;
709
993
  }
710
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
711
- out.push(this.lowerEach(child));
712
- continue;
713
- }
714
994
  }
715
995
  const node = this.lowerNode(child);
716
996
  if (node) out.push(node);
@@ -724,7 +1004,7 @@ var Lowerer = class {
724
1004
  switch (node.kind) {
725
1005
  case "Text": {
726
1006
  if (node.raw) return { kind: "text", value: node.value };
727
- const cleaned = cleanTemplateText(node.value);
1007
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
728
1008
  return cleaned ? { kind: "text", value: cleaned } : null;
729
1009
  }
730
1010
  case "Comment":
@@ -752,13 +1032,16 @@ var Lowerer = class {
752
1032
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
753
1033
  return node;
754
1034
  }
1035
+ const each = this.lowerEach(el);
1036
+ const children = each ? [each] : this.lowerChildren(el.children);
1037
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
755
1038
  const at = this.tagAt(el);
756
1039
  return {
757
1040
  kind: "element",
758
1041
  tag: el.tag,
759
1042
  svg: el.namespace === "svg",
760
- props: this.lowerAttributes(el.attributes),
761
- children: this.lowerChildren(el.children),
1043
+ props: this.lowerAttributes(el.attributes, true),
1044
+ children,
762
1045
  static: false,
763
1046
  ...at ? { at } : {}
764
1047
  };
@@ -830,10 +1113,14 @@ var Lowerer = class {
830
1113
  const at = this.tagAt(el);
831
1114
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
832
1115
  }
833
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1116
+ /**
1117
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1118
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1119
+ * it is. Null when the element has no `each`.
1120
+ */
834
1121
  lowerEach(el) {
835
1122
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
836
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1123
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
837
1124
  const each = this.hole(eachDir.hole);
838
1125
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
839
1126
  if (eachDir.key) {
@@ -843,12 +1130,12 @@ var Lowerer = class {
843
1130
  const arrow = this.itemArrow(el);
844
1131
  let childrenCode;
845
1132
  if (arrow) {
846
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
847
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
848
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1133
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1134
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
849
1135
  } else {
850
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
851
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1136
+ const content = this.lowerChildren(el.children);
1137
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1138
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
852
1139
  }
853
1140
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
854
1141
  const at = this.tagAt(el);
@@ -865,33 +1152,23 @@ var Lowerer = class {
865
1152
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
866
1153
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
867
1154
  }
868
- /** Same element (minus each/key) but with explicit IR children. */
869
- rebuildWithChildren(el, children) {
870
- const props = this.lowerAttributes(
871
- el.attributes.filter((a) => a.kind !== "EachDirective")
872
- );
873
- if (el.kind === "Element") {
874
- const at = this.tagAt(el);
875
- return {
876
- kind: "element",
877
- tag: el.tag,
878
- svg: el.namespace === "svg",
879
- props,
880
- children,
881
- static: false,
882
- ...at ? { at } : {}
883
- };
884
- }
885
- return { kind: "component", name: el.tag, props, children };
886
- }
887
1155
  // --- attributes ---------------------------------------------------------
888
- lowerAttributes(attrs) {
1156
+ /**
1157
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
1158
+ * A component gets the object as before, rebuilt whole when any value moves.
1159
+ */
1160
+ lowerAttributes(attrs, element = false) {
1161
+ let anyReactive = false;
1162
+ const entry = (name, h) => {
1163
+ anyReactive ||= h.reactive;
1164
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
1165
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
1166
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
1167
+ };
889
1168
  const props = [];
890
1169
  const classEntries = [];
891
- let classReactive = false;
892
1170
  let classAt;
893
1171
  const styleEntries = [];
894
- let styleReactive = false;
895
1172
  let styleAt;
896
1173
  const usePairs = [];
897
1174
  for (const a of attrs) {
@@ -931,15 +1208,13 @@ var Lowerer = class {
931
1208
  break;
932
1209
  case "ClassDirective": {
933
1210
  const h = this.hole(a.hole);
934
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
935
- classReactive = classReactive || h.reactive;
1211
+ classEntries.push(entry(a.name, h));
936
1212
  classAt ??= h.at;
937
1213
  break;
938
1214
  }
939
1215
  case "StyleDirective": {
940
1216
  const h = this.hole(a.hole);
941
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
942
- styleReactive = styleReactive || h.reactive;
1217
+ styleEntries.push(entry(a.name, h));
943
1218
  styleAt ??= h.at;
944
1219
  break;
945
1220
  }
@@ -959,18 +1234,19 @@ var Lowerer = class {
959
1234
  name: "classList",
960
1235
  kind: "attr",
961
1236
  expr: `{ ${classEntries.join(", ")} }`,
962
- reactive: classReactive,
1237
+ reactive: !element && anyReactive,
963
1238
  ...classAt ? { at: classAt } : {}
964
1239
  });
965
1240
  }
966
1241
  if (styleEntries.length > 0) {
967
- props.push({
968
- name: "style",
969
- kind: "attr",
970
- expr: `{ ${styleEntries.join(", ")} }`,
971
- reactive: styleReactive,
972
- ...styleAt ? { at: styleAt } : {}
973
- });
1242
+ const entries = `{ ${styleEntries.join(", ")} }`;
1243
+ const base = props.findIndex((p) => p.name === "style");
1244
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
1245
+ else {
1246
+ const prop = props[base];
1247
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1248
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1249
+ }
974
1250
  }
975
1251
  if (usePairs.length > 0) {
976
1252
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1059,7 +1335,7 @@ var Lowerer = class {
1059
1335
  return base;
1060
1336
  }
1061
1337
  if (v.kind === "static") {
1062
- base.literal = v.value;
1338
+ base.literal = decodeEntities(v.value);
1063
1339
  return base;
1064
1340
  }
1065
1341
  if (v.kind === "hole") {
@@ -1072,7 +1348,7 @@ var Lowerer = class {
1072
1348
  let reactive = false;
1073
1349
  let at;
1074
1350
  const body = v.parts.map((p) => {
1075
- if ("text" in p) return escapeTemplate(p.text);
1351
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1076
1352
  const h = this.hole(p.hole);
1077
1353
  reactive = reactive || h.reactive;
1078
1354
  at ??= h.at;
@@ -1295,6 +1571,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1295
1571
  "mouseup"
1296
1572
  ]);
1297
1573
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1574
+ var CONTAINER2 = "container";
1575
+ function directive(node, name) {
1576
+ if (node.type !== "JSXElement") return void 0;
1577
+ const opening = node.openingElement;
1578
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1579
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1580
+ }
1581
+ function directiveExpr(attr) {
1582
+ const v = attr.value;
1583
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1584
+ }
1585
+ function emitIR(ir, ctx) {
1586
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1587
+ for (const s of imports) ctx.used.add(s);
1588
+ return inlineTemplates(code, templates);
1589
+ }
1298
1590
  var posOf = (node, ctx) => {
1299
1591
  if (!ctx.sourceFile) return void 0;
1300
1592
  const at = node.loc?.start;
@@ -1372,9 +1664,12 @@ function attrExprCode(attr, ctx) {
1372
1664
  }
1373
1665
  return null;
1374
1666
  }
1375
- function buildProps(attrs, ctx) {
1667
+ function buildProps(attrs, ctx, element = false) {
1376
1668
  const props = [];
1377
1669
  const usePairs = [];
1670
+ const classEntries = [];
1671
+ const styleEntries = [];
1672
+ let styleAt;
1378
1673
  for (const a of attrs) {
1379
1674
  if (a.type === "JSXSpreadAttribute") {
1380
1675
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -1390,6 +1685,27 @@ function buildProps(attrs, ctx) {
1390
1685
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
1391
1686
  continue;
1392
1687
  }
1688
+ if (element && (ns === "class" || ns === "style")) {
1689
+ const key = JSON.stringify(a.name.name.name);
1690
+ const v = a.value;
1691
+ let value;
1692
+ if (v == null) value = "true";
1693
+ else if (isString(v)) value = JSON.stringify(literalOf(v));
1694
+ else if (v.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression") {
1695
+ value = keyValue(v.expression, ctx);
1696
+ if (ns === "style") styleAt ??= posOf(v.expression, ctx);
1697
+ } else continue;
1698
+ (ns === "class" ? classEntries : styleEntries).push(`${key}: ${value}`);
1699
+ continue;
1700
+ }
1701
+ if (element && a.name.type === "JSXIdentifier" && a.name.name === "style" && a.value?.type === "JSXExpressionContainer") {
1702
+ const entries = objectEntries(a.value.expression, ctx);
1703
+ if (entries) {
1704
+ styleEntries.unshift(...entries);
1705
+ styleAt = posOf(a.value.expression, ctx);
1706
+ continue;
1707
+ }
1708
+ }
1393
1709
  if (ns === "oncapture") {
1394
1710
  const evt = a.name.name.name.toLowerCase();
1395
1711
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -1398,12 +1714,49 @@ function buildProps(attrs, ctx) {
1398
1714
  }
1399
1715
  props.push(buildAttr(a, ctx));
1400
1716
  }
1717
+ if (classEntries.length > 0) props.push({ name: "classList", kind: "attr", expr: `{ ${classEntries.join(", ")} }`, reactive: false });
1718
+ if (styleEntries.length > 0) {
1719
+ const entries = `{ ${styleEntries.join(", ")} }`;
1720
+ const base = props.findIndex((p) => p.name === "style" && p.kind === "attr");
1721
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: false, ...styleAt ? { at: styleAt } : {} });
1722
+ else {
1723
+ const prop = props[base];
1724
+ const value = prop.literal !== void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1725
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1726
+ }
1727
+ }
1401
1728
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
1402
1729
  return props;
1403
1730
  }
1731
+ function keyValue(expr, ctx) {
1732
+ const code = ctx.code(expr);
1733
+ if (!isReactive(expr)) return code;
1734
+ const accessor = `() => ${paren(code, "arrowBody")}`;
1735
+ const at = posOf(expr, ctx);
1736
+ return at ? markHole(accessor, at.line, at.column) : accessor;
1737
+ }
1738
+ function objectEntries(expr, ctx) {
1739
+ if (expr?.type !== "ObjectExpression") return null;
1740
+ const entries = [];
1741
+ for (const p of expr.properties) {
1742
+ if (p.type !== "ObjectProperty") return null;
1743
+ const key = p.computed ? `[${ctx.code(p.key)}]` : p.key.type === "Identifier" ? JSON.stringify(p.key.name) : JSON.stringify(String(p.key.value));
1744
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
1745
+ }
1746
+ return entries;
1747
+ }
1404
1748
  function buildChildren(children, ctx) {
1405
1749
  const out = [];
1406
- for (const child of children) {
1750
+ for (let i = 0; i < children.length; i++) {
1751
+ const child = children[i];
1752
+ if (directive(child, "if")) {
1753
+ let next = i + 1;
1754
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1755
+ const fallback = directive(children[next], "else") ? children[next] : null;
1756
+ out.push(buildIf(child, fallback, ctx));
1757
+ if (fallback) i = next;
1758
+ continue;
1759
+ }
1407
1760
  if (child.type === "JSXText") {
1408
1761
  const text = cleanTemplateText(child.value);
1409
1762
  if (text) out.push({ kind: "text", value: text });
@@ -1419,6 +1772,7 @@ function buildChildren(children, ctx) {
1419
1772
  });
1420
1773
  }
1421
1774
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
1775
+ if (ctx.dropStyles?.has(child.start)) continue;
1422
1776
  out.push(buildNode(child, ctx));
1423
1777
  } else if (child.type === "JSXSpreadChild") {
1424
1778
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -1440,19 +1794,72 @@ function readLoad(attrs) {
1440
1794
  }
1441
1795
  return void 0;
1442
1796
  }
1797
+ function buildIf(node, fallback, ctx) {
1798
+ const cond = directiveExpr(directive(node, "if"));
1799
+ const props = [
1800
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1801
+ ];
1802
+ if (fallback) {
1803
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1804
+ }
1805
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1806
+ }
1807
+ function buildEach(node, ctx) {
1808
+ const attr = directive(node, "each");
1809
+ if (!attr) return null;
1810
+ const list = directiveExpr(attr);
1811
+ const props = [
1812
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1813
+ ];
1814
+ const key = directive(node, "key");
1815
+ if (key) {
1816
+ const keyExpr = directiveExpr(key);
1817
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1818
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1819
+ }
1820
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1821
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1822
+ let render;
1823
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1824
+ const fn = ctx.code(only);
1825
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1826
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1827
+ } else {
1828
+ const built = buildChildren(node.children, ctx);
1829
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1830
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1831
+ }
1832
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1833
+ }
1834
+ function buildElement(node, ctx, handled) {
1835
+ const skip = new Set(handled);
1836
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1837
+ const attributes = node.openingElement.attributes.filter(
1838
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1839
+ );
1840
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1841
+ const each = buildEach(node, ctx);
1842
+ const children = each ? [each] : buildChildren(node.children, ctx);
1843
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1844
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1845
+ const props = buildProps(attributes, ctx, true);
1846
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
1847
+ if (scope) props.unshift({ name: "data-fx-s", kind: "attr", literal: scope.value });
1848
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1849
+ }
1443
1850
  function buildNode(node, ctx) {
1444
1851
  if (node.type === "JSXFragment") {
1445
1852
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1446
1853
  }
1854
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1855
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
1447
1856
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1857
+ if (!component) return buildElement(node, ctx, []);
1448
1858
  const props = buildProps(node.openingElement.attributes, ctx);
1449
1859
  const children = buildChildren(node.children, ctx);
1450
1860
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1451
- if (component) {
1452
- const load = readLoad(node.openingElement.attributes);
1453
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1454
- }
1455
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1861
+ const load = readLoad(node.openingElement.attributes);
1862
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1456
1863
  }
1457
1864
  function buildJsxIR(node, ctx) {
1458
1865
  const ir = buildNode(node, ctx);