@geajs/vite-plugin 1.3.0 → 1.4.0

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.
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import _generate from "@babel/generator";
3
3
  import * as t from "@babel/types";
4
4
  import { parse } from "@babel/parser";
5
5
  import { id, js, jsExpr, jsImport } from "eszter";
6
- import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
6
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
7
7
  import { dirname, relative, resolve } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
  //#region src/utils/babel-interop.ts
@@ -84,15 +84,27 @@ function parseSource(code) {
84
84
  name = t.isIdentifier(varDecl.id) ? varDecl.id.name : null;
85
85
  returnsJSX = nodeReturnsJSX(init.body);
86
86
  }
87
- if (name && returnsJSX) functionalComponentInfo = { name };
87
+ if (name && returnsJSX) functionalComponentInfo = {
88
+ name,
89
+ kind: "default"
90
+ };
88
91
  },
89
92
  ExportNamedDeclaration(path) {
90
93
  const decl = path.node.declaration;
91
94
  if (!decl) return;
92
- if (t.isFunctionDeclaration(decl) && decl.id) throwIfReturnsJSX(decl.id.name, decl.body);
95
+ const registerNamed = (name) => {
96
+ if (functionalComponentInfo) return;
97
+ functionalComponentInfo = {
98
+ name,
99
+ kind: "named"
100
+ };
101
+ };
102
+ if (t.isFunctionDeclaration(decl) && decl.id && nodeReturnsJSX(decl.body)) registerNamed(decl.id.name);
93
103
  else if (t.isVariableDeclaration(decl)) for (const declarator of decl.declarations) {
94
104
  if (!t.isIdentifier(declarator.id) || !declarator.init) continue;
95
- if (t.isArrowFunctionExpression(declarator.init) || t.isFunctionExpression(declarator.init)) throwIfReturnsJSX(declarator.id.name, declarator.init.body);
105
+ if (t.isArrowFunctionExpression(declarator.init) || t.isFunctionExpression(declarator.init)) {
106
+ if (nodeReturnsJSX(declarator.init.body)) registerNamed(declarator.id.name);
107
+ }
96
108
  }
97
109
  },
98
110
  JSXElement() {
@@ -129,48 +141,44 @@ function bodyReturnsJSX(block) {
129
141
  const ret = block.body.find((s) => t.isReturnStatement(s) && s.argument != null);
130
142
  return !!ret && nodeReturnsJSX(ret.argument);
131
143
  }
132
- /** Throw a compile error if a named export returns JSX. */
133
- function throwIfReturnsJSX(name, body) {
134
- if (nodeReturnsJSX(body)) {
135
- const err = /* @__PURE__ */ new Error(`[gea] Named JSX component export '${name}' is not supported. Use 'export default' or convert to a class extending Component. Only one component per file is allowed.`);
136
- err.__geaCompileError = true;
137
- throw err;
138
- }
139
- }
140
144
  //#endregion
141
145
  //#region src/preprocess/functional-to-class.ts
142
146
  /**
143
- * Converts a functional component default export into a class-based Gea component.
147
+ * Converts a functional component into a class-based Gea component.
144
148
  *
145
- * Supports:
149
+ * Default-export variants:
146
150
  * - `export default function Foo(props) { return <div/> }`
147
151
  * - `export default (props) => <div/>`
148
152
  * - `const Foo = (props) => <div/>; export default Foo`
149
153
  *
150
- * The result is:
151
- * ```
152
- * class Foo extends Component { template(props) { return <div/> } }
153
- * export default Foo
154
- * ```
154
+ * Named-export variants (info.kind === 'named'):
155
+ * - `export function Foo(props) { return <div/> }`
156
+ * - `export const Foo = (props) => <div/>`
157
+ *
158
+ * The result preserves the export shape:
159
+ * - default → `class Foo extends Component { template(props) { ... } }
160
+ * export default Foo`
161
+ * - named → `export class Foo extends Component { template(props) { ... } }`
155
162
  *
156
163
  * Mutates the AST in place.
157
164
  */
158
165
  function convertFunctionalToClass(ast, info, imports) {
159
166
  const name = info.name;
167
+ const kind = info.kind ?? "default";
160
168
  let params = [t.identifier("props")];
161
169
  let templateBody = [];
162
170
  let removeVarDeclPath = null;
163
171
  let exportPath = null;
164
- traverse(ast, { ExportDefaultDeclaration(path) {
172
+ const extractFunction = (fn) => {
173
+ if (fn.params.length > 0) params = fn.params.map((p) => t.cloneNode(p));
174
+ if (t.isBlockStatement(fn.body)) {
175
+ const returnIdx = fn.body.body.findIndex((s) => t.isReturnStatement(s) && s.argument);
176
+ if (returnIdx >= 0) templateBody = fn.body.body.slice(0, returnIdx + 1).map((s) => t.cloneNode(s));
177
+ } else templateBody = [t.returnStatement(t.cloneNode(fn.body))];
178
+ };
179
+ if (kind === "default") traverse(ast, { ExportDefaultDeclaration(path) {
165
180
  exportPath = path;
166
181
  const decl = path.node.declaration;
167
- const extractFunction = (fn) => {
168
- if (fn.params.length > 0) params = fn.params.map((p) => t.cloneNode(p));
169
- if (t.isBlockStatement(fn.body)) {
170
- const returnIdx = fn.body.body.findIndex((s) => t.isReturnStatement(s) && s.argument);
171
- if (returnIdx >= 0) templateBody = fn.body.body.slice(0, returnIdx + 1).map((s) => t.cloneNode(s));
172
- } else templateBody = [t.returnStatement(t.cloneNode(fn.body))];
173
- };
174
182
  if (t.isFunctionDeclaration(decl)) extractFunction(decl);
175
183
  else if (t.isArrowFunctionExpression(decl)) extractFunction(decl);
176
184
  else if (t.isIdentifier(decl)) {
@@ -184,6 +192,23 @@ function convertFunctionalToClass(ast, info, imports) {
184
192
  }
185
193
  path.stop();
186
194
  } });
195
+ else traverse(ast, { ExportNamedDeclaration(path) {
196
+ const decl = path.node.declaration;
197
+ if (!decl) return;
198
+ if (t.isFunctionDeclaration(decl) && decl.id?.name === name) {
199
+ exportPath = path;
200
+ extractFunction(decl);
201
+ path.stop();
202
+ } else if (t.isVariableDeclaration(decl)) for (const declarator of decl.declarations) {
203
+ if (!t.isIdentifier(declarator.id, { name }) || !declarator.init) continue;
204
+ if (t.isArrowFunctionExpression(declarator.init) || t.isFunctionExpression(declarator.init)) {
205
+ exportPath = path;
206
+ extractFunction(declarator.init);
207
+ path.stop();
208
+ return;
209
+ }
210
+ }
211
+ } });
187
212
  if (templateBody.length === 0 || !exportPath) return;
188
213
  const firstParam = params[0];
189
214
  const firstStmt = templateBody[0];
@@ -200,7 +225,7 @@ function convertFunctionalToClass(ast, info, imports) {
200
225
  if (removeVarDeclPath) removeVarDeclPath.remove();
201
226
  const program = ast.program;
202
227
  const idx = program.body.indexOf(exportPath.node);
203
- if (idx >= 0) program.body[idx] = t.exportDefaultDeclaration(classDecl);
228
+ if (idx >= 0) program.body[idx] = kind === "named" ? t.exportNamedDeclaration(classDecl, []) : t.exportDefaultDeclaration(classDecl);
204
229
  }
205
230
  function ensureComponentImport(ast, imports) {
206
231
  if (imports.get("Component")) return;
@@ -788,6 +813,54 @@ function tryExtractPathAndRoot(expr) {
788
813
  return null;
789
814
  }
790
815
  //#endregion
816
+ //#region src/closure-codegen/generator/generator-jsx-helpers.ts
817
+ /** True for JSX elements, fragments, null, undefined, or string literals (things that can be rendered in a branch). */
818
+ function isJsxOrNullish(n) {
819
+ if (t.isJSXElement(n) || t.isJSXFragment(n)) return true;
820
+ if (t.isNullLiteral(n)) return true;
821
+ if (t.isIdentifier(n, { name: "undefined" })) return true;
822
+ return false;
823
+ }
824
+ /** True when `n` is `<expr>.map(arrow)` where the arrow's body returns JSX
825
+ * (directly or via a `return` in a BlockStatement). Used by the `&&` arm
826
+ * to promote `cond && xs.map(x => <JSX/>)` to a conditional slot whose
827
+ * truthy branch is a keyed-list. */
828
+ function isMapWithJsxBody(n) {
829
+ if (!t.isCallExpression(n) && !t.isOptionalCallExpression(n)) return false;
830
+ const callee = n.callee;
831
+ if (!t.isMemberExpression(callee) && !t.isOptionalMemberExpression(callee)) return false;
832
+ if (callee.computed) return false;
833
+ if (!t.isIdentifier(callee.property, { name: "map" })) return false;
834
+ const arg = n.arguments[0];
835
+ if (!arg) return false;
836
+ if (!t.isArrowFunctionExpression(arg) && !t.isFunctionExpression(arg)) return false;
837
+ const body = arg.body;
838
+ if (t.isJSXElement(body) || t.isJSXFragment(body)) return true;
839
+ if (t.isBlockStatement(body)) {
840
+ const ret = body.body.find((s) => t.isReturnStatement(s));
841
+ if (ret && ret.argument && (t.isJSXElement(ret.argument) || t.isJSXFragment(ret.argument))) return true;
842
+ }
843
+ return false;
844
+ }
845
+ /**
846
+ * Check if a class declaration has a `template()` method and return it.
847
+ * Returns null if the class does not have a template method (non-component).
848
+ */
849
+ function findTemplateMethod(classDecl) {
850
+ for (const member of classDecl.body.body) if (t.isClassMethod(member) && t.isIdentifier(member.key, { name: "template" }) && !member.computed && !member.static) return member;
851
+ return null;
852
+ }
853
+ /**
854
+ * Extract the JSX root from a template() method. The method body is expected to
855
+ * be `template() { return <jsx />; }` — possibly with prop destructuring.
856
+ */
857
+ function extractTemplateJsx(templateMethod) {
858
+ for (const stmt of templateMethod.body.body) if (t.isReturnStatement(stmt) && stmt.argument) {
859
+ if (t.isJSXElement(stmt.argument) || t.isJSXFragment(stmt.argument)) return stmt.argument;
860
+ }
861
+ return null;
862
+ }
863
+ //#endregion
791
864
  //#region src/closure-codegen/emit/emit-conditional.ts
792
865
  function emitConditionalSlot(slot, stmts, ctx) {
793
866
  const anchorId = t.identifier("anchor" + slot.index);
@@ -828,6 +901,10 @@ function buildBranchFn(branchExpr, ctx) {
828
901
  const block = compileJsxToBlock(branchExpr, ctx);
829
902
  return t.arrowFunctionExpression([t.identifier("d")], block);
830
903
  }
904
+ if (isNestableConditionalExpression(branchExpr)) {
905
+ const block = compileJsxToBlock(t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), [t.jsxExpressionContainer(branchExpr)]), ctx);
906
+ return t.arrowFunctionExpression([t.identifier("d")], block);
907
+ }
831
908
  if (t.isCallExpression(branchExpr) && branchExpr.arguments.length === 0 && branchExpr.callee.__geaHoistedIIFE && t.isBlockStatement(branchExpr.callee.body)) {
832
909
  const block = branchExpr.callee.body;
833
910
  const last = block.body[block.body.length - 1];
@@ -852,6 +929,11 @@ function buildBranchFn(branchExpr, ctx) {
852
929
  if (t.isCallExpression(substituted) && t.isMemberExpression(substituted.callee) && !substituted.callee.computed && t.isIdentifier(substituted.callee.property, { name: "map" }) && substituted.arguments.length >= 1 && (t.isArrowFunctionExpression(substituted.arguments[0]) || t.isFunctionExpression(substituted.arguments[0]))) return buildMapBranchFn(substituted, ctx);
853
930
  return t.arrowFunctionExpression([t.identifier("d")], t.callExpression(t.memberExpression(t.identifier("document"), t.identifier("createComment")), [t.stringLiteral("")]));
854
931
  }
932
+ function isNestableConditionalExpression(node) {
933
+ if (t.isConditionalExpression(node)) return isJsxOrNullish(node.consequent) || isJsxOrNullish(node.alternate);
934
+ if (t.isLogicalExpression(node) && node.operator === "&&") return isJsxOrNullish(node.right);
935
+ return false;
936
+ }
855
937
  /**
856
938
  * Build a branch fn for a bare `xs.map(item => <jsx/>)` expression. Creates a
857
939
  * `<span style="display:contents">` wrapper, inserts a comment anchor inside,
@@ -1636,6 +1718,7 @@ function createItemBodyReferencesItemInReactiveGetter(fn, itemName) {
1636
1718
  "reactiveBoolAttr",
1637
1719
  "reactiveClass",
1638
1720
  "reactiveStyle",
1721
+ "reactiveStyleProp",
1639
1722
  "reactiveValue",
1640
1723
  "reactiveValueRead"
1641
1724
  ]);
@@ -2164,6 +2247,66 @@ const INLINE_PROP_LIST_SOURCE = `{
2164
2247
  }
2165
2248
  let __kl_prev = __kl_first;
2166
2249
  const __kl_reconcile = (arr, changes) => {
2250
+ if (!Array.isArray(arr)) arr = [];
2251
+ if (changes && changes.length > 0) {
2252
+ let aipuPatchable = true;
2253
+ for (let i = 0; i < changes.length; i++) {
2254
+ const change = changes[i];
2255
+ const idx = change.arix;
2256
+ if (!change.aipu || idx < 0 || idx >= __kl_entries.length || (!change.itemDirty && __kl_entries[idx].key !== __kl_key(change.newValue, idx))) {
2257
+ aipuPatchable = false;
2258
+ break;
2259
+ }
2260
+ }
2261
+ if (aipuPatchable) {
2262
+ for (let i = 0; i < changes.length; i++) {
2263
+ const idx = changes[i].arix;
2264
+ let superseded = false;
2265
+ for (let j = i + 1; j < changes.length; j++) {
2266
+ if (changes[j].arix === idx) {
2267
+ superseded = true;
2268
+ break;
2269
+ }
2270
+ }
2271
+ if (superseded) continue;
2272
+ const item = changes[i].newValue;
2273
+ __kl_patch(__kl_entries[idx], item, idx);
2274
+ if (item && typeof item === "object") {
2275
+ item[GEA_DIRTY] = false;
2276
+ item[GEA_DIRTY_PROPS]?.clear();
2277
+ }
2278
+ __kl_entries[idx].item = __kl_unwrap(item);
2279
+ if (__kl_prev && __kl_prev.length === __kl_entries.length) __kl_prev[idx] = __kl_unwrap(item);
2280
+ }
2281
+ return;
2282
+ }
2283
+ let dirtyOnly = true;
2284
+ for (let i = 0; i < changes.length; i++) {
2285
+ const change = changes[i];
2286
+ if (change.aipu || change.type === "append" || change.type === "remove" || change.type === "delete" || change.type === "reorder") {
2287
+ dirtyOnly = false;
2288
+ break;
2289
+ }
2290
+ }
2291
+ if (dirtyOnly) {
2292
+ const raw = arr[GEA_PROXY_RAW] || arr;
2293
+ if (__kl_entries.length === arr.length) {
2294
+ let patched = false;
2295
+ for (let i = 0; i < raw.length; i++) {
2296
+ const item = raw[i];
2297
+ if (item && typeof item === "object" && item[GEA_DIRTY]) {
2298
+ __kl_patch(__kl_entries[i], item, i);
2299
+ item[GEA_DIRTY] = false;
2300
+ item[GEA_DIRTY_PROPS]?.clear();
2301
+ __kl_entries[i].item = item;
2302
+ patched = true;
2303
+ }
2304
+ }
2305
+ if (patched) return;
2306
+ }
2307
+ }
2308
+ }
2309
+ const raw = arr[GEA_PROXY_RAW] || arr;
2167
2310
  if (arr === __kl_prev && __kl_entries.length === arr.length) {
2168
2311
  let structural = false;
2169
2312
  let aipuOnly = changes && changes.length > 0;
@@ -2202,7 +2345,6 @@ const INLINE_PROP_LIST_SOURCE = `{
2202
2345
  }
2203
2346
  }
2204
2347
  if (!structural) {
2205
- const raw = arr[GEA_PROXY_RAW] || arr;
2206
2348
  for (let i = 0; i < raw.length; i++) {
2207
2349
  const item = raw[i];
2208
2350
  if (item && typeof item === "object" && item[GEA_DIRTY]) {
@@ -2347,7 +2489,14 @@ const INLINE_PROP_LIST_SOURCE = `{
2347
2489
  if (oldLen > 0 && newLen > 0 && __kl_container.childNodes.length === oldLen + (__kl_anchor ? 1 : 0)) {
2348
2490
  let disjoint = true;
2349
2491
  for (let i = 0; i < newLen; i++) {
2350
- if (__kl_byKey.has(newKeys[i])) {
2492
+ let existing = null;
2493
+ for (let j = 0; j < oldLen; j++) {
2494
+ if (__kl_entries[j].key === newKeys[i]) {
2495
+ existing = __kl_entries[j];
2496
+ break;
2497
+ }
2498
+ }
2499
+ if (existing) {
2351
2500
  disjoint = false;
2352
2501
  break;
2353
2502
  }
@@ -2375,7 +2524,13 @@ const INLINE_PROP_LIST_SOURCE = `{
2375
2524
  const seenOld = new Array(oldLen).fill(false);
2376
2525
  const nextEntries = new Array(newLen);
2377
2526
  for (let i = 0; i < newLen; i++) {
2378
- const entry = __kl_byKey.get(newKeys[i]);
2527
+ let entry = null;
2528
+ for (let j = 0; j < oldLen; j++) {
2529
+ if (__kl_entries[j].key === newKeys[i]) {
2530
+ entry = __kl_entries[j];
2531
+ break;
2532
+ }
2533
+ }
2379
2534
  if (entry) {
2380
2535
  const oldIdx = entry._i;
2381
2536
  seenOld[oldIdx] = true;
@@ -2404,7 +2559,7 @@ const INLINE_PROP_LIST_SOURCE = `{
2404
2559
 
2405
2560
  if (__kl_root && typeof __kl_root.observe === "function") {
2406
2561
  const off = __kl_root.observe(__PROP__, (_value, changes) => {
2407
- __kl_reconcile(__kl_resolve(), changes);
2562
+ __kl_reconcile(_value, changes);
2408
2563
  });
2409
2564
  d.add(off);
2410
2565
  }
@@ -2429,6 +2584,7 @@ const INLINE_PROP_LIST_COMPACT_ANCHORLESS_BLOCK = parse(`{
2429
2584
  };
2430
2585
  let __kl_prev = __kl_resolve();
2431
2586
  let __kl_reconcile = (arr, changes) => {
2587
+ if (!Array.isArray(arr)) arr = [];
2432
2588
  if (arr.length === 0) {
2433
2589
  __kl_prev = arr;
2434
2590
  return;
@@ -2475,6 +2631,65 @@ const INLINE_PROP_LIST_COMPACT_ANCHORLESS_BLOCK = parse(`{
2475
2631
  __kl_entries = nextEntries;
2476
2632
  };
2477
2633
  const __kl_real_reconcile = (arr, changes) => {
2634
+ if (!Array.isArray(arr)) arr = [];
2635
+ if (changes && changes.length > 0) {
2636
+ let aipuPatchable = true;
2637
+ for (let i = 0; i < changes.length; i++) {
2638
+ const change = changes[i];
2639
+ const idx = change.arix;
2640
+ if (!change.aipu || idx < 0 || idx >= __kl_entries.length || (!change.itemDirty && __kl_entries[idx].key !== change.newValue.id)) {
2641
+ aipuPatchable = false;
2642
+ break;
2643
+ }
2644
+ }
2645
+ if (aipuPatchable) {
2646
+ for (let i = 0; i < changes.length; i++) {
2647
+ const idx = changes[i].arix;
2648
+ let superseded = false;
2649
+ for (let j = i + 1; j < changes.length; j++) {
2650
+ if (changes[j].arix === idx) {
2651
+ superseded = true;
2652
+ break;
2653
+ }
2654
+ }
2655
+ if (superseded) continue;
2656
+ const item = changes[i].newValue;
2657
+ __kl_patch(__kl_entries[idx], item, idx);
2658
+ if (item && typeof item === "object") {
2659
+ item[GEA_DIRTY] = false;
2660
+ item[GEA_DIRTY_PROPS]?.clear();
2661
+ }
2662
+ __kl_entries[idx].item = __kl_raw(item);
2663
+ if (__kl_prev && __kl_prev.length === __kl_entries.length) __kl_prev[idx] = __kl_raw(item);
2664
+ }
2665
+ return;
2666
+ }
2667
+ let dirtyOnly = true;
2668
+ for (let i = 0; i < changes.length; i++) {
2669
+ const change = changes[i];
2670
+ if (change.aipu || change.type === "append" || change.type === "remove" || change.type === "delete" || change.type === "reorder") {
2671
+ dirtyOnly = false;
2672
+ break;
2673
+ }
2674
+ }
2675
+ if (dirtyOnly) {
2676
+ const raw = __kl_raw(arr);
2677
+ if (__kl_entries.length === arr.length) {
2678
+ let patched = false;
2679
+ for (let i = 0; i < raw.length; i++) {
2680
+ const item = raw[i];
2681
+ if (item && typeof item === "object" && item[GEA_DIRTY]) {
2682
+ __kl_patch(__kl_entries[i], item, i);
2683
+ item[GEA_DIRTY] = false;
2684
+ item[GEA_DIRTY_PROPS]?.clear();
2685
+ __kl_entries[i].item = item;
2686
+ patched = true;
2687
+ }
2688
+ }
2689
+ if (patched) return;
2690
+ }
2691
+ }
2692
+ }
2478
2693
  const raw = __kl_raw(arr);
2479
2694
  if (arr === __kl_prev && __kl_entries.length === arr.length) {
2480
2695
  let structural = false;
@@ -2577,7 +2792,7 @@ const INLINE_PROP_LIST_COMPACT_ANCHORLESS_BLOCK = parse(`{
2577
2792
 
2578
2793
  if (__kl_root && typeof __kl_root.observe === "function") {
2579
2794
  const off = __kl_root.observe(__PROP__, (_value, changes) => {
2580
- __kl_reconcile(__kl_resolve(), changes);
2795
+ __kl_reconcile(_value, changes);
2581
2796
  });
2582
2797
  d.add(off);
2583
2798
  }
@@ -2625,6 +2840,65 @@ const INLINE_PROP_LIST_COMPONENT_SOURCE = `{
2625
2840
  }
2626
2841
  let __kl_prev = __kl_first;
2627
2842
  const __kl_reconcile = (arr, changes) => {
2843
+ if (!Array.isArray(arr)) arr = [];
2844
+ if (changes && changes.length > 0) {
2845
+ let aipuPatchable = true;
2846
+ for (let i = 0; i < changes.length; i++) {
2847
+ const change = changes[i];
2848
+ const idx = change.arix;
2849
+ if (!change.aipu || idx < 0 || idx >= __kl_entries.length || (!change.itemDirty && __kl_entries[idx].key !== __kl_key(change.newValue, idx))) {
2850
+ aipuPatchable = false;
2851
+ break;
2852
+ }
2853
+ }
2854
+ if (aipuPatchable) {
2855
+ for (let i = 0; i < changes.length; i++) {
2856
+ const idx = changes[i].arix;
2857
+ let superseded = false;
2858
+ for (let j = i + 1; j < changes.length; j++) {
2859
+ if (changes[j].arix === idx) {
2860
+ superseded = true;
2861
+ break;
2862
+ }
2863
+ }
2864
+ if (superseded) continue;
2865
+ const item = changes[i].newValue;
2866
+ __kl_patch(__kl_entries[idx], item, idx);
2867
+ if (item && typeof item === "object") {
2868
+ item[GEA_DIRTY] = false;
2869
+ item[GEA_DIRTY_PROPS]?.clear();
2870
+ }
2871
+ __kl_entries[idx].item = __kl_raw(item);
2872
+ if (__kl_prev && __kl_prev.length === __kl_entries.length) __kl_prev[idx] = __kl_raw(item);
2873
+ }
2874
+ return;
2875
+ }
2876
+ let dirtyOnly = true;
2877
+ for (let i = 0; i < changes.length; i++) {
2878
+ const change = changes[i];
2879
+ if (change.aipu || change.type === "append" || change.type === "remove" || change.type === "delete" || change.type === "reorder") {
2880
+ dirtyOnly = false;
2881
+ break;
2882
+ }
2883
+ }
2884
+ if (dirtyOnly) {
2885
+ const raw = __kl_raw(arr);
2886
+ if (__kl_entries.length === arr.length) {
2887
+ let patched = false;
2888
+ for (let i = 0; i < raw.length; i++) {
2889
+ const item = raw[i];
2890
+ if (item && typeof item === "object" && item[GEA_DIRTY]) {
2891
+ __kl_patch(__kl_entries[i], item, i);
2892
+ item[GEA_DIRTY] = false;
2893
+ item[GEA_DIRTY_PROPS]?.clear();
2894
+ __kl_entries[i].item = item;
2895
+ patched = true;
2896
+ }
2897
+ }
2898
+ if (patched) return;
2899
+ }
2900
+ }
2901
+ }
2628
2902
  const raw = __kl_raw(arr);
2629
2903
  if (arr === __kl_prev && __kl_entries.length === arr.length) {
2630
2904
  let structural = false;
@@ -2689,7 +2963,7 @@ const INLINE_PROP_LIST_COMPONENT_SOURCE = `{
2689
2963
  };
2690
2964
  if (__kl_root && typeof __kl_root.observe === "function") {
2691
2965
  const off = __kl_root.observe(__PROP__, (_value, changes) => {
2692
- __kl_reconcile(__kl_resolve(), changes);
2966
+ __kl_reconcile(_value, changes);
2693
2967
  });
2694
2968
  d.add(off);
2695
2969
  }
@@ -2703,11 +2977,11 @@ const INLINE_PROP_LIST_COMPONENT_ANCHORLESS_BLOCK = parse(INLINE_PROP_LIST_COMPO
2703
2977
  sourceType: "module",
2704
2978
  plugins: ["optionalChaining"]
2705
2979
  }).program.body[0];
2706
- const INLINE_PROP_LIST_COMPONENT_ID_BLOCK = parse(INLINE_PROP_LIST_COMPONENT_SOURCE.replace(" const __kl_key = __KEY__;\n", "").replace("__kl_byKey.get(__kl_key(arr[i], i))", "__kl_byKey.get(arr[i].id)"), {
2980
+ const INLINE_PROP_LIST_COMPONENT_ID_BLOCK = parse(INLINE_PROP_LIST_COMPONENT_SOURCE.replace(" const __kl_key = __KEY__;\n", "").replace("__kl_byKey.get(__kl_key(arr[i], i))", "__kl_byKey.get(arr[i].id)").replaceAll("__kl_key(change.newValue, idx)", "change.newValue.id"), {
2707
2981
  sourceType: "module",
2708
2982
  plugins: ["optionalChaining"]
2709
2983
  }).program.body[0];
2710
- const INLINE_PROP_LIST_COMPONENT_ID_ANCHORLESS_BLOCK = parse(INLINE_PROP_LIST_COMPONENT_ANCHORLESS_SOURCE.replace(" const __kl_key = __KEY__;\n", "").replace("__kl_byKey.get(__kl_key(arr[i], i))", "__kl_byKey.get(arr[i].id)"), {
2984
+ const INLINE_PROP_LIST_COMPONENT_ID_ANCHORLESS_BLOCK = parse(INLINE_PROP_LIST_COMPONENT_ANCHORLESS_SOURCE.replace(" const __kl_key = __KEY__;\n", "").replace("__kl_byKey.get(__kl_key(arr[i], i))", "__kl_byKey.get(arr[i].id)").replaceAll("__kl_key(change.newValue, idx)", "change.newValue.id"), {
2711
2985
  sourceType: "module",
2712
2986
  plugins: ["optionalChaining"]
2713
2987
  }).program.body[0];
@@ -3405,54 +3679,6 @@ function normalizeEventAttrName(name) {
3405
3679
  return toGeaEventType(name);
3406
3680
  }
3407
3681
  //#endregion
3408
- //#region src/closure-codegen/generator/generator-jsx-helpers.ts
3409
- /** True for JSX elements, fragments, null, undefined, or string literals (things that can be rendered in a branch). */
3410
- function isJsxOrNullish(n) {
3411
- if (t.isJSXElement(n) || t.isJSXFragment(n)) return true;
3412
- if (t.isNullLiteral(n)) return true;
3413
- if (t.isIdentifier(n, { name: "undefined" })) return true;
3414
- return false;
3415
- }
3416
- /** True when `n` is `<expr>.map(arrow)` where the arrow's body returns JSX
3417
- * (directly or via a `return` in a BlockStatement). Used by the `&&` arm
3418
- * to promote `cond && xs.map(x => <JSX/>)` to a conditional slot whose
3419
- * truthy branch is a keyed-list. */
3420
- function isMapWithJsxBody(n) {
3421
- if (!t.isCallExpression(n) && !t.isOptionalCallExpression(n)) return false;
3422
- const callee = n.callee;
3423
- if (!t.isMemberExpression(callee) && !t.isOptionalMemberExpression(callee)) return false;
3424
- if (callee.computed) return false;
3425
- if (!t.isIdentifier(callee.property, { name: "map" })) return false;
3426
- const arg = n.arguments[0];
3427
- if (!arg) return false;
3428
- if (!t.isArrowFunctionExpression(arg) && !t.isFunctionExpression(arg)) return false;
3429
- const body = arg.body;
3430
- if (t.isJSXElement(body) || t.isJSXFragment(body)) return true;
3431
- if (t.isBlockStatement(body)) {
3432
- const ret = body.body.find((s) => t.isReturnStatement(s));
3433
- if (ret && ret.argument && (t.isJSXElement(ret.argument) || t.isJSXFragment(ret.argument))) return true;
3434
- }
3435
- return false;
3436
- }
3437
- /**
3438
- * Check if a class declaration has a `template()` method and return it.
3439
- * Returns null if the class does not have a template method (non-component).
3440
- */
3441
- function findTemplateMethod(classDecl) {
3442
- for (const member of classDecl.body.body) if (t.isClassMethod(member) && t.isIdentifier(member.key, { name: "template" }) && !member.computed && !member.static) return member;
3443
- return null;
3444
- }
3445
- /**
3446
- * Extract the JSX root from a template() method. The method body is expected to
3447
- * be `template() { return <jsx />; }` — possibly with prop destructuring.
3448
- */
3449
- function extractTemplateJsx(templateMethod) {
3450
- for (const stmt of templateMethod.body.body) if (t.isReturnStatement(stmt) && stmt.argument) {
3451
- if (t.isJSXElement(stmt.argument) || t.isJSXFragment(stmt.argument)) return stmt.argument;
3452
- }
3453
- return null;
3454
- }
3455
- //#endregion
3456
3682
  //#region src/closure-codegen/generator/walk.ts
3457
3683
  const OPTIONAL_TABLE_END_TAGS = new Set([
3458
3684
  "colgroup",
@@ -3910,13 +4136,31 @@ function emitSlot(slot, stmts, ctx) {
3910
4136
  pathOrGetter.value
3911
4137
  ])));
3912
4138
  } else if (slot.kind === "style") {
3913
- ctx.importsNeeded.add("reactiveStyle");
3914
- stmts.push(t.expressionStatement(t.callExpression(t.identifier("reactiveStyle"), [
3915
- elId,
3916
- t.identifier("d"),
3917
- ctx.reactiveRoot,
3918
- pathOrGetter.value
3919
- ])));
4139
+ const styleExpr = slot.expr;
4140
+ const styleProps = t.isObjectExpression(styleExpr) && styleExpr.properties.length > 0 ? styleExpr.properties : null;
4141
+ if (styleProps !== null && styleProps.every((p) => t.isObjectProperty(p) && !p.computed && (t.isIdentifier(p.key) || t.isStringLiteral(p.key)))) {
4142
+ ctx.importsNeeded.add("reactiveStyleProp");
4143
+ for (const p of styleProps) {
4144
+ const keyName = t.isIdentifier(p.key) ? p.key.name : p.key.value;
4145
+ const kebabKey = String(keyName).replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
4146
+ const valSource = expressionToPathOrGetter(p.value, ctx);
4147
+ stmts.push(t.expressionStatement(t.callExpression(t.identifier("reactiveStyleProp"), [
4148
+ elId,
4149
+ t.identifier("d"),
4150
+ ctx.reactiveRoot,
4151
+ t.stringLiteral(kebabKey),
4152
+ valSource.value
4153
+ ])));
4154
+ }
4155
+ } else {
4156
+ ctx.importsNeeded.add("reactiveStyle");
4157
+ stmts.push(t.expressionStatement(t.callExpression(t.identifier("reactiveStyle"), [
4158
+ elId,
4159
+ t.identifier("d"),
4160
+ ctx.reactiveRoot,
4161
+ pathOrGetter.value
4162
+ ])));
4163
+ }
3920
4164
  } else if (slot.kind === "value") {
3921
4165
  ctx.importsNeeded.add("reactiveValueRead");
3922
4166
  stmts.push(t.expressionStatement(t.callExpression(t.identifier("reactiveValueRead"), [
@@ -4107,37 +4351,742 @@ function isOneShotStringPropLocal(expr, ctx) {
4107
4351
  return t.isIdentifier(expr) && ctx.oneShotStringPropLocals?.has(expr.name) === true;
4108
4352
  }
4109
4353
  //#endregion
4110
- //#region src/closure-codegen/emit/emit-core.ts
4111
- function compileJsxToBlock(jsxRoot, ctx) {
4112
- const spec = walkJsxToTemplate(jsxRoot, {
4113
- emitEventDataAttr: false,
4114
- directFnComponents: ctx.directFnComponents,
4115
- bindings: ctx.bindings
4116
- });
4117
- if (t.isJSXFragment(jsxRoot)) spec.html = "<span style=\"display:contents\">" + spec.html + "</span>";
4118
- else if (!spec.html.startsWith("<") || spec.html.startsWith("<!--")) {
4119
- spec.html = "<span style=\"display:contents\">" + spec.html + "</span>";
4120
- for (const slot of spec.slots) slot.walk = [0, ...slot.walk];
4354
+ //#region src/closure-codegen/ir.ts
4355
+ function componentIrId(moduleId, exportName) {
4356
+ return `${moduleId}#${exportName}`;
4357
+ }
4358
+ function storeIrId(moduleId, className) {
4359
+ return `${moduleId}#${className}`;
4360
+ }
4361
+ function templateSpecToIr(spec, bindings = /* @__PURE__ */ new Map()) {
4362
+ return {
4363
+ html: spec.html,
4364
+ slots: spec.slots.map((slot) => slotToIr(slot, bindings))
4365
+ };
4366
+ }
4367
+ function storeFieldsToIr(classDecl) {
4368
+ const fields = [];
4369
+ for (const member of classDecl.body.body) {
4370
+ if (!t.isClassProperty(member) || member.static || member.computed || !t.isIdentifier(member.key)) continue;
4371
+ const field = {
4372
+ name: member.key.name,
4373
+ ...member.value ? { initializer: generate(member.value).code } : {},
4374
+ ...member.value ? shapeForExpression(member.value) : {}
4375
+ };
4376
+ if (field.shape?.kind === "array") {
4377
+ const elementTypeName = arrayElementTypeNameFromAnnotation(member);
4378
+ if (elementTypeName) field.shape = {
4379
+ ...field.shape,
4380
+ elementTypeName
4381
+ };
4382
+ }
4383
+ fields.push(field);
4121
4384
  }
4122
- const tplName = "_tpl" + ctx.tplCounter++;
4123
- ctx.templateDecls.push(...emitTemplateDecl(spec.html, tplName));
4124
- const stmts = [];
4125
- stmts.push(t.variableDeclaration("const", [t.variableDeclarator(t.identifier("root"), emitTemplateCloneExpression(tplName, spec.html))]));
4126
- const walkCache = /* @__PURE__ */ new Map();
4127
- for (const slot of spec.slots) emitWalkCapture(slot, stmts, false, walkCache);
4128
- const savedPending = ctx._pendingEvents;
4129
- const savedInputValueExprByEventSlot = ctx._inputValueExprByEventSlot;
4130
- const savedDocumentClickDelegateInstalled = ctx._documentClickDelegateInstalled;
4131
- ctx._pendingEvents = [];
4132
- ctx._inputValueExprByEventSlot = findInputValueReconciliations(spec);
4133
- ctx._documentClickDelegateInstalled = false;
4134
- for (const slot of spec.slots) emitSlot(slot, stmts, ctx);
4135
- const events = ctx._pendingEvents;
4136
- ctx._pendingEvents = savedPending;
4137
- ctx._inputValueExprByEventSlot = savedInputValueExprByEventSlot;
4138
- if (events.length > 0) {
4139
- const groups = /* @__PURE__ */ new Map();
4140
- for (const e of events) {
4385
+ return fields;
4386
+ }
4387
+ function arrayElementTypeNameFromAnnotation(member) {
4388
+ const annotation = member.typeAnnotation;
4389
+ if (!annotation || !t.isTSTypeAnnotation(annotation)) return void 0;
4390
+ return arrayElementTypeNameFromTSType(annotation.typeAnnotation);
4391
+ }
4392
+ function arrayElementTypeNameFromTSType(typeNode) {
4393
+ if (t.isTSArrayType(typeNode)) {
4394
+ const element = typeNode.elementType;
4395
+ if (t.isTSTypeReference(element) && t.isIdentifier(element.typeName)) return element.typeName.name;
4396
+ return;
4397
+ }
4398
+ if (t.isTSTypeReference(typeNode) && t.isIdentifier(typeNode.typeName)) {
4399
+ const containerName = typeNode.typeName.name;
4400
+ if (containerName !== "Array" && containerName !== "ReadonlyArray") return void 0;
4401
+ const args = typeNode.typeParameters?.params;
4402
+ if (!args || args.length !== 1) return void 0;
4403
+ const element = args[0];
4404
+ if (t.isTSTypeReference(element) && t.isIdentifier(element.typeName)) return element.typeName.name;
4405
+ return;
4406
+ }
4407
+ }
4408
+ const ARRAY_PRODUCING_METHODS = new Set([
4409
+ "filter",
4410
+ "map",
4411
+ "slice",
4412
+ "concat",
4413
+ "flat",
4414
+ "flatMap",
4415
+ "sort",
4416
+ "toSorted",
4417
+ "reverse",
4418
+ "toReversed"
4419
+ ]);
4420
+ function storeGettersToIr(classDecl) {
4421
+ const getters = [];
4422
+ const fieldShapeByName = /* @__PURE__ */ new Map();
4423
+ for (const field of storeFieldsToIr(classDecl)) if (field.shape) fieldShapeByName.set(field.name, field.shape);
4424
+ for (const member of classDecl.body.body) {
4425
+ if (!t.isClassMethod(member) || member.static || member.computed || member.kind !== "get") continue;
4426
+ if (!t.isIdentifier(member.key)) continue;
4427
+ const returnType = member.returnType;
4428
+ const elementTypeName = returnType && t.isTSTypeAnnotation(returnType) ? arrayElementTypeNameFromTSType(returnType.typeAnnotation) : void 0;
4429
+ const deps = collectThisFieldReads(member.body);
4430
+ const elementShape = getterElementShape(member.body, deps, elementTypeName, fieldShapeByName);
4431
+ const returnsArray = !!elementTypeName || !!elementShape || getterBodyReturnsArray(member.body);
4432
+ const shape = returnsArray ? {
4433
+ kind: "array",
4434
+ ...elementShape ? { element: elementShape } : {},
4435
+ ...elementTypeName ? { elementTypeName } : {}
4436
+ } : void 0;
4437
+ const ops = storeStmtsToIr(member.body.body);
4438
+ const getter = {
4439
+ name: member.key.name,
4440
+ returnsArray,
4441
+ deps,
4442
+ body: generate(member.body).code,
4443
+ ...elementTypeName ? { elementTypeName } : {},
4444
+ ...shape ? { shape } : {},
4445
+ ...ops ? { ops } : {},
4446
+ ...sourceSpan(member) ? { sourceSpan: sourceSpan(member) } : {}
4447
+ };
4448
+ getters.push(getter);
4449
+ }
4450
+ return getters;
4451
+ }
4452
+ function getterElementShape(body, deps, elementTypeName, fieldShapeByName) {
4453
+ const arg = topLevelReturnArgument(body);
4454
+ const fromBody = arg ? elementShapeFromArrayExpression(arg, fieldShapeByName) : void 0;
4455
+ if (fromBody) return fromBody;
4456
+ if (elementTypeName) {
4457
+ for (const shape of fieldShapeByName.values()) if (shape.kind === "array" && shape.elementTypeName === elementTypeName && shape.element?.kind === "object") return shape.element;
4458
+ }
4459
+ for (const dep of deps) {
4460
+ const shape = fieldShapeByName.get(dep);
4461
+ if (shape?.kind === "array" && shape.element?.kind === "object") return shape.element;
4462
+ }
4463
+ }
4464
+ function elementShapeFromArrayExpression(expr, fieldShapeByName) {
4465
+ if (t.isArrayExpression(expr)) {
4466
+ const first = expr.elements.find((element) => !!element && !t.isSpreadElement(element));
4467
+ if (!first) return void 0;
4468
+ const shaped = shapeForExpression(first);
4469
+ return "shape" in shaped ? shaped.shape : void 0;
4470
+ }
4471
+ if (t.isCallExpression(expr) && t.isMemberExpression(expr.callee) && t.isIdentifier(expr.callee.property)) {
4472
+ const method = expr.callee.property.name;
4473
+ if (method === "map") {
4474
+ const objectLiteral = mapCallbackObjectLiteral(expr.arguments[0]);
4475
+ if (!objectLiteral) return void 0;
4476
+ const shaped = shapeForExpression(objectLiteral);
4477
+ return "shape" in shaped ? shaped.shape : void 0;
4478
+ }
4479
+ if (ARRAY_PRODUCING_METHODS.has(method)) return elementShapeFromArrayExpression(expr.callee.object, fieldShapeByName);
4480
+ }
4481
+ if (t.isMemberExpression(expr) && t.isThisExpression(expr.object) && t.isIdentifier(expr.property)) {
4482
+ const shape = fieldShapeByName.get(expr.property.name);
4483
+ if (shape?.kind === "array") return shape.element;
4484
+ }
4485
+ }
4486
+ function mapCallbackObjectLiteral(callback) {
4487
+ if (!callback || !t.isArrowFunctionExpression(callback) && !t.isFunctionExpression(callback)) return void 0;
4488
+ const body = callback.body;
4489
+ if (t.isObjectExpression(body)) return body;
4490
+ if (t.isBlockStatement(body)) {
4491
+ for (const statement of body.body) if (t.isReturnStatement(statement) && statement.argument && t.isObjectExpression(statement.argument)) return statement.argument;
4492
+ }
4493
+ }
4494
+ function topLevelReturnArgument(body) {
4495
+ let result;
4496
+ let done = false;
4497
+ const visit = (value) => {
4498
+ if (done || !value || typeof value !== "object") return;
4499
+ if (Array.isArray(value)) {
4500
+ for (const item of value) visit(item);
4501
+ return;
4502
+ }
4503
+ const node = value;
4504
+ const type = node.type;
4505
+ if (type === "FunctionExpression" || type === "ArrowFunctionExpression" || type === "FunctionDeclaration") return;
4506
+ if (type === "ReturnStatement") {
4507
+ result = node.argument;
4508
+ done = true;
4509
+ return;
4510
+ }
4511
+ for (const key of Object.keys(node)) {
4512
+ if (key === "loc" || key === "start" || key === "end" || key === "range") continue;
4513
+ visit(node[key]);
4514
+ }
4515
+ };
4516
+ visit(body);
4517
+ return result;
4518
+ }
4519
+ function getterBodyReturnsArray(body) {
4520
+ let found = false;
4521
+ const visit = (value) => {
4522
+ if (found || !value || typeof value !== "object") return;
4523
+ if (Array.isArray(value)) {
4524
+ for (const item of value) visit(item);
4525
+ return;
4526
+ }
4527
+ const node = value;
4528
+ const type = node.type;
4529
+ if (type === "FunctionExpression" || type === "ArrowFunctionExpression" || type === "FunctionDeclaration") return;
4530
+ if (type === "ReturnStatement" && isArrayProducingExpression(node.argument)) {
4531
+ found = true;
4532
+ return;
4533
+ }
4534
+ for (const key of Object.keys(node)) {
4535
+ if (key === "loc" || key === "start" || key === "end" || key === "range") continue;
4536
+ visit(node[key]);
4537
+ }
4538
+ };
4539
+ visit(body);
4540
+ return found;
4541
+ }
4542
+ function isArrayProducingExpression(expr) {
4543
+ if (!expr || typeof expr !== "object") return false;
4544
+ const node = expr;
4545
+ if (node.type === "ArrayExpression") return true;
4546
+ if (node.type === "TSAsExpression" || node.type === "TSNonNullExpression") return isArrayProducingExpression(node.expression);
4547
+ if (node.type === "CallExpression") {
4548
+ const callee = node.callee;
4549
+ const property = callee?.property;
4550
+ if (callee?.type === "MemberExpression" && property?.type === "Identifier") return ARRAY_PRODUCING_METHODS.has(property.name);
4551
+ }
4552
+ return false;
4553
+ }
4554
+ function collectThisFieldReads(node) {
4555
+ const names = /* @__PURE__ */ new Set();
4556
+ const visit = (value) => {
4557
+ if (!value || typeof value !== "object") return;
4558
+ if (Array.isArray(value)) {
4559
+ for (const item of value) visit(item);
4560
+ return;
4561
+ }
4562
+ const rec = value;
4563
+ const object = rec.object;
4564
+ const property = rec.property;
4565
+ if (rec.type === "MemberExpression" && object?.type === "ThisExpression" && rec.computed !== true && property?.type === "Identifier") names.add(property.name);
4566
+ for (const key of Object.keys(rec)) {
4567
+ if (key === "loc" || key === "start" || key === "end" || key === "range") continue;
4568
+ visit(rec[key]);
4569
+ }
4570
+ };
4571
+ visit(node);
4572
+ return [...names];
4573
+ }
4574
+ function storeMethodsToIr(classDecl, moduleAst) {
4575
+ const methods = [];
4576
+ const literalUnionAliases = moduleAst ? collectLiteralUnionAliasValueTypes(moduleAst) : void 0;
4577
+ for (const member of classDecl.body.body) {
4578
+ if (!t.isClassMethod(member) || member.static || member.computed || member.kind !== "method") continue;
4579
+ if (!t.isIdentifier(member.key)) continue;
4580
+ const params = [];
4581
+ let unsupportedParam = false;
4582
+ for (const param of member.params) {
4583
+ const assignment = t.isAssignmentPattern(param) ? param : null;
4584
+ const identifier = t.isIdentifier(param) ? param : assignment && t.isIdentifier(assignment.left) ? assignment.left : null;
4585
+ if (identifier) {
4586
+ const valueType = paramValueType(identifier, literalUnionAliases) ?? (assignment ? paramDefaultValueType(assignment.right) : void 0);
4587
+ params.push(valueType ? {
4588
+ name: identifier.name,
4589
+ valueType
4590
+ } : { name: identifier.name });
4591
+ } else {
4592
+ unsupportedParam = true;
4593
+ break;
4594
+ }
4595
+ }
4596
+ if (unsupportedParam) continue;
4597
+ const ops = storeStmtsToIr(member.body.body);
4598
+ methods.push({
4599
+ name: member.key.name,
4600
+ params,
4601
+ body: generate(member.body).code,
4602
+ ...ops ? { ops } : {},
4603
+ ...sourceSpan(member) ? { sourceSpan: sourceSpan(member) } : {}
4604
+ });
4605
+ }
4606
+ return methods;
4607
+ }
4608
+ function paramValueType(param, literalUnionAliases) {
4609
+ const annotation = param.typeAnnotation;
4610
+ if (!annotation || !t.isTSTypeAnnotation(annotation)) return void 0;
4611
+ const kind = annotation.typeAnnotation;
4612
+ if (t.isTSStringKeyword(kind)) return "string";
4613
+ if (t.isTSNumberKeyword(kind)) return "number";
4614
+ if (t.isTSBooleanKeyword(kind)) return "boolean";
4615
+ const inlineLiteral = literalUnionValueType(kind);
4616
+ if (inlineLiteral) return inlineLiteral;
4617
+ if (literalUnionAliases && t.isTSTypeReference(kind) && t.isIdentifier(kind.typeName)) return literalUnionAliases.get(kind.typeName.name);
4618
+ }
4619
+ function literalUnionValueType(kind) {
4620
+ if (t.isTSLiteralType(kind)) {
4621
+ if (t.isStringLiteral(kind.literal)) return "string";
4622
+ if (t.isNumericLiteral(kind.literal)) return "number";
4623
+ if (t.isBooleanLiteral(kind.literal)) return "boolean";
4624
+ return;
4625
+ }
4626
+ if (t.isTSUnionType(kind)) {
4627
+ let valueType;
4628
+ for (const member of kind.types) {
4629
+ const memberType = literalUnionValueType(member);
4630
+ if (!memberType || valueType && memberType !== valueType) return void 0;
4631
+ valueType = memberType;
4632
+ }
4633
+ return valueType;
4634
+ }
4635
+ }
4636
+ function collectLiteralUnionAliasValueTypes(ast) {
4637
+ const aliases = /* @__PURE__ */ new Map();
4638
+ for (const node of ast.program.body) {
4639
+ const alias = t.isTSTypeAliasDeclaration(node) ? node : t.isExportNamedDeclaration(node) && t.isTSTypeAliasDeclaration(node.declaration) ? node.declaration : null;
4640
+ if (!alias || !t.isIdentifier(alias.id)) continue;
4641
+ const valueType = literalUnionValueType(alias.typeAnnotation);
4642
+ if (valueType) aliases.set(alias.id.name, valueType);
4643
+ }
4644
+ return aliases;
4645
+ }
4646
+ function paramDefaultValueType(expr) {
4647
+ if (t.isStringLiteral(expr)) return "string";
4648
+ if (t.isNumericLiteral(expr)) return "number";
4649
+ if (t.isBooleanLiteral(expr)) return "boolean";
4650
+ }
4651
+ function storeStmtsToIr(statements) {
4652
+ const out = [];
4653
+ for (const statement of statements) {
4654
+ const converted = storeStmtToIr(statement);
4655
+ if (!converted) return null;
4656
+ out.push(...converted);
4657
+ }
4658
+ return out;
4659
+ }
4660
+ function storeStmtToIr(statement) {
4661
+ if (t.isBlockStatement(statement)) return storeStmtsToIr(statement.body);
4662
+ if (t.isVariableDeclaration(statement)) {
4663
+ const declarations = [];
4664
+ for (const declaration of statement.declarations) {
4665
+ if (!t.isIdentifier(declaration.id)) return null;
4666
+ const init = declaration.init ? storeExprToIr(declaration.init) : void 0;
4667
+ if (declaration.init && !init) return null;
4668
+ declarations.push({
4669
+ kind: "var",
4670
+ name: declaration.id.name,
4671
+ mutable: statement.kind !== "const",
4672
+ ...init ? { init } : {}
4673
+ });
4674
+ }
4675
+ return declarations;
4676
+ }
4677
+ if (t.isExpressionStatement(statement)) {
4678
+ const expr = statement.expression;
4679
+ if (t.isAssignmentExpression(expr) && expr.operator === "=") {
4680
+ const target = storeExprToIr(expr.left);
4681
+ const value = storeExprToIr(expr.right);
4682
+ return target && value ? [{
4683
+ kind: "assign",
4684
+ target,
4685
+ value
4686
+ }] : null;
4687
+ }
4688
+ const converted = storeExprToIr(expr);
4689
+ return converted ? [{
4690
+ kind: "expr",
4691
+ expr: converted
4692
+ }] : null;
4693
+ }
4694
+ if (t.isIfStatement(statement)) {
4695
+ const test = storeExprToIr(statement.test);
4696
+ const consequent = storeStatementList(statement.consequent);
4697
+ const alternate = statement.alternate ? storeStatementList(statement.alternate) : void 0;
4698
+ if (!test || !consequent || statement.alternate && !alternate) return null;
4699
+ return [{
4700
+ kind: "if",
4701
+ test,
4702
+ consequent,
4703
+ ...alternate ? { alternate } : {}
4704
+ }];
4705
+ }
4706
+ if (t.isForStatement(statement)) {
4707
+ const init = statement.init ? storeStmtToIr(t.isVariableDeclaration(statement.init) ? statement.init : t.expressionStatement(statement.init)) : void 0;
4708
+ const test = statement.test ? storeExprToIr(statement.test) : void 0;
4709
+ const update = statement.update ? storeExprToIr(statement.update) : void 0;
4710
+ const body = storeStatementList(statement.body);
4711
+ if (statement.init && (!init || init.length !== 1) || statement.test && !test || statement.update && !update || !body) return null;
4712
+ return [{
4713
+ kind: "for",
4714
+ ...init ? { init: init[0] } : {},
4715
+ ...test ? { test } : {},
4716
+ ...update ? { update } : {},
4717
+ body
4718
+ }];
4719
+ }
4720
+ if (t.isReturnStatement(statement)) {
4721
+ const value = statement.argument ? storeExprToIr(statement.argument) : void 0;
4722
+ if (statement.argument && !value) return null;
4723
+ return [{
4724
+ kind: "return",
4725
+ ...value ? { value } : {}
4726
+ }];
4727
+ }
4728
+ return null;
4729
+ }
4730
+ function storeStatementList(statement) {
4731
+ if (t.isBlockStatement(statement)) return storeStmtsToIr(statement.body);
4732
+ return storeStmtToIr(statement);
4733
+ }
4734
+ function storeExprToIr(expression) {
4735
+ if (t.isIdentifier(expression)) return {
4736
+ kind: "identifier",
4737
+ name: expression.name
4738
+ };
4739
+ if (t.isThisExpression(expression)) return { kind: "this" };
4740
+ if (t.isNumericLiteral(expression)) return {
4741
+ kind: "number",
4742
+ value: expression.value
4743
+ };
4744
+ if (t.isStringLiteral(expression)) return {
4745
+ kind: "string",
4746
+ value: expression.value
4747
+ };
4748
+ if (t.isBooleanLiteral(expression)) return {
4749
+ kind: "boolean",
4750
+ value: expression.value
4751
+ };
4752
+ if (t.isNullLiteral(expression)) return { kind: "null" };
4753
+ if (t.isMemberExpression(expression)) {
4754
+ const object = storeExprToIr(expression.object);
4755
+ if (!object) return null;
4756
+ if (expression.computed) {
4757
+ const index = storeExprToIr(expression.property);
4758
+ return index ? {
4759
+ kind: "index",
4760
+ object,
4761
+ index
4762
+ } : null;
4763
+ }
4764
+ return t.isIdentifier(expression.property) ? {
4765
+ kind: "member",
4766
+ object,
4767
+ property: expression.property.name
4768
+ } : null;
4769
+ }
4770
+ if (t.isCallExpression(expression)) {
4771
+ const callee = storeExprToIr(expression.callee);
4772
+ const args = expression.arguments.map((arg) => t.isSpreadElement(arg) ? null : storeExprToIr(arg));
4773
+ return callee && args.every((arg) => !!arg) ? {
4774
+ kind: "call",
4775
+ callee,
4776
+ args
4777
+ } : null;
4778
+ }
4779
+ if (t.isObjectExpression(expression)) {
4780
+ const fields = [];
4781
+ for (const property of expression.properties) {
4782
+ if (!t.isObjectProperty(property) || property.computed) return null;
4783
+ const name = objectPropertyName(property.key);
4784
+ const value = storeExprToIr(property.value);
4785
+ if (!name || !value) return null;
4786
+ fields.push({
4787
+ name,
4788
+ value
4789
+ });
4790
+ }
4791
+ return {
4792
+ kind: "object",
4793
+ fields
4794
+ };
4795
+ }
4796
+ if (t.isUnaryExpression(expression)) {
4797
+ const arg = storeExprToIr(expression.argument);
4798
+ return arg ? {
4799
+ kind: "unary",
4800
+ op: expression.operator,
4801
+ arg
4802
+ } : null;
4803
+ }
4804
+ if (t.isBinaryExpression(expression)) {
4805
+ const left = storeExprToIr(expression.left);
4806
+ const right = storeExprToIr(expression.right);
4807
+ return left && right ? {
4808
+ kind: "binary",
4809
+ op: expression.operator,
4810
+ left,
4811
+ right
4812
+ } : null;
4813
+ }
4814
+ if (t.isLogicalExpression(expression)) {
4815
+ const left = storeExprToIr(expression.left);
4816
+ const right = storeExprToIr(expression.right);
4817
+ return left && right ? {
4818
+ kind: "logical",
4819
+ op: expression.operator,
4820
+ left,
4821
+ right
4822
+ } : null;
4823
+ }
4824
+ if (t.isUpdateExpression(expression)) {
4825
+ const arg = storeExprToIr(expression.argument);
4826
+ return arg ? {
4827
+ kind: "update",
4828
+ op: expression.operator,
4829
+ arg,
4830
+ prefix: expression.prefix
4831
+ } : null;
4832
+ }
4833
+ return null;
4834
+ }
4835
+ function sourceSpan(node) {
4836
+ const span = {};
4837
+ if (typeof node.start === "number") span.start = node.start;
4838
+ if (typeof node.end === "number") span.end = node.end;
4839
+ return span.start === void 0 && span.end === void 0 ? void 0 : span;
4840
+ }
4841
+ function slotToIr(slot, bindings) {
4842
+ const expr = slot.expr ? substituteBindings(slot.expr, bindings) : null;
4843
+ return {
4844
+ index: slot.index,
4845
+ kind: slot.kind,
4846
+ walk: slot.walk,
4847
+ ...slot.walkKinds ? { walkKinds: slot.walkKinds } : {},
4848
+ ...expr ? { expr: generate(expr).code } : {},
4849
+ ...expr ? expressionPathToIr(expr) : {},
4850
+ ...expr ? expressionObjectFieldsToIr(expr) : {},
4851
+ ...slot.payload ? { payload: slotPayloadToIr(slot, bindings) } : {},
4852
+ ...slot.directText ? { directText: true } : {}
4853
+ };
4854
+ }
4855
+ function expressionPathToIr(expr) {
4856
+ const path = expressionPath(expr);
4857
+ return path && path.length > 0 ? { exprPath: path } : {};
4858
+ }
4859
+ function expressionPath(expr) {
4860
+ if (t.isIdentifier(expr)) return [expr.name];
4861
+ if (t.isThisExpression(expr)) return ["this"];
4862
+ if (t.isMemberExpression(expr) && !expr.computed) {
4863
+ const objectPath = expressionPath(expr.object);
4864
+ const property = t.isIdentifier(expr.property) ? expr.property.name : null;
4865
+ return objectPath && property ? [...objectPath, property] : null;
4866
+ }
4867
+ if (t.isOptionalMemberExpression(expr) && !expr.computed) {
4868
+ const objectPath = expressionPath(expr.object);
4869
+ const property = t.isIdentifier(expr.property) ? expr.property.name : null;
4870
+ return objectPath && property ? [...objectPath, property] : null;
4871
+ }
4872
+ return null;
4873
+ }
4874
+ function expressionObjectFieldsToIr(expr) {
4875
+ if (!t.isObjectExpression(expr)) return {};
4876
+ const fields = [];
4877
+ for (const property of expr.properties) {
4878
+ if (!t.isObjectProperty(property) || property.computed) continue;
4879
+ const name = objectPropertyName(property.key);
4880
+ if (!name) continue;
4881
+ fields.push({
4882
+ name,
4883
+ expr: generate(property.value).code,
4884
+ ...expressionPathToIr(property.value)
4885
+ });
4886
+ }
4887
+ return fields.length > 0 ? { exprObjectFields: fields } : {};
4888
+ }
4889
+ function slotPayloadToIr(slot, bindings) {
4890
+ const payload = serializePayload(slot.payload, bindings);
4891
+ if (!isRecord(payload)) return payload;
4892
+ if (slot.kind === "keyed-list") {
4893
+ const cb = slot.payload?.mapCallback;
4894
+ const row = keyedListRowIr(cb);
4895
+ return row ? {
4896
+ ...payload,
4897
+ ...row
4898
+ } : payload;
4899
+ }
4900
+ if (slot.kind === "conditional") {
4901
+ const result = { ...payload };
4902
+ const consequent = jsxNodeToTemplateIr(slot.payload?.mkTrue, bindings);
4903
+ if (consequent) result.consequentTemplate = consequent;
4904
+ const alternate = jsxNodeToTemplateIr(slot.payload?.mkFalse, bindings);
4905
+ if (alternate) result.alternateTemplate = alternate;
4906
+ return result;
4907
+ }
4908
+ if (slot.kind === "mount") {
4909
+ const children = slot.payload?.children;
4910
+ const childrenTemplate = jsxChildrenToTemplateIr(children, bindings);
4911
+ if (childrenTemplate) return {
4912
+ ...payload,
4913
+ childrenTemplate
4914
+ };
4915
+ }
4916
+ return payload;
4917
+ }
4918
+ function jsxNodeToTemplateIr(node, bindings) {
4919
+ if (!node) return null;
4920
+ if (t.isJSXElement(node) || t.isJSXFragment(node)) return templateSpecToIr(walkJsxToTemplate(node), bindings);
4921
+ if (t.isJSXExpressionContainer(node)) {
4922
+ const inner = node.expression;
4923
+ if (t.isJSXElement(inner) || t.isJSXFragment(inner)) return templateSpecToIr(walkJsxToTemplate(inner), bindings);
4924
+ if (isWalkableConditionalExpression(inner)) return wrapAsFragmentTemplate(inner, bindings);
4925
+ }
4926
+ if (isWalkableConditionalExpression(node)) return wrapAsFragmentTemplate(node, bindings);
4927
+ return null;
4928
+ }
4929
+ function isWalkableConditionalExpression(node) {
4930
+ if (t.isConditionalExpression(node)) return isJsxOrNullish(node.consequent) || isJsxOrNullish(node.alternate);
4931
+ if (t.isLogicalExpression(node) && node.operator === "&&") return isJsxOrNullish(node.right);
4932
+ return false;
4933
+ }
4934
+ function wrapAsFragmentTemplate(expression, bindings) {
4935
+ return templateSpecToIr(walkJsxToTemplate(t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), [t.jsxExpressionContainer(expression)])), bindings);
4936
+ }
4937
+ function jsxChildrenToTemplateIr(children, bindings) {
4938
+ if (!Array.isArray(children) || children.length === 0) return null;
4939
+ if (!children.some((c) => t.isJSXElement(c) || t.isJSXFragment(c) || t.isJSXExpressionContainer(c) && !t.isJSXEmptyExpression(c.expression))) return null;
4940
+ return templateSpecToIr(walkJsxToTemplate(t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), children)), bindings);
4941
+ }
4942
+ function keyedListRowIr(cb) {
4943
+ if (!t.isArrowFunctionExpression(cb) && !t.isFunctionExpression(cb)) return null;
4944
+ const body = callbackJsxBody(cb.body);
4945
+ if (!body) return null;
4946
+ const itemParam = callbackParamName(cb.params[0]);
4947
+ const indexParam = callbackParamName(cb.params[1]);
4948
+ return {
4949
+ ...itemParam ? { itemParam } : {},
4950
+ ...indexParam ? { indexParam } : {},
4951
+ rowTemplate: templateSpecToIr(walkJsxToTemplate(body))
4952
+ };
4953
+ }
4954
+ function callbackJsxBody(body) {
4955
+ if (t.isJSXElement(body) || t.isJSXFragment(body)) return body;
4956
+ if (!t.isBlockStatement(body)) return null;
4957
+ for (const statement of body.body) {
4958
+ if (!t.isReturnStatement(statement) || !statement.argument) continue;
4959
+ if (t.isJSXElement(statement.argument) || t.isJSXFragment(statement.argument)) return statement.argument;
4960
+ }
4961
+ return null;
4962
+ }
4963
+ function callbackParamName(param) {
4964
+ return t.isIdentifier(param) ? param.name : void 0;
4965
+ }
4966
+ function serializePayload(value, bindings) {
4967
+ if (value === null || value === void 0) return value;
4968
+ if (typeof value !== "object") return value;
4969
+ if (Array.isArray(value)) return value.map((child) => serializePayload(child, bindings));
4970
+ if (isBabelNode(value)) {
4971
+ const node = substitutePayloadNode(value, bindings);
4972
+ return {
4973
+ nodeType: node.type,
4974
+ code: generate(node).code
4975
+ };
4976
+ }
4977
+ const out = {};
4978
+ for (const [key, child] of Object.entries(value)) out[key] = serializePayload(child, bindings);
4979
+ return out;
4980
+ }
4981
+ function substitutePayloadNode(value, bindings) {
4982
+ if (bindings.size === 0) return value;
4983
+ if (t.isJSXAttribute(value) && value.value && t.isJSXExpressionContainer(value.value) && !t.isJSXEmptyExpression(value.value.expression)) return {
4984
+ ...value,
4985
+ value: {
4986
+ ...value.value,
4987
+ expression: substituteBindings(value.value.expression, bindings)
4988
+ }
4989
+ };
4990
+ if (t.isJSXExpressionContainer(value) && !t.isJSXEmptyExpression(value.expression)) return {
4991
+ ...value,
4992
+ expression: substituteBindings(value.expression, bindings)
4993
+ };
4994
+ return substituteBindings(value, bindings);
4995
+ }
4996
+ function isBabelNode(value) {
4997
+ return !!value && typeof value === "object" && typeof value.type === "string";
4998
+ }
4999
+ function isRecord(value) {
5000
+ return !!value && typeof value === "object" && !Array.isArray(value);
5001
+ }
5002
+ function shapeForExpression(value) {
5003
+ if (t.isStringLiteral(value)) return { shape: {
5004
+ kind: "literal",
5005
+ valueType: "string"
5006
+ } };
5007
+ if (t.isNumericLiteral(value)) return { shape: {
5008
+ kind: "literal",
5009
+ valueType: "number"
5010
+ } };
5011
+ if (t.isBooleanLiteral(value)) return { shape: {
5012
+ kind: "literal",
5013
+ valueType: "boolean"
5014
+ } };
5015
+ if (t.isNullLiteral(value)) return { shape: {
5016
+ kind: "literal",
5017
+ valueType: "null"
5018
+ } };
5019
+ if (t.isArrayExpression(value)) {
5020
+ const firstElement = value.elements.find((element) => !!element && !t.isSpreadElement(element));
5021
+ const shapedElement = firstElement ? shapeForExpression(firstElement) : {};
5022
+ const elementShape = "shape" in shapedElement ? shapedElement.shape : void 0;
5023
+ return { shape: {
5024
+ kind: "array",
5025
+ ...elementShape ? { element: elementShape } : {}
5026
+ } };
5027
+ }
5028
+ if (t.isObjectExpression(value)) {
5029
+ const fields = [];
5030
+ for (const property of value.properties) {
5031
+ if (!t.isObjectProperty(property) || property.computed) continue;
5032
+ const name = objectPropertyName(property.key);
5033
+ if (!name) continue;
5034
+ fields.push({
5035
+ name,
5036
+ ...property.value ? { initializer: generate(property.value).code } : {},
5037
+ ...property.value ? shapeForExpression(property.value) : {}
5038
+ });
5039
+ }
5040
+ return { shape: {
5041
+ kind: "object",
5042
+ fields
5043
+ } };
5044
+ }
5045
+ return {};
5046
+ }
5047
+ function objectPropertyName(key) {
5048
+ if (t.isIdentifier(key)) return key.name;
5049
+ if (t.isStringLiteral(key)) return key.value;
5050
+ if (t.isNumericLiteral(key)) return String(key.value);
5051
+ return null;
5052
+ }
5053
+ //#endregion
5054
+ //#region src/closure-codegen/emit/emit-core.ts
5055
+ function compileJsxToBlock(jsxRoot, ctx) {
5056
+ const spec = walkJsxToTemplate(jsxRoot, {
5057
+ emitEventDataAttr: false,
5058
+ directFnComponents: ctx.directFnComponents,
5059
+ bindings: ctx.bindings
5060
+ });
5061
+ if (t.isJSXFragment(jsxRoot)) spec.html = "<span style=\"display:contents\">" + spec.html + "</span>";
5062
+ else if (!spec.html.startsWith("<") || spec.html.startsWith("<!--")) {
5063
+ spec.html = "<span style=\"display:contents\">" + spec.html + "</span>";
5064
+ for (const slot of spec.slots) slot.walk = [0, ...slot.walk];
5065
+ }
5066
+ if (ctx.irTemplates && ctx.currentIrComponent && ctx.currentIrRuntimeBase) ctx.irTemplates.push({
5067
+ component: ctx.currentIrComponent,
5068
+ runtimeBase: ctx.currentIrRuntimeBase,
5069
+ template: templateSpecToIr(spec, ctx.bindings)
5070
+ });
5071
+ const tplName = "_tpl" + ctx.tplCounter++;
5072
+ ctx.templateDecls.push(...emitTemplateDecl(spec.html, tplName));
5073
+ const stmts = [];
5074
+ stmts.push(t.variableDeclaration("const", [t.variableDeclarator(t.identifier("root"), emitTemplateCloneExpression(tplName, spec.html))]));
5075
+ const walkCache = /* @__PURE__ */ new Map();
5076
+ for (const slot of spec.slots) emitWalkCapture(slot, stmts, false, walkCache);
5077
+ const savedPending = ctx._pendingEvents;
5078
+ const savedInputValueExprByEventSlot = ctx._inputValueExprByEventSlot;
5079
+ const savedDocumentClickDelegateInstalled = ctx._documentClickDelegateInstalled;
5080
+ ctx._pendingEvents = [];
5081
+ ctx._inputValueExprByEventSlot = findInputValueReconciliations(spec);
5082
+ ctx._documentClickDelegateInstalled = false;
5083
+ for (const slot of spec.slots) emitSlot(slot, stmts, ctx);
5084
+ const events = ctx._pendingEvents;
5085
+ ctx._pendingEvents = savedPending;
5086
+ ctx._inputValueExprByEventSlot = savedInputValueExprByEventSlot;
5087
+ if (events.length > 0) {
5088
+ const groups = /* @__PURE__ */ new Map();
5089
+ for (const e of events) {
4141
5090
  const arr = groups.get(e.eventType);
4142
5091
  if (arr) arr.push(e);
4143
5092
  else groups.set(e.eventType, [e]);
@@ -4431,6 +5380,11 @@ function rewriteFnComponent(fnDecl, parentCtx) {
4431
5380
  fnCtx.directFnComponentParams = parentCtx.directFnComponentParams;
4432
5381
  fnCtx.directFnStringProps = parentCtx.directFnStringProps;
4433
5382
  fnCtx.directFnNoDisposer = parentCtx.directFnNoDisposer;
5383
+ if (parentCtx.irTemplates && fnName) {
5384
+ fnCtx.irTemplates = parentCtx.irTemplates;
5385
+ fnCtx.currentIrComponent = fnName;
5386
+ fnCtx.currentIrRuntimeBase = "reactive";
5387
+ }
4434
5388
  if (fnCtx.oneShotProps) {
4435
5389
  fnCtx._inKeyedListRow = true;
4436
5390
  fnCtx._rowEventTypes = /* @__PURE__ */ new Set();
@@ -4856,6 +5810,7 @@ function transformFile(source, _filename, options = {}) {
4856
5810
  };
4857
5811
  }
4858
5812
  const ctx = createEmitContext();
5813
+ ctx.irTemplates = [];
4859
5814
  ctx.directFnComponents = collectDirectFnComponents(ast);
4860
5815
  ctx.directFnComponentParams = collectDirectFnComponentParams(ast, ctx.directFnComponents);
4861
5816
  ctx.directFnStringProps = collectDirectFnStringProps(ast, ctx.directFnComponents);
@@ -4864,6 +5819,7 @@ function transformFile(source, _filename, options = {}) {
4864
5819
  for (const name of options.directClassComponents ?? []) ctx.directClassComponents.add(name);
4865
5820
  ctx.directFactoryComponents = new Set(options.directFactoryComponents);
4866
5821
  const rewritten = [];
5822
+ const reactiveComponentNames = /* @__PURE__ */ new Set();
4867
5823
  let firstClassIdx = -1;
4868
5824
  for (let i = 0; i < ast.program.body.length; i++) {
4869
5825
  const node = ast.program.body[i];
@@ -4899,9 +5855,21 @@ function transformFile(source, _filename, options = {}) {
4899
5855
  const useTinyReactiveComponent = options.enableTinyReactiveComponents !== false && !useStaticCompiledComponent && !useCompiledComponent && canUseTinyReactiveComponent(classDecl);
4900
5856
  const useLeanReactiveComponent = !useStaticCompiledComponent && !useCompiledComponent && !useTinyReactiveComponent && canUseLeanReactiveComponent(classDecl);
4901
5857
  const hasAfterRenderAsyncHook = hasOwnInstanceMethod(classDecl, "onAfterRenderAsync");
5858
+ const className = classDecl.id && classDecl.id.name || "<anonymous>";
5859
+ if (t.isIdentifier(classDecl.superClass, { name: "ReactiveComponent" })) reactiveComponentNames.add(className);
5860
+ const runtimeBase = runtimeBaseForComponent({
5861
+ useStaticCompiledComponent,
5862
+ useCompiledComponent,
5863
+ useTinyReactiveComponent,
5864
+ useLeanReactiveComponent
5865
+ });
5866
+ ctx.currentIrComponent = className;
5867
+ ctx.currentIrRuntimeBase = runtimeBase;
4902
5868
  for (const m of methodsWithJsx) m.body.body = m.body.body.map((s) => lowerJsxInStatement(s, ctx));
4903
5869
  if (!templateMethod) {
4904
- rewritten.push(classDecl.id && classDecl.id.name || "<anonymous>");
5870
+ rewritten.push(className);
5871
+ ctx.currentIrComponent = void 0;
5872
+ ctx.currentIrRuntimeBase = void 0;
4905
5873
  continue;
4906
5874
  }
4907
5875
  const paramBindings = [];
@@ -4921,14 +5889,26 @@ function transformFile(source, _filename, options = {}) {
4921
5889
  ctx.importsNeeded.add(templateSymbol);
4922
5890
  const method = buildCreateTemplateMethod(jsx, ctx, preceding, templateSymbol);
4923
5891
  const useStaticElementComponent = useStaticCompiledComponent && isStaticBuiltinElementRoot(jsx) && !nodeContainsIdentifier$1(method.body, "d");
5892
+ if (useStaticElementComponent && ctx.irTemplates) {
5893
+ for (const template of ctx.irTemplates) if (template.component === className) template.runtimeBase = "static-element";
5894
+ }
4924
5895
  if (useStaticElementComponent) method.params = [];
4925
5896
  if (plainPropsParamName) ctx.bindings.delete(plainPropsParamName);
4926
5897
  for (const k of paramBindings) ctx.bindings.delete(k);
5898
+ const isReactiveComponent = reactiveComponentNames.has(className);
4927
5899
  const bodyItems = classDecl.body.body;
4928
5900
  const templateIdx = bodyItems.indexOf(templateMethod);
4929
- if (templateIdx >= 0) bodyItems[templateIdx] = method;
5901
+ if (templateIdx >= 0) if (isReactiveComponent) {
5902
+ bodyItems.splice(templateIdx, 1);
5903
+ const keepAlive = mountedComponentKeepAliveStatements(jsx, ast);
5904
+ if (keepAlive.length > 0) ast.program.body.splice(i + 1, 0, ...keepAlive);
5905
+ } else bodyItems[templateIdx] = method;
4930
5906
  let usesCompiledRuntimeBase = false;
4931
- if (useStaticElementComponent) {
5907
+ if (isReactiveComponent) {
5908
+ classDecl.superClass = null;
5909
+ usesCompiledRuntimeBase = false;
5910
+ if (!classDecl.body.body.some((member) => (t.isClassProperty(member) || t.isClassMethod(member)) && !member.computed && t.isIdentifier(member.key, { name: "el" })) && nodeContainsThisMember(classDecl, "el")) classDecl.body.body.unshift(t.classProperty(t.identifier("el"), t.nullLiteral()));
5911
+ } else if (useStaticElementComponent) {
4932
5912
  ctx.importsNeeded.add("CompiledStaticElementComponent");
4933
5913
  classDecl.superClass = t.identifier("CompiledStaticElementComponent");
4934
5914
  usesCompiledRuntimeBase = true;
@@ -4957,7 +5937,9 @@ function transformFile(source, _filename, options = {}) {
4957
5937
  ctx.importsNeeded.add("scheduleAfterRenderAsync");
4958
5938
  classDecl.body.body.push(buildAfterRenderAsyncRenderMethod());
4959
5939
  }
4960
- rewritten.push(classDecl.id && classDecl.id.name || "<anonymous>");
5940
+ rewritten.push(className);
5941
+ ctx.currentIrComponent = void 0;
5942
+ ctx.currentIrRuntimeBase = void 0;
4961
5943
  continue;
4962
5944
  }
4963
5945
  if (fnDecl && isFunctionComponent(fnDecl)) {
@@ -4984,9 +5966,90 @@ function transformFile(source, _filename, options = {}) {
4984
5966
  map: out.map,
4985
5967
  changed: true,
4986
5968
  rewritten,
4987
- importsNeeded: Array.from(ctx.importsNeeded)
5969
+ importsNeeded: Array.from(ctx.importsNeeded),
5970
+ ir: buildModuleIr(_filename ?? "<unknown>", rewritten, ctx.irTemplates ?? [], ast, reactiveComponentNames)
5971
+ };
5972
+ }
5973
+ function mountedComponentKeepAliveStatements(jsx, ast) {
5974
+ const tags = /* @__PURE__ */ new Set();
5975
+ collectCapitalizedJsxTags(jsx, tags);
5976
+ if (tags.size === 0) return [];
5977
+ const imported = /* @__PURE__ */ new Set();
5978
+ for (const stmt of ast.program.body) {
5979
+ if (!t.isImportDeclaration(stmt)) continue;
5980
+ for (const spec of stmt.specifiers) imported.add(spec.local.name);
5981
+ }
5982
+ const kept = Array.from(tags).filter((tag) => imported.has(tag));
5983
+ if (kept.length === 0) return [];
5984
+ const keepArray = t.assignmentExpression("||=", t.memberExpression(t.identifier("globalThis"), t.identifier("__GEA_IR_KEEP__")), t.arrayExpression([]));
5985
+ return [t.expressionStatement(t.callExpression(t.memberExpression(t.parenthesizedExpression(keepArray), t.identifier("push")), kept.map((tag) => t.identifier(tag))))];
5986
+ }
5987
+ function collectCapitalizedJsxTags(node, tags) {
5988
+ if (!node || typeof node !== "object") return;
5989
+ if (Array.isArray(node)) {
5990
+ for (const child of node) collectCapitalizedJsxTags(child, tags);
5991
+ return;
5992
+ }
5993
+ if (t.isJSXElement(node)) {
5994
+ const name = node.openingElement.name;
5995
+ if (t.isJSXIdentifier(name) && /^[A-Z]/.test(name.name)) tags.add(name.name);
5996
+ }
5997
+ for (const key of Object.keys(node)) {
5998
+ if (key === "loc" || key === "start" || key === "end" || key === "type") continue;
5999
+ collectCapitalizedJsxTags(node[key], tags);
6000
+ }
6001
+ }
6002
+ function runtimeBaseForComponent(options) {
6003
+ if (options.useStaticCompiledComponent) return "static";
6004
+ if (options.useCompiledComponent) return "compiled";
6005
+ if (options.useTinyReactiveComponent) return "tiny-reactive";
6006
+ if (options.useLeanReactiveComponent) return "lean-reactive";
6007
+ return "reactive";
6008
+ }
6009
+ function buildModuleIr(moduleId, rewritten, templates, ast, reactiveComponentNames = /* @__PURE__ */ new Set()) {
6010
+ const components = [];
6011
+ for (const name of rewritten) {
6012
+ const record = templates.find((template) => template.component === name);
6013
+ if (!record) continue;
6014
+ const declaration = findClassDeclarationByName(ast, name);
6015
+ const reactiveState = declaration && reactiveComponentNames.has(name) ? (() => {
6016
+ const fields = storeFieldsToIr(declaration);
6017
+ const methods = storeMethodsToIr(declaration, ast);
6018
+ const getters = storeGettersToIr(declaration);
6019
+ return {
6020
+ fields,
6021
+ ...methods.length > 0 ? { methods } : {},
6022
+ ...getters.length > 0 ? { getters } : {}
6023
+ };
6024
+ })() : void 0;
6025
+ components.push({
6026
+ id: componentIrId(moduleId, name),
6027
+ module: moduleId,
6028
+ exportName: name,
6029
+ runtimeBase: record.runtimeBase,
6030
+ template: record.template,
6031
+ ...reactiveState ? { reactiveState } : {},
6032
+ ...declaration ? { sourceSpan: sourceSpan(declaration) } : {}
6033
+ });
6034
+ }
6035
+ return {
6036
+ module: {
6037
+ id: moduleId,
6038
+ file: moduleId,
6039
+ components: components.map((component) => component.id),
6040
+ stores: []
6041
+ },
6042
+ components
4988
6043
  };
4989
6044
  }
6045
+ function findClassDeclarationByName(ast, name) {
6046
+ for (const node of ast.program.body) {
6047
+ if (t.isClassDeclaration(node) && node.id?.name === name) return node;
6048
+ if (t.isExportDefaultDeclaration(node) && t.isClassDeclaration(node.declaration) && node.declaration.id?.name === name) return node.declaration;
6049
+ if (t.isExportNamedDeclaration(node) && t.isClassDeclaration(node.declaration) && node.declaration.id?.name === name) return node.declaration;
6050
+ }
6051
+ return null;
6052
+ }
4990
6053
  function collectLocalClassComponents(ast) {
4991
6054
  const names = /* @__PURE__ */ new Set();
4992
6055
  for (const node of ast.program.body) {
@@ -5516,6 +6579,7 @@ function transform(ctx) {
5516
6579
  }
5517
6580
  }
5518
6581
  });
6582
+ let ir;
5519
6583
  if (hasJSX) {
5520
6584
  const emitted = transformFile(code, sourceFile, {
5521
6585
  directClassComponents: knownClassComponentImports,
@@ -5523,6 +6587,7 @@ function transform(ctx) {
5523
6587
  enableTinyReactiveComponents: !isServe
5524
6588
  });
5525
6589
  if (emitted.changed) {
6590
+ ir = emitted.ir;
5526
6591
  const reparsed = parseSource(emitted.code);
5527
6592
  if (reparsed) {
5528
6593
  ast.program.body = reparsed.ast.program.body;
@@ -5573,7 +6638,8 @@ function transform(ctx) {
5573
6638
  }, code);
5574
6639
  return {
5575
6640
  code: output.code,
5576
- map: output.map
6641
+ map: output.map,
6642
+ ir
5577
6643
  };
5578
6644
  } catch (error) {
5579
6645
  if (error?.__geaCompileError) throw error;
@@ -5583,10 +6649,9 @@ function transform(ctx) {
5583
6649
  }
5584
6650
  //#endregion
5585
6651
  //#region src/closure-codegen/transform/transform-store.ts
5586
- function transformCompiledStoreModule(source) {
6652
+ function transformCompiledStoreModule(source, moduleId = "<unknown>", resolveImportPath) {
5587
6653
  if (!source.includes("extends Store")) return null;
5588
6654
  if (source.includes("CompiledStore")) return null;
5589
- if (/\b(flushSync|silent|Store\.|new\s+Store\s*\()/.test(source)) return null;
5590
6655
  let ast;
5591
6656
  try {
5592
6657
  ast = parse(source, {
@@ -5603,29 +6668,48 @@ function transformCompiledStoreModule(source) {
5603
6668
  } catch {
5604
6669
  return null;
5605
6670
  }
5606
- const imported = findStoreImport(ast);
6671
+ const imported = findStoreImport(ast, moduleId, resolveImportPath);
5607
6672
  if (!imported) return null;
5608
- const classDecl = findStoreClass(ast, imported.localName);
5609
- if (!classDecl || !classDecl.id) return null;
5610
- if (!isCompiledStoreSafeClass(classDecl)) return null;
5611
- if (!hasDefaultNewStore(ast, classDecl.id.name)) return null;
5612
- const leanResult = transformLeanDataSelectedStore(ast, classDecl, imported);
5613
- if (leanResult) return leanResult;
5614
- const storeBase = canUseLeanStore(classDecl) ? "CompiledLeanStore" : "CompiledStore";
6673
+ const classDecls = findStoreClasses(ast, imported.localName);
6674
+ if (classDecls.length === 0) return null;
6675
+ const constants = collectImportedLiteralConstants(ast, moduleId, resolveImportPath);
6676
+ const fallbackIrs = classDecls.map((classDecl) => buildStoreIr(classDecl, moduleId, "compiled", constants, ast));
6677
+ const fallback = {
6678
+ code: source,
6679
+ changed: false,
6680
+ ir: fallbackIrs[0],
6681
+ irs: fallbackIrs
6682
+ };
6683
+ if (/\b(flushSync|silent|Store\.|new\s+Store\s*\()/.test(source)) return fallback;
6684
+ if (!classDecls.every((classDecl) => isCompiledStoreSafeClass(classDecl))) return classDecls.length === 1 ? null : fallback;
6685
+ if (!classDecls.every((classDecl) => hasDefaultNewStore(ast, classDecl.id.name))) return fallback;
6686
+ if (classDecls.length === 1) {
6687
+ const leanResult = transformLeanDataSelectedStore(ast, classDecls[0], imported, moduleId, constants);
6688
+ if (leanResult) return {
6689
+ ...leanResult,
6690
+ irs: leanResult.ir ? [leanResult.ir] : void 0
6691
+ };
6692
+ }
6693
+ const storeBases = classDecls.map((classDecl) => canUseLeanStore(classDecl) ? "CompiledLeanStore" : "CompiledStore");
6694
+ const storeIrs = classDecls.map((classDecl, index) => buildStoreIr(classDecl, moduleId, storeBases[index] === "CompiledLeanStore" ? "lean" : "compiled", constants, ast));
5615
6695
  removeStoreSpecifier(imported.importDecl, imported.localName);
5616
6696
  ast.program.body = ast.program.body.filter((node) => {
5617
6697
  if (node !== imported.importDecl) return true;
5618
6698
  return imported.importDecl.specifiers.length > 0;
5619
6699
  });
5620
- ast.program.body.unshift(t.importDeclaration([t.importSpecifier(t.identifier(storeBase), t.identifier(storeBase))], t.stringLiteral(COMPILER_RUNTIME_ID)));
5621
- classDecl.superClass = t.identifier(storeBase);
6700
+ for (const storeBase of [...new Set(storeBases)]) ast.program.body.unshift(t.importDeclaration([t.importSpecifier(t.identifier(storeBase), t.identifier(storeBase))], t.stringLiteral(COMPILER_RUNTIME_ID)));
6701
+ classDecls.forEach((classDecl, index) => {
6702
+ classDecl.superClass = t.identifier(storeBases[index]);
6703
+ });
5622
6704
  return {
5623
6705
  code: generate(ast, {
5624
6706
  retainLines: false,
5625
6707
  compact: false,
5626
6708
  jsescOption: { minimal: true }
5627
6709
  }).code,
5628
- changed: true
6710
+ changed: true,
6711
+ ir: storeIrs[0],
6712
+ irs: storeIrs
5629
6713
  };
5630
6714
  }
5631
6715
  const LEAN_DATA_SELECTED_STORE_SOURCE = `
@@ -5754,7 +6838,7 @@ const __store = {
5754
6838
  };
5755
6839
  export default __store;
5756
6840
  `;
5757
- function transformLeanDataSelectedStore(ast, classDecl, imported) {
6841
+ function transformLeanDataSelectedStore(ast, classDecl, imported, moduleId, constants) {
5758
6842
  if (!classDecl.id) return null;
5759
6843
  const classIndex = ast.program.body.indexOf(classDecl);
5760
6844
  if (classIndex < 0) return null;
@@ -5763,6 +6847,7 @@ function transformLeanDataSelectedStore(ast, classDecl, imported) {
5763
6847
  const fields = collectLeanStoreFields(classDecl);
5764
6848
  if (!fields) return null;
5765
6849
  if (!isBenchmarkOperationStoreShape(classDecl)) return null;
6850
+ const storeIr = buildStoreIr(classDecl, moduleId, "lean", constants, ast);
5766
6851
  const methodProps = buildLeanStoreMethods(classDecl);
5767
6852
  if (!methodProps) return null;
5768
6853
  removeStoreSpecifier(imported.importDecl, imported.localName);
@@ -5792,9 +6877,95 @@ function transformLeanDataSelectedStore(ast, classDecl, imported) {
5792
6877
  compact: false,
5793
6878
  jsescOption: { minimal: true }
5794
6879
  }).code,
5795
- changed: true
6880
+ changed: true,
6881
+ ir: storeIr
6882
+ };
6883
+ }
6884
+ function buildStoreIr(classDecl, moduleId, runtimeBase, constants = [], moduleAst) {
6885
+ const className = classDecl.id?.name ?? "<anonymous>";
6886
+ return {
6887
+ id: storeIrId(moduleId, className),
6888
+ module: moduleId,
6889
+ className,
6890
+ runtimeBase,
6891
+ fields: storeFieldsToIr(classDecl),
6892
+ methods: storeMethodsToIr(classDecl, moduleAst),
6893
+ ...(() => {
6894
+ const getters = storeGettersToIr(classDecl);
6895
+ return getters.length > 0 ? { getters } : {};
6896
+ })(),
6897
+ ...constants.length > 0 ? { constants } : {},
6898
+ ...sourceSpan(classDecl) ? { sourceSpan: sourceSpan(classDecl) } : {}
5796
6899
  };
5797
6900
  }
6901
+ function collectImportedLiteralConstants(ast, moduleId, resolveImportPath) {
6902
+ if (!resolveImportPath) return [];
6903
+ const namesByFile = /* @__PURE__ */ new Map();
6904
+ for (const node of ast.program.body) {
6905
+ if (!t.isImportDeclaration(node) || typeof node.source.value !== "string") continue;
6906
+ const resolved = resolveImportPath(moduleId, node.source.value);
6907
+ if (!resolved) continue;
6908
+ for (const specifier of node.specifiers) {
6909
+ if (!t.isImportSpecifier(specifier) || !t.isIdentifier(specifier.imported)) continue;
6910
+ const names = namesByFile.get(resolved) ?? /* @__PURE__ */ new Set();
6911
+ names.add(specifier.imported.name);
6912
+ namesByFile.set(resolved, names);
6913
+ }
6914
+ }
6915
+ const constants = [];
6916
+ for (const [file, names] of namesByFile) constants.push(...literalConstantsFromFile(file, names));
6917
+ return constants;
6918
+ }
6919
+ function literalConstantsFromFile(file, names) {
6920
+ if (!existsSync(file)) return [];
6921
+ let ast;
6922
+ try {
6923
+ ast = parse(readFileSync(file, "utf8"), {
6924
+ sourceType: "module",
6925
+ plugins: [
6926
+ "typescript",
6927
+ "jsx",
6928
+ "classProperties"
6929
+ ],
6930
+ errorRecovery: false
6931
+ });
6932
+ } catch {
6933
+ return [];
6934
+ }
6935
+ const constants = [];
6936
+ for (const node of ast.program.body) {
6937
+ if (!t.isExportNamedDeclaration(node) || !t.isVariableDeclaration(node.declaration)) continue;
6938
+ for (const declaration of node.declaration.declarations) {
6939
+ if (!t.isIdentifier(declaration.id) || !names.has(declaration.id.name) || !declaration.init) continue;
6940
+ const literal = literalConstant(declaration.id.name, declaration.init);
6941
+ if (literal) constants.push(literal);
6942
+ }
6943
+ }
6944
+ return constants;
6945
+ }
6946
+ function literalConstant(name, value) {
6947
+ if (t.isStringLiteral(value)) return {
6948
+ name,
6949
+ value: value.value,
6950
+ valueType: "string"
6951
+ };
6952
+ if (t.isNumericLiteral(value)) return {
6953
+ name,
6954
+ value: String(value.value),
6955
+ valueType: "number"
6956
+ };
6957
+ if (t.isBooleanLiteral(value)) return {
6958
+ name,
6959
+ value: value.value ? "true" : "false",
6960
+ valueType: "boolean"
6961
+ };
6962
+ if (t.isNullLiteral(value)) return {
6963
+ name,
6964
+ value: "null",
6965
+ valueType: "null"
6966
+ };
6967
+ return null;
6968
+ }
5798
6969
  function isBenchmarkOperationStoreShape(classDecl) {
5799
6970
  const methods = /* @__PURE__ */ new Set();
5800
6971
  for (const member of classDecl.body.body) {
@@ -5892,10 +7063,10 @@ function replaceThisExpressions(node, replacement) {
5892
7063
  else replaceThisExpressions(value, replacement);
5893
7064
  }
5894
7065
  }
5895
- function findStoreImport(ast) {
7066
+ function findStoreImport(ast, moduleId, resolveImportPath) {
5896
7067
  for (const node of ast.program.body) {
5897
7068
  if (!t.isImportDeclaration(node)) continue;
5898
- if (node.source.value !== "gea" && node.source.value !== "@geajs/core") continue;
7069
+ if (!storeImportSourceProvidesStore(moduleId, node.source.value, resolveImportPath)) continue;
5899
7070
  for (const spec of node.specifiers) {
5900
7071
  if (!t.isImportSpecifier(spec)) continue;
5901
7072
  if ((t.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value) === "Store") return {
@@ -5906,13 +7077,87 @@ function findStoreImport(ast) {
5906
7077
  }
5907
7078
  return null;
5908
7079
  }
5909
- function findStoreClass(ast, storeName) {
5910
- let found = null;
7080
+ const knownStoreImportSources = new Set([
7081
+ "gea",
7082
+ "@geajs/core",
7083
+ "gea-embedded"
7084
+ ]);
7085
+ const storeExportCache = /* @__PURE__ */ new Map();
7086
+ function storeImportSourceProvidesStore(moduleId, source, resolveImportPath) {
7087
+ if (typeof source !== "string") return false;
7088
+ if (knownStoreImportSources.has(source)) return true;
7089
+ if (!resolveImportPath) return false;
7090
+ const resolved = resolveImportPath(moduleId, source);
7091
+ return !!resolved && moduleExportsStore(resolved, resolveImportPath, /* @__PURE__ */ new Set());
7092
+ }
7093
+ function moduleExportsStore(file, resolveImportPath, seen) {
7094
+ if (seen.has(file)) return false;
7095
+ const cached = storeExportCache.get(file);
7096
+ if (cached !== void 0) return cached;
7097
+ seen.add(file);
7098
+ if (!existsSync(file)) {
7099
+ storeExportCache.set(file, false);
7100
+ return false;
7101
+ }
7102
+ let ast;
7103
+ try {
7104
+ ast = parse(readFileSync(file, "utf8"), {
7105
+ sourceType: "module",
7106
+ plugins: [
7107
+ "typescript",
7108
+ "jsx",
7109
+ "classProperties"
7110
+ ],
7111
+ errorRecovery: false
7112
+ });
7113
+ } catch {
7114
+ storeExportCache.set(file, false);
7115
+ return false;
7116
+ }
7117
+ for (const node of ast.program.body) {
7118
+ if (t.isExportNamedDeclaration(node)) {
7119
+ if (node.declaration) {
7120
+ if (t.isVariableDeclaration(node.declaration)) {
7121
+ for (const declaration of node.declaration.declarations) if (t.isIdentifier(declaration.id, { name: "Store" })) {
7122
+ storeExportCache.set(file, true);
7123
+ return true;
7124
+ }
7125
+ }
7126
+ if (t.isClassDeclaration(node.declaration) && t.isIdentifier(node.declaration.id, { name: "Store" })) {
7127
+ storeExportCache.set(file, true);
7128
+ return true;
7129
+ }
7130
+ }
7131
+ for (const specifier of node.specifiers) {
7132
+ if ((t.isIdentifier(specifier.exported) ? specifier.exported.name : specifier.exported.value) !== "Store") continue;
7133
+ if (!node.source || typeof node.source.value !== "string") {
7134
+ storeExportCache.set(file, true);
7135
+ return true;
7136
+ }
7137
+ const resolved = resolveImportPath(file, node.source.value);
7138
+ if (resolved && moduleExportsStore(resolved, resolveImportPath, seen)) {
7139
+ storeExportCache.set(file, true);
7140
+ return true;
7141
+ }
7142
+ }
7143
+ }
7144
+ if (t.isExportAllDeclaration(node) && typeof node.source.value === "string") {
7145
+ const resolved = resolveImportPath(file, node.source.value);
7146
+ if (resolved && moduleExportsStore(resolved, resolveImportPath, seen)) {
7147
+ storeExportCache.set(file, true);
7148
+ return true;
7149
+ }
7150
+ }
7151
+ }
7152
+ storeExportCache.set(file, false);
7153
+ return false;
7154
+ }
7155
+ function findStoreClasses(ast, storeName) {
7156
+ const found = [];
5911
7157
  for (const node of ast.program.body) {
5912
7158
  const decl = t.isClassDeclaration(node) ? node : t.isExportNamedDeclaration(node) && t.isClassDeclaration(node.declaration) ? node.declaration : null;
5913
- if (!decl || !t.isIdentifier(decl.superClass, { name: storeName })) continue;
5914
- if (found) return null;
5915
- found = decl;
7159
+ if (!decl || !decl.id || !t.isIdentifier(decl.superClass, { name: storeName })) continue;
7160
+ found.push(decl);
5916
7161
  }
5917
7162
  return found;
5918
7163
  }
@@ -6066,7 +7311,8 @@ function transformStaticRootMount(source, filePath, resolveImportPath) {
6066
7311
  compact: false,
6067
7312
  jsescOption: { minimal: true }
6068
7313
  }).code,
6069
- changed: true
7314
+ changed: true,
7315
+ watchFiles: [resolved, ...factory.watchFiles]
6070
7316
  };
6071
7317
  }
6072
7318
  function collectDefaultImports(ast) {
@@ -6162,6 +7408,7 @@ function createStaticTemplateFactory(componentPath, tplName, mountFilePath, reso
6162
7408
  const jsx = templateMethod ? extractTemplateJsx(templateMethod) : null;
6163
7409
  if (!jsx || !isBuiltinElementRoot(jsx)) return null;
6164
7410
  const ctx = createEmitContext();
7411
+ const watchFiles = /* @__PURE__ */ new Set();
6165
7412
  ctx.directFnComponents = collectDirectFnComponents(ast);
6166
7413
  ctx.directFnComponentParams = collectDirectFnComponentParams(ast, ctx.directFnComponents);
6167
7414
  ctx.directFnNoDisposer = /* @__PURE__ */ new Set();
@@ -6169,7 +7416,7 @@ function createStaticTemplateFactory(componentPath, tplName, mountFilePath, reso
6169
7416
  ctx.directClassComponents = /* @__PURE__ */ new Set();
6170
7417
  ctx.directFactoryComponents = /* @__PURE__ */ new Set();
6171
7418
  const importedFns = [];
6172
- if (!collectImportedStaticFunctionComponents(ast, componentPath, resolveImportPath, ctx, importedFns)) return null;
7419
+ if (!collectImportedStaticFunctionComponents(ast, componentPath, resolveImportPath, ctx, importedFns, watchFiles)) return null;
6173
7420
  ctx.directFnStringProps = collectDirectFnStringProps(ast, ctx.directFnComponents);
6174
7421
  if (nodeContainsAnyIdentifier(jsx, collectRootModuleBindings(ast, classDecl, ctx.directFnComponents))) return null;
6175
7422
  const importedFnDecls = [];
@@ -6217,7 +7464,8 @@ function createStaticTemplateFactory(componentPath, tplName, mountFilePath, reso
6217
7464
  factoryDecl
6218
7465
  ],
6219
7466
  mountExpression: t.callExpression(t.identifier(tplName + "_create"), disposerArg),
6220
- importsNeeded: ctx.importsNeeded
7467
+ importsNeeded: ctx.importsNeeded,
7468
+ watchFiles: [...watchFiles]
6221
7469
  };
6222
7470
  }
6223
7471
  function collectCopiedImports(ast, componentPath, mountFilePath, resolveImportPath, compiledImportNames) {
@@ -6246,7 +7494,7 @@ function toRelativeImportSource(fromFile, targetFile) {
6246
7494
  if (!source.startsWith(".")) source = "./" + source;
6247
7495
  return source;
6248
7496
  }
6249
- function collectImportedStaticFunctionComponents(ast, importerPath, resolveImportPath, ctx, importedFns) {
7497
+ function collectImportedStaticFunctionComponents(ast, importerPath, resolveImportPath, ctx, importedFns, watchFiles) {
6250
7498
  for (const stmt of ast.program.body) {
6251
7499
  if (!t.isImportDeclaration(stmt)) continue;
6252
7500
  if (stmt.source.value === "@geajs/core") continue;
@@ -6256,6 +7504,7 @@ function collectImportedStaticFunctionComponents(ast, importerPath, resolveImpor
6256
7504
  if (componentSpecs.length === 0) continue;
6257
7505
  const resolved = resolveImportPath(importerPath, stmt.source.value);
6258
7506
  if (!resolved) return false;
7507
+ watchFiles.add(resolved);
6259
7508
  const imported = readImportModule(resolved);
6260
7509
  if (!imported) return false;
6261
7510
  for (const spec of componentSpecs) {
@@ -6816,11 +8065,21 @@ function shouldMinifyGeaSymbolsForBuild(config) {
6816
8065
  const formats = lib.formats ?? [];
6817
8066
  return formats.length > 0 && formats.every((format) => format === "iife" || format === "umd");
6818
8067
  }
6819
- function geaPlugin() {
8068
+ function geaPlugin(options = {}) {
8069
+ const envIrOutFile = process.env.GEA_IR_OUT || process.env.GEA_IR_FILE;
8070
+ const irOptions = options.ir ?? (envIrOutFile ? {
8071
+ enabled: true,
8072
+ outFile: envIrOutFile
8073
+ } : void 0);
6820
8074
  const storeModules = /* @__PURE__ */ new Set();
6821
8075
  const componentModules = /* @__PURE__ */ new Set();
6822
8076
  let isServeCommand = false;
6823
8077
  let shouldMinifyGeaSymbolKeys = false;
8078
+ let resolvedConfig = null;
8079
+ const irModules = /* @__PURE__ */ new Map();
8080
+ const irComponents = /* @__PURE__ */ new Map();
8081
+ const irStores = /* @__PURE__ */ new Map();
8082
+ const hostCapabilities = /* @__PURE__ */ new Set();
6824
8083
  const storeRegistry = /* @__PURE__ */ new Map();
6825
8084
  const resolveImportPath = (importer, source) => {
6826
8085
  const base = resolve(dirname(importer), source);
@@ -6931,6 +8190,7 @@ function geaPlugin() {
6931
8190
  name: "gea-plugin",
6932
8191
  enforce: "pre",
6933
8192
  configResolved(config) {
8193
+ resolvedConfig = config;
6934
8194
  isServeCommand = config.command === "serve";
6935
8195
  shouldMinifyGeaSymbolKeys = shouldMinifyGeaSymbolsForBuild(config);
6936
8196
  },
@@ -6977,6 +8237,7 @@ function geaPlugin() {
6977
8237
  if (!cleanId.match(/\.(js|jsx|ts|tsx)$/) || cleanId.includes("node_modules")) return null;
6978
8238
  let transformedCode = code;
6979
8239
  let changed = false;
8240
+ if (irOptions?.enabled) recordHostCapabilities(code);
6980
8241
  if (code.includes("extends Store") || code.includes("new Store(")) {
6981
8242
  storeModules.add(cleanId);
6982
8243
  const storeClassName = extractStoreClassName(code);
@@ -6989,22 +8250,30 @@ function geaPlugin() {
6989
8250
  }
6990
8251
  }
6991
8252
  if (/\bclass\s+Component\s+extends\s+Store\b/.test(code)) return null;
6992
- if (!isServeCommand && !isSSR) {
8253
+ if (!isSSR) {
6993
8254
  const observeResult = transformDottedObserveCalls(transformedCode);
6994
8255
  if (observeResult?.changed) {
6995
8256
  transformedCode = observeResult.code;
6996
8257
  changed = true;
6997
8258
  }
6998
- const storeResult = transformCompiledStoreModule(transformedCode);
6999
- if (storeResult?.changed) return {
7000
- code: storeResult.code,
7001
- map: null
7002
- };
8259
+ const storeResult = transformCompiledStoreModule(transformedCode, cleanId, resolveImportPath);
8260
+ for (const storeIr of storeResult?.irs ?? (storeResult?.ir ? [storeResult.ir] : [])) recordStoreIr(cleanId, storeIr);
8261
+ if (storeResult?.changed) {
8262
+ if (!/\bextends\s+(Component|ReactiveComponent)\b|\bmount\s*\(/.test(storeResult.code)) return {
8263
+ code: storeResult.code,
8264
+ map: null
8265
+ };
8266
+ transformedCode = storeResult.code;
8267
+ changed = true;
8268
+ }
7003
8269
  const rootMountResult = transformStaticRootMount(transformedCode, cleanId, resolveImportPath);
7004
- if (rootMountResult?.changed) return {
7005
- code: rootMountResult.code,
7006
- map: null
7007
- };
8270
+ if (rootMountResult?.changed) {
8271
+ for (const file of rootMountResult.watchFiles ?? []) this.addWatchFile?.(file);
8272
+ return {
8273
+ code: rootMountResult.code,
8274
+ map: null
8275
+ };
8276
+ }
7008
8277
  }
7009
8278
  const result = transform({
7010
8279
  sourceFile: cleanId,
@@ -7020,7 +8289,15 @@ function geaPlugin() {
7020
8289
  registerStoreModule: (fp) => storeModules.add(fp),
7021
8290
  registerComponentModule: (fp) => componentModules.add(fp)
7022
8291
  });
7023
- if (result) return result;
8292
+ if (result) {
8293
+ if (result.ir) recordComponentIr(cleanId, result.ir);
8294
+ if (result.ir?.components.some((component) => component.reactiveState)) return {
8295
+ code: result.code,
8296
+ map: result.map ?? null,
8297
+ moduleSideEffects: "no-treeshake"
8298
+ };
8299
+ return result;
8300
+ }
7024
8301
  if (isServeCommand && !isSSR) {
7025
8302
  const componentDeps = findComponentDeps(transformedCode, cleanId);
7026
8303
  if (componentDeps.length > 0) {
@@ -7044,8 +8321,136 @@ function geaPlugin() {
7044
8321
  code: next,
7045
8322
  map: null
7046
8323
  };
8324
+ },
8325
+ generateBundle(_options, bundle) {
8326
+ if (!irOptions?.enabled) return;
8327
+ const renderedIds = renderedModuleIds(bundle);
8328
+ const modules = Array.from(irModules.values()).filter((module) => {
8329
+ if (!renderedIds) return true;
8330
+ return renderedIds.has(cleanRollupModuleId(module.id)) || renderedIds.has(cleanRollupModuleId(module.file));
8331
+ });
8332
+ const componentIds = new Set(modules.flatMap((module) => module.components));
8333
+ const storeIds = new Set(modules.flatMap((module) => module.stores));
8334
+ const moduleById = new Map(Array.from(irModules.values()).map((module) => [module.id, module]));
8335
+ const pending = Array.from(componentIds);
8336
+ while (pending.length > 0) {
8337
+ const component = irComponents.get(pending.pop());
8338
+ if (!component) continue;
8339
+ for (const tag of collectMountTags(component.template.slots)) for (const candidate of irComponents.values()) {
8340
+ if (candidate.exportName !== tag || componentIds.has(candidate.id)) continue;
8341
+ componentIds.add(candidate.id);
8342
+ pending.push(candidate.id);
8343
+ const candidateModule = moduleById.get(candidate.module);
8344
+ if (candidateModule && !modules.includes(candidateModule)) modules.push(candidateModule);
8345
+ }
8346
+ }
8347
+ const irBundle = {
8348
+ schema: "gea-ir",
8349
+ version: 1,
8350
+ entry: geaIrEntryFromBundle(bundle) ?? geaIrConfiguredEntry(resolvedConfig),
8351
+ modules,
8352
+ components: Array.from(irComponents.values()).filter((component) => componentIds.has(component.id)),
8353
+ stores: Array.from(irStores.values()).filter((store) => storeIds.has(store.id)),
8354
+ hostCapabilities: Array.from(hostCapabilities).sort()
8355
+ };
8356
+ const source = JSON.stringify(irBundle, null, 2);
8357
+ const outFile = irOptions.outFile ?? "gea-ir.json";
8358
+ if (outFile.startsWith("/") || /^[A-Za-z]:[\\/]/.test(outFile)) {
8359
+ mkdirSync(dirname(outFile), { recursive: true });
8360
+ writeFileSync(outFile, source);
8361
+ } else this.emitFile({
8362
+ type: "asset",
8363
+ fileName: outFile,
8364
+ source
8365
+ });
7047
8366
  }
7048
8367
  };
8368
+ function collectMountTags(slots, tags = /* @__PURE__ */ new Set(), depth = 0) {
8369
+ if (depth > 8 || !Array.isArray(slots)) return tags;
8370
+ for (const slot of slots) {
8371
+ if (!slot || typeof slot !== "object") continue;
8372
+ const { kind, payload } = slot;
8373
+ if (!payload || typeof payload !== "object") continue;
8374
+ const record = payload;
8375
+ if (kind === "mount" && typeof record.tag === "string") tags.add(record.tag);
8376
+ for (const key of Object.keys(record)) {
8377
+ if (key === "attrs" || key === "children") continue;
8378
+ const value = record[key];
8379
+ if (value && typeof value === "object" && Array.isArray(value.slots)) collectMountTags(value.slots, tags, depth + 1);
8380
+ }
8381
+ }
8382
+ return tags;
8383
+ }
8384
+ function recordComponentIr(moduleId, ir) {
8385
+ const existing = irModules.get(moduleId) ?? {
8386
+ id: moduleId,
8387
+ file: moduleId,
8388
+ components: [],
8389
+ stores: []
8390
+ };
8391
+ const componentIds = new Set(existing.components);
8392
+ for (const component of ir.components) {
8393
+ irComponents.set(component.id, component);
8394
+ componentIds.add(component.id);
8395
+ }
8396
+ irModules.set(moduleId, {
8397
+ ...existing,
8398
+ components: Array.from(componentIds)
8399
+ });
8400
+ }
8401
+ function recordStoreIr(moduleId, store) {
8402
+ const existing = irModules.get(moduleId) ?? {
8403
+ id: moduleId,
8404
+ file: moduleId,
8405
+ components: [],
8406
+ stores: []
8407
+ };
8408
+ const storeIds = new Set(existing.stores);
8409
+ irStores.set(store.id, store);
8410
+ storeIds.add(store.id);
8411
+ irModules.set(moduleId, {
8412
+ ...existing,
8413
+ stores: Array.from(storeIds)
8414
+ });
8415
+ }
8416
+ function recordHostCapabilities(source) {
8417
+ if (/\bfetch\s*\(/.test(source)) hostCapabilities.add("fetch");
8418
+ if (source.includes("https://")) hostCapabilities.add("https");
8419
+ if (/\bApps\s*\./.test(source)) hostCapabilities.add("apps");
8420
+ if (/\b(?:BLE|BLEServer)\b|\bgea_embedded_ble_|\b__gea_embedded_ble_/.test(source)) hostCapabilities.add("ble");
8421
+ if (/\bWiFi\s*\./.test(source)) hostCapabilities.add("wifi");
8422
+ if (/\b(?:Accelerometer|accelerometer)\s*\.|\bgea_embedded_imu_/.test(source)) hostCapabilities.add("imu");
8423
+ if (/\baudioContext\s*\.|\b__gea_audioContext\b|\b__gea_Audio\b/.test(source)) hostCapabilities.add("audio");
8424
+ if (/\bscreen\s*\./.test(source)) hostCapabilities.add("screen");
8425
+ if (/\b__gea_embedded_image\b/.test(source)) hostCapabilities.add("image");
8426
+ if (/\b__gea_embedded_touch\b/.test(source)) hostCapabilities.add("touch");
8427
+ if (/\bdocument\s*\./.test(source)) hostCapabilities.add("dom");
8428
+ }
8429
+ }
8430
+ function renderedModuleIds(bundle) {
8431
+ const ids = /* @__PURE__ */ new Set();
8432
+ for (const item of Object.values(bundle)) {
8433
+ if (item.type !== "chunk") continue;
8434
+ if (item.facadeModuleId) ids.add(cleanRollupModuleId(item.facadeModuleId));
8435
+ if (item.modules && typeof item.modules === "object") for (const id of Object.keys(item.modules)) ids.add(cleanRollupModuleId(id));
8436
+ }
8437
+ return ids.size > 0 ? ids : null;
8438
+ }
8439
+ function cleanRollupModuleId(id) {
8440
+ return id.split("?")[0] ?? id;
8441
+ }
8442
+ function geaIrEntryFromBundle(bundle) {
8443
+ return Object.values(bundle).filter((item) => item.type === "chunk" && item.isEntry && item.fileName).map((item) => item.fileName).sort()[0] ?? null;
8444
+ }
8445
+ function geaIrConfiguredEntry(config) {
8446
+ const input = config?.build.rollupOptions.input;
8447
+ if (typeof input === "string") return input;
8448
+ if (Array.isArray(input)) return input[0] ? String(input[0]) : "";
8449
+ if (input && typeof input === "object") {
8450
+ const firstKey = Object.keys(input).sort()[0];
8451
+ return firstKey ? String(input[firstKey]) : "";
8452
+ }
8453
+ return "";
7049
8454
  }
7050
8455
  function resolveToFile(base) {
7051
8456
  const exts = [
@@ -7098,6 +8503,7 @@ function compilerRuntimeSource(runtimePath) {
7098
8503
  relationalClass,
7099
8504
  relationalClassProp,
7100
8505
  reactiveStyle,
8506
+ reactiveStyleProp,
7101
8507
  reactiveValue,
7102
8508
  reactiveValueRead,
7103
8509
  delegateEvent,