@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
@@ -300,6 +300,7 @@ var INTRINSICS = {
300
300
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
301
301
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
302
302
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
303
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
303
304
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
304
305
  // name. Still worth having: no import to write, and when a shorter form is
305
306
  // designed, the change is this line rather than a compiler pass.
@@ -310,6 +311,19 @@ var INTRINSICS = {
310
311
  // dependency analysis must not guess at.
311
312
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
312
313
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
314
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
315
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
316
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
317
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
318
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
319
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
320
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
321
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
322
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
323
+ // handle as far as reads go: the same accessor with `set` on it.
324
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
325
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
326
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
313
327
  // An environment variable, public or server-only; the browser build refuses a secret.
314
328
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
315
329
  };
@@ -487,13 +501,16 @@ function emitComponent(name, props, children, used, templates, at) {
487
501
  used.add("createComponent");
488
502
  const spreads = props.filter((p) => p.kind === "spread");
489
503
  const normal = props.filter((p) => p.kind !== "spread");
490
- const entries = normal.map(
491
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
492
- );
504
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
493
505
  if (children.length > 0) {
494
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
506
+ bodies.push(["children", emitChildren(children, used, templates)]);
507
+ }
508
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
509
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
510
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
511
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
512
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
495
513
  }
496
- let obj = `{ ${entries.join(", ")} }`;
497
514
  if (spreads.length > 0) {
498
515
  used.add("mergeProps");
499
516
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -575,7 +592,8 @@ function emitNode(node, used, templates) {
575
592
  } else {
576
593
  used.add("spread");
577
594
  const svgFlag = node.svg ? ", isSVG: true" : "";
578
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
595
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
596
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
579
597
  parts.push(
580
598
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
581
599
  );
@@ -628,6 +646,271 @@ function cleanTemplateText(value) {
628
646
  return out;
629
647
  }
630
648
 
649
+ // src/lower/entities.ts
650
+ var NAMED = {
651
+ quot: 34,
652
+ amp: 38,
653
+ apos: 39,
654
+ lt: 60,
655
+ gt: 62,
656
+ nbsp: 160,
657
+ iexcl: 161,
658
+ cent: 162,
659
+ pound: 163,
660
+ curren: 164,
661
+ yen: 165,
662
+ brvbar: 166,
663
+ sect: 167,
664
+ uml: 168,
665
+ copy: 169,
666
+ ordf: 170,
667
+ laquo: 171,
668
+ not: 172,
669
+ shy: 173,
670
+ reg: 174,
671
+ macr: 175,
672
+ deg: 176,
673
+ plusmn: 177,
674
+ sup2: 178,
675
+ sup3: 179,
676
+ acute: 180,
677
+ micro: 181,
678
+ para: 182,
679
+ middot: 183,
680
+ cedil: 184,
681
+ sup1: 185,
682
+ ordm: 186,
683
+ raquo: 187,
684
+ frac14: 188,
685
+ frac12: 189,
686
+ frac34: 190,
687
+ iquest: 191,
688
+ Agrave: 192,
689
+ Aacute: 193,
690
+ Acirc: 194,
691
+ Atilde: 195,
692
+ Auml: 196,
693
+ Aring: 197,
694
+ AElig: 198,
695
+ Ccedil: 199,
696
+ Egrave: 200,
697
+ Eacute: 201,
698
+ Ecirc: 202,
699
+ Euml: 203,
700
+ Igrave: 204,
701
+ Iacute: 205,
702
+ Icirc: 206,
703
+ Iuml: 207,
704
+ ETH: 208,
705
+ Ntilde: 209,
706
+ Ograve: 210,
707
+ Oacute: 211,
708
+ Ocirc: 212,
709
+ Otilde: 213,
710
+ Ouml: 214,
711
+ times: 215,
712
+ Oslash: 216,
713
+ Ugrave: 217,
714
+ Uacute: 218,
715
+ Ucirc: 219,
716
+ Uuml: 220,
717
+ Yacute: 221,
718
+ THORN: 222,
719
+ szlig: 223,
720
+ agrave: 224,
721
+ aacute: 225,
722
+ acirc: 226,
723
+ atilde: 227,
724
+ auml: 228,
725
+ aring: 229,
726
+ aelig: 230,
727
+ ccedil: 231,
728
+ egrave: 232,
729
+ eacute: 233,
730
+ ecirc: 234,
731
+ euml: 235,
732
+ igrave: 236,
733
+ iacute: 237,
734
+ icirc: 238,
735
+ iuml: 239,
736
+ eth: 240,
737
+ ntilde: 241,
738
+ ograve: 242,
739
+ oacute: 243,
740
+ ocirc: 244,
741
+ otilde: 245,
742
+ ouml: 246,
743
+ divide: 247,
744
+ oslash: 248,
745
+ ugrave: 249,
746
+ uacute: 250,
747
+ ucirc: 251,
748
+ uuml: 252,
749
+ yacute: 253,
750
+ thorn: 254,
751
+ yuml: 255,
752
+ OElig: 338,
753
+ oelig: 339,
754
+ Scaron: 352,
755
+ scaron: 353,
756
+ Yuml: 376,
757
+ fnof: 402,
758
+ circ: 710,
759
+ tilde: 732,
760
+ Alpha: 913,
761
+ Beta: 914,
762
+ Gamma: 915,
763
+ Delta: 916,
764
+ Epsilon: 917,
765
+ Zeta: 918,
766
+ Eta: 919,
767
+ Theta: 920,
768
+ Iota: 921,
769
+ Kappa: 922,
770
+ Lambda: 923,
771
+ Mu: 924,
772
+ Nu: 925,
773
+ Xi: 926,
774
+ Omicron: 927,
775
+ Pi: 928,
776
+ Rho: 929,
777
+ Sigma: 931,
778
+ Tau: 932,
779
+ Upsilon: 933,
780
+ Phi: 934,
781
+ Chi: 935,
782
+ Psi: 936,
783
+ Omega: 937,
784
+ alpha: 945,
785
+ beta: 946,
786
+ gamma: 947,
787
+ delta: 948,
788
+ epsilon: 949,
789
+ zeta: 950,
790
+ eta: 951,
791
+ theta: 952,
792
+ iota: 953,
793
+ kappa: 954,
794
+ lambda: 955,
795
+ mu: 956,
796
+ nu: 957,
797
+ xi: 958,
798
+ omicron: 959,
799
+ pi: 960,
800
+ rho: 961,
801
+ sigmaf: 962,
802
+ sigma: 963,
803
+ tau: 964,
804
+ upsilon: 965,
805
+ phi: 966,
806
+ chi: 967,
807
+ psi: 968,
808
+ omega: 969,
809
+ thetasym: 977,
810
+ upsih: 978,
811
+ piv: 982,
812
+ ensp: 8194,
813
+ emsp: 8195,
814
+ thinsp: 8201,
815
+ zwnj: 8204,
816
+ zwj: 8205,
817
+ lrm: 8206,
818
+ rlm: 8207,
819
+ ndash: 8211,
820
+ mdash: 8212,
821
+ lsquo: 8216,
822
+ rsquo: 8217,
823
+ sbquo: 8218,
824
+ ldquo: 8220,
825
+ rdquo: 8221,
826
+ bdquo: 8222,
827
+ dagger: 8224,
828
+ Dagger: 8225,
829
+ bull: 8226,
830
+ hellip: 8230,
831
+ permil: 8240,
832
+ prime: 8242,
833
+ Prime: 8243,
834
+ lsaquo: 8249,
835
+ rsaquo: 8250,
836
+ oline: 8254,
837
+ frasl: 8260,
838
+ euro: 8364,
839
+ image: 8465,
840
+ weierp: 8472,
841
+ real: 8476,
842
+ trade: 8482,
843
+ alefsym: 8501,
844
+ larr: 8592,
845
+ uarr: 8593,
846
+ rarr: 8594,
847
+ darr: 8595,
848
+ harr: 8596,
849
+ crarr: 8629,
850
+ lArr: 8656,
851
+ uArr: 8657,
852
+ rArr: 8658,
853
+ dArr: 8659,
854
+ hArr: 8660,
855
+ forall: 8704,
856
+ part: 8706,
857
+ exist: 8707,
858
+ empty: 8709,
859
+ nabla: 8711,
860
+ isin: 8712,
861
+ notin: 8713,
862
+ ni: 8715,
863
+ prod: 8719,
864
+ sum: 8721,
865
+ minus: 8722,
866
+ lowast: 8727,
867
+ radic: 8730,
868
+ prop: 8733,
869
+ infin: 8734,
870
+ ang: 8736,
871
+ and: 8743,
872
+ or: 8744,
873
+ cap: 8745,
874
+ cup: 8746,
875
+ int: 8747,
876
+ there4: 8756,
877
+ sim: 8764,
878
+ cong: 8773,
879
+ asymp: 8776,
880
+ ne: 8800,
881
+ equiv: 8801,
882
+ le: 8804,
883
+ ge: 8805,
884
+ sub: 8834,
885
+ sup: 8835,
886
+ nsub: 8836,
887
+ sube: 8838,
888
+ supe: 8839,
889
+ oplus: 8853,
890
+ otimes: 8855,
891
+ perp: 8869,
892
+ sdot: 8901,
893
+ lceil: 8968,
894
+ rceil: 8969,
895
+ lfloor: 8970,
896
+ rfloor: 8971,
897
+ lang: 9001,
898
+ rang: 9002,
899
+ loz: 9674,
900
+ spades: 9824,
901
+ clubs: 9827,
902
+ hearts: 9829,
903
+ diams: 9830
904
+ };
905
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
906
+ function decodeEntities(value) {
907
+ if (!value.includes("&")) return value;
908
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
909
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
910
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
911
+ });
912
+ }
913
+
631
914
  // src/lower/template.ts
632
915
  var DELEGATED = /* @__PURE__ */ new Set([
633
916
  "click",
@@ -646,6 +929,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
646
929
  function escapeTemplate(s) {
647
930
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
648
931
  }
932
+ var CONTAINER = "container";
649
933
  function capitalize(s) {
650
934
  return s.charAt(0).toUpperCase() + s.slice(1);
651
935
  }
@@ -700,10 +984,6 @@ var Lowerer = class {
700
984
  if (hasElse) i = elseIdx;
701
985
  continue;
702
986
  }
703
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
704
- out.push(this.lowerEach(child));
705
- continue;
706
- }
707
987
  }
708
988
  const node = this.lowerNode(child);
709
989
  if (node) out.push(node);
@@ -717,7 +997,7 @@ var Lowerer = class {
717
997
  switch (node.kind) {
718
998
  case "Text": {
719
999
  if (node.raw) return { kind: "text", value: node.value };
720
- const cleaned = cleanTemplateText(node.value);
1000
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
721
1001
  return cleaned ? { kind: "text", value: cleaned } : null;
722
1002
  }
723
1003
  case "Comment":
@@ -745,13 +1025,16 @@ var Lowerer = class {
745
1025
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
746
1026
  return node;
747
1027
  }
1028
+ const each = this.lowerEach(el);
1029
+ const children = each ? [each] : this.lowerChildren(el.children);
1030
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
748
1031
  const at = this.tagAt(el);
749
1032
  return {
750
1033
  kind: "element",
751
1034
  tag: el.tag,
752
1035
  svg: el.namespace === "svg",
753
- props: this.lowerAttributes(el.attributes),
754
- children: this.lowerChildren(el.children),
1036
+ props: this.lowerAttributes(el.attributes, true),
1037
+ children,
755
1038
  static: false,
756
1039
  ...at ? { at } : {}
757
1040
  };
@@ -823,10 +1106,14 @@ var Lowerer = class {
823
1106
  const at = this.tagAt(el);
824
1107
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
825
1108
  }
826
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1109
+ /**
1110
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1111
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1112
+ * it is. Null when the element has no `each`.
1113
+ */
827
1114
  lowerEach(el) {
828
1115
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
829
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1116
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
830
1117
  const each = this.hole(eachDir.hole);
831
1118
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
832
1119
  if (eachDir.key) {
@@ -836,12 +1123,12 @@ var Lowerer = class {
836
1123
  const arrow = this.itemArrow(el);
837
1124
  let childrenCode;
838
1125
  if (arrow) {
839
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
840
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
841
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1126
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1127
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
842
1128
  } else {
843
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
844
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1129
+ const content = this.lowerChildren(el.children);
1130
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1131
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
845
1132
  }
846
1133
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
847
1134
  const at = this.tagAt(el);
@@ -858,33 +1145,23 @@ var Lowerer = class {
858
1145
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
859
1146
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
860
1147
  }
861
- /** Same element (minus each/key) but with explicit IR children. */
862
- rebuildWithChildren(el, children) {
863
- const props = this.lowerAttributes(
864
- el.attributes.filter((a) => a.kind !== "EachDirective")
865
- );
866
- if (el.kind === "Element") {
867
- const at = this.tagAt(el);
868
- return {
869
- kind: "element",
870
- tag: el.tag,
871
- svg: el.namespace === "svg",
872
- props,
873
- children,
874
- static: false,
875
- ...at ? { at } : {}
876
- };
877
- }
878
- return { kind: "component", name: el.tag, props, children };
879
- }
880
1148
  // --- attributes ---------------------------------------------------------
881
- lowerAttributes(attrs) {
1149
+ /**
1150
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
1151
+ * A component gets the object as before, rebuilt whole when any value moves.
1152
+ */
1153
+ lowerAttributes(attrs, element = false) {
1154
+ let anyReactive = false;
1155
+ const entry = (name, h) => {
1156
+ anyReactive ||= h.reactive;
1157
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
1158
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
1159
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
1160
+ };
882
1161
  const props = [];
883
1162
  const classEntries = [];
884
- let classReactive = false;
885
1163
  let classAt;
886
1164
  const styleEntries = [];
887
- let styleReactive = false;
888
1165
  let styleAt;
889
1166
  const usePairs = [];
890
1167
  for (const a of attrs) {
@@ -924,15 +1201,13 @@ var Lowerer = class {
924
1201
  break;
925
1202
  case "ClassDirective": {
926
1203
  const h = this.hole(a.hole);
927
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
928
- classReactive = classReactive || h.reactive;
1204
+ classEntries.push(entry(a.name, h));
929
1205
  classAt ??= h.at;
930
1206
  break;
931
1207
  }
932
1208
  case "StyleDirective": {
933
1209
  const h = this.hole(a.hole);
934
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
935
- styleReactive = styleReactive || h.reactive;
1210
+ styleEntries.push(entry(a.name, h));
936
1211
  styleAt ??= h.at;
937
1212
  break;
938
1213
  }
@@ -952,18 +1227,19 @@ var Lowerer = class {
952
1227
  name: "classList",
953
1228
  kind: "attr",
954
1229
  expr: `{ ${classEntries.join(", ")} }`,
955
- reactive: classReactive,
1230
+ reactive: !element && anyReactive,
956
1231
  ...classAt ? { at: classAt } : {}
957
1232
  });
958
1233
  }
959
1234
  if (styleEntries.length > 0) {
960
- props.push({
961
- name: "style",
962
- kind: "attr",
963
- expr: `{ ${styleEntries.join(", ")} }`,
964
- reactive: styleReactive,
965
- ...styleAt ? { at: styleAt } : {}
966
- });
1235
+ const entries = `{ ${styleEntries.join(", ")} }`;
1236
+ const base = props.findIndex((p) => p.name === "style");
1237
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
1238
+ else {
1239
+ const prop = props[base];
1240
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1241
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1242
+ }
967
1243
  }
968
1244
  if (usePairs.length > 0) {
969
1245
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1052,7 +1328,7 @@ var Lowerer = class {
1052
1328
  return base;
1053
1329
  }
1054
1330
  if (v.kind === "static") {
1055
- base.literal = v.value;
1331
+ base.literal = decodeEntities(v.value);
1056
1332
  return base;
1057
1333
  }
1058
1334
  if (v.kind === "hole") {
@@ -1065,7 +1341,7 @@ var Lowerer = class {
1065
1341
  let reactive = false;
1066
1342
  let at;
1067
1343
  const body = v.parts.map((p) => {
1068
- if ("text" in p) return escapeTemplate(p.text);
1344
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1069
1345
  const h = this.hole(p.hole);
1070
1346
  reactive = reactive || h.reactive;
1071
1347
  at ??= h.at;
@@ -1253,6 +1529,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1253
1529
  "mouseup"
1254
1530
  ]);
1255
1531
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1532
+ var CONTAINER2 = "container";
1533
+ function directive(node, name) {
1534
+ if (node.type !== "JSXElement") return void 0;
1535
+ const opening = node.openingElement;
1536
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1537
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1538
+ }
1539
+ function directiveExpr(attr) {
1540
+ const v = attr.value;
1541
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1542
+ }
1543
+ function emitIR(ir, ctx) {
1544
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1545
+ for (const s of imports) ctx.used.add(s);
1546
+ return inlineTemplates(code, templates);
1547
+ }
1256
1548
  var posOf = (node, ctx) => {
1257
1549
  if (!ctx.sourceFile) return void 0;
1258
1550
  const at = node.loc?.start;
@@ -1330,9 +1622,12 @@ function attrExprCode(attr, ctx) {
1330
1622
  }
1331
1623
  return null;
1332
1624
  }
1333
- function buildProps(attrs, ctx) {
1625
+ function buildProps(attrs, ctx, element = false) {
1334
1626
  const props = [];
1335
1627
  const usePairs = [];
1628
+ const classEntries = [];
1629
+ const styleEntries = [];
1630
+ let styleAt;
1336
1631
  for (const a of attrs) {
1337
1632
  if (a.type === "JSXSpreadAttribute") {
1338
1633
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -1348,6 +1643,27 @@ function buildProps(attrs, ctx) {
1348
1643
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
1349
1644
  continue;
1350
1645
  }
1646
+ if (element && (ns === "class" || ns === "style")) {
1647
+ const key = JSON.stringify(a.name.name.name);
1648
+ const v = a.value;
1649
+ let value;
1650
+ if (v == null) value = "true";
1651
+ else if (isString(v)) value = JSON.stringify(literalOf(v));
1652
+ else if (v.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression") {
1653
+ value = keyValue(v.expression, ctx);
1654
+ if (ns === "style") styleAt ??= posOf(v.expression, ctx);
1655
+ } else continue;
1656
+ (ns === "class" ? classEntries : styleEntries).push(`${key}: ${value}`);
1657
+ continue;
1658
+ }
1659
+ if (element && a.name.type === "JSXIdentifier" && a.name.name === "style" && a.value?.type === "JSXExpressionContainer") {
1660
+ const entries = objectEntries(a.value.expression, ctx);
1661
+ if (entries) {
1662
+ styleEntries.unshift(...entries);
1663
+ styleAt = posOf(a.value.expression, ctx);
1664
+ continue;
1665
+ }
1666
+ }
1351
1667
  if (ns === "oncapture") {
1352
1668
  const evt = a.name.name.name.toLowerCase();
1353
1669
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -1356,12 +1672,49 @@ function buildProps(attrs, ctx) {
1356
1672
  }
1357
1673
  props.push(buildAttr(a, ctx));
1358
1674
  }
1675
+ if (classEntries.length > 0) props.push({ name: "classList", kind: "attr", expr: `{ ${classEntries.join(", ")} }`, reactive: false });
1676
+ if (styleEntries.length > 0) {
1677
+ const entries = `{ ${styleEntries.join(", ")} }`;
1678
+ const base = props.findIndex((p) => p.name === "style" && p.kind === "attr");
1679
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: false, ...styleAt ? { at: styleAt } : {} });
1680
+ else {
1681
+ const prop = props[base];
1682
+ const value = prop.literal !== void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1683
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1684
+ }
1685
+ }
1359
1686
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
1360
1687
  return props;
1361
1688
  }
1689
+ function keyValue(expr, ctx) {
1690
+ const code = ctx.code(expr);
1691
+ if (!isReactive(expr)) return code;
1692
+ const accessor = `() => ${paren(code, "arrowBody")}`;
1693
+ const at = posOf(expr, ctx);
1694
+ return at ? markHole(accessor, at.line, at.column) : accessor;
1695
+ }
1696
+ function objectEntries(expr, ctx) {
1697
+ if (expr?.type !== "ObjectExpression") return null;
1698
+ const entries = [];
1699
+ for (const p of expr.properties) {
1700
+ if (p.type !== "ObjectProperty") return null;
1701
+ const key = p.computed ? `[${ctx.code(p.key)}]` : p.key.type === "Identifier" ? JSON.stringify(p.key.name) : JSON.stringify(String(p.key.value));
1702
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
1703
+ }
1704
+ return entries;
1705
+ }
1362
1706
  function buildChildren(children, ctx) {
1363
1707
  const out = [];
1364
- for (const child of children) {
1708
+ for (let i = 0; i < children.length; i++) {
1709
+ const child = children[i];
1710
+ if (directive(child, "if")) {
1711
+ let next = i + 1;
1712
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1713
+ const fallback = directive(children[next], "else") ? children[next] : null;
1714
+ out.push(buildIf(child, fallback, ctx));
1715
+ if (fallback) i = next;
1716
+ continue;
1717
+ }
1365
1718
  if (child.type === "JSXText") {
1366
1719
  const text = cleanTemplateText(child.value);
1367
1720
  if (text) out.push({ kind: "text", value: text });
@@ -1377,6 +1730,7 @@ function buildChildren(children, ctx) {
1377
1730
  });
1378
1731
  }
1379
1732
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
1733
+ if (ctx.dropStyles?.has(child.start)) continue;
1380
1734
  out.push(buildNode(child, ctx));
1381
1735
  } else if (child.type === "JSXSpreadChild") {
1382
1736
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -1398,19 +1752,72 @@ function readLoad(attrs) {
1398
1752
  }
1399
1753
  return void 0;
1400
1754
  }
1755
+ function buildIf(node, fallback, ctx) {
1756
+ const cond = directiveExpr(directive(node, "if"));
1757
+ const props = [
1758
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1759
+ ];
1760
+ if (fallback) {
1761
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1762
+ }
1763
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1764
+ }
1765
+ function buildEach(node, ctx) {
1766
+ const attr = directive(node, "each");
1767
+ if (!attr) return null;
1768
+ const list = directiveExpr(attr);
1769
+ const props = [
1770
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1771
+ ];
1772
+ const key = directive(node, "key");
1773
+ if (key) {
1774
+ const keyExpr = directiveExpr(key);
1775
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1776
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1777
+ }
1778
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1779
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1780
+ let render;
1781
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1782
+ const fn = ctx.code(only);
1783
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1784
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1785
+ } else {
1786
+ const built = buildChildren(node.children, ctx);
1787
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1788
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1789
+ }
1790
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1791
+ }
1792
+ function buildElement(node, ctx, handled) {
1793
+ const skip = new Set(handled);
1794
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1795
+ const attributes = node.openingElement.attributes.filter(
1796
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1797
+ );
1798
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1799
+ const each = buildEach(node, ctx);
1800
+ const children = each ? [each] : buildChildren(node.children, ctx);
1801
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1802
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1803
+ const props = buildProps(attributes, ctx, true);
1804
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
1805
+ if (scope) props.unshift({ name: "data-fx-s", kind: "attr", literal: scope.value });
1806
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1807
+ }
1401
1808
  function buildNode(node, ctx) {
1402
1809
  if (node.type === "JSXFragment") {
1403
1810
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1404
1811
  }
1812
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1813
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
1405
1814
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1815
+ if (!component) return buildElement(node, ctx, []);
1406
1816
  const props = buildProps(node.openingElement.attributes, ctx);
1407
1817
  const children = buildChildren(node.children, ctx);
1408
1818
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1409
- if (component) {
1410
- const load = readLoad(node.openingElement.attributes);
1411
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1412
- }
1413
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1819
+ const load = readLoad(node.openingElement.attributes);
1820
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1414
1821
  }
1415
1822
  function buildJsxIR(node, ctx) {
1416
1823
  const ir = buildNode(node, ctx);