@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
@@ -268,6 +268,7 @@ var INTRINSICS = {
268
268
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
269
269
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
270
270
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
271
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
271
272
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
272
273
  // name. Still worth having: no import to write, and when a shorter form is
273
274
  // designed, the change is this line rather than a compiler pass.
@@ -278,6 +279,19 @@ var INTRINSICS = {
278
279
  // dependency analysis must not guess at.
279
280
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
280
281
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
282
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
283
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
284
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
285
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
286
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
287
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
288
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
289
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
290
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
291
+ // handle as far as reads go: the same accessor with `set` on it.
292
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
293
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
294
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
281
295
  // An environment variable, public or server-only; the browser build refuses a secret.
282
296
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
283
297
  };
@@ -455,13 +469,16 @@ function emitComponent(name, props, children, used, templates, at) {
455
469
  used.add("createComponent");
456
470
  const spreads = props.filter((p) => p.kind === "spread");
457
471
  const normal = props.filter((p) => p.kind !== "spread");
458
- const entries = normal.map(
459
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
460
- );
472
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
461
473
  if (children.length > 0) {
462
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
474
+ bodies.push(["children", emitChildren(children, used, templates)]);
475
+ }
476
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
477
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
478
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
479
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
480
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
463
481
  }
464
- let obj = `{ ${entries.join(", ")} }`;
465
482
  if (spreads.length > 0) {
466
483
  used.add("mergeProps");
467
484
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -543,7 +560,8 @@ function emitNode(node, used, templates) {
543
560
  } else {
544
561
  used.add("spread");
545
562
  const svgFlag = node.svg ? ", isSVG: true" : "";
546
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
563
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
564
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
547
565
  parts.push(
548
566
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
549
567
  );
@@ -596,6 +614,271 @@ function cleanTemplateText(value) {
596
614
  return out;
597
615
  }
598
616
 
617
+ // src/lower/entities.ts
618
+ var NAMED = {
619
+ quot: 34,
620
+ amp: 38,
621
+ apos: 39,
622
+ lt: 60,
623
+ gt: 62,
624
+ nbsp: 160,
625
+ iexcl: 161,
626
+ cent: 162,
627
+ pound: 163,
628
+ curren: 164,
629
+ yen: 165,
630
+ brvbar: 166,
631
+ sect: 167,
632
+ uml: 168,
633
+ copy: 169,
634
+ ordf: 170,
635
+ laquo: 171,
636
+ not: 172,
637
+ shy: 173,
638
+ reg: 174,
639
+ macr: 175,
640
+ deg: 176,
641
+ plusmn: 177,
642
+ sup2: 178,
643
+ sup3: 179,
644
+ acute: 180,
645
+ micro: 181,
646
+ para: 182,
647
+ middot: 183,
648
+ cedil: 184,
649
+ sup1: 185,
650
+ ordm: 186,
651
+ raquo: 187,
652
+ frac14: 188,
653
+ frac12: 189,
654
+ frac34: 190,
655
+ iquest: 191,
656
+ Agrave: 192,
657
+ Aacute: 193,
658
+ Acirc: 194,
659
+ Atilde: 195,
660
+ Auml: 196,
661
+ Aring: 197,
662
+ AElig: 198,
663
+ Ccedil: 199,
664
+ Egrave: 200,
665
+ Eacute: 201,
666
+ Ecirc: 202,
667
+ Euml: 203,
668
+ Igrave: 204,
669
+ Iacute: 205,
670
+ Icirc: 206,
671
+ Iuml: 207,
672
+ ETH: 208,
673
+ Ntilde: 209,
674
+ Ograve: 210,
675
+ Oacute: 211,
676
+ Ocirc: 212,
677
+ Otilde: 213,
678
+ Ouml: 214,
679
+ times: 215,
680
+ Oslash: 216,
681
+ Ugrave: 217,
682
+ Uacute: 218,
683
+ Ucirc: 219,
684
+ Uuml: 220,
685
+ Yacute: 221,
686
+ THORN: 222,
687
+ szlig: 223,
688
+ agrave: 224,
689
+ aacute: 225,
690
+ acirc: 226,
691
+ atilde: 227,
692
+ auml: 228,
693
+ aring: 229,
694
+ aelig: 230,
695
+ ccedil: 231,
696
+ egrave: 232,
697
+ eacute: 233,
698
+ ecirc: 234,
699
+ euml: 235,
700
+ igrave: 236,
701
+ iacute: 237,
702
+ icirc: 238,
703
+ iuml: 239,
704
+ eth: 240,
705
+ ntilde: 241,
706
+ ograve: 242,
707
+ oacute: 243,
708
+ ocirc: 244,
709
+ otilde: 245,
710
+ ouml: 246,
711
+ divide: 247,
712
+ oslash: 248,
713
+ ugrave: 249,
714
+ uacute: 250,
715
+ ucirc: 251,
716
+ uuml: 252,
717
+ yacute: 253,
718
+ thorn: 254,
719
+ yuml: 255,
720
+ OElig: 338,
721
+ oelig: 339,
722
+ Scaron: 352,
723
+ scaron: 353,
724
+ Yuml: 376,
725
+ fnof: 402,
726
+ circ: 710,
727
+ tilde: 732,
728
+ Alpha: 913,
729
+ Beta: 914,
730
+ Gamma: 915,
731
+ Delta: 916,
732
+ Epsilon: 917,
733
+ Zeta: 918,
734
+ Eta: 919,
735
+ Theta: 920,
736
+ Iota: 921,
737
+ Kappa: 922,
738
+ Lambda: 923,
739
+ Mu: 924,
740
+ Nu: 925,
741
+ Xi: 926,
742
+ Omicron: 927,
743
+ Pi: 928,
744
+ Rho: 929,
745
+ Sigma: 931,
746
+ Tau: 932,
747
+ Upsilon: 933,
748
+ Phi: 934,
749
+ Chi: 935,
750
+ Psi: 936,
751
+ Omega: 937,
752
+ alpha: 945,
753
+ beta: 946,
754
+ gamma: 947,
755
+ delta: 948,
756
+ epsilon: 949,
757
+ zeta: 950,
758
+ eta: 951,
759
+ theta: 952,
760
+ iota: 953,
761
+ kappa: 954,
762
+ lambda: 955,
763
+ mu: 956,
764
+ nu: 957,
765
+ xi: 958,
766
+ omicron: 959,
767
+ pi: 960,
768
+ rho: 961,
769
+ sigmaf: 962,
770
+ sigma: 963,
771
+ tau: 964,
772
+ upsilon: 965,
773
+ phi: 966,
774
+ chi: 967,
775
+ psi: 968,
776
+ omega: 969,
777
+ thetasym: 977,
778
+ upsih: 978,
779
+ piv: 982,
780
+ ensp: 8194,
781
+ emsp: 8195,
782
+ thinsp: 8201,
783
+ zwnj: 8204,
784
+ zwj: 8205,
785
+ lrm: 8206,
786
+ rlm: 8207,
787
+ ndash: 8211,
788
+ mdash: 8212,
789
+ lsquo: 8216,
790
+ rsquo: 8217,
791
+ sbquo: 8218,
792
+ ldquo: 8220,
793
+ rdquo: 8221,
794
+ bdquo: 8222,
795
+ dagger: 8224,
796
+ Dagger: 8225,
797
+ bull: 8226,
798
+ hellip: 8230,
799
+ permil: 8240,
800
+ prime: 8242,
801
+ Prime: 8243,
802
+ lsaquo: 8249,
803
+ rsaquo: 8250,
804
+ oline: 8254,
805
+ frasl: 8260,
806
+ euro: 8364,
807
+ image: 8465,
808
+ weierp: 8472,
809
+ real: 8476,
810
+ trade: 8482,
811
+ alefsym: 8501,
812
+ larr: 8592,
813
+ uarr: 8593,
814
+ rarr: 8594,
815
+ darr: 8595,
816
+ harr: 8596,
817
+ crarr: 8629,
818
+ lArr: 8656,
819
+ uArr: 8657,
820
+ rArr: 8658,
821
+ dArr: 8659,
822
+ hArr: 8660,
823
+ forall: 8704,
824
+ part: 8706,
825
+ exist: 8707,
826
+ empty: 8709,
827
+ nabla: 8711,
828
+ isin: 8712,
829
+ notin: 8713,
830
+ ni: 8715,
831
+ prod: 8719,
832
+ sum: 8721,
833
+ minus: 8722,
834
+ lowast: 8727,
835
+ radic: 8730,
836
+ prop: 8733,
837
+ infin: 8734,
838
+ ang: 8736,
839
+ and: 8743,
840
+ or: 8744,
841
+ cap: 8745,
842
+ cup: 8746,
843
+ int: 8747,
844
+ there4: 8756,
845
+ sim: 8764,
846
+ cong: 8773,
847
+ asymp: 8776,
848
+ ne: 8800,
849
+ equiv: 8801,
850
+ le: 8804,
851
+ ge: 8805,
852
+ sub: 8834,
853
+ sup: 8835,
854
+ nsub: 8836,
855
+ sube: 8838,
856
+ supe: 8839,
857
+ oplus: 8853,
858
+ otimes: 8855,
859
+ perp: 8869,
860
+ sdot: 8901,
861
+ lceil: 8968,
862
+ rceil: 8969,
863
+ lfloor: 8970,
864
+ rfloor: 8971,
865
+ lang: 9001,
866
+ rang: 9002,
867
+ loz: 9674,
868
+ spades: 9824,
869
+ clubs: 9827,
870
+ hearts: 9829,
871
+ diams: 9830
872
+ };
873
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
874
+ function decodeEntities(value) {
875
+ if (!value.includes("&")) return value;
876
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
877
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
878
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
879
+ });
880
+ }
881
+
599
882
  // src/lower/template.ts
600
883
  var DELEGATED = /* @__PURE__ */ new Set([
601
884
  "click",
@@ -614,6 +897,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
614
897
  function escapeTemplate(s) {
615
898
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
616
899
  }
900
+ var CONTAINER = "container";
617
901
  function capitalize(s) {
618
902
  return s.charAt(0).toUpperCase() + s.slice(1);
619
903
  }
@@ -668,10 +952,6 @@ var Lowerer = class {
668
952
  if (hasElse) i = elseIdx;
669
953
  continue;
670
954
  }
671
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
672
- out.push(this.lowerEach(child));
673
- continue;
674
- }
675
955
  }
676
956
  const node = this.lowerNode(child);
677
957
  if (node) out.push(node);
@@ -685,7 +965,7 @@ var Lowerer = class {
685
965
  switch (node.kind) {
686
966
  case "Text": {
687
967
  if (node.raw) return { kind: "text", value: node.value };
688
- const cleaned = cleanTemplateText(node.value);
968
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
689
969
  return cleaned ? { kind: "text", value: cleaned } : null;
690
970
  }
691
971
  case "Comment":
@@ -713,13 +993,16 @@ var Lowerer = class {
713
993
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
714
994
  return node;
715
995
  }
996
+ const each = this.lowerEach(el);
997
+ const children = each ? [each] : this.lowerChildren(el.children);
998
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
716
999
  const at = this.tagAt(el);
717
1000
  return {
718
1001
  kind: "element",
719
1002
  tag: el.tag,
720
1003
  svg: el.namespace === "svg",
721
- props: this.lowerAttributes(el.attributes),
722
- children: this.lowerChildren(el.children),
1004
+ props: this.lowerAttributes(el.attributes, true),
1005
+ children,
723
1006
  static: false,
724
1007
  ...at ? { at } : {}
725
1008
  };
@@ -791,10 +1074,14 @@ var Lowerer = class {
791
1074
  const at = this.tagAt(el);
792
1075
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
793
1076
  }
794
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1077
+ /**
1078
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1079
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1080
+ * it is. Null when the element has no `each`.
1081
+ */
795
1082
  lowerEach(el) {
796
1083
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
797
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1084
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
798
1085
  const each = this.hole(eachDir.hole);
799
1086
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
800
1087
  if (eachDir.key) {
@@ -804,12 +1091,12 @@ var Lowerer = class {
804
1091
  const arrow = this.itemArrow(el);
805
1092
  let childrenCode;
806
1093
  if (arrow) {
807
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
808
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
809
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1094
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1095
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
810
1096
  } else {
811
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
812
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1097
+ const content = this.lowerChildren(el.children);
1098
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1099
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
813
1100
  }
814
1101
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
815
1102
  const at = this.tagAt(el);
@@ -826,33 +1113,23 @@ var Lowerer = class {
826
1113
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
827
1114
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
828
1115
  }
829
- /** Same element (minus each/key) but with explicit IR children. */
830
- rebuildWithChildren(el, children) {
831
- const props = this.lowerAttributes(
832
- el.attributes.filter((a) => a.kind !== "EachDirective")
833
- );
834
- if (el.kind === "Element") {
835
- const at = this.tagAt(el);
836
- return {
837
- kind: "element",
838
- tag: el.tag,
839
- svg: el.namespace === "svg",
840
- props,
841
- children,
842
- static: false,
843
- ...at ? { at } : {}
844
- };
845
- }
846
- return { kind: "component", name: el.tag, props, children };
847
- }
848
1116
  // --- attributes ---------------------------------------------------------
849
- lowerAttributes(attrs) {
1117
+ /**
1118
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
1119
+ * A component gets the object as before, rebuilt whole when any value moves.
1120
+ */
1121
+ lowerAttributes(attrs, element = false) {
1122
+ let anyReactive = false;
1123
+ const entry = (name, h) => {
1124
+ anyReactive ||= h.reactive;
1125
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
1126
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
1127
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
1128
+ };
850
1129
  const props = [];
851
1130
  const classEntries = [];
852
- let classReactive = false;
853
1131
  let classAt;
854
1132
  const styleEntries = [];
855
- let styleReactive = false;
856
1133
  let styleAt;
857
1134
  const usePairs = [];
858
1135
  for (const a of attrs) {
@@ -892,15 +1169,13 @@ var Lowerer = class {
892
1169
  break;
893
1170
  case "ClassDirective": {
894
1171
  const h = this.hole(a.hole);
895
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
896
- classReactive = classReactive || h.reactive;
1172
+ classEntries.push(entry(a.name, h));
897
1173
  classAt ??= h.at;
898
1174
  break;
899
1175
  }
900
1176
  case "StyleDirective": {
901
1177
  const h = this.hole(a.hole);
902
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
903
- styleReactive = styleReactive || h.reactive;
1178
+ styleEntries.push(entry(a.name, h));
904
1179
  styleAt ??= h.at;
905
1180
  break;
906
1181
  }
@@ -920,18 +1195,19 @@ var Lowerer = class {
920
1195
  name: "classList",
921
1196
  kind: "attr",
922
1197
  expr: `{ ${classEntries.join(", ")} }`,
923
- reactive: classReactive,
1198
+ reactive: !element && anyReactive,
924
1199
  ...classAt ? { at: classAt } : {}
925
1200
  });
926
1201
  }
927
1202
  if (styleEntries.length > 0) {
928
- props.push({
929
- name: "style",
930
- kind: "attr",
931
- expr: `{ ${styleEntries.join(", ")} }`,
932
- reactive: styleReactive,
933
- ...styleAt ? { at: styleAt } : {}
934
- });
1203
+ const entries = `{ ${styleEntries.join(", ")} }`;
1204
+ const base = props.findIndex((p) => p.name === "style");
1205
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
1206
+ else {
1207
+ const prop = props[base];
1208
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1209
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1210
+ }
935
1211
  }
936
1212
  if (usePairs.length > 0) {
937
1213
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1020,7 +1296,7 @@ var Lowerer = class {
1020
1296
  return base;
1021
1297
  }
1022
1298
  if (v.kind === "static") {
1023
- base.literal = v.value;
1299
+ base.literal = decodeEntities(v.value);
1024
1300
  return base;
1025
1301
  }
1026
1302
  if (v.kind === "hole") {
@@ -1033,7 +1309,7 @@ var Lowerer = class {
1033
1309
  let reactive = false;
1034
1310
  let at;
1035
1311
  const body = v.parts.map((p) => {
1036
- if ("text" in p) return escapeTemplate(p.text);
1312
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1037
1313
  const h = this.hole(p.hole);
1038
1314
  reactive = reactive || h.reactive;
1039
1315
  at ??= h.at;
@@ -1221,6 +1497,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
1221
1497
  "mouseup"
1222
1498
  ]);
1223
1499
  var isReactive = (node) => isReactiveShape(shapeOf(node));
1500
+ var CONTAINER2 = "container";
1501
+ function directive(node, name) {
1502
+ if (node.type !== "JSXElement") return void 0;
1503
+ const opening = node.openingElement;
1504
+ if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
1505
+ return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
1506
+ }
1507
+ function directiveExpr(attr) {
1508
+ const v = attr.value;
1509
+ return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
1510
+ }
1511
+ function emitIR(ir, ctx) {
1512
+ const { code, imports, templates } = imperativeBackend.emit(ir, {});
1513
+ for (const s of imports) ctx.used.add(s);
1514
+ return inlineTemplates(code, templates);
1515
+ }
1224
1516
  var posOf = (node, ctx) => {
1225
1517
  if (!ctx.sourceFile) return void 0;
1226
1518
  const at = node.loc?.start;
@@ -1298,9 +1590,12 @@ function attrExprCode(attr, ctx) {
1298
1590
  }
1299
1591
  return null;
1300
1592
  }
1301
- function buildProps(attrs, ctx) {
1593
+ function buildProps(attrs, ctx, element = false) {
1302
1594
  const props = [];
1303
1595
  const usePairs = [];
1596
+ const classEntries = [];
1597
+ const styleEntries = [];
1598
+ let styleAt;
1304
1599
  for (const a of attrs) {
1305
1600
  if (a.type === "JSXSpreadAttribute") {
1306
1601
  props.push({ name: "", kind: "spread", expr: ctx.code(a.argument) });
@@ -1316,6 +1611,27 @@ function buildProps(attrs, ctx) {
1316
1611
  usePairs.push(opts != null ? `[${dir}, () => ${paren(opts, "arrowBody")}]` : `[${dir}]`);
1317
1612
  continue;
1318
1613
  }
1614
+ if (element && (ns === "class" || ns === "style")) {
1615
+ const key = JSON.stringify(a.name.name.name);
1616
+ const v = a.value;
1617
+ let value;
1618
+ if (v == null) value = "true";
1619
+ else if (isString(v)) value = JSON.stringify(literalOf(v));
1620
+ else if (v.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression") {
1621
+ value = keyValue(v.expression, ctx);
1622
+ if (ns === "style") styleAt ??= posOf(v.expression, ctx);
1623
+ } else continue;
1624
+ (ns === "class" ? classEntries : styleEntries).push(`${key}: ${value}`);
1625
+ continue;
1626
+ }
1627
+ if (element && a.name.type === "JSXIdentifier" && a.name.name === "style" && a.value?.type === "JSXExpressionContainer") {
1628
+ const entries = objectEntries(a.value.expression, ctx);
1629
+ if (entries) {
1630
+ styleEntries.unshift(...entries);
1631
+ styleAt = posOf(a.value.expression, ctx);
1632
+ continue;
1633
+ }
1634
+ }
1319
1635
  if (ns === "oncapture") {
1320
1636
  const evt = a.name.name.name.toLowerCase();
1321
1637
  const h = attrExprCode(a, ctx) ?? "undefined";
@@ -1324,12 +1640,49 @@ function buildProps(attrs, ctx) {
1324
1640
  }
1325
1641
  props.push(buildAttr(a, ctx));
1326
1642
  }
1643
+ if (classEntries.length > 0) props.push({ name: "classList", kind: "attr", expr: `{ ${classEntries.join(", ")} }`, reactive: false });
1644
+ if (styleEntries.length > 0) {
1645
+ const entries = `{ ${styleEntries.join(", ")} }`;
1646
+ const base = props.findIndex((p) => p.name === "style" && p.kind === "attr");
1647
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: false, ...styleAt ? { at: styleAt } : {} });
1648
+ else {
1649
+ const prop = props[base];
1650
+ const value = prop.literal !== void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1651
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1652
+ }
1653
+ }
1327
1654
  if (usePairs.length > 0) props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
1328
1655
  return props;
1329
1656
  }
1657
+ function keyValue(expr, ctx) {
1658
+ const code = ctx.code(expr);
1659
+ if (!isReactive(expr)) return code;
1660
+ const accessor = `() => ${paren(code, "arrowBody")}`;
1661
+ const at = posOf(expr, ctx);
1662
+ return at ? markHole(accessor, at.line, at.column) : accessor;
1663
+ }
1664
+ function objectEntries(expr, ctx) {
1665
+ if (expr?.type !== "ObjectExpression") return null;
1666
+ const entries = [];
1667
+ for (const p of expr.properties) {
1668
+ if (p.type !== "ObjectProperty") return null;
1669
+ const key = p.computed ? `[${ctx.code(p.key)}]` : p.key.type === "Identifier" ? JSON.stringify(p.key.name) : JSON.stringify(String(p.key.value));
1670
+ entries.push(`${key}: ${keyValue(p.value, ctx)}`);
1671
+ }
1672
+ return entries;
1673
+ }
1330
1674
  function buildChildren(children, ctx) {
1331
1675
  const out = [];
1332
- for (const child of children) {
1676
+ for (let i = 0; i < children.length; i++) {
1677
+ const child = children[i];
1678
+ if (directive(child, "if")) {
1679
+ let next = i + 1;
1680
+ while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
1681
+ const fallback = directive(children[next], "else") ? children[next] : null;
1682
+ out.push(buildIf(child, fallback, ctx));
1683
+ if (fallback) i = next;
1684
+ continue;
1685
+ }
1333
1686
  if (child.type === "JSXText") {
1334
1687
  const text = cleanTemplateText(child.value);
1335
1688
  if (text) out.push({ kind: "text", value: text });
@@ -1345,6 +1698,7 @@ function buildChildren(children, ctx) {
1345
1698
  });
1346
1699
  }
1347
1700
  } else if (child.type === "JSXElement" || child.type === "JSXFragment") {
1701
+ if (ctx.dropStyles?.has(child.start)) continue;
1348
1702
  out.push(buildNode(child, ctx));
1349
1703
  } else if (child.type === "JSXSpreadChild") {
1350
1704
  out.push({ kind: "expr", code: ctx.code(child.expression), reactive: false });
@@ -1366,19 +1720,72 @@ function readLoad(attrs) {
1366
1720
  }
1367
1721
  return void 0;
1368
1722
  }
1723
+ function buildIf(node, fallback, ctx) {
1724
+ const cond = directiveExpr(directive(node, "if"));
1725
+ const props = [
1726
+ cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
1727
+ ];
1728
+ if (fallback) {
1729
+ props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
1730
+ }
1731
+ return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
1732
+ }
1733
+ function buildEach(node, ctx) {
1734
+ const attr = directive(node, "each");
1735
+ if (!attr) return null;
1736
+ const list = directiveExpr(attr);
1737
+ const props = [
1738
+ list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
1739
+ ];
1740
+ const key = directive(node, "key");
1741
+ if (key) {
1742
+ const keyExpr = directiveExpr(key);
1743
+ const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
1744
+ if (by) props.push({ name: "by", kind: "attr", expr: by });
1745
+ }
1746
+ const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
1747
+ const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
1748
+ let render;
1749
+ if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
1750
+ const fn = ctx.code(only);
1751
+ const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
1752
+ render = live ? `(...args) => () => (${fn})(...args)` : fn;
1753
+ } else {
1754
+ const built = buildChildren(node.children, ctx);
1755
+ const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
1756
+ render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
1757
+ }
1758
+ return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
1759
+ }
1760
+ function buildElement(node, ctx, handled) {
1761
+ const skip = new Set(handled);
1762
+ const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
1763
+ const attributes = node.openingElement.attributes.filter(
1764
+ (a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
1765
+ );
1766
+ const { tag } = tagInfo(node.openingElement.name, ctx);
1767
+ const each = buildEach(node, ctx);
1768
+ const children = each ? [each] : buildChildren(node.children, ctx);
1769
+ if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
1770
+ const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1771
+ const props = buildProps(attributes, ctx, true);
1772
+ const scope = ctx.scopes?.find((s) => node.start >= s.start && node.end <= s.end);
1773
+ if (scope) props.unshift({ name: "data-fx-s", kind: "attr", literal: scope.value });
1774
+ return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1775
+ }
1369
1776
  function buildNode(node, ctx) {
1370
1777
  if (node.type === "JSXFragment") {
1371
1778
  return { kind: "fragment", children: buildChildren(node.children, ctx) };
1372
1779
  }
1780
+ if (directive(node, "if")) return buildIf(node, null, ctx);
1781
+ if (ctx.dropStyles?.has(node.start)) return { kind: "fragment", children: [] };
1373
1782
  const { tag, component } = tagInfo(node.openingElement.name, ctx);
1783
+ if (!component) return buildElement(node, ctx, []);
1374
1784
  const props = buildProps(node.openingElement.attributes, ctx);
1375
1785
  const children = buildChildren(node.children, ctx);
1376
1786
  const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
1377
- if (component) {
1378
- const load = readLoad(node.openingElement.attributes);
1379
- return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1380
- }
1381
- return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
1787
+ const load = readLoad(node.openingElement.attributes);
1788
+ return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
1382
1789
  }
1383
1790
  function buildJsxIR(node, ctx) {
1384
1791
  const ir = buildNode(node, ctx);