@geajs/vite-plugin 1.3.1 → 1.4.1

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];
@@ -2734,8 +3008,21 @@ function buildInlinePropKeyedListBlock(options) {
2734
3008
  __PATCH__: options.patchEntryArrow
2735
3009
  });
2736
3010
  replaceByKeyMarker(block, options.relMatches);
3011
+ if (options.ctx.embedded) rewriteObserveReconcileForEmbedded(block);
2737
3012
  return block;
2738
3013
  }
3014
+ function rewriteObserveReconcileForEmbedded(node) {
3015
+ if (!node || typeof node !== "object") return;
3016
+ if (Array.isArray(node)) {
3017
+ for (const value of node) rewriteObserveReconcileForEmbedded(value);
3018
+ return;
3019
+ }
3020
+ if (node.type === "CallExpression" && t.isIdentifier(node.callee, { name: "__kl_reconcile" }) && node.arguments.length >= 1 && t.isIdentifier(node.arguments[0], { name: "_value" })) node.arguments[0] = t.callExpression(t.identifier("__kl_resolve"), []);
3021
+ for (const key of Object.keys(node)) {
3022
+ if (key === "loc" || key === "start" || key === "end" || key === "type") continue;
3023
+ rewriteObserveReconcileForEmbedded(node[key]);
3024
+ }
3025
+ }
2739
3026
  function isDirectIdKey(expr) {
2740
3027
  if (!t.isArrowFunctionExpression(expr) || expr.params.length === 0) return false;
2741
3028
  const firstParam = expr.params[0];
@@ -3405,54 +3692,6 @@ function normalizeEventAttrName(name) {
3405
3692
  return toGeaEventType(name);
3406
3693
  }
3407
3694
  //#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
3695
  //#region src/closure-codegen/generator/walk.ts
3457
3696
  const OPTIONAL_TABLE_END_TAGS = new Set([
3458
3697
  "colgroup",
@@ -3910,13 +4149,31 @@ function emitSlot(slot, stmts, ctx) {
3910
4149
  pathOrGetter.value
3911
4150
  ])));
3912
4151
  } 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
- ])));
4152
+ const styleExpr = slot.expr;
4153
+ const styleProps = t.isObjectExpression(styleExpr) && styleExpr.properties.length > 0 ? styleExpr.properties : null;
4154
+ if (styleProps !== null && styleProps.every((p) => t.isObjectProperty(p) && !p.computed && (t.isIdentifier(p.key) || t.isStringLiteral(p.key)))) {
4155
+ ctx.importsNeeded.add("reactiveStyleProp");
4156
+ for (const p of styleProps) {
4157
+ const keyName = t.isIdentifier(p.key) ? p.key.name : p.key.value;
4158
+ const kebabKey = String(keyName).replace(/[A-Z]/g, (m) => "-" + m.toLowerCase());
4159
+ const valSource = expressionToPathOrGetter(p.value, ctx);
4160
+ stmts.push(t.expressionStatement(t.callExpression(t.identifier("reactiveStyleProp"), [
4161
+ elId,
4162
+ t.identifier("d"),
4163
+ ctx.reactiveRoot,
4164
+ t.stringLiteral(kebabKey),
4165
+ valSource.value
4166
+ ])));
4167
+ }
4168
+ } else {
4169
+ ctx.importsNeeded.add("reactiveStyle");
4170
+ stmts.push(t.expressionStatement(t.callExpression(t.identifier("reactiveStyle"), [
4171
+ elId,
4172
+ t.identifier("d"),
4173
+ ctx.reactiveRoot,
4174
+ pathOrGetter.value
4175
+ ])));
4176
+ }
3920
4177
  } else if (slot.kind === "value") {
3921
4178
  ctx.importsNeeded.add("reactiveValueRead");
3922
4179
  stmts.push(t.expressionStatement(t.callExpression(t.identifier("reactiveValueRead"), [
@@ -4107,29 +4364,734 @@ function isOneShotStringPropLocal(expr, ctx) {
4107
4364
  return t.isIdentifier(expr) && ctx.oneShotStringPropLocals?.has(expr.name) === true;
4108
4365
  }
4109
4366
  //#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];
4367
+ //#region src/closure-codegen/ir.ts
4368
+ function componentIrId(moduleId, exportName) {
4369
+ return `${moduleId}#${exportName}`;
4370
+ }
4371
+ function storeIrId(moduleId, className) {
4372
+ return `${moduleId}#${className}`;
4373
+ }
4374
+ function templateSpecToIr(spec, bindings = /* @__PURE__ */ new Map()) {
4375
+ return {
4376
+ html: spec.html,
4377
+ slots: spec.slots.map((slot) => slotToIr(slot, bindings))
4378
+ };
4379
+ }
4380
+ function storeFieldsToIr(classDecl) {
4381
+ const fields = [];
4382
+ for (const member of classDecl.body.body) {
4383
+ if (!t.isClassProperty(member) || member.static || member.computed || !t.isIdentifier(member.key)) continue;
4384
+ const field = {
4385
+ name: member.key.name,
4386
+ ...member.value ? { initializer: generate(member.value).code } : {},
4387
+ ...member.value ? shapeForExpression(member.value) : {}
4388
+ };
4389
+ if (field.shape?.kind === "array") {
4390
+ const elementTypeName = arrayElementTypeNameFromAnnotation(member);
4391
+ if (elementTypeName) field.shape = {
4392
+ ...field.shape,
4393
+ elementTypeName
4394
+ };
4395
+ }
4396
+ fields.push(field);
4121
4397
  }
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);
4398
+ return fields;
4399
+ }
4400
+ function arrayElementTypeNameFromAnnotation(member) {
4401
+ const annotation = member.typeAnnotation;
4402
+ if (!annotation || !t.isTSTypeAnnotation(annotation)) return void 0;
4403
+ return arrayElementTypeNameFromTSType(annotation.typeAnnotation);
4404
+ }
4405
+ function arrayElementTypeNameFromTSType(typeNode) {
4406
+ if (t.isTSArrayType(typeNode)) {
4407
+ const element = typeNode.elementType;
4408
+ if (t.isTSTypeReference(element) && t.isIdentifier(element.typeName)) return element.typeName.name;
4409
+ return;
4410
+ }
4411
+ if (t.isTSTypeReference(typeNode) && t.isIdentifier(typeNode.typeName)) {
4412
+ const containerName = typeNode.typeName.name;
4413
+ if (containerName !== "Array" && containerName !== "ReadonlyArray") return void 0;
4414
+ const args = typeNode.typeParameters?.params;
4415
+ if (!args || args.length !== 1) return void 0;
4416
+ const element = args[0];
4417
+ if (t.isTSTypeReference(element) && t.isIdentifier(element.typeName)) return element.typeName.name;
4418
+ return;
4419
+ }
4420
+ }
4421
+ const ARRAY_PRODUCING_METHODS = new Set([
4422
+ "filter",
4423
+ "map",
4424
+ "slice",
4425
+ "concat",
4426
+ "flat",
4427
+ "flatMap",
4428
+ "sort",
4429
+ "toSorted",
4430
+ "reverse",
4431
+ "toReversed"
4432
+ ]);
4433
+ function storeGettersToIr(classDecl) {
4434
+ const getters = [];
4435
+ const fieldShapeByName = /* @__PURE__ */ new Map();
4436
+ for (const field of storeFieldsToIr(classDecl)) if (field.shape) fieldShapeByName.set(field.name, field.shape);
4437
+ for (const member of classDecl.body.body) {
4438
+ if (!t.isClassMethod(member) || member.static || member.computed || member.kind !== "get") continue;
4439
+ if (!t.isIdentifier(member.key)) continue;
4440
+ const returnType = member.returnType;
4441
+ const elementTypeName = returnType && t.isTSTypeAnnotation(returnType) ? arrayElementTypeNameFromTSType(returnType.typeAnnotation) : void 0;
4442
+ const deps = collectThisFieldReads(member.body);
4443
+ const elementShape = getterElementShape(member.body, deps, elementTypeName, fieldShapeByName);
4444
+ const returnsArray = !!elementTypeName || !!elementShape || getterBodyReturnsArray(member.body);
4445
+ const shape = returnsArray ? {
4446
+ kind: "array",
4447
+ ...elementShape ? { element: elementShape } : {},
4448
+ ...elementTypeName ? { elementTypeName } : {}
4449
+ } : void 0;
4450
+ const ops = storeStmtsToIr(member.body.body);
4451
+ const getter = {
4452
+ name: member.key.name,
4453
+ returnsArray,
4454
+ deps,
4455
+ body: generate(member.body).code,
4456
+ ...elementTypeName ? { elementTypeName } : {},
4457
+ ...shape ? { shape } : {},
4458
+ ...ops ? { ops } : {},
4459
+ ...sourceSpan(member) ? { sourceSpan: sourceSpan(member) } : {}
4460
+ };
4461
+ getters.push(getter);
4462
+ }
4463
+ return getters;
4464
+ }
4465
+ function getterElementShape(body, deps, elementTypeName, fieldShapeByName) {
4466
+ const arg = topLevelReturnArgument(body);
4467
+ const fromBody = arg ? elementShapeFromArrayExpression(arg, fieldShapeByName) : void 0;
4468
+ if (fromBody) return fromBody;
4469
+ if (elementTypeName) {
4470
+ for (const shape of fieldShapeByName.values()) if (shape.kind === "array" && shape.elementTypeName === elementTypeName && shape.element?.kind === "object") return shape.element;
4471
+ }
4472
+ for (const dep of deps) {
4473
+ const shape = fieldShapeByName.get(dep);
4474
+ if (shape?.kind === "array" && shape.element?.kind === "object") return shape.element;
4475
+ }
4476
+ }
4477
+ function elementShapeFromArrayExpression(expr, fieldShapeByName) {
4478
+ if (t.isArrayExpression(expr)) {
4479
+ const first = expr.elements.find((element) => !!element && !t.isSpreadElement(element));
4480
+ if (!first) return void 0;
4481
+ const shaped = shapeForExpression(first);
4482
+ return "shape" in shaped ? shaped.shape : void 0;
4483
+ }
4484
+ if (t.isCallExpression(expr) && t.isMemberExpression(expr.callee) && t.isIdentifier(expr.callee.property)) {
4485
+ const method = expr.callee.property.name;
4486
+ if (method === "map") {
4487
+ const objectLiteral = mapCallbackObjectLiteral(expr.arguments[0]);
4488
+ if (!objectLiteral) return void 0;
4489
+ const shaped = shapeForExpression(objectLiteral);
4490
+ return "shape" in shaped ? shaped.shape : void 0;
4491
+ }
4492
+ if (ARRAY_PRODUCING_METHODS.has(method)) return elementShapeFromArrayExpression(expr.callee.object, fieldShapeByName);
4493
+ }
4494
+ if (t.isMemberExpression(expr) && t.isThisExpression(expr.object) && t.isIdentifier(expr.property)) {
4495
+ const shape = fieldShapeByName.get(expr.property.name);
4496
+ if (shape?.kind === "array") return shape.element;
4497
+ }
4498
+ }
4499
+ function mapCallbackObjectLiteral(callback) {
4500
+ if (!callback || !t.isArrowFunctionExpression(callback) && !t.isFunctionExpression(callback)) return void 0;
4501
+ const body = callback.body;
4502
+ if (t.isObjectExpression(body)) return body;
4503
+ if (t.isBlockStatement(body)) {
4504
+ for (const statement of body.body) if (t.isReturnStatement(statement) && statement.argument && t.isObjectExpression(statement.argument)) return statement.argument;
4505
+ }
4506
+ }
4507
+ function topLevelReturnArgument(body) {
4508
+ let result;
4509
+ let done = false;
4510
+ const visit = (value) => {
4511
+ if (done || !value || typeof value !== "object") return;
4512
+ if (Array.isArray(value)) {
4513
+ for (const item of value) visit(item);
4514
+ return;
4515
+ }
4516
+ const node = value;
4517
+ const type = node.type;
4518
+ if (type === "FunctionExpression" || type === "ArrowFunctionExpression" || type === "FunctionDeclaration") return;
4519
+ if (type === "ReturnStatement") {
4520
+ result = node.argument;
4521
+ done = true;
4522
+ return;
4523
+ }
4524
+ for (const key of Object.keys(node)) {
4525
+ if (key === "loc" || key === "start" || key === "end" || key === "range") continue;
4526
+ visit(node[key]);
4527
+ }
4528
+ };
4529
+ visit(body);
4530
+ return result;
4531
+ }
4532
+ function getterBodyReturnsArray(body) {
4533
+ let found = false;
4534
+ const visit = (value) => {
4535
+ if (found || !value || typeof value !== "object") return;
4536
+ if (Array.isArray(value)) {
4537
+ for (const item of value) visit(item);
4538
+ return;
4539
+ }
4540
+ const node = value;
4541
+ const type = node.type;
4542
+ if (type === "FunctionExpression" || type === "ArrowFunctionExpression" || type === "FunctionDeclaration") return;
4543
+ if (type === "ReturnStatement" && isArrayProducingExpression(node.argument)) {
4544
+ found = true;
4545
+ return;
4546
+ }
4547
+ for (const key of Object.keys(node)) {
4548
+ if (key === "loc" || key === "start" || key === "end" || key === "range") continue;
4549
+ visit(node[key]);
4550
+ }
4551
+ };
4552
+ visit(body);
4553
+ return found;
4554
+ }
4555
+ function isArrayProducingExpression(expr) {
4556
+ if (!expr || typeof expr !== "object") return false;
4557
+ const node = expr;
4558
+ if (node.type === "ArrayExpression") return true;
4559
+ if (node.type === "TSAsExpression" || node.type === "TSNonNullExpression") return isArrayProducingExpression(node.expression);
4560
+ if (node.type === "CallExpression") {
4561
+ const callee = node.callee;
4562
+ const property = callee?.property;
4563
+ if (callee?.type === "MemberExpression" && property?.type === "Identifier") return ARRAY_PRODUCING_METHODS.has(property.name);
4564
+ }
4565
+ return false;
4566
+ }
4567
+ function collectThisFieldReads(node) {
4568
+ const names = /* @__PURE__ */ new Set();
4569
+ const visit = (value) => {
4570
+ if (!value || typeof value !== "object") return;
4571
+ if (Array.isArray(value)) {
4572
+ for (const item of value) visit(item);
4573
+ return;
4574
+ }
4575
+ const rec = value;
4576
+ const object = rec.object;
4577
+ const property = rec.property;
4578
+ if (rec.type === "MemberExpression" && object?.type === "ThisExpression" && rec.computed !== true && property?.type === "Identifier") names.add(property.name);
4579
+ for (const key of Object.keys(rec)) {
4580
+ if (key === "loc" || key === "start" || key === "end" || key === "range") continue;
4581
+ visit(rec[key]);
4582
+ }
4583
+ };
4584
+ visit(node);
4585
+ return [...names];
4586
+ }
4587
+ function storeMethodsToIr(classDecl, moduleAst) {
4588
+ const methods = [];
4589
+ const literalUnionAliases = moduleAst ? collectLiteralUnionAliasValueTypes(moduleAst) : void 0;
4590
+ for (const member of classDecl.body.body) {
4591
+ if (!t.isClassMethod(member) || member.static || member.computed || member.kind !== "method") continue;
4592
+ if (!t.isIdentifier(member.key)) continue;
4593
+ const params = [];
4594
+ let unsupportedParam = false;
4595
+ for (const param of member.params) {
4596
+ const assignment = t.isAssignmentPattern(param) ? param : null;
4597
+ const identifier = t.isIdentifier(param) ? param : assignment && t.isIdentifier(assignment.left) ? assignment.left : null;
4598
+ if (identifier) {
4599
+ const valueType = paramValueType(identifier, literalUnionAliases) ?? (assignment ? paramDefaultValueType(assignment.right) : void 0);
4600
+ params.push(valueType ? {
4601
+ name: identifier.name,
4602
+ valueType
4603
+ } : { name: identifier.name });
4604
+ } else {
4605
+ unsupportedParam = true;
4606
+ break;
4607
+ }
4608
+ }
4609
+ if (unsupportedParam) continue;
4610
+ const ops = storeStmtsToIr(member.body.body);
4611
+ methods.push({
4612
+ name: member.key.name,
4613
+ params,
4614
+ body: generate(member.body).code,
4615
+ ...ops ? { ops } : {},
4616
+ ...sourceSpan(member) ? { sourceSpan: sourceSpan(member) } : {}
4617
+ });
4618
+ }
4619
+ return methods;
4620
+ }
4621
+ function paramValueType(param, literalUnionAliases) {
4622
+ const annotation = param.typeAnnotation;
4623
+ if (!annotation || !t.isTSTypeAnnotation(annotation)) return void 0;
4624
+ const kind = annotation.typeAnnotation;
4625
+ if (t.isTSStringKeyword(kind)) return "string";
4626
+ if (t.isTSNumberKeyword(kind)) return "number";
4627
+ if (t.isTSBooleanKeyword(kind)) return "boolean";
4628
+ const inlineLiteral = literalUnionValueType(kind);
4629
+ if (inlineLiteral) return inlineLiteral;
4630
+ if (literalUnionAliases && t.isTSTypeReference(kind) && t.isIdentifier(kind.typeName)) return literalUnionAliases.get(kind.typeName.name);
4631
+ }
4632
+ function literalUnionValueType(kind) {
4633
+ if (t.isTSLiteralType(kind)) {
4634
+ if (t.isStringLiteral(kind.literal)) return "string";
4635
+ if (t.isNumericLiteral(kind.literal)) return "number";
4636
+ if (t.isBooleanLiteral(kind.literal)) return "boolean";
4637
+ return;
4638
+ }
4639
+ if (t.isTSUnionType(kind)) {
4640
+ let valueType;
4641
+ for (const member of kind.types) {
4642
+ const memberType = literalUnionValueType(member);
4643
+ if (!memberType || valueType && memberType !== valueType) return void 0;
4644
+ valueType = memberType;
4645
+ }
4646
+ return valueType;
4647
+ }
4648
+ }
4649
+ function collectLiteralUnionAliasValueTypes(ast) {
4650
+ const aliases = /* @__PURE__ */ new Map();
4651
+ for (const node of ast.program.body) {
4652
+ const alias = t.isTSTypeAliasDeclaration(node) ? node : t.isExportNamedDeclaration(node) && t.isTSTypeAliasDeclaration(node.declaration) ? node.declaration : null;
4653
+ if (!alias || !t.isIdentifier(alias.id)) continue;
4654
+ const valueType = literalUnionValueType(alias.typeAnnotation);
4655
+ if (valueType) aliases.set(alias.id.name, valueType);
4656
+ }
4657
+ return aliases;
4658
+ }
4659
+ function paramDefaultValueType(expr) {
4660
+ if (t.isStringLiteral(expr)) return "string";
4661
+ if (t.isNumericLiteral(expr)) return "number";
4662
+ if (t.isBooleanLiteral(expr)) return "boolean";
4663
+ }
4664
+ function storeStmtsToIr(statements) {
4665
+ const out = [];
4666
+ for (const statement of statements) {
4667
+ const converted = storeStmtToIr(statement);
4668
+ if (!converted) return null;
4669
+ out.push(...converted);
4670
+ }
4671
+ return out;
4672
+ }
4673
+ function storeStmtToIr(statement) {
4674
+ if (t.isBlockStatement(statement)) return storeStmtsToIr(statement.body);
4675
+ if (t.isVariableDeclaration(statement)) {
4676
+ const declarations = [];
4677
+ for (const declaration of statement.declarations) {
4678
+ if (!t.isIdentifier(declaration.id)) return null;
4679
+ const init = declaration.init ? storeExprToIr(declaration.init) : void 0;
4680
+ if (declaration.init && !init) return null;
4681
+ declarations.push({
4682
+ kind: "var",
4683
+ name: declaration.id.name,
4684
+ mutable: statement.kind !== "const",
4685
+ ...init ? { init } : {}
4686
+ });
4687
+ }
4688
+ return declarations;
4689
+ }
4690
+ if (t.isExpressionStatement(statement)) {
4691
+ const expr = statement.expression;
4692
+ if (t.isAssignmentExpression(expr) && expr.operator === "=") {
4693
+ const target = storeExprToIr(expr.left);
4694
+ const value = storeExprToIr(expr.right);
4695
+ return target && value ? [{
4696
+ kind: "assign",
4697
+ target,
4698
+ value
4699
+ }] : null;
4700
+ }
4701
+ const converted = storeExprToIr(expr);
4702
+ return converted ? [{
4703
+ kind: "expr",
4704
+ expr: converted
4705
+ }] : null;
4706
+ }
4707
+ if (t.isIfStatement(statement)) {
4708
+ const test = storeExprToIr(statement.test);
4709
+ const consequent = storeStatementList(statement.consequent);
4710
+ const alternate = statement.alternate ? storeStatementList(statement.alternate) : void 0;
4711
+ if (!test || !consequent || statement.alternate && !alternate) return null;
4712
+ return [{
4713
+ kind: "if",
4714
+ test,
4715
+ consequent,
4716
+ ...alternate ? { alternate } : {}
4717
+ }];
4718
+ }
4719
+ if (t.isForStatement(statement)) {
4720
+ const init = statement.init ? storeStmtToIr(t.isVariableDeclaration(statement.init) ? statement.init : t.expressionStatement(statement.init)) : void 0;
4721
+ const test = statement.test ? storeExprToIr(statement.test) : void 0;
4722
+ const update = statement.update ? storeExprToIr(statement.update) : void 0;
4723
+ const body = storeStatementList(statement.body);
4724
+ if (statement.init && (!init || init.length !== 1) || statement.test && !test || statement.update && !update || !body) return null;
4725
+ return [{
4726
+ kind: "for",
4727
+ ...init ? { init: init[0] } : {},
4728
+ ...test ? { test } : {},
4729
+ ...update ? { update } : {},
4730
+ body
4731
+ }];
4732
+ }
4733
+ if (t.isReturnStatement(statement)) {
4734
+ const value = statement.argument ? storeExprToIr(statement.argument) : void 0;
4735
+ if (statement.argument && !value) return null;
4736
+ return [{
4737
+ kind: "return",
4738
+ ...value ? { value } : {}
4739
+ }];
4740
+ }
4741
+ return null;
4742
+ }
4743
+ function storeStatementList(statement) {
4744
+ if (t.isBlockStatement(statement)) return storeStmtsToIr(statement.body);
4745
+ return storeStmtToIr(statement);
4746
+ }
4747
+ function storeExprToIr(expression) {
4748
+ if (t.isIdentifier(expression)) return {
4749
+ kind: "identifier",
4750
+ name: expression.name
4751
+ };
4752
+ if (t.isThisExpression(expression)) return { kind: "this" };
4753
+ if (t.isNumericLiteral(expression)) return {
4754
+ kind: "number",
4755
+ value: expression.value
4756
+ };
4757
+ if (t.isStringLiteral(expression)) return {
4758
+ kind: "string",
4759
+ value: expression.value
4760
+ };
4761
+ if (t.isBooleanLiteral(expression)) return {
4762
+ kind: "boolean",
4763
+ value: expression.value
4764
+ };
4765
+ if (t.isNullLiteral(expression)) return { kind: "null" };
4766
+ if (t.isMemberExpression(expression)) {
4767
+ const object = storeExprToIr(expression.object);
4768
+ if (!object) return null;
4769
+ if (expression.computed) {
4770
+ const index = storeExprToIr(expression.property);
4771
+ return index ? {
4772
+ kind: "index",
4773
+ object,
4774
+ index
4775
+ } : null;
4776
+ }
4777
+ return t.isIdentifier(expression.property) ? {
4778
+ kind: "member",
4779
+ object,
4780
+ property: expression.property.name
4781
+ } : null;
4782
+ }
4783
+ if (t.isCallExpression(expression)) {
4784
+ const callee = storeExprToIr(expression.callee);
4785
+ const args = expression.arguments.map((arg) => t.isSpreadElement(arg) ? null : storeExprToIr(arg));
4786
+ return callee && args.every((arg) => !!arg) ? {
4787
+ kind: "call",
4788
+ callee,
4789
+ args
4790
+ } : null;
4791
+ }
4792
+ if (t.isObjectExpression(expression)) {
4793
+ const fields = [];
4794
+ for (const property of expression.properties) {
4795
+ if (!t.isObjectProperty(property) || property.computed) return null;
4796
+ const name = objectPropertyName(property.key);
4797
+ const value = storeExprToIr(property.value);
4798
+ if (!name || !value) return null;
4799
+ fields.push({
4800
+ name,
4801
+ value
4802
+ });
4803
+ }
4804
+ return {
4805
+ kind: "object",
4806
+ fields
4807
+ };
4808
+ }
4809
+ if (t.isUnaryExpression(expression)) {
4810
+ const arg = storeExprToIr(expression.argument);
4811
+ return arg ? {
4812
+ kind: "unary",
4813
+ op: expression.operator,
4814
+ arg
4815
+ } : null;
4816
+ }
4817
+ if (t.isBinaryExpression(expression)) {
4818
+ const left = storeExprToIr(expression.left);
4819
+ const right = storeExprToIr(expression.right);
4820
+ return left && right ? {
4821
+ kind: "binary",
4822
+ op: expression.operator,
4823
+ left,
4824
+ right
4825
+ } : null;
4826
+ }
4827
+ if (t.isLogicalExpression(expression)) {
4828
+ const left = storeExprToIr(expression.left);
4829
+ const right = storeExprToIr(expression.right);
4830
+ return left && right ? {
4831
+ kind: "logical",
4832
+ op: expression.operator,
4833
+ left,
4834
+ right
4835
+ } : null;
4836
+ }
4837
+ if (t.isUpdateExpression(expression)) {
4838
+ const arg = storeExprToIr(expression.argument);
4839
+ return arg ? {
4840
+ kind: "update",
4841
+ op: expression.operator,
4842
+ arg,
4843
+ prefix: expression.prefix
4844
+ } : null;
4845
+ }
4846
+ return null;
4847
+ }
4848
+ function sourceSpan(node) {
4849
+ const span = {};
4850
+ if (typeof node.start === "number") span.start = node.start;
4851
+ if (typeof node.end === "number") span.end = node.end;
4852
+ return span.start === void 0 && span.end === void 0 ? void 0 : span;
4853
+ }
4854
+ function slotToIr(slot, bindings) {
4855
+ const expr = slot.expr ? substituteBindings(slot.expr, bindings) : null;
4856
+ return {
4857
+ index: slot.index,
4858
+ kind: slot.kind,
4859
+ walk: slot.walk,
4860
+ ...slot.walkKinds ? { walkKinds: slot.walkKinds } : {},
4861
+ ...expr ? { expr: generate(expr).code } : {},
4862
+ ...expr ? expressionPathToIr(expr) : {},
4863
+ ...expr ? expressionObjectFieldsToIr(expr) : {},
4864
+ ...slot.payload ? { payload: slotPayloadToIr(slot, bindings) } : {},
4865
+ ...slot.directText ? { directText: true } : {}
4866
+ };
4867
+ }
4868
+ function expressionPathToIr(expr) {
4869
+ const path = expressionPath(expr);
4870
+ return path && path.length > 0 ? { exprPath: path } : {};
4871
+ }
4872
+ function expressionPath(expr) {
4873
+ if (t.isIdentifier(expr)) return [expr.name];
4874
+ if (t.isThisExpression(expr)) return ["this"];
4875
+ if (t.isMemberExpression(expr) && !expr.computed) {
4876
+ const objectPath = expressionPath(expr.object);
4877
+ const property = t.isIdentifier(expr.property) ? expr.property.name : null;
4878
+ return objectPath && property ? [...objectPath, property] : null;
4879
+ }
4880
+ if (t.isOptionalMemberExpression(expr) && !expr.computed) {
4881
+ const objectPath = expressionPath(expr.object);
4882
+ const property = t.isIdentifier(expr.property) ? expr.property.name : null;
4883
+ return objectPath && property ? [...objectPath, property] : null;
4884
+ }
4885
+ return null;
4886
+ }
4887
+ function expressionObjectFieldsToIr(expr) {
4888
+ if (!t.isObjectExpression(expr)) return {};
4889
+ const fields = [];
4890
+ for (const property of expr.properties) {
4891
+ if (!t.isObjectProperty(property) || property.computed) continue;
4892
+ const name = objectPropertyName(property.key);
4893
+ if (!name) continue;
4894
+ fields.push({
4895
+ name,
4896
+ expr: generate(property.value).code,
4897
+ ...expressionPathToIr(property.value)
4898
+ });
4899
+ }
4900
+ return fields.length > 0 ? { exprObjectFields: fields } : {};
4901
+ }
4902
+ function slotPayloadToIr(slot, bindings) {
4903
+ const payload = serializePayload(slot.payload, bindings);
4904
+ if (!isRecord(payload)) return payload;
4905
+ if (slot.kind === "keyed-list") {
4906
+ const cb = slot.payload?.mapCallback;
4907
+ const row = keyedListRowIr(cb);
4908
+ return row ? {
4909
+ ...payload,
4910
+ ...row
4911
+ } : payload;
4912
+ }
4913
+ if (slot.kind === "conditional") {
4914
+ const result = { ...payload };
4915
+ const consequent = jsxNodeToTemplateIr(slot.payload?.mkTrue, bindings);
4916
+ if (consequent) result.consequentTemplate = consequent;
4917
+ const alternate = jsxNodeToTemplateIr(slot.payload?.mkFalse, bindings);
4918
+ if (alternate) result.alternateTemplate = alternate;
4919
+ return result;
4920
+ }
4921
+ if (slot.kind === "mount") {
4922
+ const children = slot.payload?.children;
4923
+ const childrenTemplate = jsxChildrenToTemplateIr(children, bindings);
4924
+ if (childrenTemplate) return {
4925
+ ...payload,
4926
+ childrenTemplate
4927
+ };
4928
+ }
4929
+ return payload;
4930
+ }
4931
+ function jsxNodeToTemplateIr(node, bindings) {
4932
+ if (!node) return null;
4933
+ if (t.isJSXElement(node) || t.isJSXFragment(node)) return templateSpecToIr(walkJsxToTemplate(node), bindings);
4934
+ if (t.isJSXExpressionContainer(node)) {
4935
+ const inner = node.expression;
4936
+ if (t.isJSXElement(inner) || t.isJSXFragment(inner)) return templateSpecToIr(walkJsxToTemplate(inner), bindings);
4937
+ if (isWalkableConditionalExpression(inner)) return wrapAsFragmentTemplate(inner, bindings);
4938
+ }
4939
+ if (isWalkableConditionalExpression(node)) return wrapAsFragmentTemplate(node, bindings);
4940
+ return null;
4941
+ }
4942
+ function isWalkableConditionalExpression(node) {
4943
+ if (t.isConditionalExpression(node)) return isJsxOrNullish(node.consequent) || isJsxOrNullish(node.alternate);
4944
+ if (t.isLogicalExpression(node) && node.operator === "&&") return isJsxOrNullish(node.right);
4945
+ return false;
4946
+ }
4947
+ function wrapAsFragmentTemplate(expression, bindings) {
4948
+ return templateSpecToIr(walkJsxToTemplate(t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), [t.jsxExpressionContainer(expression)])), bindings);
4949
+ }
4950
+ function jsxChildrenToTemplateIr(children, bindings) {
4951
+ if (!Array.isArray(children) || children.length === 0) return null;
4952
+ if (!children.some((c) => t.isJSXElement(c) || t.isJSXFragment(c) || t.isJSXExpressionContainer(c) && !t.isJSXEmptyExpression(c.expression))) return null;
4953
+ return templateSpecToIr(walkJsxToTemplate(t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), children)), bindings);
4954
+ }
4955
+ function keyedListRowIr(cb) {
4956
+ if (!t.isArrowFunctionExpression(cb) && !t.isFunctionExpression(cb)) return null;
4957
+ const body = callbackJsxBody(cb.body);
4958
+ if (!body) return null;
4959
+ const itemParam = callbackParamName(cb.params[0]);
4960
+ const indexParam = callbackParamName(cb.params[1]);
4961
+ return {
4962
+ ...itemParam ? { itemParam } : {},
4963
+ ...indexParam ? { indexParam } : {},
4964
+ rowTemplate: templateSpecToIr(walkJsxToTemplate(body))
4965
+ };
4966
+ }
4967
+ function callbackJsxBody(body) {
4968
+ if (t.isJSXElement(body) || t.isJSXFragment(body)) return body;
4969
+ if (!t.isBlockStatement(body)) return null;
4970
+ for (const statement of body.body) {
4971
+ if (!t.isReturnStatement(statement) || !statement.argument) continue;
4972
+ if (t.isJSXElement(statement.argument) || t.isJSXFragment(statement.argument)) return statement.argument;
4973
+ }
4974
+ return null;
4975
+ }
4976
+ function callbackParamName(param) {
4977
+ return t.isIdentifier(param) ? param.name : void 0;
4978
+ }
4979
+ function serializePayload(value, bindings) {
4980
+ if (value === null || value === void 0) return value;
4981
+ if (typeof value !== "object") return value;
4982
+ if (Array.isArray(value)) return value.map((child) => serializePayload(child, bindings));
4983
+ if (isBabelNode(value)) {
4984
+ const node = substitutePayloadNode(value, bindings);
4985
+ return {
4986
+ nodeType: node.type,
4987
+ code: generate(node).code
4988
+ };
4989
+ }
4990
+ const out = {};
4991
+ for (const [key, child] of Object.entries(value)) out[key] = serializePayload(child, bindings);
4992
+ return out;
4993
+ }
4994
+ function substitutePayloadNode(value, bindings) {
4995
+ if (bindings.size === 0) return value;
4996
+ if (t.isJSXAttribute(value) && value.value && t.isJSXExpressionContainer(value.value) && !t.isJSXEmptyExpression(value.value.expression)) return {
4997
+ ...value,
4998
+ value: {
4999
+ ...value.value,
5000
+ expression: substituteBindings(value.value.expression, bindings)
5001
+ }
5002
+ };
5003
+ if (t.isJSXExpressionContainer(value) && !t.isJSXEmptyExpression(value.expression)) return {
5004
+ ...value,
5005
+ expression: substituteBindings(value.expression, bindings)
5006
+ };
5007
+ return substituteBindings(value, bindings);
5008
+ }
5009
+ function isBabelNode(value) {
5010
+ return !!value && typeof value === "object" && typeof value.type === "string";
5011
+ }
5012
+ function isRecord(value) {
5013
+ return !!value && typeof value === "object" && !Array.isArray(value);
5014
+ }
5015
+ function shapeForExpression(value) {
5016
+ if (t.isStringLiteral(value)) return { shape: {
5017
+ kind: "literal",
5018
+ valueType: "string"
5019
+ } };
5020
+ if (t.isNumericLiteral(value)) return { shape: {
5021
+ kind: "literal",
5022
+ valueType: "number"
5023
+ } };
5024
+ if (t.isBooleanLiteral(value)) return { shape: {
5025
+ kind: "literal",
5026
+ valueType: "boolean"
5027
+ } };
5028
+ if (t.isNullLiteral(value)) return { shape: {
5029
+ kind: "literal",
5030
+ valueType: "null"
5031
+ } };
5032
+ if (t.isArrayExpression(value)) {
5033
+ const firstElement = value.elements.find((element) => !!element && !t.isSpreadElement(element));
5034
+ const shapedElement = firstElement ? shapeForExpression(firstElement) : {};
5035
+ const elementShape = "shape" in shapedElement ? shapedElement.shape : void 0;
5036
+ return { shape: {
5037
+ kind: "array",
5038
+ ...elementShape ? { element: elementShape } : {}
5039
+ } };
5040
+ }
5041
+ if (t.isObjectExpression(value)) {
5042
+ const fields = [];
5043
+ for (const property of value.properties) {
5044
+ if (!t.isObjectProperty(property) || property.computed) continue;
5045
+ const name = objectPropertyName(property.key);
5046
+ if (!name) continue;
5047
+ fields.push({
5048
+ name,
5049
+ ...property.value ? { initializer: generate(property.value).code } : {},
5050
+ ...property.value ? shapeForExpression(property.value) : {}
5051
+ });
5052
+ }
5053
+ return { shape: {
5054
+ kind: "object",
5055
+ fields
5056
+ } };
5057
+ }
5058
+ return {};
5059
+ }
5060
+ function objectPropertyName(key) {
5061
+ if (t.isIdentifier(key)) return key.name;
5062
+ if (t.isStringLiteral(key)) return key.value;
5063
+ if (t.isNumericLiteral(key)) return String(key.value);
5064
+ return null;
5065
+ }
5066
+ //#endregion
5067
+ //#region src/closure-codegen/emit/emit-core.ts
5068
+ function compileJsxToBlock(jsxRoot, ctx) {
5069
+ const spec = walkJsxToTemplate(jsxRoot, {
5070
+ emitEventDataAttr: false,
5071
+ directFnComponents: ctx.directFnComponents,
5072
+ bindings: ctx.bindings
5073
+ });
5074
+ if (t.isJSXFragment(jsxRoot)) spec.html = "<span style=\"display:contents\">" + spec.html + "</span>";
5075
+ else if (!spec.html.startsWith("<") || spec.html.startsWith("<!--")) {
5076
+ spec.html = "<span style=\"display:contents\">" + spec.html + "</span>";
5077
+ for (const slot of spec.slots) slot.walk = [0, ...slot.walk];
5078
+ }
5079
+ if (ctx.irTemplates && ctx.currentIrComponent && ctx.currentIrRuntimeBase) ctx.irTemplates.push({
5080
+ component: ctx.currentIrComponent,
5081
+ runtimeBase: ctx.currentIrRuntimeBase,
5082
+ template: templateSpecToIr(spec, ctx.bindings)
5083
+ });
5084
+ const tplName = "_tpl" + ctx.tplCounter++;
5085
+ ctx.templateDecls.push(...emitTemplateDecl(spec.html, tplName));
5086
+ const stmts = [];
5087
+ stmts.push(t.variableDeclaration("const", [t.variableDeclarator(t.identifier("root"), emitTemplateCloneExpression(tplName, spec.html))]));
5088
+ const walkCache = /* @__PURE__ */ new Map();
5089
+ for (const slot of spec.slots) emitWalkCapture(slot, stmts, false, walkCache);
5090
+ const savedPending = ctx._pendingEvents;
5091
+ const savedInputValueExprByEventSlot = ctx._inputValueExprByEventSlot;
5092
+ const savedDocumentClickDelegateInstalled = ctx._documentClickDelegateInstalled;
5093
+ ctx._pendingEvents = [];
5094
+ ctx._inputValueExprByEventSlot = findInputValueReconciliations(spec);
4133
5095
  ctx._documentClickDelegateInstalled = false;
4134
5096
  for (const slot of spec.slots) emitSlot(slot, stmts, ctx);
4135
5097
  const events = ctx._pendingEvents;
@@ -4431,6 +5393,11 @@ function rewriteFnComponent(fnDecl, parentCtx) {
4431
5393
  fnCtx.directFnComponentParams = parentCtx.directFnComponentParams;
4432
5394
  fnCtx.directFnStringProps = parentCtx.directFnStringProps;
4433
5395
  fnCtx.directFnNoDisposer = parentCtx.directFnNoDisposer;
5396
+ if (parentCtx.irTemplates && fnName) {
5397
+ fnCtx.irTemplates = parentCtx.irTemplates;
5398
+ fnCtx.currentIrComponent = fnName;
5399
+ fnCtx.currentIrRuntimeBase = "reactive";
5400
+ }
4434
5401
  if (fnCtx.oneShotProps) {
4435
5402
  fnCtx._inKeyedListRow = true;
4436
5403
  fnCtx._rowEventTypes = /* @__PURE__ */ new Set();
@@ -4856,6 +5823,8 @@ function transformFile(source, _filename, options = {}) {
4856
5823
  };
4857
5824
  }
4858
5825
  const ctx = createEmitContext();
5826
+ ctx.irTemplates = [];
5827
+ ctx.embedded = options.embedded;
4859
5828
  ctx.directFnComponents = collectDirectFnComponents(ast);
4860
5829
  ctx.directFnComponentParams = collectDirectFnComponentParams(ast, ctx.directFnComponents);
4861
5830
  ctx.directFnStringProps = collectDirectFnStringProps(ast, ctx.directFnComponents);
@@ -4864,6 +5833,7 @@ function transformFile(source, _filename, options = {}) {
4864
5833
  for (const name of options.directClassComponents ?? []) ctx.directClassComponents.add(name);
4865
5834
  ctx.directFactoryComponents = new Set(options.directFactoryComponents);
4866
5835
  const rewritten = [];
5836
+ const reactiveComponentNames = /* @__PURE__ */ new Set();
4867
5837
  let firstClassIdx = -1;
4868
5838
  for (let i = 0; i < ast.program.body.length; i++) {
4869
5839
  const node = ast.program.body[i];
@@ -4899,9 +5869,21 @@ function transformFile(source, _filename, options = {}) {
4899
5869
  const useTinyReactiveComponent = options.enableTinyReactiveComponents !== false && !useStaticCompiledComponent && !useCompiledComponent && canUseTinyReactiveComponent(classDecl);
4900
5870
  const useLeanReactiveComponent = !useStaticCompiledComponent && !useCompiledComponent && !useTinyReactiveComponent && canUseLeanReactiveComponent(classDecl);
4901
5871
  const hasAfterRenderAsyncHook = hasOwnInstanceMethod(classDecl, "onAfterRenderAsync");
5872
+ const className = classDecl.id && classDecl.id.name || "<anonymous>";
5873
+ if (t.isIdentifier(classDecl.superClass, { name: "ReactiveComponent" })) reactiveComponentNames.add(className);
5874
+ const runtimeBase = runtimeBaseForComponent({
5875
+ useStaticCompiledComponent,
5876
+ useCompiledComponent,
5877
+ useTinyReactiveComponent,
5878
+ useLeanReactiveComponent
5879
+ });
5880
+ ctx.currentIrComponent = className;
5881
+ ctx.currentIrRuntimeBase = runtimeBase;
4902
5882
  for (const m of methodsWithJsx) m.body.body = m.body.body.map((s) => lowerJsxInStatement(s, ctx));
4903
5883
  if (!templateMethod) {
4904
- rewritten.push(classDecl.id && classDecl.id.name || "<anonymous>");
5884
+ rewritten.push(className);
5885
+ ctx.currentIrComponent = void 0;
5886
+ ctx.currentIrRuntimeBase = void 0;
4905
5887
  continue;
4906
5888
  }
4907
5889
  const paramBindings = [];
@@ -4921,14 +5903,26 @@ function transformFile(source, _filename, options = {}) {
4921
5903
  ctx.importsNeeded.add(templateSymbol);
4922
5904
  const method = buildCreateTemplateMethod(jsx, ctx, preceding, templateSymbol);
4923
5905
  const useStaticElementComponent = useStaticCompiledComponent && isStaticBuiltinElementRoot(jsx) && !nodeContainsIdentifier$1(method.body, "d");
5906
+ if (useStaticElementComponent && ctx.irTemplates) {
5907
+ for (const template of ctx.irTemplates) if (template.component === className) template.runtimeBase = "static-element";
5908
+ }
4924
5909
  if (useStaticElementComponent) method.params = [];
4925
5910
  if (plainPropsParamName) ctx.bindings.delete(plainPropsParamName);
4926
5911
  for (const k of paramBindings) ctx.bindings.delete(k);
5912
+ const isReactiveComponent = reactiveComponentNames.has(className);
4927
5913
  const bodyItems = classDecl.body.body;
4928
5914
  const templateIdx = bodyItems.indexOf(templateMethod);
4929
- if (templateIdx >= 0) bodyItems[templateIdx] = method;
5915
+ if (templateIdx >= 0) if (isReactiveComponent) {
5916
+ bodyItems.splice(templateIdx, 1);
5917
+ const keepAlive = mountedComponentKeepAliveStatements(jsx, ast);
5918
+ if (keepAlive.length > 0) ast.program.body.splice(i + 1, 0, ...keepAlive);
5919
+ } else bodyItems[templateIdx] = method;
4930
5920
  let usesCompiledRuntimeBase = false;
4931
- if (useStaticElementComponent) {
5921
+ if (isReactiveComponent) {
5922
+ classDecl.superClass = null;
5923
+ usesCompiledRuntimeBase = false;
5924
+ 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()));
5925
+ } else if (useStaticElementComponent) {
4932
5926
  ctx.importsNeeded.add("CompiledStaticElementComponent");
4933
5927
  classDecl.superClass = t.identifier("CompiledStaticElementComponent");
4934
5928
  usesCompiledRuntimeBase = true;
@@ -4957,7 +5951,9 @@ function transformFile(source, _filename, options = {}) {
4957
5951
  ctx.importsNeeded.add("scheduleAfterRenderAsync");
4958
5952
  classDecl.body.body.push(buildAfterRenderAsyncRenderMethod());
4959
5953
  }
4960
- rewritten.push(classDecl.id && classDecl.id.name || "<anonymous>");
5954
+ rewritten.push(className);
5955
+ ctx.currentIrComponent = void 0;
5956
+ ctx.currentIrRuntimeBase = void 0;
4961
5957
  continue;
4962
5958
  }
4963
5959
  if (fnDecl && isFunctionComponent(fnDecl)) {
@@ -4984,9 +5980,90 @@ function transformFile(source, _filename, options = {}) {
4984
5980
  map: out.map,
4985
5981
  changed: true,
4986
5982
  rewritten,
4987
- importsNeeded: Array.from(ctx.importsNeeded)
5983
+ importsNeeded: Array.from(ctx.importsNeeded),
5984
+ ir: buildModuleIr(_filename ?? "<unknown>", rewritten, ctx.irTemplates ?? [], ast, reactiveComponentNames)
5985
+ };
5986
+ }
5987
+ function mountedComponentKeepAliveStatements(jsx, ast) {
5988
+ const tags = /* @__PURE__ */ new Set();
5989
+ collectCapitalizedJsxTags(jsx, tags);
5990
+ if (tags.size === 0) return [];
5991
+ const imported = /* @__PURE__ */ new Set();
5992
+ for (const stmt of ast.program.body) {
5993
+ if (!t.isImportDeclaration(stmt)) continue;
5994
+ for (const spec of stmt.specifiers) imported.add(spec.local.name);
5995
+ }
5996
+ const kept = Array.from(tags).filter((tag) => imported.has(tag));
5997
+ if (kept.length === 0) return [];
5998
+ const keepArray = t.assignmentExpression("||=", t.memberExpression(t.identifier("globalThis"), t.identifier("__GEA_IR_KEEP__")), t.arrayExpression([]));
5999
+ return [t.expressionStatement(t.callExpression(t.memberExpression(t.parenthesizedExpression(keepArray), t.identifier("push")), kept.map((tag) => t.identifier(tag))))];
6000
+ }
6001
+ function collectCapitalizedJsxTags(node, tags) {
6002
+ if (!node || typeof node !== "object") return;
6003
+ if (Array.isArray(node)) {
6004
+ for (const child of node) collectCapitalizedJsxTags(child, tags);
6005
+ return;
6006
+ }
6007
+ if (t.isJSXElement(node)) {
6008
+ const name = node.openingElement.name;
6009
+ if (t.isJSXIdentifier(name) && /^[A-Z]/.test(name.name)) tags.add(name.name);
6010
+ }
6011
+ for (const key of Object.keys(node)) {
6012
+ if (key === "loc" || key === "start" || key === "end" || key === "type") continue;
6013
+ collectCapitalizedJsxTags(node[key], tags);
6014
+ }
6015
+ }
6016
+ function runtimeBaseForComponent(options) {
6017
+ if (options.useStaticCompiledComponent) return "static";
6018
+ if (options.useCompiledComponent) return "compiled";
6019
+ if (options.useTinyReactiveComponent) return "tiny-reactive";
6020
+ if (options.useLeanReactiveComponent) return "lean-reactive";
6021
+ return "reactive";
6022
+ }
6023
+ function buildModuleIr(moduleId, rewritten, templates, ast, reactiveComponentNames = /* @__PURE__ */ new Set()) {
6024
+ const components = [];
6025
+ for (const name of rewritten) {
6026
+ const record = templates.find((template) => template.component === name);
6027
+ if (!record) continue;
6028
+ const declaration = findClassDeclarationByName(ast, name);
6029
+ const reactiveState = declaration && reactiveComponentNames.has(name) ? (() => {
6030
+ const fields = storeFieldsToIr(declaration);
6031
+ const methods = storeMethodsToIr(declaration, ast);
6032
+ const getters = storeGettersToIr(declaration);
6033
+ return {
6034
+ fields,
6035
+ ...methods.length > 0 ? { methods } : {},
6036
+ ...getters.length > 0 ? { getters } : {}
6037
+ };
6038
+ })() : void 0;
6039
+ components.push({
6040
+ id: componentIrId(moduleId, name),
6041
+ module: moduleId,
6042
+ exportName: name,
6043
+ runtimeBase: record.runtimeBase,
6044
+ template: record.template,
6045
+ ...reactiveState ? { reactiveState } : {},
6046
+ ...declaration ? { sourceSpan: sourceSpan(declaration) } : {}
6047
+ });
6048
+ }
6049
+ return {
6050
+ module: {
6051
+ id: moduleId,
6052
+ file: moduleId,
6053
+ components: components.map((component) => component.id),
6054
+ stores: []
6055
+ },
6056
+ components
4988
6057
  };
4989
6058
  }
6059
+ function findClassDeclarationByName(ast, name) {
6060
+ for (const node of ast.program.body) {
6061
+ if (t.isClassDeclaration(node) && node.id?.name === name) return node;
6062
+ if (t.isExportDefaultDeclaration(node) && t.isClassDeclaration(node.declaration) && node.declaration.id?.name === name) return node.declaration;
6063
+ if (t.isExportNamedDeclaration(node) && t.isClassDeclaration(node.declaration) && node.declaration.id?.name === name) return node.declaration;
6064
+ }
6065
+ return null;
6066
+ }
4990
6067
  function collectLocalClassComponents(ast) {
4991
6068
  const names = /* @__PURE__ */ new Set();
4992
6069
  for (const node of ast.program.body) {
@@ -5516,13 +6593,16 @@ function transform(ctx) {
5516
6593
  }
5517
6594
  }
5518
6595
  });
6596
+ let ir;
5519
6597
  if (hasJSX) {
5520
6598
  const emitted = transformFile(code, sourceFile, {
5521
6599
  directClassComponents: knownClassComponentImports,
5522
6600
  directFactoryComponents: knownFactoryComponentImports,
5523
- enableTinyReactiveComponents: !isServe
6601
+ enableTinyReactiveComponents: !isServe,
6602
+ embedded: ctx.embedded
5524
6603
  });
5525
6604
  if (emitted.changed) {
6605
+ ir = emitted.ir;
5526
6606
  const reparsed = parseSource(emitted.code);
5527
6607
  if (reparsed) {
5528
6608
  ast.program.body = reparsed.ast.program.body;
@@ -5573,7 +6653,8 @@ function transform(ctx) {
5573
6653
  }, code);
5574
6654
  return {
5575
6655
  code: output.code,
5576
- map: output.map
6656
+ map: output.map,
6657
+ ir
5577
6658
  };
5578
6659
  } catch (error) {
5579
6660
  if (error?.__geaCompileError) throw error;
@@ -5583,10 +6664,9 @@ function transform(ctx) {
5583
6664
  }
5584
6665
  //#endregion
5585
6666
  //#region src/closure-codegen/transform/transform-store.ts
5586
- function transformCompiledStoreModule(source) {
6667
+ function transformCompiledStoreModule(source, moduleId = "<unknown>", resolveImportPath) {
5587
6668
  if (!source.includes("extends Store")) return null;
5588
6669
  if (source.includes("CompiledStore")) return null;
5589
- if (/\b(flushSync|silent|Store\.|new\s+Store\s*\()/.test(source)) return null;
5590
6670
  let ast;
5591
6671
  try {
5592
6672
  ast = parse(source, {
@@ -5603,29 +6683,49 @@ function transformCompiledStoreModule(source) {
5603
6683
  } catch {
5604
6684
  return null;
5605
6685
  }
5606
- const imported = findStoreImport(ast);
6686
+ const imported = findStoreImport(ast, moduleId, resolveImportPath);
5607
6687
  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";
6688
+ const classDecls = findStoreClasses(ast, imported.localName);
6689
+ if (classDecls.length === 0) return null;
6690
+ const constants = collectImportedLiteralConstants(ast, moduleId, resolveImportPath);
6691
+ const fallbackIrs = classDecls.map((classDecl) => buildStoreIr(classDecl, moduleId, "compiled", constants, ast));
6692
+ const fallbackKeepAlive = storeMethodFreeFunctionKeepAlive(ast, classDecls);
6693
+ const fallback = {
6694
+ code: fallbackKeepAlive ? `${source}${fallbackKeepAlive}` : source,
6695
+ changed: !!fallbackKeepAlive,
6696
+ ir: fallbackIrs[0],
6697
+ irs: fallbackIrs
6698
+ };
6699
+ if (/\b(flushSync|silent|Store\.|new\s+Store\s*\()/.test(source)) return fallback;
6700
+ if (!classDecls.every((classDecl) => isCompiledStoreSafeClass(classDecl))) return classDecls.length === 1 ? null : fallback;
6701
+ if (!classDecls.every((classDecl) => hasDefaultNewStore(ast, classDecl.id.name))) return fallback;
6702
+ if (classDecls.length === 1) {
6703
+ const leanResult = transformLeanDataSelectedStore(ast, classDecls[0], imported, moduleId, constants);
6704
+ if (leanResult) return {
6705
+ ...leanResult,
6706
+ irs: leanResult.ir ? [leanResult.ir] : void 0
6707
+ };
6708
+ }
6709
+ const storeBases = classDecls.map((classDecl) => canUseLeanStore(classDecl) ? "CompiledLeanStore" : "CompiledStore");
6710
+ const storeIrs = classDecls.map((classDecl, index) => buildStoreIr(classDecl, moduleId, storeBases[index] === "CompiledLeanStore" ? "lean" : "compiled", constants, ast));
5615
6711
  removeStoreSpecifier(imported.importDecl, imported.localName);
5616
6712
  ast.program.body = ast.program.body.filter((node) => {
5617
6713
  if (node !== imported.importDecl) return true;
5618
6714
  return imported.importDecl.specifiers.length > 0;
5619
6715
  });
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);
6716
+ 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)));
6717
+ classDecls.forEach((classDecl, index) => {
6718
+ classDecl.superClass = t.identifier(storeBases[index]);
6719
+ });
5622
6720
  return {
5623
6721
  code: generate(ast, {
5624
6722
  retainLines: false,
5625
6723
  compact: false,
5626
6724
  jsescOption: { minimal: true }
5627
6725
  }).code,
5628
- changed: true
6726
+ changed: true,
6727
+ ir: storeIrs[0],
6728
+ irs: storeIrs
5629
6729
  };
5630
6730
  }
5631
6731
  const LEAN_DATA_SELECTED_STORE_SOURCE = `
@@ -5754,7 +6854,7 @@ const __store = {
5754
6854
  };
5755
6855
  export default __store;
5756
6856
  `;
5757
- function transformLeanDataSelectedStore(ast, classDecl, imported) {
6857
+ function transformLeanDataSelectedStore(ast, classDecl, imported, moduleId, constants) {
5758
6858
  if (!classDecl.id) return null;
5759
6859
  const classIndex = ast.program.body.indexOf(classDecl);
5760
6860
  if (classIndex < 0) return null;
@@ -5763,6 +6863,7 @@ function transformLeanDataSelectedStore(ast, classDecl, imported) {
5763
6863
  const fields = collectLeanStoreFields(classDecl);
5764
6864
  if (!fields) return null;
5765
6865
  if (!isBenchmarkOperationStoreShape(classDecl)) return null;
6866
+ const storeIr = buildStoreIr(classDecl, moduleId, "lean", constants, ast);
5766
6867
  const methodProps = buildLeanStoreMethods(classDecl);
5767
6868
  if (!methodProps) return null;
5768
6869
  removeStoreSpecifier(imported.importDecl, imported.localName);
@@ -5792,9 +6893,123 @@ function transformLeanDataSelectedStore(ast, classDecl, imported) {
5792
6893
  compact: false,
5793
6894
  jsescOption: { minimal: true }
5794
6895
  }).code,
5795
- changed: true
6896
+ changed: true,
6897
+ ir: storeIr
6898
+ };
6899
+ }
6900
+ function buildStoreIr(classDecl, moduleId, runtimeBase, constants = [], moduleAst) {
6901
+ const className = classDecl.id?.name ?? "<anonymous>";
6902
+ return {
6903
+ id: storeIrId(moduleId, className),
6904
+ module: moduleId,
6905
+ className,
6906
+ runtimeBase,
6907
+ fields: storeFieldsToIr(classDecl),
6908
+ methods: storeMethodsToIr(classDecl, moduleAst),
6909
+ ...(() => {
6910
+ const getters = storeGettersToIr(classDecl);
6911
+ return getters.length > 0 ? { getters } : {};
6912
+ })(),
6913
+ ...constants.length > 0 ? { constants } : {},
6914
+ ...sourceSpan(classDecl) ? { sourceSpan: sourceSpan(classDecl) } : {}
5796
6915
  };
5797
6916
  }
6917
+ function collectImportedLiteralConstants(ast, moduleId, resolveImportPath) {
6918
+ if (!resolveImportPath) return [];
6919
+ const namesByFile = /* @__PURE__ */ new Map();
6920
+ for (const node of ast.program.body) {
6921
+ if (!t.isImportDeclaration(node) || typeof node.source.value !== "string") continue;
6922
+ const resolved = resolveImportPath(moduleId, node.source.value);
6923
+ if (!resolved) continue;
6924
+ for (const specifier of node.specifiers) {
6925
+ if (!t.isImportSpecifier(specifier) || !t.isIdentifier(specifier.imported)) continue;
6926
+ const names = namesByFile.get(resolved) ?? /* @__PURE__ */ new Set();
6927
+ names.add(specifier.imported.name);
6928
+ namesByFile.set(resolved, names);
6929
+ }
6930
+ }
6931
+ const constants = [];
6932
+ for (const [file, names] of namesByFile) constants.push(...literalConstantsFromFile(file, names));
6933
+ return constants;
6934
+ }
6935
+ function literalConstantsFromFile(file, names) {
6936
+ if (!existsSync(file)) return [];
6937
+ let ast;
6938
+ try {
6939
+ ast = parse(readFileSync(file, "utf8"), {
6940
+ sourceType: "module",
6941
+ plugins: [
6942
+ "typescript",
6943
+ "jsx",
6944
+ "classProperties"
6945
+ ],
6946
+ errorRecovery: false
6947
+ });
6948
+ } catch {
6949
+ return [];
6950
+ }
6951
+ const constants = [];
6952
+ for (const node of ast.program.body) {
6953
+ if (!t.isExportNamedDeclaration(node) || !t.isVariableDeclaration(node.declaration)) continue;
6954
+ for (const declaration of node.declaration.declarations) {
6955
+ if (!t.isIdentifier(declaration.id) || !names.has(declaration.id.name) || !declaration.init) continue;
6956
+ const literal = literalConstant(declaration.id.name, declaration.init);
6957
+ if (literal) constants.push(literal);
6958
+ }
6959
+ }
6960
+ return constants;
6961
+ }
6962
+ function moduleFreeFunctionNames(ast) {
6963
+ const names = /* @__PURE__ */ new Set();
6964
+ for (const node of ast.program.body) if (t.isFunctionDeclaration(node) && node.id) names.add(node.id.name);
6965
+ else if (t.isExportNamedDeclaration(node) && node.declaration && t.isFunctionDeclaration(node.declaration) && node.declaration.id) names.add(node.declaration.id.name);
6966
+ return names;
6967
+ }
6968
+ function nodeReferencesIdentifier(node, name) {
6969
+ if (!node || typeof node !== "object") return false;
6970
+ if (Array.isArray(node)) {
6971
+ for (const child of node) if (nodeReferencesIdentifier(child, name)) return true;
6972
+ return false;
6973
+ }
6974
+ const record = node;
6975
+ if (record.type === "Identifier" && record.name === name) return true;
6976
+ for (const key of Object.keys(record)) {
6977
+ if (key === "type" || key === "loc" || key === "start" || key === "end" || key === "leadingComments" || key === "trailingComments") continue;
6978
+ if (nodeReferencesIdentifier(record[key], name)) return true;
6979
+ }
6980
+ return false;
6981
+ }
6982
+ function storeMethodFreeFunctionKeepAlive(ast, classDecls) {
6983
+ const freeFns = moduleFreeFunctionNames(ast);
6984
+ if (freeFns.size === 0) return "";
6985
+ const kept = [];
6986
+ for (const name of freeFns) if (classDecls.some((classDecl) => classDecl.body.body.some((member) => t.isClassMethod(member) && nodeReferencesIdentifier(member.body, name)))) kept.push(name);
6987
+ if (kept.length === 0) return "";
6988
+ return `\n;(globalThis.__GEA_IR_KEEP__ ||= []).push(${kept.join(", ")});\n`;
6989
+ }
6990
+ function literalConstant(name, value) {
6991
+ if (t.isStringLiteral(value)) return {
6992
+ name,
6993
+ value: value.value,
6994
+ valueType: "string"
6995
+ };
6996
+ if (t.isNumericLiteral(value)) return {
6997
+ name,
6998
+ value: String(value.value),
6999
+ valueType: "number"
7000
+ };
7001
+ if (t.isBooleanLiteral(value)) return {
7002
+ name,
7003
+ value: value.value ? "true" : "false",
7004
+ valueType: "boolean"
7005
+ };
7006
+ if (t.isNullLiteral(value)) return {
7007
+ name,
7008
+ value: "null",
7009
+ valueType: "null"
7010
+ };
7011
+ return null;
7012
+ }
5798
7013
  function isBenchmarkOperationStoreShape(classDecl) {
5799
7014
  const methods = /* @__PURE__ */ new Set();
5800
7015
  for (const member of classDecl.body.body) {
@@ -5892,10 +7107,10 @@ function replaceThisExpressions(node, replacement) {
5892
7107
  else replaceThisExpressions(value, replacement);
5893
7108
  }
5894
7109
  }
5895
- function findStoreImport(ast) {
7110
+ function findStoreImport(ast, moduleId, resolveImportPath) {
5896
7111
  for (const node of ast.program.body) {
5897
7112
  if (!t.isImportDeclaration(node)) continue;
5898
- if (node.source.value !== "gea" && node.source.value !== "@geajs/core") continue;
7113
+ if (!storeImportSourceProvidesStore(moduleId, node.source.value, resolveImportPath)) continue;
5899
7114
  for (const spec of node.specifiers) {
5900
7115
  if (!t.isImportSpecifier(spec)) continue;
5901
7116
  if ((t.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value) === "Store") return {
@@ -5906,13 +7121,87 @@ function findStoreImport(ast) {
5906
7121
  }
5907
7122
  return null;
5908
7123
  }
5909
- function findStoreClass(ast, storeName) {
5910
- let found = null;
7124
+ const knownStoreImportSources = new Set([
7125
+ "gea",
7126
+ "@geajs/core",
7127
+ "gea-embedded"
7128
+ ]);
7129
+ const storeExportCache = /* @__PURE__ */ new Map();
7130
+ function storeImportSourceProvidesStore(moduleId, source, resolveImportPath) {
7131
+ if (typeof source !== "string") return false;
7132
+ if (knownStoreImportSources.has(source)) return true;
7133
+ if (!resolveImportPath) return false;
7134
+ const resolved = resolveImportPath(moduleId, source);
7135
+ return !!resolved && moduleExportsStore(resolved, resolveImportPath, /* @__PURE__ */ new Set());
7136
+ }
7137
+ function moduleExportsStore(file, resolveImportPath, seen) {
7138
+ if (seen.has(file)) return false;
7139
+ const cached = storeExportCache.get(file);
7140
+ if (cached !== void 0) return cached;
7141
+ seen.add(file);
7142
+ if (!existsSync(file)) {
7143
+ storeExportCache.set(file, false);
7144
+ return false;
7145
+ }
7146
+ let ast;
7147
+ try {
7148
+ ast = parse(readFileSync(file, "utf8"), {
7149
+ sourceType: "module",
7150
+ plugins: [
7151
+ "typescript",
7152
+ "jsx",
7153
+ "classProperties"
7154
+ ],
7155
+ errorRecovery: false
7156
+ });
7157
+ } catch {
7158
+ storeExportCache.set(file, false);
7159
+ return false;
7160
+ }
7161
+ for (const node of ast.program.body) {
7162
+ if (t.isExportNamedDeclaration(node)) {
7163
+ if (node.declaration) {
7164
+ if (t.isVariableDeclaration(node.declaration)) {
7165
+ for (const declaration of node.declaration.declarations) if (t.isIdentifier(declaration.id, { name: "Store" })) {
7166
+ storeExportCache.set(file, true);
7167
+ return true;
7168
+ }
7169
+ }
7170
+ if (t.isClassDeclaration(node.declaration) && t.isIdentifier(node.declaration.id, { name: "Store" })) {
7171
+ storeExportCache.set(file, true);
7172
+ return true;
7173
+ }
7174
+ }
7175
+ for (const specifier of node.specifiers) {
7176
+ if ((t.isIdentifier(specifier.exported) ? specifier.exported.name : specifier.exported.value) !== "Store") continue;
7177
+ if (!node.source || typeof node.source.value !== "string") {
7178
+ storeExportCache.set(file, true);
7179
+ return true;
7180
+ }
7181
+ const resolved = resolveImportPath(file, node.source.value);
7182
+ if (resolved && moduleExportsStore(resolved, resolveImportPath, seen)) {
7183
+ storeExportCache.set(file, true);
7184
+ return true;
7185
+ }
7186
+ }
7187
+ }
7188
+ if (t.isExportAllDeclaration(node) && typeof node.source.value === "string") {
7189
+ const resolved = resolveImportPath(file, node.source.value);
7190
+ if (resolved && moduleExportsStore(resolved, resolveImportPath, seen)) {
7191
+ storeExportCache.set(file, true);
7192
+ return true;
7193
+ }
7194
+ }
7195
+ }
7196
+ storeExportCache.set(file, false);
7197
+ return false;
7198
+ }
7199
+ function findStoreClasses(ast, storeName) {
7200
+ const found = [];
5911
7201
  for (const node of ast.program.body) {
5912
7202
  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;
7203
+ if (!decl || !decl.id || !t.isIdentifier(decl.superClass, { name: storeName })) continue;
7204
+ found.push(decl);
5916
7205
  }
5917
7206
  return found;
5918
7207
  }
@@ -6820,11 +8109,21 @@ function shouldMinifyGeaSymbolsForBuild(config) {
6820
8109
  const formats = lib.formats ?? [];
6821
8110
  return formats.length > 0 && formats.every((format) => format === "iife" || format === "umd");
6822
8111
  }
6823
- function geaPlugin() {
8112
+ function geaPlugin(options = {}) {
8113
+ const envIrOutFile = process.env.GEA_IR_OUT || process.env.GEA_IR_FILE;
8114
+ const irOptions = options.ir ?? (envIrOutFile ? {
8115
+ enabled: true,
8116
+ outFile: envIrOutFile
8117
+ } : void 0);
6824
8118
  const storeModules = /* @__PURE__ */ new Set();
6825
8119
  const componentModules = /* @__PURE__ */ new Set();
6826
8120
  let isServeCommand = false;
6827
8121
  let shouldMinifyGeaSymbolKeys = false;
8122
+ let resolvedConfig = null;
8123
+ const irModules = /* @__PURE__ */ new Map();
8124
+ const irComponents = /* @__PURE__ */ new Map();
8125
+ const irStores = /* @__PURE__ */ new Map();
8126
+ const hostCapabilities = /* @__PURE__ */ new Set();
6828
8127
  const storeRegistry = /* @__PURE__ */ new Map();
6829
8128
  const resolveImportPath = (importer, source) => {
6830
8129
  const base = resolve(dirname(importer), source);
@@ -6935,6 +8234,7 @@ function geaPlugin() {
6935
8234
  name: "gea-plugin",
6936
8235
  enforce: "pre",
6937
8236
  configResolved(config) {
8237
+ resolvedConfig = config;
6938
8238
  isServeCommand = config.command === "serve";
6939
8239
  shouldMinifyGeaSymbolKeys = shouldMinifyGeaSymbolsForBuild(config);
6940
8240
  },
@@ -6981,6 +8281,7 @@ function geaPlugin() {
6981
8281
  if (!cleanId.match(/\.(js|jsx|ts|tsx)$/) || cleanId.includes("node_modules")) return null;
6982
8282
  let transformedCode = code;
6983
8283
  let changed = false;
8284
+ if (irOptions?.enabled) recordHostCapabilities(code);
6984
8285
  if (code.includes("extends Store") || code.includes("new Store(")) {
6985
8286
  storeModules.add(cleanId);
6986
8287
  const storeClassName = extractStoreClassName(code);
@@ -6999,11 +8300,16 @@ function geaPlugin() {
6999
8300
  transformedCode = observeResult.code;
7000
8301
  changed = true;
7001
8302
  }
7002
- const storeResult = transformCompiledStoreModule(transformedCode);
7003
- if (storeResult?.changed) return {
7004
- code: storeResult.code,
7005
- map: null
7006
- };
8303
+ const storeResult = transformCompiledStoreModule(transformedCode, cleanId, resolveImportPath);
8304
+ for (const storeIr of storeResult?.irs ?? (storeResult?.ir ? [storeResult.ir] : [])) recordStoreIr(cleanId, storeIr);
8305
+ if (storeResult?.changed) {
8306
+ if (!/\bextends\s+(Component|ReactiveComponent)\b|\bmount\s*\(/.test(storeResult.code)) return {
8307
+ code: storeResult.code,
8308
+ map: null
8309
+ };
8310
+ transformedCode = storeResult.code;
8311
+ changed = true;
8312
+ }
7007
8313
  const rootMountResult = transformStaticRootMount(transformedCode, cleanId, resolveImportPath);
7008
8314
  if (rootMountResult?.changed) {
7009
8315
  for (const file of rootMountResult.watchFiles ?? []) this.addWatchFile?.(file);
@@ -7018,6 +8324,7 @@ function geaPlugin() {
7018
8324
  code: transformedCode,
7019
8325
  isServe: isServeCommand,
7020
8326
  isSSR,
8327
+ embedded: !!irOptions?.enabled,
7021
8328
  hmrImportSource: HMR_RUNTIME_ID,
7022
8329
  isStoreModule,
7023
8330
  isComponentModule,
@@ -7027,7 +8334,15 @@ function geaPlugin() {
7027
8334
  registerStoreModule: (fp) => storeModules.add(fp),
7028
8335
  registerComponentModule: (fp) => componentModules.add(fp)
7029
8336
  });
7030
- if (result) return result;
8337
+ if (result) {
8338
+ if (result.ir) recordComponentIr(cleanId, result.ir);
8339
+ if (result.ir?.components.some((component) => component.reactiveState)) return {
8340
+ code: result.code,
8341
+ map: result.map ?? null,
8342
+ moduleSideEffects: "no-treeshake"
8343
+ };
8344
+ return result;
8345
+ }
7031
8346
  if (isServeCommand && !isSSR) {
7032
8347
  const componentDeps = findComponentDeps(transformedCode, cleanId);
7033
8348
  if (componentDeps.length > 0) {
@@ -7051,8 +8366,136 @@ function geaPlugin() {
7051
8366
  code: next,
7052
8367
  map: null
7053
8368
  };
8369
+ },
8370
+ generateBundle(_options, bundle) {
8371
+ if (!irOptions?.enabled) return;
8372
+ const renderedIds = renderedModuleIds(bundle);
8373
+ const modules = Array.from(irModules.values()).filter((module) => {
8374
+ if (!renderedIds) return true;
8375
+ return renderedIds.has(cleanRollupModuleId(module.id)) || renderedIds.has(cleanRollupModuleId(module.file));
8376
+ });
8377
+ const componentIds = new Set(modules.flatMap((module) => module.components));
8378
+ const storeIds = new Set(modules.flatMap((module) => module.stores));
8379
+ const moduleById = new Map(Array.from(irModules.values()).map((module) => [module.id, module]));
8380
+ const pending = Array.from(componentIds);
8381
+ while (pending.length > 0) {
8382
+ const component = irComponents.get(pending.pop());
8383
+ if (!component) continue;
8384
+ for (const tag of collectMountTags(component.template.slots)) for (const candidate of irComponents.values()) {
8385
+ if (candidate.exportName !== tag || componentIds.has(candidate.id)) continue;
8386
+ componentIds.add(candidate.id);
8387
+ pending.push(candidate.id);
8388
+ const candidateModule = moduleById.get(candidate.module);
8389
+ if (candidateModule && !modules.includes(candidateModule)) modules.push(candidateModule);
8390
+ }
8391
+ }
8392
+ const irBundle = {
8393
+ schema: "gea-ir",
8394
+ version: 1,
8395
+ entry: geaIrEntryFromBundle(bundle) ?? geaIrConfiguredEntry(resolvedConfig),
8396
+ modules,
8397
+ components: Array.from(irComponents.values()).filter((component) => componentIds.has(component.id)),
8398
+ stores: Array.from(irStores.values()).filter((store) => storeIds.has(store.id)),
8399
+ hostCapabilities: Array.from(hostCapabilities).sort()
8400
+ };
8401
+ const source = JSON.stringify(irBundle, null, 2);
8402
+ const outFile = irOptions.outFile ?? "gea-ir.json";
8403
+ if (outFile.startsWith("/") || /^[A-Za-z]:[\\/]/.test(outFile)) {
8404
+ mkdirSync(dirname(outFile), { recursive: true });
8405
+ writeFileSync(outFile, source);
8406
+ } else this.emitFile({
8407
+ type: "asset",
8408
+ fileName: outFile,
8409
+ source
8410
+ });
7054
8411
  }
7055
8412
  };
8413
+ function collectMountTags(slots, tags = /* @__PURE__ */ new Set(), depth = 0) {
8414
+ if (depth > 8 || !Array.isArray(slots)) return tags;
8415
+ for (const slot of slots) {
8416
+ if (!slot || typeof slot !== "object") continue;
8417
+ const { kind, payload } = slot;
8418
+ if (!payload || typeof payload !== "object") continue;
8419
+ const record = payload;
8420
+ if (kind === "mount" && typeof record.tag === "string") tags.add(record.tag);
8421
+ for (const key of Object.keys(record)) {
8422
+ if (key === "attrs" || key === "children") continue;
8423
+ const value = record[key];
8424
+ if (value && typeof value === "object" && Array.isArray(value.slots)) collectMountTags(value.slots, tags, depth + 1);
8425
+ }
8426
+ }
8427
+ return tags;
8428
+ }
8429
+ function recordComponentIr(moduleId, ir) {
8430
+ const existing = irModules.get(moduleId) ?? {
8431
+ id: moduleId,
8432
+ file: moduleId,
8433
+ components: [],
8434
+ stores: []
8435
+ };
8436
+ const componentIds = new Set(existing.components);
8437
+ for (const component of ir.components) {
8438
+ irComponents.set(component.id, component);
8439
+ componentIds.add(component.id);
8440
+ }
8441
+ irModules.set(moduleId, {
8442
+ ...existing,
8443
+ components: Array.from(componentIds)
8444
+ });
8445
+ }
8446
+ function recordStoreIr(moduleId, store) {
8447
+ const existing = irModules.get(moduleId) ?? {
8448
+ id: moduleId,
8449
+ file: moduleId,
8450
+ components: [],
8451
+ stores: []
8452
+ };
8453
+ const storeIds = new Set(existing.stores);
8454
+ irStores.set(store.id, store);
8455
+ storeIds.add(store.id);
8456
+ irModules.set(moduleId, {
8457
+ ...existing,
8458
+ stores: Array.from(storeIds)
8459
+ });
8460
+ }
8461
+ function recordHostCapabilities(source) {
8462
+ if (/\bfetch\s*\(/.test(source)) hostCapabilities.add("fetch");
8463
+ if (source.includes("https://")) hostCapabilities.add("https");
8464
+ if (/\bApps\s*\./.test(source)) hostCapabilities.add("apps");
8465
+ if (/\b(?:BLE|BLEServer)\b|\bgea_embedded_ble_|\b__gea_embedded_ble_/.test(source)) hostCapabilities.add("ble");
8466
+ if (/\bWiFi\s*\./.test(source)) hostCapabilities.add("wifi");
8467
+ if (/\b(?:Accelerometer|accelerometer)\s*\.|\bgea_embedded_imu_/.test(source)) hostCapabilities.add("imu");
8468
+ if (/\baudioContext\s*\.|\b__gea_audioContext\b|\b__gea_Audio\b/.test(source)) hostCapabilities.add("audio");
8469
+ if (/\bscreen\s*\./.test(source)) hostCapabilities.add("screen");
8470
+ if (/\b__gea_embedded_image\b/.test(source)) hostCapabilities.add("image");
8471
+ if (/\b__gea_embedded_touch\b/.test(source)) hostCapabilities.add("touch");
8472
+ if (/\bdocument\s*\./.test(source)) hostCapabilities.add("dom");
8473
+ }
8474
+ }
8475
+ function renderedModuleIds(bundle) {
8476
+ const ids = /* @__PURE__ */ new Set();
8477
+ for (const item of Object.values(bundle)) {
8478
+ if (item.type !== "chunk") continue;
8479
+ if (item.facadeModuleId) ids.add(cleanRollupModuleId(item.facadeModuleId));
8480
+ if (item.modules && typeof item.modules === "object") for (const id of Object.keys(item.modules)) ids.add(cleanRollupModuleId(id));
8481
+ }
8482
+ return ids.size > 0 ? ids : null;
8483
+ }
8484
+ function cleanRollupModuleId(id) {
8485
+ return id.split("?")[0] ?? id;
8486
+ }
8487
+ function geaIrEntryFromBundle(bundle) {
8488
+ return Object.values(bundle).filter((item) => item.type === "chunk" && item.isEntry && item.fileName).map((item) => item.fileName).sort()[0] ?? null;
8489
+ }
8490
+ function geaIrConfiguredEntry(config) {
8491
+ const input = config?.build.rollupOptions.input;
8492
+ if (typeof input === "string") return input;
8493
+ if (Array.isArray(input)) return input[0] ? String(input[0]) : "";
8494
+ if (input && typeof input === "object") {
8495
+ const firstKey = Object.keys(input).sort()[0];
8496
+ return firstKey ? String(input[firstKey]) : "";
8497
+ }
8498
+ return "";
7056
8499
  }
7057
8500
  function resolveToFile(base) {
7058
8501
  const exts = [
@@ -7105,6 +8548,7 @@ function compilerRuntimeSource(runtimePath) {
7105
8548
  relationalClass,
7106
8549
  relationalClassProp,
7107
8550
  reactiveStyle,
8551
+ reactiveStyleProp,
7108
8552
  reactiveValue,
7109
8553
  reactiveValueRead,
7110
8554
  delegateEvent,