@geajs/vite-plugin 1.2.1 → 1.2.3

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 +136 -36
  2. package/package.json +1 -1
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 { appendToBody, id, js, jsAll, jsBlockBody, jsClassProp, jsExpr, jsImport, jsMethod, jsObjectExpr, jsPrivateProp, num, str, tpl } from "eszter";
6
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
6
+ import { existsSync, 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
@@ -281,6 +281,9 @@ const GEA_COMPILER_SYMBOL_IMPORTS = [
281
281
  "GEA_MAP_CONFIG_COUNT",
282
282
  "GEA_CLONE_ITEM",
283
283
  "GEA_CLONE_TEMPLATE",
284
+ "GEA_COMPILED",
285
+ "GEA_EVENTS_CACHE",
286
+ "GEA_LIFECYCLE_CALLED",
284
287
  "GEA_ON_PROP_CHANGE",
285
288
  "GEA_SETUP_LOCAL_STATE_OBSERVERS",
286
289
  "GEA_SETUP_REFS",
@@ -4490,7 +4493,7 @@ function wrapEventsGetterWithCache(getter) {
4490
4493
  const body = getter.body.body;
4491
4494
  const returnStmt = body.find((s) => t.isReturnStatement(s) && s.argument !== null);
4492
4495
  if (!returnStmt?.argument) return;
4493
- const cachedProp = t.memberExpression(t.thisExpression(), t.identifier("__evts"));
4496
+ const cachedProp = t.memberExpression(t.thisExpression(), t.identifier("GEA_EVENTS_CACHE"), true);
4494
4497
  const elementProp = t.memberExpression(t.thisExpression(), id("GEA_ELEMENT"), true);
4495
4498
  const tmpId = t.identifier("__geaEvtsResult");
4496
4499
  const objectExpr = returnStmt.argument;
@@ -9618,6 +9621,9 @@ function transformComponentFile(ast, imports, storeImports, className, sourceFil
9618
9621
  if (name === "events" && member.kind === "get") wrapEventsGetterWithCache(member);
9619
9622
  if (member.computed && name === "GEA_ON_PROP_CHANGE") wrapSubpathCacheGuards(member, subpathPcCounter, path.node.body);
9620
9623
  }
9624
+ const compiledProp = t.classProperty(t.identifier("GEA_COMPILED"), t.booleanLiteral(true), void 0, void 0, true, true);
9625
+ path.node.body.body.push(compiledProp);
9626
+ injectLifecycleCallsIntoConstructor(path.node.body);
9621
9627
  path.stop();
9622
9628
  } });
9623
9629
  return transformed;
@@ -9712,6 +9718,36 @@ function transformRemainingJSX(ast, imports) {
9712
9718
  }
9713
9719
  });
9714
9720
  }
9721
+ function injectLifecycleCallsIntoConstructor(classBody) {
9722
+ const createdCall = js`this.created(this.props);`;
9723
+ const createdHooksCall = js`this.createdHooks(this.props);`;
9724
+ const setupObserversAccess = t.memberExpression(t.thisExpression(), t.identifier("GEA_SETUP_LOCAL_STATE_OBSERVERS"), true);
9725
+ const setupObserversCall = t.ifStatement(t.binaryExpression("===", t.unaryExpression("typeof", setupObserversAccess), t.stringLiteral("function")), t.expressionStatement(t.callExpression(setupObserversAccess, [])));
9726
+ const lifecycleCalledAccess = t.memberExpression(t.thisExpression(), t.identifier("GEA_LIFECYCLE_CALLED"), true);
9727
+ const setLifecycleCalled = t.expressionStatement(t.assignmentExpression("=", t.cloneNode(lifecycleCalledAccess), t.booleanLiteral(true)));
9728
+ const guardedBlock = t.ifStatement(t.unaryExpression("!", lifecycleCalledAccess), t.blockStatement([
9729
+ setLifecycleCalled,
9730
+ createdCall,
9731
+ createdHooksCall,
9732
+ setupObserversCall
9733
+ ]));
9734
+ const ctor = classBody.body.find((m) => t.isClassMethod(m) && (m.kind === "constructor" || t.isIdentifier(m.key) && m.key.name === "constructor"));
9735
+ if (ctor) {
9736
+ let superIdx = -1;
9737
+ for (let i = 0; i < ctor.body.body.length; i++) {
9738
+ const s = ctor.body.body[i];
9739
+ if (t.isExpressionStatement(s) && t.isCallExpression(s.expression) && t.isSuper(s.expression.callee)) {
9740
+ superIdx = i;
9741
+ break;
9742
+ }
9743
+ }
9744
+ if (superIdx >= 0) ctor.body.body.splice(superIdx + 1, 0, guardedBlock);
9745
+ else ctor.body.body.unshift(guardedBlock);
9746
+ } else {
9747
+ const newCtor = appendToBody(jsMethod`${id("constructor")}(...args) {}`, js`super(...args);`, guardedBlock);
9748
+ classBody.body.unshift(newCtor);
9749
+ }
9750
+ }
9715
9751
  //#endregion
9716
9752
  //#region src/postprocess/hmr.ts
9717
9753
  const hot = () => t.memberExpression(t.metaProperty(t.identifier("import"), t.identifier("meta")), t.identifier("hot"));
@@ -9726,39 +9762,41 @@ const legacyShouldProxyDep = (p) => /\.(js|ts)$/.test(p) && !p.match(/(store|sta
9726
9762
  * Returns `true` when HMR code was added, `false` otherwise (no component
9727
9763
  * class, or the file contains a `gea-auto-register plugin` comment).
9728
9764
  */
9729
- function injectHMR(ast, componentClassName, componentImports, componentImportsUsedAsTags, isDefaultExport, hmrImportSource = "virtual:gea-hmr", shouldProxyDep) {
9765
+ function injectHMR(ast, componentClassNames, defaultExportClassName, componentImports, componentImportsUsedAsTags, hmrImportSource = "virtual:gea-hmr", shouldProxyDep, shouldSkipDepAccept) {
9730
9766
  if (hasAutoRegisterComment(ast)) return false;
9767
+ if (componentClassNames.length === 0) return false;
9731
9768
  const hmrStmts = [];
9732
- if (componentClassName) {
9733
- ensureImport(ast, hmrImportSource, "handleComponentUpdate");
9734
- ensureImport(ast, hmrImportSource, "registerHotModule");
9735
- ensureImport(ast, hmrImportSource, "registerComponentInstance");
9736
- ensureImport(ast, hmrImportSource, "unregisterComponentInstance");
9737
- const proxyDep = shouldProxyDep ?? legacyShouldProxyDep;
9738
- if (rewriteComponentDeps(ast, componentImports, proxyDep).length > 0) ensureImport(ast, hmrImportSource, "createHotComponentProxy");
9739
- const modExports = isDefaultExport ? t.objectExpression([t.objectProperty(t.identifier("default"), t.identifier(componentClassName))]) : t.objectExpression([t.objectProperty(t.identifier(componentClassName), t.identifier(componentClassName), false, true)]);
9740
- hmrStmts.push(js`const __moduleExports = ${modExports};`);
9741
- hmrStmts.push(js`registerHotModule(${jsExpr`${importMeta()}.url`}, __moduleExports);`);
9742
- hmrStmts.push(js`
9743
- ${hot()}.accept((newModule) => {
9744
- const __updatedModule = newModule || __moduleExports;
9745
- registerHotModule(${jsExpr`${importMeta()}.url`}, __updatedModule);
9746
- handleComponentUpdate(${jsExpr`${importMeta()}.url`}, __updatedModule);
9747
- });
9748
- `);
9749
- hmrStmts.push(...createAccepts(componentImports, proxyDep));
9750
- hmrStmts.push(js`const __origCreated = ${id(componentClassName)}.prototype.created;`);
9751
- hmrStmts.push(js`${id(componentClassName)}.prototype.created = function(__geaProps) {
9769
+ ensureImport(ast, hmrImportSource, "handleComponentUpdate");
9770
+ ensureImport(ast, hmrImportSource, "registerHotModule");
9771
+ ensureImport(ast, hmrImportSource, "registerComponentInstance");
9772
+ ensureImport(ast, hmrImportSource, "unregisterComponentInstance");
9773
+ const proxyDep = shouldProxyDep ?? legacyShouldProxyDep;
9774
+ if (rewriteComponentDeps(ast, componentImports, proxyDep).length > 0) ensureImport(ast, hmrImportSource, "createHotComponentProxy");
9775
+ const exportProperties = [];
9776
+ for (const cn of componentClassNames) if (cn === defaultExportClassName) exportProperties.push(t.objectProperty(t.identifier("default"), t.identifier(cn)));
9777
+ else exportProperties.push(t.objectProperty(t.identifier(cn), t.identifier(cn), false, true));
9778
+ hmrStmts.push(js`const __moduleExports = ${t.objectExpression(exportProperties)};`);
9779
+ hmrStmts.push(js`registerHotModule(${jsExpr`${importMeta()}.url`}, __moduleExports);`);
9780
+ 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")]))];
9781
+ for (const cn of componentClassNames) {
9782
+ const key = cn === defaultExportClassName ? "default" : cn;
9783
+ 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)))])])));
9784
+ }
9785
+ hmrStmts.push(t.expressionStatement(t.callExpression(t.memberExpression(hot(), t.identifier("accept")), [t.arrowFunctionExpression([t.identifier("newModule")], t.blockStatement(acceptBody))])));
9786
+ hmrStmts.push(...createAccepts(componentImports, proxyDep, shouldSkipDepAccept));
9787
+ for (const cn of componentClassNames) {
9788
+ const suffix = componentClassNames.length > 1 ? `_${cn}` : "";
9789
+ hmrStmts.push(js`const ${id("__origCreated" + suffix)} = ${id(cn)}.prototype.created;`);
9790
+ hmrStmts.push(js`${id(cn)}.prototype.created = function(__geaProps) {
9752
9791
  registerComponentInstance(this.constructor.name, this);
9753
- return __origCreated.call(this, __geaProps);
9792
+ return ${id("__origCreated" + suffix)}.call(this, __geaProps);
9754
9793
  };`);
9755
- hmrStmts.push(js`const __origDispose = ${id(componentClassName)}.prototype.dispose;`);
9756
- hmrStmts.push(js`${id(componentClassName)}.prototype.dispose = function() {
9794
+ hmrStmts.push(js`const ${id("__origDispose" + suffix)} = ${id(cn)}.prototype.dispose;`);
9795
+ hmrStmts.push(js`${id(cn)}.prototype.dispose = function() {
9757
9796
  unregisterComponentInstance(this.constructor.name, this);
9758
- return __origDispose.call(this);
9797
+ return ${id("__origDispose" + suffix)}.call(this);
9759
9798
  };`);
9760
9799
  }
9761
- if (hmrStmts.length === 0) return false;
9762
9800
  ast.program.body.push(t.ifStatement(hot(), t.blockStatement(hmrStmts)));
9763
9801
  return true;
9764
9802
  }
@@ -9800,10 +9838,11 @@ function rewriteComponentDeps(ast, imports, proxyDep) {
9800
9838
  * add `hot.accept(dep, () => hot.invalidate())` so the page reloads when
9801
9839
  * stores/utils change.
9802
9840
  */
9803
- function createAccepts(imports, proxyDep) {
9841
+ function createAccepts(imports, proxyDep, shouldSkip) {
9804
9842
  const stmts = [];
9805
9843
  for (const p of imports) {
9806
9844
  if (!isRelative(p)) continue;
9845
+ if (shouldSkip?.(p)) continue;
9807
9846
  if (!proxyDep(p)) stmts.push(js`${hot()}.accept(${p}, ${invalidateCb()});`);
9808
9847
  }
9809
9848
  return stmts;
@@ -9864,6 +9903,14 @@ function isComponentImportSource(source) {
9864
9903
  if (source.startsWith("node:")) return false;
9865
9904
  return true;
9866
9905
  }
9906
+ /**
9907
+ * In dev mode, convert relative store imports (e.g. `import { router } from '../router'`)
9908
+ * to `const { router } = await import('../router')` placed after all class declarations.
9909
+ *
9910
+ * This breaks circular-dependency TDZ during HMR: classes are fully initialized
9911
+ * before the store module is loaded, so the store can safely import the component
9912
+ * classes back without hitting the temporal dead zone.
9913
+ */
9867
9914
  function transform(ctx) {
9868
9915
  const { sourceFile, code, isServe, isSSR, hmrImportSource } = ctx;
9869
9916
  if (!(code.includes("<") && code.includes(">"))) return null;
@@ -9889,7 +9936,6 @@ function transform(ctx) {
9889
9936
  let transformed = false;
9890
9937
  const componentImportSet = /* @__PURE__ */ new Set();
9891
9938
  const componentImportsUsedAsTags = /* @__PURE__ */ new Set();
9892
- let isDefaultExport = false;
9893
9939
  imports.forEach((source) => {
9894
9940
  if (!isComponentImportSource(source)) return;
9895
9941
  componentImportSet.add(source);
@@ -9899,9 +9945,6 @@ function transform(ctx) {
9899
9945
  const knownComponentImports = /* @__PURE__ */ new Set();
9900
9946
  const namedImportSources = /* @__PURE__ */ new Map();
9901
9947
  traverse(ast, {
9902
- ExportDefaultDeclaration() {
9903
- isDefaultExport = true;
9904
- },
9905
9948
  ImportDeclaration(path) {
9906
9949
  const source = path.node.source.value;
9907
9950
  if (!isComponentImportSource(source)) return;
@@ -9950,7 +9993,9 @@ function transform(ctx) {
9950
9993
  }
9951
9994
  } else transformed = transformNonComponentJSX(ast, imports);
9952
9995
  }
9953
- if (isServe && componentClassName) {
9996
+ if (isServe && componentClassNames.length > 0) {
9997
+ let defaultExportClassName = null;
9998
+ for (const node of ast.program.body) if (t.isExportDefaultDeclaration(node) && t.isClassDeclaration(node.declaration) && node.declaration.id) defaultExportClassName = node.declaration.id.name;
9954
9999
  const shouldProxyDep = (source) => {
9955
10000
  if (!source.startsWith(".")) return false;
9956
10001
  const resolved = ctx.resolveImportPath(sourceFile, source);
@@ -9959,7 +10004,13 @@ function transform(ctx) {
9959
10004
  if (ctx.isComponentModule(resolved)) return true;
9960
10005
  return false;
9961
10006
  };
9962
- if (injectHMR(ast, componentClassName, componentImports, componentImportsUsedAsTags, isDefaultExport, hmrImportSource, shouldProxyDep)) transformed = true;
10007
+ const shouldSkipDepAccept = (source) => {
10008
+ if (!source.startsWith(".")) return false;
10009
+ const resolved = ctx.resolveImportPath(sourceFile, source);
10010
+ if (!resolved) return false;
10011
+ return ctx.isStoreModule(resolved);
10012
+ };
10013
+ if (injectHMR(ast, componentClassNames, defaultExportClassName, componentImports, componentImportsUsedAsTags, hmrImportSource, shouldProxyDep, shouldSkipDepAccept)) transformed = true;
9963
10014
  }
9964
10015
  if (!transformed) return null;
9965
10016
  ensureGeaCompilerSymbolImports(ast);
@@ -10038,6 +10089,13 @@ export function reconcile(oldC, newC) {
10038
10089
  }
10039
10090
  `;
10040
10091
  const HMR_RUNTIME_SOURCE = `
10092
+ var GEA_ELEMENT = Symbol.for('gea.element');
10093
+ var GEA_RENDERED = Symbol.for('gea.rendered');
10094
+ var GEA_CLEANUP_BINDINGS = Symbol.for('gea.component.cleanupBindings');
10095
+ var GEA_TEARDOWN_SELF_LISTENERS = Symbol.for('gea.component.teardownSelfListeners');
10096
+ var GEA_CHILD_COMPONENTS = Symbol.for('gea.childComponents');
10097
+ var GEA_BINDINGS = Symbol.for('gea.bindings');
10098
+ var GEA_SELF_LISTENERS = Symbol.for('gea.selfListeners');
10041
10099
  var hot = import.meta.hot;
10042
10100
  var componentInstances = hot && hot.data && hot.data.componentInstances || new Map();
10043
10101
  if (hot) hot.data.componentInstances = componentInstances;
@@ -10349,7 +10407,7 @@ function geaPlugin() {
10349
10407
  }
10350
10408
  }
10351
10409
  if (/\bclass\s+Component\s+extends\s+Store\b/.test(code)) return null;
10352
- return transform({
10410
+ const result = transform({
10353
10411
  sourceFile: cleanId,
10354
10412
  code,
10355
10413
  isServe: isServeCommand,
@@ -10361,8 +10419,50 @@ function geaPlugin() {
10361
10419
  registerStoreModule: (fp) => storeModules.add(fp),
10362
10420
  registerComponentModule: (fp) => componentModules.add(fp)
10363
10421
  });
10422
+ if (result) return result;
10423
+ if (isServeCommand && !isSSR) {
10424
+ const componentDeps = findComponentDeps(code, cleanId);
10425
+ if (componentDeps.length > 0) return {
10426
+ code: code + `\nif (import.meta.hot) {\n${componentDeps.map((dep) => ` import.meta.hot.accept('${dep}', () => {});`).join("\n")}\n}\n`,
10427
+ map: null
10428
+ };
10429
+ }
10430
+ return null;
10364
10431
  }
10365
10432
  };
10366
10433
  }
10434
+ function resolveToFile(base) {
10435
+ const exts = [
10436
+ ".ts",
10437
+ ".tsx",
10438
+ ".js",
10439
+ ".jsx"
10440
+ ];
10441
+ const indexFiles = exts.map((ext) => resolve(base, "index" + ext));
10442
+ const candidates = [
10443
+ base,
10444
+ ...exts.map((ext) => base + ext),
10445
+ ...indexFiles
10446
+ ];
10447
+ for (const c of candidates) try {
10448
+ if (existsSync(c) && statSync(c).isFile()) return c;
10449
+ } catch {}
10450
+ return null;
10451
+ }
10452
+ function findComponentDeps(code, filePath) {
10453
+ const deps = [];
10454
+ const importRegex = /import\s+(?:[\w{},\s*]+)\s+from\s+['"](\.[^'"]+)['"]/g;
10455
+ let match;
10456
+ while ((match = importRegex.exec(code)) !== null) {
10457
+ const source = match[1];
10458
+ const resolved = resolveToFile(resolve(dirname(filePath), source));
10459
+ if (!resolved) continue;
10460
+ try {
10461
+ const depCode = readFileSync(resolved, "utf8");
10462
+ if (/class\s+\w+\s+extends\s+Component\b/.test(depCode) && depCode.includes("<") && depCode.includes(">")) deps.push(source);
10463
+ } catch {}
10464
+ }
10465
+ return deps;
10466
+ }
10367
10467
  //#endregion
10368
10468
  export { geaPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geajs/vite-plugin",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Vite plugin for Gea framework - JSX/TSX transform, reactivity, HMR",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",