@geajs/vite-plugin 1.2.1 → 1.2.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 +99 -35
  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
@@ -9726,39 +9726,41 @@ const legacyShouldProxyDep = (p) => /\.(js|ts)$/.test(p) && !p.match(/(store|sta
9726
9726
  * Returns `true` when HMR code was added, `false` otherwise (no component
9727
9727
  * class, or the file contains a `gea-auto-register plugin` comment).
9728
9728
  */
9729
- function injectHMR(ast, componentClassName, componentImports, componentImportsUsedAsTags, isDefaultExport, hmrImportSource = "virtual:gea-hmr", shouldProxyDep) {
9729
+ function injectHMR(ast, componentClassNames, defaultExportClassName, componentImports, componentImportsUsedAsTags, hmrImportSource = "virtual:gea-hmr", shouldProxyDep, shouldSkipDepAccept) {
9730
9730
  if (hasAutoRegisterComment(ast)) return false;
9731
+ if (componentClassNames.length === 0) return false;
9731
9732
  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) {
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 exportProperties = [];
9740
+ for (const cn of componentClassNames) if (cn === defaultExportClassName) exportProperties.push(t.objectProperty(t.identifier("default"), t.identifier(cn)));
9741
+ else exportProperties.push(t.objectProperty(t.identifier(cn), t.identifier(cn), false, true));
9742
+ hmrStmts.push(js`const __moduleExports = ${t.objectExpression(exportProperties)};`);
9743
+ hmrStmts.push(js`registerHotModule(${jsExpr`${importMeta()}.url`}, __moduleExports);`);
9744
+ 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")]))];
9745
+ for (const cn of componentClassNames) {
9746
+ const key = cn === defaultExportClassName ? "default" : cn;
9747
+ 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)))])])));
9748
+ }
9749
+ hmrStmts.push(t.expressionStatement(t.callExpression(t.memberExpression(hot(), t.identifier("accept")), [t.arrowFunctionExpression([t.identifier("newModule")], t.blockStatement(acceptBody))])));
9750
+ hmrStmts.push(...createAccepts(componentImports, proxyDep, shouldSkipDepAccept));
9751
+ for (const cn of componentClassNames) {
9752
+ const suffix = componentClassNames.length > 1 ? `_${cn}` : "";
9753
+ hmrStmts.push(js`const ${id("__origCreated" + suffix)} = ${id(cn)}.prototype.created;`);
9754
+ hmrStmts.push(js`${id(cn)}.prototype.created = function(__geaProps) {
9752
9755
  registerComponentInstance(this.constructor.name, this);
9753
- return __origCreated.call(this, __geaProps);
9756
+ return ${id("__origCreated" + suffix)}.call(this, __geaProps);
9754
9757
  };`);
9755
- hmrStmts.push(js`const __origDispose = ${id(componentClassName)}.prototype.dispose;`);
9756
- hmrStmts.push(js`${id(componentClassName)}.prototype.dispose = function() {
9758
+ hmrStmts.push(js`const ${id("__origDispose" + suffix)} = ${id(cn)}.prototype.dispose;`);
9759
+ hmrStmts.push(js`${id(cn)}.prototype.dispose = function() {
9757
9760
  unregisterComponentInstance(this.constructor.name, this);
9758
- return __origDispose.call(this);
9761
+ return ${id("__origDispose" + suffix)}.call(this);
9759
9762
  };`);
9760
9763
  }
9761
- if (hmrStmts.length === 0) return false;
9762
9764
  ast.program.body.push(t.ifStatement(hot(), t.blockStatement(hmrStmts)));
9763
9765
  return true;
9764
9766
  }
@@ -9800,10 +9802,11 @@ function rewriteComponentDeps(ast, imports, proxyDep) {
9800
9802
  * add `hot.accept(dep, () => hot.invalidate())` so the page reloads when
9801
9803
  * stores/utils change.
9802
9804
  */
9803
- function createAccepts(imports, proxyDep) {
9805
+ function createAccepts(imports, proxyDep, shouldSkip) {
9804
9806
  const stmts = [];
9805
9807
  for (const p of imports) {
9806
9808
  if (!isRelative(p)) continue;
9809
+ if (shouldSkip?.(p)) continue;
9807
9810
  if (!proxyDep(p)) stmts.push(js`${hot()}.accept(${p}, ${invalidateCb()});`);
9808
9811
  }
9809
9812
  return stmts;
@@ -9864,6 +9867,14 @@ function isComponentImportSource(source) {
9864
9867
  if (source.startsWith("node:")) return false;
9865
9868
  return true;
9866
9869
  }
9870
+ /**
9871
+ * In dev mode, convert relative store imports (e.g. `import { router } from '../router'`)
9872
+ * to `const { router } = await import('../router')` placed after all class declarations.
9873
+ *
9874
+ * This breaks circular-dependency TDZ during HMR: classes are fully initialized
9875
+ * before the store module is loaded, so the store can safely import the component
9876
+ * classes back without hitting the temporal dead zone.
9877
+ */
9867
9878
  function transform(ctx) {
9868
9879
  const { sourceFile, code, isServe, isSSR, hmrImportSource } = ctx;
9869
9880
  if (!(code.includes("<") && code.includes(">"))) return null;
@@ -9889,7 +9900,6 @@ function transform(ctx) {
9889
9900
  let transformed = false;
9890
9901
  const componentImportSet = /* @__PURE__ */ new Set();
9891
9902
  const componentImportsUsedAsTags = /* @__PURE__ */ new Set();
9892
- let isDefaultExport = false;
9893
9903
  imports.forEach((source) => {
9894
9904
  if (!isComponentImportSource(source)) return;
9895
9905
  componentImportSet.add(source);
@@ -9899,9 +9909,6 @@ function transform(ctx) {
9899
9909
  const knownComponentImports = /* @__PURE__ */ new Set();
9900
9910
  const namedImportSources = /* @__PURE__ */ new Map();
9901
9911
  traverse(ast, {
9902
- ExportDefaultDeclaration() {
9903
- isDefaultExport = true;
9904
- },
9905
9912
  ImportDeclaration(path) {
9906
9913
  const source = path.node.source.value;
9907
9914
  if (!isComponentImportSource(source)) return;
@@ -9950,7 +9957,9 @@ function transform(ctx) {
9950
9957
  }
9951
9958
  } else transformed = transformNonComponentJSX(ast, imports);
9952
9959
  }
9953
- if (isServe && componentClassName) {
9960
+ if (isServe && componentClassNames.length > 0) {
9961
+ let defaultExportClassName = null;
9962
+ for (const node of ast.program.body) if (t.isExportDefaultDeclaration(node) && t.isClassDeclaration(node.declaration) && node.declaration.id) defaultExportClassName = node.declaration.id.name;
9954
9963
  const shouldProxyDep = (source) => {
9955
9964
  if (!source.startsWith(".")) return false;
9956
9965
  const resolved = ctx.resolveImportPath(sourceFile, source);
@@ -9959,7 +9968,13 @@ function transform(ctx) {
9959
9968
  if (ctx.isComponentModule(resolved)) return true;
9960
9969
  return false;
9961
9970
  };
9962
- if (injectHMR(ast, componentClassName, componentImports, componentImportsUsedAsTags, isDefaultExport, hmrImportSource, shouldProxyDep)) transformed = true;
9971
+ const shouldSkipDepAccept = (source) => {
9972
+ if (!source.startsWith(".")) return false;
9973
+ const resolved = ctx.resolveImportPath(sourceFile, source);
9974
+ if (!resolved) return false;
9975
+ return ctx.isStoreModule(resolved);
9976
+ };
9977
+ if (injectHMR(ast, componentClassNames, defaultExportClassName, componentImports, componentImportsUsedAsTags, hmrImportSource, shouldProxyDep, shouldSkipDepAccept)) transformed = true;
9963
9978
  }
9964
9979
  if (!transformed) return null;
9965
9980
  ensureGeaCompilerSymbolImports(ast);
@@ -10038,6 +10053,13 @@ export function reconcile(oldC, newC) {
10038
10053
  }
10039
10054
  `;
10040
10055
  const HMR_RUNTIME_SOURCE = `
10056
+ var GEA_ELEMENT = Symbol.for('gea.element');
10057
+ var GEA_RENDERED = Symbol.for('gea.rendered');
10058
+ var GEA_CLEANUP_BINDINGS = Symbol.for('gea.component.cleanupBindings');
10059
+ var GEA_TEARDOWN_SELF_LISTENERS = Symbol.for('gea.component.teardownSelfListeners');
10060
+ var GEA_CHILD_COMPONENTS = Symbol.for('gea.childComponents');
10061
+ var GEA_BINDINGS = Symbol.for('gea.bindings');
10062
+ var GEA_SELF_LISTENERS = Symbol.for('gea.selfListeners');
10041
10063
  var hot = import.meta.hot;
10042
10064
  var componentInstances = hot && hot.data && hot.data.componentInstances || new Map();
10043
10065
  if (hot) hot.data.componentInstances = componentInstances;
@@ -10349,7 +10371,7 @@ function geaPlugin() {
10349
10371
  }
10350
10372
  }
10351
10373
  if (/\bclass\s+Component\s+extends\s+Store\b/.test(code)) return null;
10352
- return transform({
10374
+ const result = transform({
10353
10375
  sourceFile: cleanId,
10354
10376
  code,
10355
10377
  isServe: isServeCommand,
@@ -10361,8 +10383,50 @@ function geaPlugin() {
10361
10383
  registerStoreModule: (fp) => storeModules.add(fp),
10362
10384
  registerComponentModule: (fp) => componentModules.add(fp)
10363
10385
  });
10386
+ if (result) return result;
10387
+ if (isServeCommand && !isSSR) {
10388
+ const componentDeps = findComponentDeps(code, cleanId);
10389
+ if (componentDeps.length > 0) return {
10390
+ code: code + `\nif (import.meta.hot) {\n${componentDeps.map((dep) => ` import.meta.hot.accept('${dep}', () => {});`).join("\n")}\n}\n`,
10391
+ map: null
10392
+ };
10393
+ }
10394
+ return null;
10364
10395
  }
10365
10396
  };
10366
10397
  }
10398
+ function resolveToFile(base) {
10399
+ const exts = [
10400
+ ".ts",
10401
+ ".tsx",
10402
+ ".js",
10403
+ ".jsx"
10404
+ ];
10405
+ const indexFiles = exts.map((ext) => resolve(base, "index" + ext));
10406
+ const candidates = [
10407
+ base,
10408
+ ...exts.map((ext) => base + ext),
10409
+ ...indexFiles
10410
+ ];
10411
+ for (const c of candidates) try {
10412
+ if (existsSync(c) && statSync(c).isFile()) return c;
10413
+ } catch {}
10414
+ return null;
10415
+ }
10416
+ function findComponentDeps(code, filePath) {
10417
+ const deps = [];
10418
+ const importRegex = /import\s+(?:[\w{},\s*]+)\s+from\s+['"](\.[^'"]+)['"]/g;
10419
+ let match;
10420
+ while ((match = importRegex.exec(code)) !== null) {
10421
+ const source = match[1];
10422
+ const resolved = resolveToFile(resolve(dirname(filePath), source));
10423
+ if (!resolved) continue;
10424
+ try {
10425
+ const depCode = readFileSync(resolved, "utf8");
10426
+ if (/class\s+\w+\s+extends\s+Component\b/.test(depCode) && depCode.includes("<") && depCode.includes(">")) deps.push(source);
10427
+ } catch {}
10428
+ }
10429
+ return deps;
10430
+ }
10367
10431
  //#endregion
10368
10432
  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.2",
4
4
  "description": "Vite plugin for Gea framework - JSX/TSX transform, reactivity, HMR",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",