@geajs/vite-plugin 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +507 -207
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,10 +1,11 @@
1
+ import { parse } from "@babel/parser";
1
2
  import _traverse from "@babel/traverse";
2
3
  import _generate from "@babel/generator";
3
4
  import * as t from "@babel/types";
4
- import { parse } from "@babel/parser";
5
5
  import { id, js, jsExpr, jsImport } from "eszter";
6
6
  import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
7
- import { dirname, relative, resolve } from "node:path";
7
+ import { dirname, posix, relative, resolve } from "node:path";
8
+ import { createHash } from "node:crypto";
8
9
  import { fileURLToPath } from "node:url";
9
10
  //#region src/utils/babel-interop.ts
10
11
  /**
@@ -277,215 +278,59 @@ function collectBindings(stmts, bindings) {
277
278
  let localName;
278
279
  if (t.isIdentifier(prop.value)) localName = prop.value.name;
279
280
  else continue;
280
- const mem = t.memberExpression(cloneExpr$2(decl.init), t.identifier(sourceKey));
281
+ const mem = t.memberExpression(cloneExpr$1(decl.init), t.identifier(sourceKey));
281
282
  bindings.set(localName, mem);
282
283
  }
283
- else if (t.isIdentifier(decl.id)) bindings.set(decl.id.name, cloneExpr$2(decl.init));
284
+ else if (t.isIdentifier(decl.id)) bindings.set(decl.id.name, cloneExpr$1(decl.init));
284
285
  }
285
286
  }
286
287
  }
287
288
  /** Shallow clone to avoid AST aliasing hazards when substituting. */
288
- function cloneExpr$2(expr) {
289
+ function cloneExpr$1(expr) {
289
290
  return t.cloneNode(expr);
290
291
  }
291
292
  //#endregion
292
293
  //#region src/closure-codegen/emit/emit-substitution.ts
293
- function cloneExpr$1(expr) {
294
- return t.cloneNode(expr);
295
- }
296
- /**
297
- * Walk an expression tree and replace Identifier references that match a
298
- * binding with the bound expression (also recursively). Used to rewrite
299
- * JSX-expression member chains so their root is the real reactive source.
300
- * Returns a new expression; does not mutate input.
301
- */
294
+ /** Resolve template aliases at free read/write sites, preserving lexical scope. */
302
295
  function substituteBindings(expr, bindings) {
303
296
  if (!expr) return expr;
304
297
  if (t.isTSAsExpression(expr) || t.isTSTypeAssertion(expr) || t.isTSNonNullExpression(expr) || t.isTSInstantiationExpression(expr)) return substituteBindings(expr.expression, bindings);
305
298
  if (bindings.size === 0) return expr;
306
- if (t.isIdentifier(expr)) {
307
- const b = bindings.get(expr.name);
308
- if (b) {
309
- const scoped = new Map(bindings);
310
- scoped.delete(expr.name);
311
- return substituteBindings(cloneExpr$1(b), scoped);
299
+ const node = t.cloneNode(expr, true);
300
+ const statement = t.isStatement(node);
301
+ const container = t.isJSXExpressionContainer(node);
302
+ if (container && t.isJSXEmptyExpression(node.expression)) return node;
303
+ if (!statement && !container && !t.isExpression(node)) return node;
304
+ const file = t.file(t.program([statement ? node : t.expressionStatement(container ? node.expression : node)]));
305
+ traverse(file, { Identifier(path) {
306
+ const name = path.node.name;
307
+ if (!bindings.has(name) || path.scope.getBinding(name)) return;
308
+ if (!path.isReferencedIdentifier() && !isAssignmentTarget(path)) return;
309
+ const remaining = new Map(bindings);
310
+ remaining.delete(name);
311
+ const replacement = substituteBindings(bindings.get(name), remaining);
312
+ path.replaceWith(t.cloneNode(replacement, true));
313
+ path.skip();
314
+ } });
315
+ const result = statement ? file.program.body[0] : file.program.body[0].expression;
316
+ return container ? {
317
+ ...node,
318
+ expression: result
319
+ } : result;
320
+ }
321
+ /** Assignment patterns write references; labels and declarations do not. */
322
+ function isAssignmentTarget(path) {
323
+ let target = path;
324
+ while (target.parentPath) {
325
+ const parent = target.parentPath;
326
+ if (parent.isObjectProperty() && target.key === "value" || parent.isObjectPattern() || parent.isArrayPattern() || parent.isRestElement() && target.key === "argument" || parent.isAssignmentPattern() && target.key === "left") {
327
+ target = parent;
328
+ continue;
312
329
  }
313
- return expr;
314
- }
315
- if (t.isMemberExpression(expr)) return {
316
- ...expr,
317
- object: substituteBindings(expr.object, bindings),
318
- property: expr.computed ? substituteBindings(expr.property, bindings) : expr.property
319
- };
320
- if (t.isCallExpression(expr)) return {
321
- ...expr,
322
- callee: substituteBindings(expr.callee, bindings),
323
- arguments: expr.arguments.map((a) => substituteBindings(a, bindings))
324
- };
325
- if (t.isBinaryExpression(expr) || t.isLogicalExpression(expr)) return {
326
- ...expr,
327
- left: substituteBindings(expr.left, bindings),
328
- right: substituteBindings(expr.right, bindings)
329
- };
330
- if (t.isConditionalExpression(expr)) return {
331
- ...expr,
332
- test: substituteBindings(expr.test, bindings),
333
- consequent: substituteBindings(expr.consequent, bindings),
334
- alternate: substituteBindings(expr.alternate, bindings)
335
- };
336
- if (t.isUnaryExpression(expr) || t.isUpdateExpression(expr)) return {
337
- ...expr,
338
- argument: substituteBindings(expr.argument, bindings)
339
- };
340
- if (t.isTemplateLiteral(expr)) return {
341
- ...expr,
342
- expressions: expr.expressions.map((e) => substituteBindings(e, bindings))
343
- };
344
- if (t.isOptionalMemberExpression(expr)) return {
345
- ...expr,
346
- object: substituteBindings(expr.object, bindings),
347
- property: expr.computed ? substituteBindings(expr.property, bindings) : expr.property
348
- };
349
- if (t.isOptionalCallExpression(expr)) return {
350
- ...expr,
351
- callee: substituteBindings(expr.callee, bindings),
352
- arguments: expr.arguments.map((a) => substituteBindings(a, bindings))
353
- };
354
- if (t.isArrowFunctionExpression(expr) || t.isFunctionExpression(expr)) {
355
- const paramNames = /* @__PURE__ */ new Set();
356
- for (const p of expr.params) if (t.isIdentifier(p)) paramNames.add(p.name);
357
- else if (t.isObjectPattern(p)) {
358
- for (const prop of p.properties) if (t.isObjectProperty(prop) && t.isIdentifier(prop.value)) paramNames.add(prop.value.name);
359
- }
360
- const shadowed = new Map(bindings);
361
- for (const k of paramNames) shadowed.delete(k);
362
- if (shadowed.size === 0) return expr;
363
- return {
364
- ...expr,
365
- body: substituteBindings(expr.body, shadowed)
366
- };
330
+ return (parent.isAssignmentExpression() || parent.isForInStatement() || parent.isForOfStatement()) && target.key === "left" || parent.isUpdateExpression() && target.key === "argument";
367
331
  }
368
- if (t.isBlockStatement(expr)) return {
369
- ...expr,
370
- body: expr.body.map((s) => substituteBindings(s, bindings))
371
- };
372
- if (t.isExpressionStatement(expr)) return {
373
- ...expr,
374
- expression: substituteBindings(expr.expression, bindings)
375
- };
376
- if (t.isReturnStatement(expr)) return expr.argument ? {
377
- ...expr,
378
- argument: substituteBindings(expr.argument, bindings)
379
- } : expr;
380
- if (t.isIfStatement(expr)) return {
381
- ...expr,
382
- test: substituteBindings(expr.test, bindings),
383
- consequent: substituteBindings(expr.consequent, bindings),
384
- alternate: expr.alternate ? substituteBindings(expr.alternate, bindings) : null
385
- };
386
- if (t.isVariableDeclaration(expr)) return {
387
- ...expr,
388
- declarations: expr.declarations.map((d) => ({
389
- ...d,
390
- init: d.init ? substituteBindings(d.init, bindings) : null
391
- }))
392
- };
393
- if (t.isTryStatement(expr)) return {
394
- ...expr,
395
- block: substituteBindings(expr.block, bindings),
396
- handler: expr.handler ? {
397
- ...expr.handler,
398
- body: substituteBindings(expr.handler.body, bindings)
399
- } : null,
400
- finalizer: expr.finalizer ? substituteBindings(expr.finalizer, bindings) : null
401
- };
402
- if (t.isForStatement(expr) || t.isForInStatement(expr) || t.isForOfStatement(expr)) return {
403
- ...expr,
404
- body: substituteBindings(expr.body, bindings)
405
- };
406
- if (t.isWhileStatement(expr) || t.isDoWhileStatement(expr)) return {
407
- ...expr,
408
- test: substituteBindings(expr.test, bindings),
409
- body: substituteBindings(expr.body, bindings)
410
- };
411
- if (t.isSwitchStatement(expr)) return {
412
- ...expr,
413
- discriminant: substituteBindings(expr.discriminant, bindings),
414
- cases: expr.cases.map((c) => ({
415
- ...c,
416
- consequent: c.consequent.map((s) => substituteBindings(s, bindings))
417
- }))
418
- };
419
- if (t.isArrayExpression(expr)) return {
420
- ...expr,
421
- elements: expr.elements.map((e) => e ? substituteBindings(e, bindings) : e)
422
- };
423
- if (t.isObjectExpression(expr)) return {
424
- ...expr,
425
- properties: expr.properties.map((p) => t.isObjectProperty(p) ? {
426
- ...p,
427
- value: substituteBindings(p.value, bindings)
428
- } : p)
429
- };
430
- if (t.isSpreadElement(expr)) return {
431
- ...expr,
432
- argument: substituteBindings(expr.argument, bindings)
433
- };
434
- if (t.isNewExpression(expr)) return {
435
- ...expr,
436
- callee: substituteBindings(expr.callee, bindings),
437
- arguments: expr.arguments.map((a) => substituteBindings(a, bindings))
438
- };
439
- if (t.isSequenceExpression(expr)) return {
440
- ...expr,
441
- expressions: expr.expressions.map((e) => substituteBindings(e, bindings))
442
- };
443
- if (t.isAssignmentExpression(expr)) return {
444
- ...expr,
445
- right: substituteBindings(expr.right, bindings)
446
- };
447
- if (t.isJSXElement(expr)) {
448
- const newOpening = {
449
- ...expr.openingElement,
450
- attributes: expr.openingElement.attributes.map((a) => {
451
- if (t.isJSXAttribute(a) && a.value && t.isJSXExpressionContainer(a.value) && !t.isJSXEmptyExpression(a.value.expression)) return {
452
- ...a,
453
- value: {
454
- ...a.value,
455
- expression: substituteBindings(a.value.expression, bindings)
456
- }
457
- };
458
- return a;
459
- })
460
- };
461
- const newChildren = expr.children.map((c) => substituteBindings(c, bindings));
462
- return {
463
- ...expr,
464
- openingElement: newOpening,
465
- children: newChildren
466
- };
467
- }
468
- if (t.isJSXFragment(expr)) return {
469
- ...expr,
470
- children: expr.children.map((c) => substituteBindings(c, bindings))
471
- };
472
- if (t.isJSXExpressionContainer(expr) && !t.isJSXEmptyExpression(expr.expression)) return {
473
- ...expr,
474
- expression: substituteBindings(expr.expression, bindings)
475
- };
476
- if (t.isJSXText(expr) || t.isJSXSpreadChild(expr) || t.isJSXEmptyExpression(expr)) return expr;
477
- return expr;
332
+ return false;
478
333
  }
479
- /**
480
- * Compile a JSX element/fragment into a clone-and-wire expression.
481
- *
482
- * Returns a BlockStatement that:
483
- * - declares `const root = (_tpl<N>_root || (_tpl<N>_root = _tpl<N>_create())).cloneNode(true)`
484
- * - wires all slots
485
- * - returns `root`
486
- *
487
- * Side effect: adds the lazy template root cache to `ctx.templateDecls`.
488
- */
489
334
  //#endregion
490
335
  //#region src/closure-codegen/emit/template-decl.ts
491
336
  const SVG_TEMPLATE_ROOT_TAGS = new Set([
@@ -621,6 +466,27 @@ function emitWalkExpr(root, walk, walkKinds) {
621
466
  return out;
622
467
  }
623
468
  //#endregion
469
+ //#region src/closure-codegen/keyed-list/keyed-list-item-reader.ts
470
+ /** Read through the row observable so primitive replacements remain reactive too. */
471
+ function useObservableItem(createItem, importsNeeded) {
472
+ if (!t.isArrowFunctionExpression(createItem)) throw new Error("Expected a keyed-list row factory");
473
+ const item = createItem.params[0];
474
+ const names = /* @__PURE__ */ new Set();
475
+ t.traverseFast(createItem, (node) => {
476
+ if (t.isIdentifier(node)) names.add(node.name);
477
+ });
478
+ let name = "__geaItemObservable";
479
+ while (names.has(name)) name += "_";
480
+ const observable = t.identifier(name);
481
+ const read = t.callExpression(t.identifier("readItem"), [observable]);
482
+ const bindings = /* @__PURE__ */ new Map();
483
+ if (t.isIdentifier(item)) bindings.set(item.name, read);
484
+ else for (const name of Object.keys(t.getBindingIdentifiers(item))) bindings.set(name, t.callExpression(t.arrowFunctionExpression([t.cloneNode(item, true)], t.identifier(name)), [t.cloneNode(read, true)]));
485
+ createItem.body = substituteBindings(createItem.body, bindings);
486
+ createItem.params[0] = observable;
487
+ importsNeeded.add("readItem");
488
+ }
489
+ //#endregion
624
490
  //#region src/closure-codegen/emit/emit-map-branch.ts
625
491
  function buildMapBranchFn(mapExpr, ctx) {
626
492
  const arrowExpr = mapExpr.arguments[0];
@@ -644,7 +510,7 @@ function buildMapBranchFn(mapExpr, ctx) {
644
510
  t.identifier("d")
645
511
  ], createItemBlock);
646
512
  ctx.importsNeeded.add("createItemObservable");
647
- ctx.importsNeeded.add("createItemProxy");
513
+ useObservableItem(createItemFn, ctx.importsNeeded);
648
514
  ctx.importsNeeded.add("_rescue");
649
515
  ctx.importsNeeded.add("GEA_PROXY_RAW");
650
516
  const listId = "L" + ctx.listCounter++;
@@ -658,7 +524,7 @@ function buildMapBranchFn(mapExpr, ctx) {
658
524
  t.identifier(rqName),
659
525
  t.callExpression(t.identifier("String"), [t.identifier("__k")]),
660
526
  t.identifier(itemParam)
661
- ]))]), t.ifStatement(t.identifier("__r"), t.blockStatement([t.returnStatement(t.identifier("__r"))])), t.variableDeclaration("const", [t.variableDeclarator(t.identifier("__rd"), t.callExpression(t.memberExpression(t.identifier("d"), t.identifier("child")), []))]), t.variableDeclaration("const", [t.variableDeclarator(t.identifier("__obs"), t.callExpression(t.identifier("createItemObservable"), [t.identifier(itemParam)]))]), t.variableDeclaration("const", [t.variableDeclarator(t.identifier("__li"), t.conditionalExpression(t.logicalExpression("&&", t.binaryExpression("!==", t.identifier(itemParam), t.nullLiteral()), t.binaryExpression("===", t.unaryExpression("typeof", t.identifier(itemParam)), t.stringLiteral("object"))), t.callExpression(t.identifier("createItemProxy"), [t.identifier("__obs")]), t.identifier(itemParam)))]), t.variableDeclaration("const", [t.variableDeclarator(t.identifier("__el"), t.callExpression(t.identifier(ciName), [
527
+ ]))]), t.ifStatement(t.identifier("__r"), t.blockStatement([t.returnStatement(t.identifier("__r"))])), t.variableDeclaration("const", [t.variableDeclarator(t.identifier("__rd"), t.callExpression(t.memberExpression(t.identifier("d"), t.identifier("child")), []))]), t.variableDeclaration("const", [t.variableDeclarator(t.identifier("__obs"), t.callExpression(t.identifier("createItemObservable"), [t.identifier(itemParam)]))]), t.variableDeclaration("const", [t.variableDeclarator(t.identifier("__li"), t.identifier("__obs"))]), t.variableDeclaration("const", [t.variableDeclarator(t.identifier("__el"), t.callExpression(t.identifier(ciName), [
662
528
  t.identifier("__li"),
663
529
  t.identifier(idxParam),
664
530
  t.identifier("__rd")
@@ -2125,7 +1991,7 @@ function buildCreateEntryArrow(options, itemId, idxId) {
2125
1991
  }
2126
1992
  const obsId = t.identifier("__obs");
2127
1993
  const liveItemId = t.identifier("__li");
2128
- if (needsItemProxy) createEntryStmts.push(t.variableDeclaration("const", [t.variableDeclarator(obsId, t.callExpression(t.identifier("createItemObservable"), [itemId]))]), t.variableDeclaration("const", [t.variableDeclarator(liveItemId, t.conditionalExpression(t.logicalExpression("&&", t.binaryExpression("!==", itemId, t.nullLiteral()), t.binaryExpression("===", t.unaryExpression("typeof", itemId), t.stringLiteral("object"))), t.callExpression(t.identifier("createItemProxy"), [obsId]), itemId))]));
1994
+ if (needsItemProxy) createEntryStmts.push(t.variableDeclaration("const", [t.variableDeclarator(obsId, t.callExpression(t.identifier("createItemObservable"), [itemId]))]), t.variableDeclaration("const", [t.variableDeclarator(liveItemId, obsId)]));
2129
1995
  else createEntryStmts.push(t.variableDeclaration("const", [t.variableDeclarator(obsId, t.nullLiteral())]), t.variableDeclaration("const", [t.variableDeclarator(liveItemId, itemId)]));
2130
1996
  const elementId = t.identifier("__el");
2131
1997
  createEntryStmts.push(t.variableDeclaration("const", [t.variableDeclarator(elementId, t.callExpression(t.identifier(ciName), [
@@ -2150,7 +2016,7 @@ function buildSimpleCreateEntryArrow(options, itemId, idxId) {
2150
2016
  const liveItemId = t.identifier("__li");
2151
2017
  const createEntryStmts = [t.variableDeclaration("const", [t.variableDeclarator(kId, keyExprWith(options, itemId, idxId))])];
2152
2018
  if (needsRowDisposer) createEntryStmts.push(t.variableDeclaration("const", [t.variableDeclarator(rowDId, t.callExpression(t.memberExpression(t.identifier("d"), t.identifier("child")), []))]));
2153
- if (needsItemProxy) createEntryStmts.push(t.variableDeclaration("const", [t.variableDeclarator(obsId, t.callExpression(t.identifier("createItemObservable"), [itemId]))]), t.variableDeclaration("const", [t.variableDeclarator(liveItemId, t.conditionalExpression(t.logicalExpression("&&", t.binaryExpression("!==", itemId, t.nullLiteral()), t.binaryExpression("===", t.unaryExpression("typeof", itemId), t.stringLiteral("object"))), t.callExpression(t.identifier("createItemProxy"), [obsId]), itemId))]));
2019
+ if (needsItemProxy) createEntryStmts.push(t.variableDeclaration("const", [t.variableDeclarator(obsId, t.callExpression(t.identifier("createItemObservable"), [itemId]))]), t.variableDeclaration("const", [t.variableDeclarator(liveItemId, obsId)]));
2154
2020
  const createArgs = [needsItemProxy ? liveItemId : itemId, idxId];
2155
2021
  if (needsRowDisposer) createArgs.push(rowDId);
2156
2022
  createEntryStmts.push(t.variableDeclaration("const", [t.variableDeclarator(elementId, t.callExpression(t.identifier(ciName), createArgs))]));
@@ -3008,8 +2874,21 @@ function buildInlinePropKeyedListBlock(options) {
3008
2874
  __PATCH__: options.patchEntryArrow
3009
2875
  });
3010
2876
  replaceByKeyMarker(block, options.relMatches);
2877
+ if (options.ctx.embedded) rewriteObserveReconcileForEmbedded(block);
3011
2878
  return block;
3012
2879
  }
2880
+ function rewriteObserveReconcileForEmbedded(node) {
2881
+ if (!node || typeof node !== "object") return;
2882
+ if (Array.isArray(node)) {
2883
+ for (const value of node) rewriteObserveReconcileForEmbedded(value);
2884
+ return;
2885
+ }
2886
+ 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"), []);
2887
+ for (const key of Object.keys(node)) {
2888
+ if (key === "loc" || key === "start" || key === "end" || key === "type") continue;
2889
+ rewriteObserveReconcileForEmbedded(node[key]);
2890
+ }
2891
+ }
3013
2892
  function isDirectIdKey(expr) {
3014
2893
  if (!t.isArrowFunctionExpression(expr) || expr.params.length === 0) return false;
3015
2894
  const firstParam = expr.params[0];
@@ -3150,7 +3029,7 @@ function emitKeyedListSlot(slot, stmts, ctx) {
3150
3029
  const needsRowDisposer = !(patchRowExpr && t.isIdentifier(itemParam) && !createItemBodyReferencesRowDisposer(createItem));
3151
3030
  if (needsItemProxy) {
3152
3031
  ctx.importsNeeded.add("createItemObservable");
3153
- ctx.importsNeeded.add("createItemProxy");
3032
+ useObservableItem(createItem, ctx.importsNeeded);
3154
3033
  }
3155
3034
  const ciName = "__ki_" + listId;
3156
3035
  const prName = "__kp_" + listId;
@@ -5433,6 +5312,185 @@ function rewriteFnComponent(fnDecl, parentCtx) {
5433
5312
  else fnDecl.params[1] = t.identifier("d");
5434
5313
  fnDecl.body.body = newBody;
5435
5314
  }
5315
+ //#endregion
5316
+ //#region src/closure-codegen/transform/transform-component-props.ts
5317
+ /**
5318
+ * Best-effort props-shape inference for compiled component classes.
5319
+ *
5320
+ * PROBLEM: the codegen rewrites `class HomeView extends Component { ... }`
5321
+ * into `class HomeView extends CompiledComponent { ... }` with no type
5322
+ * argument. `CompiledComponent<P extends Record<string, any> = Record<string,
5323
+ * any>>` then defaults `P` to `Record<string, any>`, so every
5324
+ * `this.props.x` read inside the class infers as `any` — even though the
5325
+ * original JSX call site (`<HomeView app={this} />`) fully determines the
5326
+ * real shape.
5327
+ *
5328
+ * FIX: scan the module for JSX usages of each locally-declared component and
5329
+ * synthesize a `{ attr: Type; ... }` literal from what's actually passed, so
5330
+ * callers can thread it onto the rewritten `extends CompiledXxx<...>` clause.
5331
+ *
5332
+ * This has no type checker to lean on — only the attribute value's own AST
5333
+ * shape. Only a few forms are classified:
5334
+ * - `this` → the name of the enclosing class
5335
+ * - string / template (static) → `string`
5336
+ * - number / boolean / null literal → their primitive type
5337
+ * Anything else (a spread attribute, JSX children, an arbitrary expression)
5338
+ * makes that JSX usage "unresolvable", and the whole component is skipped —
5339
+ * emitting a narrower-than-reality type would be worse than emitting none,
5340
+ * since geatsc has no boxing to fall back on.
5341
+ */
5342
+ /**
5343
+ * Names among `componentNames` that appear as a JSX opening-element tag
5344
+ * anywhere in this module — regardless of whether the usage is resolvable
5345
+ * into a props shape (unlike `inferComponentPropsTypes`, which drops
5346
+ * unresolvable or props-less usages entirely).
5347
+ *
5348
+ * Used to distinguish "used here, but with a shape we couldn't infer" from
5349
+ * "never used as JSX in this file at all" — only the latter is safe grounds
5350
+ * for treating a class as provably propless. See `applyPropsTypeArgument`
5351
+ * in transform.ts for how this combines with `classPropsReadsAreCovered`.
5352
+ */
5353
+ function collectComponentsUsedAsJsx(ast, componentNames) {
5354
+ const used = /* @__PURE__ */ new Set();
5355
+ if (componentNames.size === 0) return used;
5356
+ traverse(ast, {
5357
+ noScope: true,
5358
+ JSXElement(path) {
5359
+ const name = path.node.openingElement.name;
5360
+ if (t.isJSXIdentifier(name) && componentNames.has(name.name)) used.add(name.name);
5361
+ }
5362
+ });
5363
+ return used;
5364
+ }
5365
+ function inferComponentPropsTypes(ast, componentNames) {
5366
+ if (componentNames.size === 0) return /* @__PURE__ */ new Map();
5367
+ const usages = /* @__PURE__ */ new Map();
5368
+ const getUsage = (name) => {
5369
+ let usage = usages.get(name);
5370
+ if (!usage) {
5371
+ usage = {
5372
+ attrs: /* @__PURE__ */ new Map(),
5373
+ siteCount: 0,
5374
+ unresolvable: false
5375
+ };
5376
+ usages.set(name, usage);
5377
+ }
5378
+ return usage;
5379
+ };
5380
+ traverse(ast, {
5381
+ noScope: true,
5382
+ JSXElement(path) {
5383
+ const opening = path.node.openingElement;
5384
+ const name = opening.name;
5385
+ if (!t.isJSXIdentifier(name) || !componentNames.has(name.name)) return;
5386
+ const usage = getUsage(name.name);
5387
+ usage.siteCount++;
5388
+ if ((path.node.children ?? []).filter((child) => !(t.isJSXText(child) && /^\s*$/.test(child.value))).length > 0) usage.unresolvable = true;
5389
+ const enclosingClassName = findEnclosingClassName(path);
5390
+ for (const attr of opening.attributes) {
5391
+ if (!t.isJSXAttribute(attr) || !t.isJSXIdentifier(attr.name)) {
5392
+ usage.unresolvable = true;
5393
+ continue;
5394
+ }
5395
+ const attrName = attr.name.name;
5396
+ const typeStr = classifyAttrValue(attr.value, enclosingClassName);
5397
+ if (typeStr == null) {
5398
+ usage.unresolvable = true;
5399
+ continue;
5400
+ }
5401
+ let observation = usage.attrs.get(attrName);
5402
+ if (!observation) {
5403
+ observation = {
5404
+ types: /* @__PURE__ */ new Set(),
5405
+ seenCount: 0
5406
+ };
5407
+ usage.attrs.set(attrName, observation);
5408
+ }
5409
+ observation.types.add(typeStr);
5410
+ observation.seenCount++;
5411
+ }
5412
+ }
5413
+ });
5414
+ const result = /* @__PURE__ */ new Map();
5415
+ for (const [name, usage] of usages) {
5416
+ if (usage.unresolvable || usage.siteCount === 0 || usage.attrs.size === 0) continue;
5417
+ const members = [...usage.attrs.entries()].map(([attrName, observation]) => {
5418
+ const required = observation.seenCount === usage.siteCount;
5419
+ const member = t.tsPropertySignature(t.identifier(attrName), t.tsTypeAnnotation(unionOfTypeStrings(observation.types)));
5420
+ member.optional = !required;
5421
+ return member;
5422
+ });
5423
+ result.set(name, t.tsTypeLiteral(members));
5424
+ }
5425
+ return result;
5426
+ }
5427
+ /**
5428
+ * Guard against emitting a props type narrower than what the class body
5429
+ * actually reads. `classDecl` must already reflect the fully-lowered body
5430
+ * (template method compiled in place) — this scans every `this.props.<key>`
5431
+ * read in the class and requires each key to be present in `propsType`.
5432
+ * Only guards against the codegen NARROWING props out from under real reads;
5433
+ * it does not otherwise validate the inferred shape.
5434
+ */
5435
+ function classPropsReadsAreCovered(classDecl, propsType) {
5436
+ const allowed = /* @__PURE__ */ new Set();
5437
+ for (const member of propsType.members) if (t.isTSPropertySignature(member) && t.isIdentifier(member.key)) allowed.add(member.key.name);
5438
+ let covered = true;
5439
+ const isPropsMember = (node) => (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) && !node.computed && t.isIdentifier(node.property, { name: "props" }) && t.isThisExpression(node.object);
5440
+ const visit = (node) => {
5441
+ if (!covered || !node || typeof node !== "object") return;
5442
+ if (Array.isArray(node)) {
5443
+ for (const child of node) visit(child);
5444
+ return;
5445
+ }
5446
+ if ((t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) && !node.computed && t.isIdentifier(node.property) && isPropsMember(node.object)) {
5447
+ if (!allowed.has(node.property.name)) covered = false;
5448
+ return;
5449
+ }
5450
+ for (const key of Object.keys(node)) {
5451
+ if (key === "loc" || key === "start" || key === "end" || key === "type") continue;
5452
+ visit(node[key]);
5453
+ }
5454
+ };
5455
+ visit(classDecl.body.body);
5456
+ return covered;
5457
+ }
5458
+ function findEnclosingClassName(path) {
5459
+ let current = path.parentPath;
5460
+ while (current) {
5461
+ if (current.isFunctionExpression() || current.isFunctionDeclaration()) return null;
5462
+ if (current.isClassDeclaration()) return current.node.id ? current.node.id.name : null;
5463
+ current = current.parentPath;
5464
+ }
5465
+ return null;
5466
+ }
5467
+ function classifyAttrValue(value, enclosingClassName) {
5468
+ if (value == null) return "boolean";
5469
+ if (t.isStringLiteral(value)) return "string";
5470
+ const expr = t.isJSXExpressionContainer(value) ? value.expression : null;
5471
+ if (!expr || t.isJSXEmptyExpression(expr)) return null;
5472
+ if (t.isThisExpression(expr)) return enclosingClassName;
5473
+ if (t.isStringLiteral(expr)) return "string";
5474
+ if (t.isNumericLiteral(expr)) return "number";
5475
+ if (t.isBooleanLiteral(expr)) return "boolean";
5476
+ if (t.isNullLiteral(expr)) return "null";
5477
+ if (t.isTemplateLiteral(expr) && expr.expressions.length === 0) return "string";
5478
+ return null;
5479
+ }
5480
+ function unionOfTypeStrings(types) {
5481
+ const nodes = [...types].sort().map(typeStringToNode);
5482
+ if (nodes.length === 1) return nodes[0];
5483
+ return t.tsUnionType(nodes);
5484
+ }
5485
+ function typeStringToNode(typeStr) {
5486
+ switch (typeStr) {
5487
+ case "string": return t.tsStringKeyword();
5488
+ case "number": return t.tsNumberKeyword();
5489
+ case "boolean": return t.tsBooleanKeyword();
5490
+ case "null": return t.tsNullKeyword();
5491
+ default: return t.tsTypeReference(t.identifier(typeStr));
5492
+ }
5493
+ }
5436
5494
  const RESOLVED_RECONCILE_ID = "\0virtual:gea-reconcile";
5437
5495
  const HMR_RUNTIME_ID = "virtual:gea-hmr";
5438
5496
  const RESOLVED_HMR_RUNTIME_ID = "\0" + HMR_RUNTIME_ID;
@@ -5811,13 +5869,17 @@ function transformFile(source, _filename, options = {}) {
5811
5869
  }
5812
5870
  const ctx = createEmitContext();
5813
5871
  ctx.irTemplates = [];
5872
+ ctx.embedded = options.embedded;
5814
5873
  ctx.directFnComponents = collectDirectFnComponents(ast);
5815
5874
  ctx.directFnComponentParams = collectDirectFnComponentParams(ast, ctx.directFnComponents);
5816
5875
  ctx.directFnStringProps = collectDirectFnStringProps(ast, ctx.directFnComponents);
5817
5876
  ctx.directFnNoDisposer = /* @__PURE__ */ new Set();
5818
- ctx.directClassComponents = collectLocalClassComponents(ast);
5877
+ const localComponentNames = collectLocalClassComponents(ast);
5878
+ ctx.directClassComponents = new Set(localComponentNames);
5819
5879
  for (const name of options.directClassComponents ?? []) ctx.directClassComponents.add(name);
5820
5880
  ctx.directFactoryComponents = new Set(options.directFactoryComponents);
5881
+ const componentPropsShapes = inferComponentPropsTypes(ast, localComponentNames);
5882
+ const componentsUsedAsJsx = collectComponentsUsedAsJsx(ast, localComponentNames);
5821
5883
  const rewritten = [];
5822
5884
  const reactiveComponentNames = /* @__PURE__ */ new Set();
5823
5885
  let firstClassIdx = -1;
@@ -5920,18 +5982,22 @@ function transformFile(source, _filename, options = {}) {
5920
5982
  ctx.importsNeeded.add("CompiledComponent");
5921
5983
  classDecl.superClass = t.identifier("CompiledComponent");
5922
5984
  usesCompiledRuntimeBase = true;
5985
+ applyPropsTypeArgument(classDecl, className, componentPropsShapes, componentsUsedAsJsx);
5923
5986
  } else if (useTinyReactiveComponent) {
5924
5987
  ctx.importsNeeded.add("CompiledTinyReactiveComponent");
5925
5988
  classDecl.superClass = t.identifier("CompiledTinyReactiveComponent");
5926
5989
  usesCompiledRuntimeBase = true;
5990
+ applyPropsTypeArgument(classDecl, className, componentPropsShapes, componentsUsedAsJsx);
5927
5991
  } else if (useLeanReactiveComponent) {
5928
5992
  ctx.importsNeeded.add("CompiledLeanReactiveComponent");
5929
5993
  classDecl.superClass = t.identifier("CompiledLeanReactiveComponent");
5930
5994
  usesCompiledRuntimeBase = true;
5995
+ applyPropsTypeArgument(classDecl, className, componentPropsShapes, componentsUsedAsJsx);
5931
5996
  } else if (t.isIdentifier(classDecl.superClass, { name: "Component" })) {
5932
5997
  ctx.importsNeeded.add("CompiledReactiveComponent");
5933
5998
  classDecl.superClass = t.identifier("CompiledReactiveComponent");
5934
5999
  usesCompiledRuntimeBase = true;
6000
+ applyPropsTypeArgument(classDecl, className, componentPropsShapes, componentsUsedAsJsx);
5935
6001
  }
5936
6002
  if (usesCompiledRuntimeBase && hasAfterRenderAsyncHook && !hasOwnInstanceMethod(classDecl, "render")) {
5937
6003
  ctx.importsNeeded.add("scheduleAfterRenderAsync");
@@ -6050,6 +6116,70 @@ function findClassDeclarationByName(ast, name) {
6050
6116
  }
6051
6117
  return null;
6052
6118
  }
6119
+ /**
6120
+ * Thread an inferred props shape onto the rewritten `extends CompiledXxx`
6121
+ * clause as an explicit type argument, e.g. `CompiledComponent<{ app: App }>`.
6122
+ *
6123
+ * Two guards keep this from ever emitting a WRONG type (never just an
6124
+ * incomplete one):
6125
+ * - If the hand-written source already annotated `extends Component<...>`,
6126
+ * that `superTypeParameters` node survives the `superClass` identifier
6127
+ * swap above untouched — do not override the author's own type.
6128
+ * - If the class body reads a `this.props.<key>` that the inferred shape
6129
+ * doesn't cover (e.g. a caller in another file passes it, invisible to
6130
+ * this per-file scan), leave the class with no type argument so it keeps
6131
+ * the permissive `Record<string, any>` default rather than breaking.
6132
+ *
6133
+ * A class with NO JSX usage site in this file at all (a root component,
6134
+ * mounted by the runtime rather than written as `<App/>` anywhere) gets no
6135
+ * entry in `componentPropsShapes` — inference only ever runs over observed
6136
+ * call sites, so an unobserved component produces no shape, resolvable or
6137
+ * not. Falling back to the permissive `Record<string, any>` default in that
6138
+ * case isn't merely imprecise, it's backwards: silence at every call site is
6139
+ * exactly the evidence that the class takes no props, so the honest emission
6140
+ * is an explicit empty type — `{}` — not the default's promise that it might
6141
+ * accept arbitrary ones.
6142
+ *
6143
+ * Emitting that empty type is gated the same way as the shape-inference path
6144
+ * above, by the SAME two-guard standard, instantiated for the "no props"
6145
+ * claim instead of an inferred one:
6146
+ * - `!componentsUsedAsJsx.has(className)` — the per-file JSX-usage guard.
6147
+ * This is a per-file scan, so it cannot see a caller in ANOTHER file
6148
+ * that writes `<App foo={x}/>`. That is sound here for a reason the
6149
+ * general shape-inference path above does not get to rely on: the guard
6150
+ * below additionally requires the class to read NO `this.props.<key>` at
6151
+ * all, in its own body. A component that genuinely uses props always
6152
+ * reads at least one, so a cross-file caller passing real data to a
6153
+ * class that reads none of it would itself be dead-argument surface on
6154
+ * the CALLER, not a case this class's compiled shape needs to accept.
6155
+ * Nothing here can silently misbehave: the only externally-visible
6156
+ * effect of a stricter-than-{} … Record<string, any> narrowing is that
6157
+ * TypeScript itself would flag an excess/unknown prop at that other JSX
6158
+ * call site — a loud compile error, not silent wrong behavior, and
6159
+ * exactly the category of bug an empty props type exists to catch.
6160
+ * - `classPropsReadsAreCovered(classDecl, <empty type>)` — reuses the
6161
+ * existing reads-coverage guard against a zero-member shape, i.e. "this
6162
+ * class's body contains no `this.props.<key>` read whatsoever." This is
6163
+ * strictly stronger than the non-empty-shape case (which only requires
6164
+ * coverage of the OBSERVED keys) — here NO key may be read, observed or
6165
+ * not — so it still catches the shape-inference guard's original worry
6166
+ * (a real prop consumed only via a cross-file call site) as a special
6167
+ * case: if the body read that prop, this guard fails and the permissive
6168
+ * default is kept, exactly as before.
6169
+ */
6170
+ function applyPropsTypeArgument(classDecl, className, componentPropsShapes, componentsUsedAsJsx) {
6171
+ if (classDecl.superTypeParameters) return;
6172
+ const propsType = componentPropsShapes.get(className);
6173
+ if (propsType) {
6174
+ if (!classPropsReadsAreCovered(classDecl, propsType)) return;
6175
+ classDecl.superTypeParameters = t.tsTypeParameterInstantiation([propsType]);
6176
+ return;
6177
+ }
6178
+ if (componentsUsedAsJsx.has(className)) return;
6179
+ const emptyPropsType = t.tsTypeLiteral([]);
6180
+ if (!classPropsReadsAreCovered(classDecl, emptyPropsType)) return;
6181
+ classDecl.superTypeParameters = t.tsTypeParameterInstantiation([emptyPropsType]);
6182
+ }
6053
6183
  function collectLocalClassComponents(ast) {
6054
6184
  const names = /* @__PURE__ */ new Set();
6055
6185
  for (const node of ast.program.body) {
@@ -6370,10 +6500,12 @@ function injectHMR(ast, componentClassNames, defaultExportClassName, componentIm
6370
6500
  hmrStmts.push(js`const __moduleExports = ${t.objectExpression(exportProperties)};`);
6371
6501
  hmrStmts.push(js`registerHotModule(${jsExpr`${importMeta()}.url`}, __moduleExports);`);
6372
6502
  const acceptBody = [t.variableDeclaration("const", [t.variableDeclarator(t.identifier("__updatedModule"), t.logicalExpression("||", t.identifier("newModule"), t.identifier("__moduleExports")))]), t.expressionStatement(t.callExpression(t.identifier("registerHotModule"), [t.memberExpression(importMeta(), t.identifier("url")), t.identifier("__updatedModule")]))];
6503
+ acceptBody.push(js`let __patched = false;`);
6373
6504
  for (const cn of componentClassNames) {
6374
6505
  const key = cn === defaultExportClassName ? "default" : cn;
6375
- acceptBody.push(t.expressionStatement(t.callExpression(t.identifier("handleComponentUpdate"), [t.memberExpression(importMeta(), t.identifier("url")), t.objectExpression([t.objectProperty(t.identifier("default"), t.memberExpression(t.identifier("__updatedModule"), t.identifier(key)))])])));
6506
+ acceptBody.push(t.expressionStatement(t.assignmentExpression("=", t.identifier("__patched"), t.logicalExpression("||", t.callExpression(t.identifier("handleComponentUpdate"), [t.memberExpression(importMeta(), t.identifier("url")), t.objectExpression([t.objectProperty(t.identifier("default"), t.memberExpression(t.identifier("__updatedModule"), t.identifier(key)))])]), t.identifier("__patched")))));
6376
6507
  }
6508
+ acceptBody.push(t.ifStatement(t.unaryExpression("!", t.identifier("__patched")), t.expressionStatement(t.callExpression(t.memberExpression(hot(), t.identifier("invalidate")), []))));
6377
6509
  hmrStmts.push(t.expressionStatement(t.callExpression(t.memberExpression(hot(), t.identifier("accept")), [t.arrowFunctionExpression([t.identifier("newModule")], t.blockStatement(acceptBody))])));
6378
6510
  hmrStmts.push(...createAccepts(componentImports, proxyDep, shouldSkipDepAccept));
6379
6511
  for (const cn of componentClassNames) {
@@ -6584,7 +6716,8 @@ function transform(ctx) {
6584
6716
  const emitted = transformFile(code, sourceFile, {
6585
6717
  directClassComponents: knownClassComponentImports,
6586
6718
  directFactoryComponents: knownFactoryComponentImports,
6587
- enableTinyReactiveComponents: !isServe
6719
+ enableTinyReactiveComponents: !isServe,
6720
+ embedded: ctx.embedded
6588
6721
  });
6589
6722
  if (emitted.changed) {
6590
6723
  ir = emitted.ir;
@@ -6674,9 +6807,10 @@ function transformCompiledStoreModule(source, moduleId = "<unknown>", resolveImp
6674
6807
  if (classDecls.length === 0) return null;
6675
6808
  const constants = collectImportedLiteralConstants(ast, moduleId, resolveImportPath);
6676
6809
  const fallbackIrs = classDecls.map((classDecl) => buildStoreIr(classDecl, moduleId, "compiled", constants, ast));
6810
+ const fallbackKeepAlive = storeMethodFreeFunctionKeepAlive(ast, classDecls);
6677
6811
  const fallback = {
6678
- code: source,
6679
- changed: false,
6812
+ code: fallbackKeepAlive ? `${source}${fallbackKeepAlive}` : source,
6813
+ changed: !!fallbackKeepAlive,
6680
6814
  ir: fallbackIrs[0],
6681
6815
  irs: fallbackIrs
6682
6816
  };
@@ -6943,6 +7077,34 @@ function literalConstantsFromFile(file, names) {
6943
7077
  }
6944
7078
  return constants;
6945
7079
  }
7080
+ function moduleFreeFunctionNames(ast) {
7081
+ const names = /* @__PURE__ */ new Set();
7082
+ for (const node of ast.program.body) if (t.isFunctionDeclaration(node) && node.id) names.add(node.id.name);
7083
+ else if (t.isExportNamedDeclaration(node) && node.declaration && t.isFunctionDeclaration(node.declaration) && node.declaration.id) names.add(node.declaration.id.name);
7084
+ return names;
7085
+ }
7086
+ function nodeReferencesIdentifier(node, name) {
7087
+ if (!node || typeof node !== "object") return false;
7088
+ if (Array.isArray(node)) {
7089
+ for (const child of node) if (nodeReferencesIdentifier(child, name)) return true;
7090
+ return false;
7091
+ }
7092
+ const record = node;
7093
+ if (record.type === "Identifier" && record.name === name) return true;
7094
+ for (const key of Object.keys(record)) {
7095
+ if (key === "type" || key === "loc" || key === "start" || key === "end" || key === "leadingComments" || key === "trailingComments") continue;
7096
+ if (nodeReferencesIdentifier(record[key], name)) return true;
7097
+ }
7098
+ return false;
7099
+ }
7100
+ function storeMethodFreeFunctionKeepAlive(ast, classDecls) {
7101
+ const freeFns = moduleFreeFunctionNames(ast);
7102
+ if (freeFns.size === 0) return "";
7103
+ const kept = [];
7104
+ for (const name of freeFns) if (classDecls.some((classDecl) => classDecl.body.body.some((member) => t.isClassMethod(member) && nodeReferencesIdentifier(member.body, name)))) kept.push(name);
7105
+ if (kept.length === 0) return "";
7106
+ return `\n;(globalThis.__GEA_IR_KEEP__ ||= []).push(${kept.join(", ")});\n`;
7107
+ }
6946
7108
  function literalConstant(name, value) {
6947
7109
  if (t.isStringLiteral(value)) return {
6948
7110
  name,
@@ -8080,6 +8242,7 @@ function geaPlugin(options = {}) {
8080
8242
  const irComponents = /* @__PURE__ */ new Map();
8081
8243
  const irStores = /* @__PURE__ */ new Map();
8082
8244
  const hostCapabilities = /* @__PURE__ */ new Set();
8245
+ const staticModuleShapes = /* @__PURE__ */ new Map();
8083
8246
  const storeRegistry = /* @__PURE__ */ new Map();
8084
8247
  const resolveImportPath = (importer, source) => {
8085
8248
  const base = resolve(dirname(importer), source);
@@ -8237,7 +8400,10 @@ function geaPlugin(options = {}) {
8237
8400
  if (!cleanId.match(/\.(js|jsx|ts|tsx)$/) || cleanId.includes("node_modules")) return null;
8238
8401
  let transformedCode = code;
8239
8402
  let changed = false;
8240
- if (irOptions?.enabled) recordHostCapabilities(code);
8403
+ if (irOptions?.enabled) {
8404
+ recordHostCapabilities(code);
8405
+ staticModuleShapes.set(cleanId, collectStaticModuleShape(code));
8406
+ }
8241
8407
  if (code.includes("extends Store") || code.includes("new Store(")) {
8242
8408
  storeModules.add(cleanId);
8243
8409
  const storeClassName = extractStoreClassName(code);
@@ -8280,6 +8446,7 @@ function geaPlugin(options = {}) {
8280
8446
  code: transformedCode,
8281
8447
  isServe: isServeCommand,
8282
8448
  isSSR,
8449
+ embedded: !!irOptions?.enabled,
8283
8450
  hmrImportSource: HMR_RUNTIME_ID,
8284
8451
  isStoreModule,
8285
8452
  isComponentModule,
@@ -8322,7 +8489,7 @@ function geaPlugin(options = {}) {
8322
8489
  map: null
8323
8490
  };
8324
8491
  },
8325
- generateBundle(_options, bundle) {
8492
+ async generateBundle(_options, bundle) {
8326
8493
  if (!irOptions?.enabled) return;
8327
8494
  const renderedIds = renderedModuleIds(bundle);
8328
8495
  const modules = Array.from(irModules.values()).filter((module) => {
@@ -8344,12 +8511,23 @@ function geaPlugin(options = {}) {
8344
8511
  if (candidateModule && !modules.includes(candidateModule)) modules.push(candidateModule);
8345
8512
  }
8346
8513
  }
8514
+ const liveComponents = Array.from(irComponents.values()).filter((component) => componentIds.has(component.id));
8515
+ const logicalRoutes = await collectLogicalModuleRoutes({
8516
+ entries: entryFacadeModuleIds(bundle),
8517
+ root: resolvedConfig?.root,
8518
+ moduleShapes: staticModuleShapes,
8519
+ resolveModule: async (specifier, importer) => {
8520
+ const resolved = await this.resolve(specifier, importer, { skipSelf: true });
8521
+ return resolved && !resolved.external ? cleanRollupModuleId(resolved.id) : null;
8522
+ }
8523
+ });
8524
+ const components = liveComponents.map((component) => attachRootRendererAuthority(component, logicalRoutes, staticModuleShapes));
8347
8525
  const irBundle = {
8348
8526
  schema: "gea-ir",
8349
8527
  version: 1,
8350
8528
  entry: geaIrEntryFromBundle(bundle) ?? geaIrConfiguredEntry(resolvedConfig),
8351
8529
  modules,
8352
- components: Array.from(irComponents.values()).filter((component) => componentIds.has(component.id)),
8530
+ components,
8353
8531
  stores: Array.from(irStores.values()).filter((store) => storeIds.has(store.id)),
8354
8532
  hostCapabilities: Array.from(hostCapabilities).sort()
8355
8533
  };
@@ -8427,6 +8605,105 @@ function geaPlugin(options = {}) {
8427
8605
  if (/\bdocument\s*\./.test(source)) hostCapabilities.add("dom");
8428
8606
  }
8429
8607
  }
8608
+ function collectStaticModuleShape(source) {
8609
+ const specifiers = /* @__PURE__ */ new Set();
8610
+ const exportsByLocalName = /* @__PURE__ */ new Map();
8611
+ const addExport = (localName, exportName) => {
8612
+ const names = exportsByLocalName.get(localName) ?? /* @__PURE__ */ new Set();
8613
+ names.add(exportName);
8614
+ exportsByLocalName.set(localName, names);
8615
+ };
8616
+ try {
8617
+ const ast = parse(source, {
8618
+ sourceType: "module",
8619
+ plugins: [
8620
+ "typescript",
8621
+ "jsx",
8622
+ "classProperties",
8623
+ "classPrivateProperties",
8624
+ "classPrivateMethods"
8625
+ ]
8626
+ });
8627
+ for (const statement of ast.program.body) {
8628
+ if ((statement.type === "ImportDeclaration" || statement.type === "ExportNamedDeclaration" || statement.type === "ExportAllDeclaration") && statement.source?.value) specifiers.add(statement.source.value);
8629
+ if (statement.type === "ExportNamedDeclaration") {
8630
+ const declaration = statement.declaration;
8631
+ if ((declaration?.type === "ClassDeclaration" || declaration?.type === "FunctionDeclaration") && declaration.id) addExport(declaration.id.name, declaration.id.name);
8632
+ else if (declaration?.type === "VariableDeclaration") {
8633
+ for (const declarator of declaration.declarations) if (declarator.id.type === "Identifier") addExport(declarator.id.name, declarator.id.name);
8634
+ }
8635
+ if (!statement.source) for (const specifier of statement.specifiers) {
8636
+ if (specifier.type !== "ExportSpecifier") continue;
8637
+ addExport(specifier.local.type === "Identifier" ? specifier.local.name : specifier.local.value, specifier.exported.type === "Identifier" ? specifier.exported.name : specifier.exported.value);
8638
+ }
8639
+ } else if (statement.type === "ExportDefaultDeclaration") {
8640
+ const declaration = statement.declaration;
8641
+ if ((declaration.type === "ClassDeclaration" || declaration.type === "FunctionDeclaration") && declaration.id) addExport(declaration.id.name, "default");
8642
+ else if (declaration.type === "Identifier") addExport(declaration.name, "default");
8643
+ }
8644
+ }
8645
+ } catch {}
8646
+ return {
8647
+ specifiers: [...specifiers].sort(),
8648
+ exportsByLocalName: new Map([...exportsByLocalName].map(([localName, exportNames]) => [localName, new Set([...exportNames].sort())]))
8649
+ };
8650
+ }
8651
+ /** Logical source coordinates are relative to the project, never to its checkout. */
8652
+ async function collectLogicalModuleRoutes(input) {
8653
+ let root = input.root ?? (input.entries[0] ? dirname(input.entries[0]) : "/");
8654
+ if (!input.root) while (input.entries.some((entry) => relative(root, entry).startsWith("../"))) root = dirname(root);
8655
+ const routes = /* @__PURE__ */ new Map();
8656
+ const owners = /* @__PURE__ */ new Map();
8657
+ const pending = [];
8658
+ const add = (moduleId, fallback) => {
8659
+ moduleId = cleanRollupModuleId(moduleId);
8660
+ if (routes.has(moduleId)) return;
8661
+ const local = relative(root, moduleId).replace(/\\/g, "/");
8662
+ const route = !local.startsWith("../") && !moduleId.startsWith("\0") ? `/${local}` : fallback;
8663
+ if (!route) return;
8664
+ const owner = owners.get(route);
8665
+ if (owner && owner !== moduleId) throw new Error(`Ambiguous Gea renderer module coordinate: ${route}`);
8666
+ owners.set(route, moduleId);
8667
+ routes.set(moduleId, new Set([route]));
8668
+ pending.push(moduleId);
8669
+ };
8670
+ for (const entry of input.entries) add(entry, `/${relative(root, entry).replace(/\\/g, "/")}`);
8671
+ while (pending.length > 0) {
8672
+ const moduleId = pending.shift();
8673
+ const route = [...routes.get(moduleId)][0];
8674
+ for (const specifier of input.moduleShapes.get(moduleId)?.specifiers ?? []) {
8675
+ const target = await input.resolveModule(specifier, moduleId);
8676
+ if (!target) continue;
8677
+ add(target, specifier.startsWith("./") || specifier.startsWith("../") ? posix.join(posix.dirname(route), specifier) : specifier.startsWith("/") ? null : `/@modules/${specifier}`);
8678
+ }
8679
+ }
8680
+ return routes;
8681
+ }
8682
+ function attachRootRendererAuthority(component, logicalRoutes, moduleShapes) {
8683
+ const moduleId = cleanRollupModuleId(component.module);
8684
+ const routes = logicalRoutes.get(moduleId);
8685
+ const exportNames = moduleShapes.get(moduleId)?.exportsByLocalName.get(component.exportName);
8686
+ if (!routes?.size || !exportNames?.size) return component;
8687
+ const candidates = [...routes].flatMap((moduleSpecifier) => [...exportNames].map((exportName) => ({
8688
+ moduleSpecifier,
8689
+ exportName
8690
+ })));
8691
+ candidates.sort((left, right) => {
8692
+ return (left.exportName === component.exportName ? 0 : 1) - (right.exportName === component.exportName ? 0 : 1) || left.moduleSpecifier.length - right.moduleSpecifier.length || `${left.moduleSpecifier}#${left.exportName}`.localeCompare(`${right.moduleSpecifier}#${right.exportName}`);
8693
+ });
8694
+ const coordinate = candidates[0];
8695
+ const digest = createHash("sha256").update(`${coordinate.moduleSpecifier}\u0000${coordinate.exportName}`).digest("hex");
8696
+ return {
8697
+ ...component,
8698
+ rootRendererAuthority: {
8699
+ component: coordinate,
8700
+ rendererResourceId: `gea-renderer:v1:${digest}`
8701
+ }
8702
+ };
8703
+ }
8704
+ function entryFacadeModuleIds(bundle) {
8705
+ return Object.values(bundle).filter((item) => item.type === "chunk" && item.isEntry && item.facadeModuleId).map((item) => cleanRollupModuleId(item.facadeModuleId)).sort();
8706
+ }
8430
8707
  function renderedModuleIds(bundle) {
8431
8708
  const ids = /* @__PURE__ */ new Set();
8432
8709
  for (const item of Object.values(bundle)) {
@@ -8479,7 +8756,29 @@ function compilerRuntimePathFromCoreEntry(entry) {
8479
8756
  if (clean.endsWith("/dist/index.mjs")) return clean.slice(0, -15) + "/dist/compiler-runtime.mjs";
8480
8757
  return null;
8481
8758
  }
8759
+ function compilerRuntimeExportLines(runtimePath) {
8760
+ if (!runtimePath.endsWith(".ts")) return null;
8761
+ let source;
8762
+ try {
8763
+ source = readFileSync(runtimePath, "utf8");
8764
+ } catch {
8765
+ return null;
8766
+ }
8767
+ const dir = dirname(runtimePath);
8768
+ const lines = [];
8769
+ const EXPORT_FROM_RE = /export\s*\{([^}]*)\}\s*from\s*['"](\.[^'"]+)['"]/g;
8770
+ let match;
8771
+ while (match = EXPORT_FROM_RE.exec(source)) {
8772
+ const names = match[1];
8773
+ const relativeSpecifier = match[2];
8774
+ const absoluteSpecifier = normalizeImportPath(resolve(dir, relativeSpecifier));
8775
+ lines.push(`export {${names}} from ${JSON.stringify(absoluteSpecifier)}`);
8776
+ }
8777
+ return lines.length > 0 ? lines : null;
8778
+ }
8482
8779
  function compilerRuntimeSource(runtimePath) {
8780
+ const perSubmoduleLines = compilerRuntimeExportLines(runtimePath);
8781
+ if (perSubmoduleLines) return `${perSubmoduleLines.join("\n")}\n`;
8483
8782
  const path = normalizeImportPath(runtimePath);
8484
8783
  return `export {
8485
8784
  NOOP_DISPOSER,
@@ -8522,6 +8821,7 @@ function compilerRuntimeSource(runtimePath) {
8522
8821
  GEA_DIRTY_PROPS,
8523
8822
  createItemObservable,
8524
8823
  createItemProxy,
8824
+ readItem,
8525
8825
  _rescue,
8526
8826
  GEA_CREATE_TEMPLATE,
8527
8827
  GEA_PARENT_COMPONENT,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geajs/vite-plugin",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Vite plugin for Gea framework - JSX/TSX transform, reactivity, HMR",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",