@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.
- package/dist/index.mjs +136 -36
- 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("
|
|
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,
|
|
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
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
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(
|
|
9756
|
-
hmrStmts.push(js`${id(
|
|
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 &&
|
|
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
|
-
|
|
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
|
-
|
|
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 };
|