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