@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
@@ -261,6 +261,7 @@ var INTRINSICS = {
261
261
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
262
262
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
263
263
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
264
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
264
265
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
265
266
  // name. Still worth having: no import to write, and when a shorter form is
266
267
  // designed, the change is this line rather than a compiler pass.
@@ -271,6 +272,19 @@ var INTRINSICS = {
271
272
  // dependency analysis must not guess at.
272
273
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
273
274
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
275
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
276
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
277
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
278
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
279
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
280
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
281
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
282
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
283
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
284
+ // handle as far as reads go: the same accessor with `set` on it.
285
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
286
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
287
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
274
288
  // An environment variable, public or server-only; the browser build refuses a secret.
275
289
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
276
290
  };
@@ -448,13 +462,16 @@ function emitComponent(name, props, children, used, templates, at) {
448
462
  used.add("createComponent");
449
463
  const spreads = props.filter((p) => p.kind === "spread");
450
464
  const normal = props.filter((p) => p.kind !== "spread");
451
- const entries = normal.map(
452
- (p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
453
- );
465
+ const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
454
466
  if (children.length > 0) {
455
- entries.push(`get children() { return ${emitChildren(children, used, templates)}; }`);
467
+ bodies.push(["children", emitChildren(children, used, templates)]);
468
+ }
469
+ let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
470
+ if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
471
+ const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => (${body})`);
472
+ const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
473
+ obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
456
474
  }
457
- let obj = `{ ${entries.join(", ")} }`;
458
475
  if (spreads.length > 0) {
459
476
  used.add("mergeProps");
460
477
  obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
@@ -536,7 +553,8 @@ function emitNode(node, used, templates) {
536
553
  } else {
537
554
  used.add("spread");
538
555
  const svgFlag = node.svg ? ", isSVG: true" : "";
539
- const customFlag = node.tag.includes("-") ? ", isCustomElement: true" : "";
556
+ const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
557
+ const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
540
558
  parts.push(
541
559
  `spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
542
560
  );
@@ -589,6 +607,271 @@ function cleanTemplateText(value) {
589
607
  return out;
590
608
  }
591
609
 
610
+ // src/lower/entities.ts
611
+ var NAMED = {
612
+ quot: 34,
613
+ amp: 38,
614
+ apos: 39,
615
+ lt: 60,
616
+ gt: 62,
617
+ nbsp: 160,
618
+ iexcl: 161,
619
+ cent: 162,
620
+ pound: 163,
621
+ curren: 164,
622
+ yen: 165,
623
+ brvbar: 166,
624
+ sect: 167,
625
+ uml: 168,
626
+ copy: 169,
627
+ ordf: 170,
628
+ laquo: 171,
629
+ not: 172,
630
+ shy: 173,
631
+ reg: 174,
632
+ macr: 175,
633
+ deg: 176,
634
+ plusmn: 177,
635
+ sup2: 178,
636
+ sup3: 179,
637
+ acute: 180,
638
+ micro: 181,
639
+ para: 182,
640
+ middot: 183,
641
+ cedil: 184,
642
+ sup1: 185,
643
+ ordm: 186,
644
+ raquo: 187,
645
+ frac14: 188,
646
+ frac12: 189,
647
+ frac34: 190,
648
+ iquest: 191,
649
+ Agrave: 192,
650
+ Aacute: 193,
651
+ Acirc: 194,
652
+ Atilde: 195,
653
+ Auml: 196,
654
+ Aring: 197,
655
+ AElig: 198,
656
+ Ccedil: 199,
657
+ Egrave: 200,
658
+ Eacute: 201,
659
+ Ecirc: 202,
660
+ Euml: 203,
661
+ Igrave: 204,
662
+ Iacute: 205,
663
+ Icirc: 206,
664
+ Iuml: 207,
665
+ ETH: 208,
666
+ Ntilde: 209,
667
+ Ograve: 210,
668
+ Oacute: 211,
669
+ Ocirc: 212,
670
+ Otilde: 213,
671
+ Ouml: 214,
672
+ times: 215,
673
+ Oslash: 216,
674
+ Ugrave: 217,
675
+ Uacute: 218,
676
+ Ucirc: 219,
677
+ Uuml: 220,
678
+ Yacute: 221,
679
+ THORN: 222,
680
+ szlig: 223,
681
+ agrave: 224,
682
+ aacute: 225,
683
+ acirc: 226,
684
+ atilde: 227,
685
+ auml: 228,
686
+ aring: 229,
687
+ aelig: 230,
688
+ ccedil: 231,
689
+ egrave: 232,
690
+ eacute: 233,
691
+ ecirc: 234,
692
+ euml: 235,
693
+ igrave: 236,
694
+ iacute: 237,
695
+ icirc: 238,
696
+ iuml: 239,
697
+ eth: 240,
698
+ ntilde: 241,
699
+ ograve: 242,
700
+ oacute: 243,
701
+ ocirc: 244,
702
+ otilde: 245,
703
+ ouml: 246,
704
+ divide: 247,
705
+ oslash: 248,
706
+ ugrave: 249,
707
+ uacute: 250,
708
+ ucirc: 251,
709
+ uuml: 252,
710
+ yacute: 253,
711
+ thorn: 254,
712
+ yuml: 255,
713
+ OElig: 338,
714
+ oelig: 339,
715
+ Scaron: 352,
716
+ scaron: 353,
717
+ Yuml: 376,
718
+ fnof: 402,
719
+ circ: 710,
720
+ tilde: 732,
721
+ Alpha: 913,
722
+ Beta: 914,
723
+ Gamma: 915,
724
+ Delta: 916,
725
+ Epsilon: 917,
726
+ Zeta: 918,
727
+ Eta: 919,
728
+ Theta: 920,
729
+ Iota: 921,
730
+ Kappa: 922,
731
+ Lambda: 923,
732
+ Mu: 924,
733
+ Nu: 925,
734
+ Xi: 926,
735
+ Omicron: 927,
736
+ Pi: 928,
737
+ Rho: 929,
738
+ Sigma: 931,
739
+ Tau: 932,
740
+ Upsilon: 933,
741
+ Phi: 934,
742
+ Chi: 935,
743
+ Psi: 936,
744
+ Omega: 937,
745
+ alpha: 945,
746
+ beta: 946,
747
+ gamma: 947,
748
+ delta: 948,
749
+ epsilon: 949,
750
+ zeta: 950,
751
+ eta: 951,
752
+ theta: 952,
753
+ iota: 953,
754
+ kappa: 954,
755
+ lambda: 955,
756
+ mu: 956,
757
+ nu: 957,
758
+ xi: 958,
759
+ omicron: 959,
760
+ pi: 960,
761
+ rho: 961,
762
+ sigmaf: 962,
763
+ sigma: 963,
764
+ tau: 964,
765
+ upsilon: 965,
766
+ phi: 966,
767
+ chi: 967,
768
+ psi: 968,
769
+ omega: 969,
770
+ thetasym: 977,
771
+ upsih: 978,
772
+ piv: 982,
773
+ ensp: 8194,
774
+ emsp: 8195,
775
+ thinsp: 8201,
776
+ zwnj: 8204,
777
+ zwj: 8205,
778
+ lrm: 8206,
779
+ rlm: 8207,
780
+ ndash: 8211,
781
+ mdash: 8212,
782
+ lsquo: 8216,
783
+ rsquo: 8217,
784
+ sbquo: 8218,
785
+ ldquo: 8220,
786
+ rdquo: 8221,
787
+ bdquo: 8222,
788
+ dagger: 8224,
789
+ Dagger: 8225,
790
+ bull: 8226,
791
+ hellip: 8230,
792
+ permil: 8240,
793
+ prime: 8242,
794
+ Prime: 8243,
795
+ lsaquo: 8249,
796
+ rsaquo: 8250,
797
+ oline: 8254,
798
+ frasl: 8260,
799
+ euro: 8364,
800
+ image: 8465,
801
+ weierp: 8472,
802
+ real: 8476,
803
+ trade: 8482,
804
+ alefsym: 8501,
805
+ larr: 8592,
806
+ uarr: 8593,
807
+ rarr: 8594,
808
+ darr: 8595,
809
+ harr: 8596,
810
+ crarr: 8629,
811
+ lArr: 8656,
812
+ uArr: 8657,
813
+ rArr: 8658,
814
+ dArr: 8659,
815
+ hArr: 8660,
816
+ forall: 8704,
817
+ part: 8706,
818
+ exist: 8707,
819
+ empty: 8709,
820
+ nabla: 8711,
821
+ isin: 8712,
822
+ notin: 8713,
823
+ ni: 8715,
824
+ prod: 8719,
825
+ sum: 8721,
826
+ minus: 8722,
827
+ lowast: 8727,
828
+ radic: 8730,
829
+ prop: 8733,
830
+ infin: 8734,
831
+ ang: 8736,
832
+ and: 8743,
833
+ or: 8744,
834
+ cap: 8745,
835
+ cup: 8746,
836
+ int: 8747,
837
+ there4: 8756,
838
+ sim: 8764,
839
+ cong: 8773,
840
+ asymp: 8776,
841
+ ne: 8800,
842
+ equiv: 8801,
843
+ le: 8804,
844
+ ge: 8805,
845
+ sub: 8834,
846
+ sup: 8835,
847
+ nsub: 8836,
848
+ sube: 8838,
849
+ supe: 8839,
850
+ oplus: 8853,
851
+ otimes: 8855,
852
+ perp: 8869,
853
+ sdot: 8901,
854
+ lceil: 8968,
855
+ rceil: 8969,
856
+ lfloor: 8970,
857
+ rfloor: 8971,
858
+ lang: 9001,
859
+ rang: 9002,
860
+ loz: 9674,
861
+ spades: 9824,
862
+ clubs: 9827,
863
+ hearts: 9829,
864
+ diams: 9830
865
+ };
866
+ var REFERENCE = /&(?:#[xX]([0-9a-fA-F]{1,6})|#([0-9]{1,7})|([a-zA-Z][a-zA-Z0-9]{1,31}));/g;
867
+ function decodeEntities(value) {
868
+ if (!value.includes("&")) return value;
869
+ return value.replace(REFERENCE, (match, hex, dec, name) => {
870
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : void 0;
871
+ return code !== void 0 && code <= 1114111 ? String.fromCodePoint(code) : match;
872
+ });
873
+ }
874
+
592
875
  // src/lower/template.ts
593
876
  var DELEGATED = /* @__PURE__ */ new Set([
594
877
  "click",
@@ -607,6 +890,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
607
890
  function escapeTemplate(s) {
608
891
  return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
609
892
  }
893
+ var CONTAINER = "container";
610
894
  function capitalize(s) {
611
895
  return s.charAt(0).toUpperCase() + s.slice(1);
612
896
  }
@@ -661,10 +945,6 @@ var Lowerer = class {
661
945
  if (hasElse) i = elseIdx;
662
946
  continue;
663
947
  }
664
- if (child.attributes.some((a) => a.kind === "EachDirective")) {
665
- out.push(this.lowerEach(child));
666
- continue;
667
- }
668
948
  }
669
949
  const node = this.lowerNode(child);
670
950
  if (node) out.push(node);
@@ -678,7 +958,7 @@ var Lowerer = class {
678
958
  switch (node.kind) {
679
959
  case "Text": {
680
960
  if (node.raw) return { kind: "text", value: node.value };
681
- const cleaned = cleanTemplateText(node.value);
961
+ const cleaned = decodeEntities(cleanTemplateText(node.value));
682
962
  return cleaned ? { kind: "text", value: cleaned } : null;
683
963
  }
684
964
  case "Comment":
@@ -706,13 +986,16 @@ var Lowerer = class {
706
986
  node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
707
987
  return node;
708
988
  }
989
+ const each = this.lowerEach(el);
990
+ const children = each ? [each] : this.lowerChildren(el.children);
991
+ if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
709
992
  const at = this.tagAt(el);
710
993
  return {
711
994
  kind: "element",
712
995
  tag: el.tag,
713
996
  svg: el.namespace === "svg",
714
- props: this.lowerAttributes(el.attributes),
715
- children: this.lowerChildren(el.children),
997
+ props: this.lowerAttributes(el.attributes, true),
998
+ children,
716
999
  static: false,
717
1000
  ...at ? { at } : {}
718
1001
  };
@@ -784,10 +1067,14 @@ var Lowerer = class {
784
1067
  const at = this.tagAt(el);
785
1068
  return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
786
1069
  }
787
- /** `<el each=${items} key?>${item => …}</el>` → `<For each>{item => <el>…</el>}</For>`. */
1070
+ /**
1071
+ * `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
1072
+ * content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
1073
+ * it is. Null when the element has no `each`.
1074
+ */
788
1075
  lowerEach(el) {
789
1076
  const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
790
- if (!eachDir || eachDir.kind !== "EachDirective") return this.lowerNode(el);
1077
+ if (!eachDir || eachDir.kind !== "EachDirective") return null;
791
1078
  const each = this.hole(eachDir.hole);
792
1079
  const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
793
1080
  if (eachDir.key) {
@@ -797,12 +1084,12 @@ var Lowerer = class {
797
1084
  const arrow = this.itemArrow(el);
798
1085
  let childrenCode;
799
1086
  if (arrow) {
800
- const bodyIR = { kind: "expr", code: arrow.body, reactive: arrow.bodyReactive };
801
- const repeated = this.rebuildWithChildren(el, [bodyIR]);
802
- childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
1087
+ const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
1088
+ childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
803
1089
  } else {
804
- const repeated = this.stripAndLower(el, (a) => a.kind === "EachDirective");
805
- childrenCode = `() => ${paren(this.emit(repeated), "arrowBody")}`;
1090
+ const content = this.lowerChildren(el.children);
1091
+ const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
1092
+ childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
806
1093
  }
807
1094
  const children = [{ kind: "expr", code: childrenCode, reactive: false }];
808
1095
  const at = this.tagAt(el);
@@ -819,33 +1106,23 @@ var Lowerer = class {
819
1106
  const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
820
1107
  return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
821
1108
  }
822
- /** Same element (minus each/key) but with explicit IR children. */
823
- rebuildWithChildren(el, children) {
824
- const props = this.lowerAttributes(
825
- el.attributes.filter((a) => a.kind !== "EachDirective")
826
- );
827
- if (el.kind === "Element") {
828
- const at = this.tagAt(el);
829
- return {
830
- kind: "element",
831
- tag: el.tag,
832
- svg: el.namespace === "svg",
833
- props,
834
- children,
835
- static: false,
836
- ...at ? { at } : {}
837
- };
838
- }
839
- return { kind: "component", name: el.tag, props, children };
840
- }
841
1109
  // --- attributes ---------------------------------------------------------
842
- lowerAttributes(attrs) {
1110
+ /**
1111
+ * `element`: a DOM element, where each class:/style: value that moves is its own accessor.
1112
+ * A component gets the object as before, rebuilt whole when any value moves.
1113
+ */
1114
+ lowerAttributes(attrs, element = false) {
1115
+ let anyReactive = false;
1116
+ const entry = (name, h) => {
1117
+ anyReactive ||= h.reactive;
1118
+ if (!element || !h.reactive) return `${JSON.stringify(name)}: ${h.code}`;
1119
+ const accessor = `() => ${paren(h.code, "arrowBody")}`;
1120
+ return `${JSON.stringify(name)}: ${h.at ? markHole(accessor, h.at.line, h.at.column) : accessor}`;
1121
+ };
843
1122
  const props = [];
844
1123
  const classEntries = [];
845
- let classReactive = false;
846
1124
  let classAt;
847
1125
  const styleEntries = [];
848
- let styleReactive = false;
849
1126
  let styleAt;
850
1127
  const usePairs = [];
851
1128
  for (const a of attrs) {
@@ -885,15 +1162,13 @@ var Lowerer = class {
885
1162
  break;
886
1163
  case "ClassDirective": {
887
1164
  const h = this.hole(a.hole);
888
- classEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
889
- classReactive = classReactive || h.reactive;
1165
+ classEntries.push(entry(a.name, h));
890
1166
  classAt ??= h.at;
891
1167
  break;
892
1168
  }
893
1169
  case "StyleDirective": {
894
1170
  const h = this.hole(a.hole);
895
- styleEntries.push(`${JSON.stringify(a.name)}: ${h.code}`);
896
- styleReactive = styleReactive || h.reactive;
1171
+ styleEntries.push(entry(a.name, h));
897
1172
  styleAt ??= h.at;
898
1173
  break;
899
1174
  }
@@ -913,18 +1188,19 @@ var Lowerer = class {
913
1188
  name: "classList",
914
1189
  kind: "attr",
915
1190
  expr: `{ ${classEntries.join(", ")} }`,
916
- reactive: classReactive,
1191
+ reactive: !element && anyReactive,
917
1192
  ...classAt ? { at: classAt } : {}
918
1193
  });
919
1194
  }
920
1195
  if (styleEntries.length > 0) {
921
- props.push({
922
- name: "style",
923
- kind: "attr",
924
- expr: `{ ${styleEntries.join(", ")} }`,
925
- reactive: styleReactive,
926
- ...styleAt ? { at: styleAt } : {}
927
- });
1196
+ const entries = `{ ${styleEntries.join(", ")} }`;
1197
+ const base = props.findIndex((p) => p.name === "style");
1198
+ if (base === -1) props.push({ name: "style", kind: "attr", expr: entries, reactive: !element && anyReactive, ...styleAt ? { at: styleAt } : {} });
1199
+ else {
1200
+ const prop = props[base];
1201
+ const value = prop.expr === void 0 ? JSON.stringify(prop.literal) : prop.reactive ? `() => ${paren(prop.expr, "arrowBody")}` : prop.expr;
1202
+ props[base] = { name: "style", kind: "attr", expr: `[${value}, ${entries}]`, reactive: false, ...prop.at ? { at: prop.at } : {} };
1203
+ }
928
1204
  }
929
1205
  if (usePairs.length > 0) {
930
1206
  props.push({ name: "use", kind: "attr", expr: `[${usePairs.join(", ")}]` });
@@ -1013,7 +1289,7 @@ var Lowerer = class {
1013
1289
  return base;
1014
1290
  }
1015
1291
  if (v.kind === "static") {
1016
- base.literal = v.value;
1292
+ base.literal = decodeEntities(v.value);
1017
1293
  return base;
1018
1294
  }
1019
1295
  if (v.kind === "hole") {
@@ -1026,7 +1302,7 @@ var Lowerer = class {
1026
1302
  let reactive = false;
1027
1303
  let at;
1028
1304
  const body = v.parts.map((p) => {
1029
- if ("text" in p) return escapeTemplate(p.text);
1305
+ if ("text" in p) return escapeTemplate(decodeEntities(p.text));
1030
1306
  const h = this.hole(p.hole);
1031
1307
  reactive = reactive || h.reactive;
1032
1308
  at ??= h.at;
@@ -1,4 +1,4 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.94 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1,4 +1,4 @@
1
- /*! @fluixi/compiler v1.0.0-alpha.92 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/compiler v1.0.0-alpha.94 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/resolve/builtins.ts
4
4
  var DOM_CONTROL_FLOW = [
@@ -124,6 +124,7 @@ var INTRINSICS = {
124
124
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
125
125
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
126
126
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
127
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
127
128
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
128
129
  // name. Still worth having: no import to write, and when a shorter form is
129
130
  // designed, the change is this line rather than a compiler pass.
@@ -134,9 +135,27 @@ var INTRINSICS = {
134
135
  // dependency analysis must not guess at.
135
136
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
136
137
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
138
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
139
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
140
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
141
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
142
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
143
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
144
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
145
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
146
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
147
+ // handle as far as reads go: the same accessor with `set` on it.
148
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
149
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
150
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
137
151
  // An environment variable, public or server-only; the browser build refuses a secret.
138
152
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
139
153
  };
154
+ var TAG_INTRINSICS = {
155
+ $html: { export: "html", module: "@fluixi/core" },
156
+ $svg: { export: "svg", module: "@fluixi/core" },
157
+ $css: { export: "css", module: "@fluixi/core" }
158
+ };
140
159
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
141
160
 
142
161
  // src/resolve/component-globals.ts
@@ -202,7 +221,7 @@ function componentGlobals(rules, options = {}) {
202
221
  resolved.set(name, target);
203
222
  }
204
223
  }
205
- const usableIntrinsics = intrinsics ? Object.entries(INTRINSICS).filter(([, i]) => {
224
+ const usableIntrinsics = intrinsics ? Object.entries({ ...INTRINSICS, ...TAG_INTRINSICS }).filter(([, i]) => {
206
225
  const pkg = packageOf(i.module);
207
226
  return !available || !pkg || available.has(pkg);
208
227
  }) : [];
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import { createComponentResolver, } from './component-resolver.js';
20
20
  import { builtinComponentMap } from './builtins.js';
21
- import { INTRINSICS } from '../analyze/intrinsics.js';
21
+ import { INTRINSICS, TAG_INTRINSICS } from '../analyze/intrinsics.js';
22
22
  /**
23
23
  * Names `lib.dom` already declares as globals.
24
24
  *
@@ -100,7 +100,7 @@ export function componentGlobals(rules, options = {}) {
100
100
  // Only the intrinsics whose package the project has: `$env` comes from `@fluixi/start`, and
101
101
  // an app without it would otherwise get a declaration that fails to resolve.
102
102
  const usableIntrinsics = intrinsics
103
- ? Object.entries(INTRINSICS).filter(([, i]) => {
103
+ ? Object.entries({ ...INTRINSICS, ...TAG_INTRINSICS }).filter(([, i]) => {
104
104
  const pkg = packageOf(i.module);
105
105
  return !available || !pkg || available.has(pkg);
106
106
  })
@@ -98,6 +98,7 @@ var INTRINSICS = {
98
98
  $effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
99
99
  $store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
100
100
  $resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
101
+ $cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
101
102
  // No short runtime form for these yet, so the intrinsic stands for the `create*`
102
103
  // name. Still worth having: no import to write, and when a shorter form is
103
104
  // designed, the change is this line rather than a compiler pass.
@@ -108,9 +109,27 @@ var INTRINSICS = {
108
109
  // dependency analysis must not guess at.
109
110
  $untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
110
111
  $untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
112
+ // Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
113
+ // no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
114
+ $batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
115
+ $cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
116
+ $root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
117
+ $context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
118
+ $renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
119
+ $computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
120
+ // Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
121
+ // handle as far as reads go: the same accessor with `set` on it.
122
+ $storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
123
+ $asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
124
+ $persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
111
125
  // An environment variable, public or server-only; the browser build refuses a secret.
112
126
  $env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
113
127
  };
128
+ var TAG_INTRINSICS = {
129
+ $html: { export: "html", module: "@fluixi/core" },
130
+ $svg: { export: "svg", module: "@fluixi/core" },
131
+ $css: { export: "css", module: "@fluixi/core" }
132
+ };
114
133
  var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
115
134
 
116
135
  // src/resolve/component-globals.ts
@@ -176,7 +195,7 @@ function componentGlobals(rules, options = {}) {
176
195
  resolved.set(name, target);
177
196
  }
178
197
  }
179
- const usableIntrinsics = intrinsics ? Object.entries(INTRINSICS).filter(([, i]) => {
198
+ const usableIntrinsics = intrinsics ? Object.entries({ ...INTRINSICS, ...TAG_INTRINSICS }).filter(([, i]) => {
180
199
  const pkg = packageOf(i.module);
181
200
  return !available || !pkg || available.has(pkg);
182
201
  }) : [];