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