@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
@@ -1,4 +1,4 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.94 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/frontend/babel/plugin.ts
4
4
  import { declare } from "@babel/helper-plugin-utils";
@@ -437,6 +437,7 @@ var INTRINSICS = {
437
437
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
438
438
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
439
439
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
440
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
440
441
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
441
442
  // name. Still worth having: no import to write, and when a shorter form is
442
443
  // designed, the change is this line rather than a compiler pass.
@@ -447,6 +448,19 @@ var INTRINSICS = {
447
448
  // dependency analysis must not guess at.
448
449
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
449
450
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
451
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
452
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
453
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
454
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
455
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
456
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
457
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
458
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
459
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
460
+ // handle as far as reads go: the same accessor with `set` on it.
461
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
462
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
463
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
450
464
  // An environment variable, public or server-only; the browser build refuses a secret.
451
465
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
452
466
  };
@@ -624,13 +638,16 @@ function emitComponent(name, props, children, used, templates, at) {
624
638
  used.add("createComponent");
625
639
  const spreads = props.filter((p) => p.kind === "spread");
626
640
  const normal = props.filter((p) => p.kind !== "spread");
627
- const entries = normal.map(
628
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
629
- );
641
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
630
642
  if (children.length > 0) {
631
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
643
+ bodies.push(["children", emitChildren(children, used, templates)]);
644
+ }
645
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
646
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
647
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
648
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
649
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
632
650
  }
633
- let obj = `{ ${entries.join(", ")} }`;
634
651
  if (spreads.length > 0) {
635
652
  used.add("mergeProps");
636
653
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -712,7 +729,8 @@ function emitNode(node, used, templates) {
712
729
  } else {
713
730
  used.add("spread");
714
731
  const svgFlag = node.svg ? ", isSVG: true" : "";
715
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
732
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
733
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
716
734
  parts.push(
717
735
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
718
736
  );
@@ -774,6 +792,271 @@ function cleanTemplateText(value) {
774
792
  return out;
775
793
  }
776
794
 
795
+ // src/lower/entities.ts
796
+ var NAMED = {
797
+ quot: 34,
798
+ amp: 38,
799
+ apos: 39,
800
+ lt: 60,
801
+ gt: 62,
802
+ nbsp: 160,
803
+ iexcl: 161,
804
+ cent: 162,
805
+ pound: 163,
806
+ curren: 164,
807
+ yen: 165,
808
+ brvbar: 166,
809
+ sect: 167,
810
+ uml: 168,
811
+ copy: 169,
812
+ ordf: 170,
813
+ laquo: 171,
814
+ not: 172,
815
+ shy: 173,
816
+ reg: 174,
817
+ macr: 175,
818
+ deg: 176,
819
+ plusmn: 177,
820
+ sup2: 178,
821
+ sup3: 179,
822
+ acute: 180,
823
+ micro: 181,
824
+ para: 182,
825
+ middot: 183,
826
+ cedil: 184,
827
+ sup1: 185,
828
+ ordm: 186,
829
+ raquo: 187,
830
+ frac14: 188,
831
+ frac12: 189,
832
+ frac34: 190,
833
+ iquest: 191,
834
+ Agrave: 192,
835
+ Aacute: 193,
836
+ Acirc: 194,
837
+ Atilde: 195,
838
+ Auml: 196,
839
+ Aring: 197,
840
+ AElig: 198,
841
+ Ccedil: 199,
842
+ Egrave: 200,
843
+ Eacute: 201,
844
+ Ecirc: 202,
845
+ Euml: 203,
846
+ Igrave: 204,
847
+ Iacute: 205,
848
+ Icirc: 206,
849
+ Iuml: 207,
850
+ ETH: 208,
851
+ Ntilde: 209,
852
+ Ograve: 210,
853
+ Oacute: 211,
854
+ Ocirc: 212,
855
+ Otilde: 213,
856
+ Ouml: 214,
857
+ times: 215,
858
+ Oslash: 216,
859
+ Ugrave: 217,
860
+ Uacute: 218,
861
+ Ucirc: 219,
862
+ Uuml: 220,
863
+ Yacute: 221,
864
+ THORN: 222,
865
+ szlig: 223,
866
+ agrave: 224,
867
+ aacute: 225,
868
+ acirc: 226,
869
+ atilde: 227,
870
+ auml: 228,
871
+ aring: 229,
872
+ aelig: 230,
873
+ ccedil: 231,
874
+ egrave: 232,
875
+ eacute: 233,
876
+ ecirc: 234,
877
+ euml: 235,
878
+ igrave: 236,
879
+ iacute: 237,
880
+ icirc: 238,
881
+ iuml: 239,
882
+ eth: 240,
883
+ ntilde: 241,
884
+ ograve: 242,
885
+ oacute: 243,
886
+ ocirc: 244,
887
+ otilde: 245,
888
+ ouml: 246,
889
+ divide: 247,
890
+ oslash: 248,
891
+ ugrave: 249,
892
+ uacute: 250,
893
+ ucirc: 251,
894
+ uuml: 252,
895
+ yacute: 253,
896
+ thorn: 254,
897
+ yuml: 255,
898
+ OElig: 338,
899
+ oelig: 339,
900
+ Scaron: 352,
901
+ scaron: 353,
902
+ Yuml: 376,
903
+ fnof: 402,
904
+ circ: 710,
905
+ tilde: 732,
906
+ Alpha: 913,
907
+ Beta: 914,
908
+ Gamma: 915,
909
+ Delta: 916,
910
+ Epsilon: 917,
911
+ Zeta: 918,
912
+ Eta: 919,
913
+ Theta: 920,
914
+ Iota: 921,
915
+ Kappa: 922,
916
+ Lambda: 923,
917
+ Mu: 924,
918
+ Nu: 925,
919
+ Xi: 926,
920
+ Omicron: 927,
921
+ Pi: 928,
922
+ Rho: 929,
923
+ Sigma: 931,
924
+ Tau: 932,
925
+ Upsilon: 933,
926
+ Phi: 934,
927
+ Chi: 935,
928
+ Psi: 936,
929
+ Omega: 937,
930
+ alpha: 945,
931
+ beta: 946,
932
+ gamma: 947,
933
+ delta: 948,
934
+ epsilon: 949,
935
+ zeta: 950,
936
+ eta: 951,
937
+ theta: 952,
938
+ iota: 953,
939
+ kappa: 954,
940
+ lambda: 955,
941
+ mu: 956,
942
+ nu: 957,
943
+ xi: 958,
944
+ omicron: 959,
945
+ pi: 960,
946
+ rho: 961,
947
+ sigmaf: 962,
948
+ sigma: 963,
949
+ tau: 964,
950
+ upsilon: 965,
951
+ phi: 966,
952
+ chi: 967,
953
+ psi: 968,
954
+ omega: 969,
955
+ thetasym: 977,
956
+ upsih: 978,
957
+ piv: 982,
958
+ ensp: 8194,
959
+ emsp: 8195,
960
+ thinsp: 8201,
961
+ zwnj: 8204,
962
+ zwj: 8205,
963
+ lrm: 8206,
964
+ rlm: 8207,
965
+ ndash: 8211,
966
+ mdash: 8212,
967
+ lsquo: 8216,
968
+ rsquo: 8217,
969
+ sbquo: 8218,
970
+ ldquo: 8220,
971
+ rdquo: 8221,
972
+ bdquo: 8222,
973
+ dagger: 8224,
974
+ Dagger: 8225,
975
+ bull: 8226,
976
+ hellip: 8230,
977
+ permil: 8240,
978
+ prime: 8242,
979
+ Prime: 8243,
980
+ lsaquo: 8249,
981
+ rsaquo: 8250,
982
+ oline: 8254,
983
+ frasl: 8260,
984
+ euro: 8364,
985
+ image: 8465,
986
+ weierp: 8472,
987
+ real: 8476,
988
+ trade: 8482,
989
+ alefsym: 8501,
990
+ larr: 8592,
991
+ uarr: 8593,
992
+ rarr: 8594,
993
+ darr: 8595,
994
+ harr: 8596,
995
+ crarr: 8629,
996
+ lArr: 8656,
997
+ uArr: 8657,
998
+ rArr: 8658,
999
+ dArr: 8659,
1000
+ hArr: 8660,
1001
+ forall: 8704,
1002
+ part: 8706,
1003
+ exist: 8707,
1004
+ empty: 8709,
1005
+ nabla: 8711,
1006
+ isin: 8712,
1007
+ notin: 8713,
1008
+ ni: 8715,
1009
+ prod: 8719,
1010
+ sum: 8721,
1011
+ minus: 8722,
1012
+ lowast: 8727,
1013
+ radic: 8730,
1014
+ prop: 8733,
1015
+ infin: 8734,
1016
+ ang: 8736,
1017
+ and: 8743,
1018
+ or: 8744,
1019
+ cap: 8745,
1020
+ cup: 8746,
1021
+ int: 8747,
1022
+ there4: 8756,
1023
+ sim: 8764,
1024
+ cong: 8773,
1025
+ asymp: 8776,
1026
+ ne: 8800,
1027
+ equiv: 8801,
1028
+ le: 8804,
1029
+ ge: 8805,
1030
+ sub: 8834,
1031
+ sup: 8835,
1032
+ nsub: 8836,
1033
+ sube: 8838,
1034
+ supe: 8839,
1035
+ oplus: 8853,
1036
+ otimes: 8855,
1037
+ perp: 8869,
1038
+ sdot: 8901,
1039
+ lceil: 8968,
1040
+ rceil: 8969,
1041
+ lfloor: 8970,
1042
+ rfloor: 8971,
1043
+ lang: 9001,
1044
+ rang: 9002,
1045
+ loz: 9674,
1046
+ spades: 9824,
1047
+ clubs: 9827,
1048
+ hearts: 9829,
1049
+ diams: 9830
1050
+ };
1051
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
1052
+ function decodeEntities(value) {
1053
+ if (!value.includes("&")) return value;
1054
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
1055
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
1056
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
1057
+ });
1058
+ }
1059
+
777
1060
  // src/lower/template.ts
778
1061
  var DELEGATED = /* @__PURE__ */ new Set([
779
1062
  "click",
@@ -792,6 +1075,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
792
1075
  function escapeTemplate(s) {
793
1076
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
794
1077
  }
1078
+ var CONTAINER = "container";
795
1079
  function capitalize(s) {
796
1080
  return s.charAt(0).toUpperCase() + s.slice(1);
797
1081
  }
@@ -846,10 +1130,6 @@ var Lowerer = class {
846
1130
  if (hasElse) i = elseIdx;
847
1131
  continue;
848
1132
  }
849
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
850
- out.push(this.lowerEach(child));
851
- continue;
852
- }
853
1133
  }
854
1134
  const node = this.lowerNode(child);
855
1135
  if (node) out.push(node);
@@ -863,7 +1143,7 @@ var Lowerer = class {
863
1143
  switch (node.kind) {
864
1144
  case "Text": {
865
1145
  if (node.raw) return { kind: "text", value: node.value };
866
- const cleaned = cleanTemplateText(node.value);
1146
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
867
1147
  return cleaned ? { kind: "text", value: cleaned } : null;
868
1148
  }
869
1149
  case "Comment":
@@ -891,13 +1171,16 @@ var Lowerer = class {
891
1171
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
892
1172
  return node;
893
1173
  }
1174
+ const each = this.lowerEach(el);
1175
+ const children = each ? [each] : this.lowerChildren(el.children);
1176
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
894
1177
  const at = this.tagAt(el);
895
1178
  return {
896
1179
  kind: "element",
897
1180
  tag: el.tag,
898
1181
  svg: el.namespace === "svg",
899
- props: this.lowerAttributes(el.attributes),
900
- children: this.lowerChildren(el.children),
1182
+ props: this.lowerAttributes(el.attributes, true),
1183
+ children,
901
1184
  static: false,
902
1185
  ...at ? { at } : {}
903
1186
  };
@@ -969,10 +1252,14 @@ var Lowerer = class {
969
1252
  const at = this.tagAt(el);
970
1253
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
971
1254
  }
972
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1255
+ /**
1256
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1257
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1258
+ * it is. Null when the element has no `each`.
1259
+ */
973
1260
  lowerEach(el) {
974
1261
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
975
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1262
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
976
1263
  const each = this.hole(eachDir.hole);
977
1264
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
978
1265
  if (eachDir.key) {
@@ -982,12 +1269,12 @@ var Lowerer = class {
982
1269
  const arrow = this.itemArrow(el);
983
1270
  let childrenCode;
984
1271
  if (arrow) {
985
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
986
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
987
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1272
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1273
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
988
1274
  } else {
989
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
990
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1275
+ const content = this.lowerChildren(el.children);
1276
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1277
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
991
1278
  }
992
1279
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
993
1280
  const at = this.tagAt(el);
@@ -1004,33 +1291,23 @@ var Lowerer = class {
1004
1291
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
1005
1292
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
1006
1293
  }
1007
- /** Same element (minus each/key) but with explicit IR children. */
1008
- rebuildWithChildren(el, children) {
1009
- const props = this.lowerAttributes(
1010
- el.attributes.filter((a) => a.kind !== "EachDirective")
1011
- );
1012
- if (el.kind === "Element") {
1013
- const at = this.tagAt(el);
1014
- return {
1015
- kind: "element",
1016
- tag: el.tag,
1017
- svg: el.namespace === "svg",
1018
- props,
1019
- children,
1020
- static: false,
1021
- ...at ? { at } : {}
1022
- };
1023
- }
1024
- return { kind: "component", name: el.tag, props, children };
1025
- }
1026
1294
  // --- attributes ---------------------------------------------------------
1027
- lowerAttributes(attrs) {
1295
+ /**
1296
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
1297
+ * A component gets the object as before, rebuilt whole when any value moves.
1298
+ */
1299
+ lowerAttributes(attrs, element = false) {
1300
+ let anyReactive = false;
1301
+ const entry = (name, h) => {
1302
+ anyReactive ||= h.reactive;
1303
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
1304
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
1305
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
1306
+ };
1028
1307
  const props = [];
1029
1308
  const classEntries = [];
1030
- let classReactive = false;
1031
1309
  let classAt;
1032
1310
  const styleEntries = [];
1033
- let styleReactive = false;
1034
1311
  let styleAt;
1035
1312
  const usePairs = [];
1036
1313
  for (const a of attrs) {
@@ -1070,15 +1347,13 @@ var Lowerer = class {
1070
1347
  break;
1071
1348
  case "ClassDirective": {
1072
1349
  const h = this.hole(a.hole);
1073
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
1074
- classReactive = classReactive || h.reactive;
1350
+ classEntries.push(entry(a.name, h));
1075
1351
  classAt ??= h.at;
1076
1352
  break;
1077
1353
  }
1078
1354
  case "StyleDirective": {
1079
1355
  const h = this.hole(a.hole);
1080
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
1081
- styleReactive = styleReactive || h.reactive;
1356
+ styleEntries.push(entry(a.name, h));
1082
1357
  styleAt ??= h.at;
1083
1358
  break;
1084
1359
  }
@@ -1098,18 +1373,19 @@ var Lowerer = class {
1098
1373
  name: "classList",
1099
1374
  kind: "attr",
1100
1375
  expr: `{ ${classEntries.join(", ")} }`,
1101
- reactive: classReactive,
1376
+ reactive: !element && anyReactive,
1102
1377
  ...classAt ? { at: classAt } : {}
1103
1378
  });
1104
1379
  }
1105
1380
  if (styleEntries.length > 0) {
1106
- props.push({
1107
- name: "style",
1108
- kind: "attr",
1109
- expr: `{ ${styleEntries.join(", ")} }`,
1110
- reactive: styleReactive,
1111
- ...styleAt ? { at: styleAt } : {}
1112
- });
1381
+ const entries = `{ ${styleEntries.join(", ")} }`;
1382
+ const base = props.findIndex((p) => p.name === "style");
1383
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
1384
+ else {
1385
+ const prop = props[base];
1386
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1387
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1388
+ }
1113
1389
  }
1114
1390
  if (usePairs.length > 0) {
1115
1391
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1198,7 +1474,7 @@ var Lowerer = class {
1198
1474
  return base;
1199
1475
  }
1200
1476
  if (v.kind === "static") {
1201
- base.literal = v.value;
1477
+ base.literal = decodeEntities(v.value);
1202
1478
  return base;
1203
1479
  }
1204
1480
  if (v.kind === "hole") {
@@ -1211,7 +1487,7 @@ var Lowerer = class {
1211
1487
  let reactive = false;
1212
1488
  let at;
1213
1489
  const body = v.parts.map((p) => {
1214
- if ("text" in p) return escapeTemplate(p.text);
1490
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1215
1491
  const h = this.hole(p.hole);
1216
1492
  reactive = reactive || h.reactive;
1217
1493
  at ??= h.at;
@@ -1434,6 +1710,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1434
1710
  "mouseup"
1435
1711
  ]);
1436
1712
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1713
+ var CONTAINER2 = "container";
1714
+ function directive(node, name) {
1715
+ if (node.type !== "JSXElement") return void 0;
1716
+ const opening = node.openingElement;
1717
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1718
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1719
+ }
1720
+ function directiveExpr(attr) {
1721
+ const v = attr.value;
1722
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1723
+ }
1724
+ function emitIR(ir, ctx) {
1725
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1726
+ for (const s of imports) ctx.used.add(s);
1727
+ return inlineTemplates(code, templates);
1728
+ }
1437
1729
  var posOf = (node, ctx) => {
1438
1730
  if (!ctx.sourceFile) return void 0;
1439
1731
  const at = node.loc?.start;
@@ -1511,9 +1803,12 @@ function attrExprCode(attr, ctx) {
1511
1803
  }
1512
1804
  return null;
1513
1805
  }
1514
- function buildProps(attrs, ctx) {
1806
+ function buildProps(attrs, ctx, element = false) {
1515
1807
  const props = [];
1516
1808
  const usePairs = [];
1809
+ const classEntries = [];
1810
+ const styleEntries = [];
1811
+ let styleAt;
1517
1812
  for (const a of attrs) {
1518
1813
  if (a.type === "JSXSpreadAttribute") {
1519
1814
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -1529,6 +1824,27 @@ function buildProps(attrs, ctx) {
1529
1824
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
1530
1825
  continue;
1531
1826
  }
1827
+ if (element && (ns === "class" || ns === "style")) {
1828
+ const key = JSON.stringify(a.name.name.name);
1829
+ const v = a.value;
1830
+ let value;
1831
+ if (v == null) value = "true";
1832
+ else if (isString(v)) value = JSON.stringify(literalOf(v));
1833
+ else if (v.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression") {
1834
+ value = keyValue(v.expression, ctx);
1835
+ if (ns === "style") styleAt ??= posOf(v.expression, ctx);
1836
+ } else continue;
1837
+ (ns === "class" ? classEntries : styleEntries).push(`${key}: ${value}`);
1838
+ continue;
1839
+ }
1840
+ if (element && a.name.type === "JSXIdentifier" && a.name.name === "style" && a.value?.type === "JSXExpressionContainer") {
1841
+ const entries = objectEntries(a.value.expression, ctx);
1842
+ if (entries) {
1843
+ styleEntries.unshift(...entries);
1844
+ styleAt = posOf(a.value.expression, ctx);
1845
+ continue;
1846
+ }
1847
+ }
1532
1848
  if (ns === "oncapture") {
1533
1849
  const evt = a.name.name.name.toLowerCase();
1534
1850
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -1537,12 +1853,49 @@ function buildProps(attrs, ctx) {
1537
1853
  }
1538
1854
  props.push(buildAttr(a, ctx));
1539
1855
  }
1856
+ if (classEntries.length > 0) props.push({ name: "classList", kind: "attr", expr: `{ ${classEntries.join(", ")} }`, reactive: false });
1857
+ if (styleEntries.length > 0) {
1858
+ const entries = `{ ${styleEntries.join(", ")} }`;
1859
+ const base = props.findIndex((p) => p.name === "style" && p.kind === "attr");
1860
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: false, ...styleAt ? { at: styleAt } : {} });
1861
+ else {
1862
+ const prop = props[base];
1863
+ const value = prop.literal !== void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1864
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1865
+ }
1866
+ }
1540
1867
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
1541
1868
  return props;
1542
1869
  }
1870
+ function keyValue(expr, ctx) {
1871
+ const code = ctx.code(expr);
1872
+ if (!isReactive(expr)) return code;
1873
+ const accessor = `() => ${paren(code, "arrowBody")}`;
1874
+ const at = posOf(expr, ctx);
1875
+ return at ? markHole(accessor, at.line, at.column) : accessor;
1876
+ }
1877
+ function objectEntries(expr, ctx) {
1878
+ if (expr?.type !== "ObjectExpression") return null;
1879
+ const entries = [];
1880
+ for (const p of expr.properties) {
1881
+ if (p.type !== "ObjectProperty") return null;
1882
+ const key = p.computed ? `[${ctx.code(p.key)}]` : p.key.type === "Identifier" ? JSON.stringify(p.key.name) : JSON.stringify(String(p.key.value));
1883
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
1884
+ }
1885
+ return entries;
1886
+ }
1543
1887
  function buildChildren(children, ctx) {
1544
1888
  const out = [];
1545
- for (const child of children) {
1889
+ for (let i = 0; i < children.length; i++) {
1890
+ const child = children[i];
1891
+ if (directive(child, "if")) {
1892
+ let next = i + 1;
1893
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1894
+ const fallback = directive(children[next], "else") ? children[next] : null;
1895
+ out.push(buildIf(child, fallback, ctx));
1896
+ if (fallback) i = next;
1897
+ continue;
1898
+ }
1546
1899
  if (child.type === "JSXText") {
1547
1900
  const text = cleanTemplateText(child.value);
1548
1901
  if (text) out.push({ kind: "text", value: text });
@@ -1558,6 +1911,7 @@ function buildChildren(children, ctx) {
1558
1911
  });
1559
1912
  }
1560
1913
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
1914
+ if (ctx.dropStyles?.has(child.start)) continue;
1561
1915
  out.push(buildNode(child, ctx));
1562
1916
  } else if (child.type === "JSXSpreadChild") {
1563
1917
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -1579,19 +1933,72 @@ function readLoad(attrs) {
1579
1933
  }
1580
1934
  return void 0;
1581
1935
  }
1936
+ function buildIf(node, fallback, ctx) {
1937
+ const cond = directiveExpr(directive(node, "if"));
1938
+ const props = [
1939
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1940
+ ];
1941
+ if (fallback) {
1942
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1943
+ }
1944
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1945
+ }
1946
+ function buildEach(node, ctx) {
1947
+ const attr = directive(node, "each");
1948
+ if (!attr) return null;
1949
+ const list = directiveExpr(attr);
1950
+ const props = [
1951
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1952
+ ];
1953
+ const key = directive(node, "key");
1954
+ if (key) {
1955
+ const keyExpr = directiveExpr(key);
1956
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1957
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1958
+ }
1959
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1960
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1961
+ let render;
1962
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1963
+ const fn = ctx.code(only);
1964
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1965
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1966
+ } else {
1967
+ const built = buildChildren(node.children, ctx);
1968
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1969
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1970
+ }
1971
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1972
+ }
1973
+ function buildElement(node, ctx, handled) {
1974
+ const skip = new Set(handled);
1975
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1976
+ const attributes = node.openingElement.attributes.filter(
1977
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1978
+ );
1979
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1980
+ const each = buildEach(node, ctx);
1981
+ const children = each ? [each] : buildChildren(node.children, ctx);
1982
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1983
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1984
+ const props = buildProps(attributes, ctx, true);
1985
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
1986
+ if (scope) props.unshift({ name: "data-fx-s", kind: "attr", literal: scope.value });
1987
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1988
+ }
1582
1989
  function buildNode(node, ctx) {
1583
1990
  if (node.type === "JSXFragment") {
1584
1991
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1585
1992
  }
1993
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1994
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
1586
1995
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1996
+ if (!component) return buildElement(node, ctx, []);
1587
1997
  const props = buildProps(node.openingElement.attributes, ctx);
1588
1998
  const children = buildChildren(node.children, ctx);
1589
1999
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1590
- if (component) {
1591
- const load = readLoad(node.openingElement.attributes);
1592
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1593
- }
1594
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
2000
+ const load = readLoad(node.openingElement.attributes);
2001
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1595
2002
  }
1596
2003
  function buildJsxIR(node, ctx) {
1597
2004
  const ir = buildNode(node, ctx);