@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
  "use strict";
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
@@ -476,6 +476,7 @@ var INTRINSICS = {
476
476
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
477
477
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
478
478
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
479
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
479
480
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
480
481
  // name. Still worth having: no import to write, and when a shorter form is
481
482
  // designed, the change is this line rather than a compiler pass.
@@ -486,6 +487,19 @@ var INTRINSICS = {
486
487
  // dependency analysis must not guess at.
487
488
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
488
489
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
490
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
491
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
492
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
493
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
494
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
495
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
496
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
497
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
498
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
499
+ // handle as far as reads go: the same accessor with `set` on it.
500
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
501
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
502
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
489
503
  // An environment variable, public or server-only; the browser build refuses a secret.
490
504
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
491
505
  };
@@ -663,13 +677,16 @@ function emitComponent(name, props, children, used, templates, at) {
663
677
  used.add("createComponent");
664
678
  const spreads = props.filter((p) => p.kind === "spread");
665
679
  const normal = props.filter((p) => p.kind !== "spread");
666
- const entries = normal.map(
667
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
668
- );
680
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
669
681
  if (children.length > 0) {
670
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
682
+ bodies.push(["children", emitChildren(children, used, templates)]);
683
+ }
684
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
685
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
686
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
687
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
688
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
671
689
  }
672
- let obj = `{ ${entries.join(", ")} }`;
673
690
  if (spreads.length > 0) {
674
691
  used.add("mergeProps");
675
692
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -751,7 +768,8 @@ function emitNode(node, used, templates) {
751
768
  } else {
752
769
  used.add("spread");
753
770
  const svgFlag = node.svg ? ", isSVG: true" : "";
754
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
771
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
772
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
755
773
  parts.push(
756
774
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
757
775
  );
@@ -811,6 +829,271 @@ function cleanTemplateText(value) {
811
829
  return out;
812
830
  }
813
831
 
832
+ // src/lower/entities.ts
833
+ var NAMED = {
834
+ quot: 34,
835
+ amp: 38,
836
+ apos: 39,
837
+ lt: 60,
838
+ gt: 62,
839
+ nbsp: 160,
840
+ iexcl: 161,
841
+ cent: 162,
842
+ pound: 163,
843
+ curren: 164,
844
+ yen: 165,
845
+ brvbar: 166,
846
+ sect: 167,
847
+ uml: 168,
848
+ copy: 169,
849
+ ordf: 170,
850
+ laquo: 171,
851
+ not: 172,
852
+ shy: 173,
853
+ reg: 174,
854
+ macr: 175,
855
+ deg: 176,
856
+ plusmn: 177,
857
+ sup2: 178,
858
+ sup3: 179,
859
+ acute: 180,
860
+ micro: 181,
861
+ para: 182,
862
+ middot: 183,
863
+ cedil: 184,
864
+ sup1: 185,
865
+ ordm: 186,
866
+ raquo: 187,
867
+ frac14: 188,
868
+ frac12: 189,
869
+ frac34: 190,
870
+ iquest: 191,
871
+ Agrave: 192,
872
+ Aacute: 193,
873
+ Acirc: 194,
874
+ Atilde: 195,
875
+ Auml: 196,
876
+ Aring: 197,
877
+ AElig: 198,
878
+ Ccedil: 199,
879
+ Egrave: 200,
880
+ Eacute: 201,
881
+ Ecirc: 202,
882
+ Euml: 203,
883
+ Igrave: 204,
884
+ Iacute: 205,
885
+ Icirc: 206,
886
+ Iuml: 207,
887
+ ETH: 208,
888
+ Ntilde: 209,
889
+ Ograve: 210,
890
+ Oacute: 211,
891
+ Ocirc: 212,
892
+ Otilde: 213,
893
+ Ouml: 214,
894
+ times: 215,
895
+ Oslash: 216,
896
+ Ugrave: 217,
897
+ Uacute: 218,
898
+ Ucirc: 219,
899
+ Uuml: 220,
900
+ Yacute: 221,
901
+ THORN: 222,
902
+ szlig: 223,
903
+ agrave: 224,
904
+ aacute: 225,
905
+ acirc: 226,
906
+ atilde: 227,
907
+ auml: 228,
908
+ aring: 229,
909
+ aelig: 230,
910
+ ccedil: 231,
911
+ egrave: 232,
912
+ eacute: 233,
913
+ ecirc: 234,
914
+ euml: 235,
915
+ igrave: 236,
916
+ iacute: 237,
917
+ icirc: 238,
918
+ iuml: 239,
919
+ eth: 240,
920
+ ntilde: 241,
921
+ ograve: 242,
922
+ oacute: 243,
923
+ ocirc: 244,
924
+ otilde: 245,
925
+ ouml: 246,
926
+ divide: 247,
927
+ oslash: 248,
928
+ ugrave: 249,
929
+ uacute: 250,
930
+ ucirc: 251,
931
+ uuml: 252,
932
+ yacute: 253,
933
+ thorn: 254,
934
+ yuml: 255,
935
+ OElig: 338,
936
+ oelig: 339,
937
+ Scaron: 352,
938
+ scaron: 353,
939
+ Yuml: 376,
940
+ fnof: 402,
941
+ circ: 710,
942
+ tilde: 732,
943
+ Alpha: 913,
944
+ Beta: 914,
945
+ Gamma: 915,
946
+ Delta: 916,
947
+ Epsilon: 917,
948
+ Zeta: 918,
949
+ Eta: 919,
950
+ Theta: 920,
951
+ Iota: 921,
952
+ Kappa: 922,
953
+ Lambda: 923,
954
+ Mu: 924,
955
+ Nu: 925,
956
+ Xi: 926,
957
+ Omicron: 927,
958
+ Pi: 928,
959
+ Rho: 929,
960
+ Sigma: 931,
961
+ Tau: 932,
962
+ Upsilon: 933,
963
+ Phi: 934,
964
+ Chi: 935,
965
+ Psi: 936,
966
+ Omega: 937,
967
+ alpha: 945,
968
+ beta: 946,
969
+ gamma: 947,
970
+ delta: 948,
971
+ epsilon: 949,
972
+ zeta: 950,
973
+ eta: 951,
974
+ theta: 952,
975
+ iota: 953,
976
+ kappa: 954,
977
+ lambda: 955,
978
+ mu: 956,
979
+ nu: 957,
980
+ xi: 958,
981
+ omicron: 959,
982
+ pi: 960,
983
+ rho: 961,
984
+ sigmaf: 962,
985
+ sigma: 963,
986
+ tau: 964,
987
+ upsilon: 965,
988
+ phi: 966,
989
+ chi: 967,
990
+ psi: 968,
991
+ omega: 969,
992
+ thetasym: 977,
993
+ upsih: 978,
994
+ piv: 982,
995
+ ensp: 8194,
996
+ emsp: 8195,
997
+ thinsp: 8201,
998
+ zwnj: 8204,
999
+ zwj: 8205,
1000
+ lrm: 8206,
1001
+ rlm: 8207,
1002
+ ndash: 8211,
1003
+ mdash: 8212,
1004
+ lsquo: 8216,
1005
+ rsquo: 8217,
1006
+ sbquo: 8218,
1007
+ ldquo: 8220,
1008
+ rdquo: 8221,
1009
+ bdquo: 8222,
1010
+ dagger: 8224,
1011
+ Dagger: 8225,
1012
+ bull: 8226,
1013
+ hellip: 8230,
1014
+ permil: 8240,
1015
+ prime: 8242,
1016
+ Prime: 8243,
1017
+ lsaquo: 8249,
1018
+ rsaquo: 8250,
1019
+ oline: 8254,
1020
+ frasl: 8260,
1021
+ euro: 8364,
1022
+ image: 8465,
1023
+ weierp: 8472,
1024
+ real: 8476,
1025
+ trade: 8482,
1026
+ alefsym: 8501,
1027
+ larr: 8592,
1028
+ uarr: 8593,
1029
+ rarr: 8594,
1030
+ darr: 8595,
1031
+ harr: 8596,
1032
+ crarr: 8629,
1033
+ lArr: 8656,
1034
+ uArr: 8657,
1035
+ rArr: 8658,
1036
+ dArr: 8659,
1037
+ hArr: 8660,
1038
+ forall: 8704,
1039
+ part: 8706,
1040
+ exist: 8707,
1041
+ empty: 8709,
1042
+ nabla: 8711,
1043
+ isin: 8712,
1044
+ notin: 8713,
1045
+ ni: 8715,
1046
+ prod: 8719,
1047
+ sum: 8721,
1048
+ minus: 8722,
1049
+ lowast: 8727,
1050
+ radic: 8730,
1051
+ prop: 8733,
1052
+ infin: 8734,
1053
+ ang: 8736,
1054
+ and: 8743,
1055
+ or: 8744,
1056
+ cap: 8745,
1057
+ cup: 8746,
1058
+ int: 8747,
1059
+ there4: 8756,
1060
+ sim: 8764,
1061
+ cong: 8773,
1062
+ asymp: 8776,
1063
+ ne: 8800,
1064
+ equiv: 8801,
1065
+ le: 8804,
1066
+ ge: 8805,
1067
+ sub: 8834,
1068
+ sup: 8835,
1069
+ nsub: 8836,
1070
+ sube: 8838,
1071
+ supe: 8839,
1072
+ oplus: 8853,
1073
+ otimes: 8855,
1074
+ perp: 8869,
1075
+ sdot: 8901,
1076
+ lceil: 8968,
1077
+ rceil: 8969,
1078
+ lfloor: 8970,
1079
+ rfloor: 8971,
1080
+ lang: 9001,
1081
+ rang: 9002,
1082
+ loz: 9674,
1083
+ spades: 9824,
1084
+ clubs: 9827,
1085
+ hearts: 9829,
1086
+ diams: 9830
1087
+ };
1088
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
1089
+ function decodeEntities(value) {
1090
+ if (!value.includes("&")) return value;
1091
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
1092
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
1093
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
1094
+ });
1095
+ }
1096
+
814
1097
  // src/lower/template.ts
815
1098
  var DELEGATED = /* @__PURE__ */ new Set([
816
1099
  "click",
@@ -829,6 +1112,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
829
1112
  function escapeTemplate(s) {
830
1113
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
831
1114
  }
1115
+ var CONTAINER = "container";
832
1116
  function capitalize(s) {
833
1117
  return s.charAt(0).toUpperCase() + s.slice(1);
834
1118
  }
@@ -883,10 +1167,6 @@ var Lowerer = class {
883
1167
  if (hasElse) i = elseIdx;
884
1168
  continue;
885
1169
  }
886
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
887
- out.push(this.lowerEach(child));
888
- continue;
889
- }
890
1170
  }
891
1171
  const node = this.lowerNode(child);
892
1172
  if (node) out.push(node);
@@ -900,7 +1180,7 @@ var Lowerer = class {
900
1180
  switch (node.kind) {
901
1181
  case "Text": {
902
1182
  if (node.raw) return { kind: "text", value: node.value };
903
- const cleaned = cleanTemplateText(node.value);
1183
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
904
1184
  return cleaned ? { kind: "text", value: cleaned } : null;
905
1185
  }
906
1186
  case "Comment":
@@ -928,13 +1208,16 @@ var Lowerer = class {
928
1208
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
929
1209
  return node;
930
1210
  }
1211
+ const each = this.lowerEach(el);
1212
+ const children = each ? [each] : this.lowerChildren(el.children);
1213
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
931
1214
  const at = this.tagAt(el);
932
1215
  return {
933
1216
  kind: "element",
934
1217
  tag: el.tag,
935
1218
  svg: el.namespace === "svg",
936
- props: this.lowerAttributes(el.attributes),
937
- children: this.lowerChildren(el.children),
1219
+ props: this.lowerAttributes(el.attributes, true),
1220
+ children,
938
1221
  static: false,
939
1222
  ...at ? { at } : {}
940
1223
  };
@@ -1006,10 +1289,14 @@ var Lowerer = class {
1006
1289
  const at = this.tagAt(el);
1007
1290
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
1008
1291
  }
1009
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1292
+ /**
1293
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1294
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1295
+ * it is. Null when the element has no `each`.
1296
+ */
1010
1297
  lowerEach(el) {
1011
1298
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
1012
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1299
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
1013
1300
  const each = this.hole(eachDir.hole);
1014
1301
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
1015
1302
  if (eachDir.key) {
@@ -1019,12 +1306,12 @@ var Lowerer = class {
1019
1306
  const arrow = this.itemArrow(el);
1020
1307
  let childrenCode;
1021
1308
  if (arrow) {
1022
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
1023
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
1024
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1309
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1310
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
1025
1311
  } else {
1026
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
1027
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1312
+ const content = this.lowerChildren(el.children);
1313
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1314
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
1028
1315
  }
1029
1316
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
1030
1317
  const at = this.tagAt(el);
@@ -1041,33 +1328,23 @@ var Lowerer = class {
1041
1328
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
1042
1329
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
1043
1330
  }
1044
- /** Same element (minus each/key) but with explicit IR children. */
1045
- rebuildWithChildren(el, children) {
1046
- const props = this.lowerAttributes(
1047
- el.attributes.filter((a) => a.kind !== "EachDirective")
1048
- );
1049
- if (el.kind === "Element") {
1050
- const at = this.tagAt(el);
1051
- return {
1052
- kind: "element",
1053
- tag: el.tag,
1054
- svg: el.namespace === "svg",
1055
- props,
1056
- children,
1057
- static: false,
1058
- ...at ? { at } : {}
1059
- };
1060
- }
1061
- return { kind: "component", name: el.tag, props, children };
1062
- }
1063
1331
  // --- attributes ---------------------------------------------------------
1064
- lowerAttributes(attrs) {
1332
+ /**
1333
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
1334
+ * A component gets the object as before, rebuilt whole when any value moves.
1335
+ */
1336
+ lowerAttributes(attrs, element = false) {
1337
+ let anyReactive = false;
1338
+ const entry = (name, h) => {
1339
+ anyReactive ||= h.reactive;
1340
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
1341
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
1342
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
1343
+ };
1065
1344
  const props = [];
1066
1345
  const classEntries = [];
1067
- let classReactive = false;
1068
1346
  let classAt;
1069
1347
  const styleEntries = [];
1070
- let styleReactive = false;
1071
1348
  let styleAt;
1072
1349
  const usePairs = [];
1073
1350
  for (const a of attrs) {
@@ -1107,15 +1384,13 @@ var Lowerer = class {
1107
1384
  break;
1108
1385
  case "ClassDirective": {
1109
1386
  const h = this.hole(a.hole);
1110
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
1111
- classReactive = classReactive || h.reactive;
1387
+ classEntries.push(entry(a.name, h));
1112
1388
  classAt ??= h.at;
1113
1389
  break;
1114
1390
  }
1115
1391
  case "StyleDirective": {
1116
1392
  const h = this.hole(a.hole);
1117
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
1118
- styleReactive = styleReactive || h.reactive;
1393
+ styleEntries.push(entry(a.name, h));
1119
1394
  styleAt ??= h.at;
1120
1395
  break;
1121
1396
  }
@@ -1135,18 +1410,19 @@ var Lowerer = class {
1135
1410
  name: "classList",
1136
1411
  kind: "attr",
1137
1412
  expr: `{ ${classEntries.join(", ")} }`,
1138
- reactive: classReactive,
1413
+ reactive: !element && anyReactive,
1139
1414
  ...classAt ? { at: classAt } : {}
1140
1415
  });
1141
1416
  }
1142
1417
  if (styleEntries.length > 0) {
1143
- props.push({
1144
- name: "style",
1145
- kind: "attr",
1146
- expr: `{ ${styleEntries.join(", ")} }`,
1147
- reactive: styleReactive,
1148
- ...styleAt ? { at: styleAt } : {}
1149
- });
1418
+ const entries = `{ ${styleEntries.join(", ")} }`;
1419
+ const base = props.findIndex((p) => p.name === "style");
1420
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
1421
+ else {
1422
+ const prop = props[base];
1423
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1424
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1425
+ }
1150
1426
  }
1151
1427
  if (usePairs.length > 0) {
1152
1428
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1235,7 +1511,7 @@ var Lowerer = class {
1235
1511
  return base;
1236
1512
  }
1237
1513
  if (v.kind === "static") {
1238
- base.literal = v.value;
1514
+ base.literal = decodeEntities(v.value);
1239
1515
  return base;
1240
1516
  }
1241
1517
  if (v.kind === "hole") {
@@ -1248,7 +1524,7 @@ var Lowerer = class {
1248
1524
  let reactive = false;
1249
1525
  let at;
1250
1526
  const body = v.parts.map((p) => {
1251
- if ("text" in p) return escapeTemplate(p.text);
1527
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1252
1528
  const h = this.hole(p.hole);
1253
1529
  reactive = reactive || h.reactive;
1254
1530
  at ??= h.at;
@@ -1471,6 +1747,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1471
1747
  "mouseup"
1472
1748
  ]);
1473
1749
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1750
+ var CONTAINER2 = "container";
1751
+ function directive(node, name) {
1752
+ if (node.type !== "JSXElement") return void 0;
1753
+ const opening = node.openingElement;
1754
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1755
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1756
+ }
1757
+ function directiveExpr(attr) {
1758
+ const v = attr.value;
1759
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1760
+ }
1761
+ function emitIR(ir, ctx) {
1762
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1763
+ for (const s of imports) ctx.used.add(s);
1764
+ return inlineTemplates(code, templates);
1765
+ }
1474
1766
  var posOf = (node, ctx) => {
1475
1767
  if (!ctx.sourceFile) return void 0;
1476
1768
  const at = node.loc?.start;
@@ -1548,9 +1840,12 @@ function attrExprCode(attr, ctx) {
1548
1840
  }
1549
1841
  return null;
1550
1842
  }
1551
- function buildProps(attrs, ctx) {
1843
+ function buildProps(attrs, ctx, element = false) {
1552
1844
  const props = [];
1553
1845
  const usePairs = [];
1846
+ const classEntries = [];
1847
+ const styleEntries = [];
1848
+ let styleAt;
1554
1849
  for (const a of attrs) {
1555
1850
  if (a.type === "JSXSpreadAttribute") {
1556
1851
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -1566,6 +1861,27 @@ function buildProps(attrs, ctx) {
1566
1861
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
1567
1862
  continue;
1568
1863
  }
1864
+ if (element && (ns === "class" || ns === "style")) {
1865
+ const key = JSON.stringify(a.name.name.name);
1866
+ const v = a.value;
1867
+ let value;
1868
+ if (v == null) value = "true";
1869
+ else if (isString(v)) value = JSON.stringify(literalOf(v));
1870
+ else if (v.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression") {
1871
+ value = keyValue(v.expression, ctx);
1872
+ if (ns === "style") styleAt ??= posOf(v.expression, ctx);
1873
+ } else continue;
1874
+ (ns === "class" ? classEntries : styleEntries).push(`${key}: ${value}`);
1875
+ continue;
1876
+ }
1877
+ if (element && a.name.type === "JSXIdentifier" && a.name.name === "style" && a.value?.type === "JSXExpressionContainer") {
1878
+ const entries = objectEntries(a.value.expression, ctx);
1879
+ if (entries) {
1880
+ styleEntries.unshift(...entries);
1881
+ styleAt = posOf(a.value.expression, ctx);
1882
+ continue;
1883
+ }
1884
+ }
1569
1885
  if (ns === "oncapture") {
1570
1886
  const evt = a.name.name.name.toLowerCase();
1571
1887
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -1574,12 +1890,49 @@ function buildProps(attrs, ctx) {
1574
1890
  }
1575
1891
  props.push(buildAttr(a, ctx));
1576
1892
  }
1893
+ if (classEntries.length > 0) props.push({ name: "classList", kind: "attr", expr: `{ ${classEntries.join(", ")} }`, reactive: false });
1894
+ if (styleEntries.length > 0) {
1895
+ const entries = `{ ${styleEntries.join(", ")} }`;
1896
+ const base = props.findIndex((p) => p.name === "style" && p.kind === "attr");
1897
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: false, ...styleAt ? { at: styleAt } : {} });
1898
+ else {
1899
+ const prop = props[base];
1900
+ const value = prop.literal !== void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1901
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1902
+ }
1903
+ }
1577
1904
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
1578
1905
  return props;
1579
1906
  }
1907
+ function keyValue(expr, ctx) {
1908
+ const code = ctx.code(expr);
1909
+ if (!isReactive(expr)) return code;
1910
+ const accessor = `() => ${paren(code, "arrowBody")}`;
1911
+ const at = posOf(expr, ctx);
1912
+ return at ? markHole(accessor, at.line, at.column) : accessor;
1913
+ }
1914
+ function objectEntries(expr, ctx) {
1915
+ if (expr?.type !== "ObjectExpression") return null;
1916
+ const entries = [];
1917
+ for (const p of expr.properties) {
1918
+ if (p.type !== "ObjectProperty") return null;
1919
+ const key = p.computed ? `[${ctx.code(p.key)}]` : p.key.type === "Identifier" ? JSON.stringify(p.key.name) : JSON.stringify(String(p.key.value));
1920
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
1921
+ }
1922
+ return entries;
1923
+ }
1580
1924
  function buildChildren(children, ctx) {
1581
1925
  const out = [];
1582
- for (const child of children) {
1926
+ for (let i = 0; i < children.length; i++) {
1927
+ const child = children[i];
1928
+ if (directive(child, "if")) {
1929
+ let next = i + 1;
1930
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1931
+ const fallback = directive(children[next], "else") ? children[next] : null;
1932
+ out.push(buildIf(child, fallback, ctx));
1933
+ if (fallback) i = next;
1934
+ continue;
1935
+ }
1583
1936
  if (child.type === "JSXText") {
1584
1937
  const text = cleanTemplateText(child.value);
1585
1938
  if (text) out.push({ kind: "text", value: text });
@@ -1595,6 +1948,7 @@ function buildChildren(children, ctx) {
1595
1948
  });
1596
1949
  }
1597
1950
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
1951
+ if (ctx.dropStyles?.has(child.start)) continue;
1598
1952
  out.push(buildNode(child, ctx));
1599
1953
  } else if (child.type === "JSXSpreadChild") {
1600
1954
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -1616,19 +1970,72 @@ function readLoad(attrs) {
1616
1970
  }
1617
1971
  return void 0;
1618
1972
  }
1973
+ function buildIf(node, fallback, ctx) {
1974
+ const cond = directiveExpr(directive(node, "if"));
1975
+ const props = [
1976
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1977
+ ];
1978
+ if (fallback) {
1979
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1980
+ }
1981
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1982
+ }
1983
+ function buildEach(node, ctx) {
1984
+ const attr = directive(node, "each");
1985
+ if (!attr) return null;
1986
+ const list = directiveExpr(attr);
1987
+ const props = [
1988
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1989
+ ];
1990
+ const key = directive(node, "key");
1991
+ if (key) {
1992
+ const keyExpr = directiveExpr(key);
1993
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1994
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1995
+ }
1996
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1997
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1998
+ let render;
1999
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
2000
+ const fn = ctx.code(only);
2001
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
2002
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
2003
+ } else {
2004
+ const built = buildChildren(node.children, ctx);
2005
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
2006
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
2007
+ }
2008
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
2009
+ }
2010
+ function buildElement(node, ctx, handled) {
2011
+ const skip = new Set(handled);
2012
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
2013
+ const attributes = node.openingElement.attributes.filter(
2014
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
2015
+ );
2016
+ const { tag } = tagInfo(node.openingElement.name, ctx);
2017
+ const each = buildEach(node, ctx);
2018
+ const children = each ? [each] : buildChildren(node.children, ctx);
2019
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
2020
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
2021
+ const props = buildProps(attributes, ctx, true);
2022
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
2023
+ if (scope) props.unshift({ name: "data-fx-s", kind: "attr", literal: scope.value });
2024
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
2025
+ }
1619
2026
  function buildNode(node, ctx) {
1620
2027
  if (node.type === "JSXFragment") {
1621
2028
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1622
2029
  }
2030
+ if (directive(node, "if")) return buildIf(node, null, ctx);
2031
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
1623
2032
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
2033
+ if (!component) return buildElement(node, ctx, []);
1624
2034
  const props = buildProps(node.openingElement.attributes, ctx);
1625
2035
  const children = buildChildren(node.children, ctx);
1626
2036
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1627
- if (component) {
1628
- const load = readLoad(node.openingElement.attributes);
1629
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1630
- }
1631
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
2037
+ const load = readLoad(node.openingElement.attributes);
2038
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1632
2039
  }
1633
2040
  function buildJsxIR(node, ctx) {
1634
2041
  const ir = buildNode(node, ctx);