@geajs/vite-plugin 1.2.0 → 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.
- package/dist/index.mjs +110 -37
- 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
|
|
@@ -1126,6 +1126,7 @@ function buildComponentPropsExpression(jsxElement, imports, componentInstances,
|
|
|
1126
1126
|
else if (t.isJSXExpressionContainer(attr.value) && !t.isJSXEmptyExpression(attr.value.expression)) {
|
|
1127
1127
|
const expr = attr.value.expression;
|
|
1128
1128
|
propValue = transformExpression(expr);
|
|
1129
|
+
if (propValue && t.isTemplateLiteral(propValue) && propValue.quasis.length === 2 && propValue.quasis[0].value.raw === "" && propValue.quasis[1].value.raw === "" && propValue.expressions.length === 1) propValue = propValue.expressions[0];
|
|
1129
1130
|
if (propValue && (/^on[A-Z]/.test(propName) || DOM_EVENT_RE.test(propName)) && t.isMemberExpression(propValue)) {
|
|
1130
1131
|
const argsId = t.identifier("args");
|
|
1131
1132
|
propValue = t.arrowFunctionExpression([t.restElement(argsId)], t.callExpression(t.cloneNode(propValue, true), [t.spreadElement(argsId)]));
|
|
@@ -1734,6 +1735,12 @@ function collectComponentTags(node, imports, instanceTags, inMapCallback = false
|
|
|
1734
1735
|
if (tag && isComponentTag(tag) && imports.has(tag)) {
|
|
1735
1736
|
if (!inMapCallback) instanceTags.push(tag);
|
|
1736
1737
|
}
|
|
1738
|
+
for (const attr of node.openingElement.attributes) {
|
|
1739
|
+
if (!t.isJSXAttribute(attr) || !attr.value || !t.isJSXExpressionContainer(attr.value)) continue;
|
|
1740
|
+
if (t.isJSXEmptyExpression(attr.value.expression)) continue;
|
|
1741
|
+
const attrExpr = attr.value.expression;
|
|
1742
|
+
visitExpr(attrExpr, inMapCallback);
|
|
1743
|
+
}
|
|
1737
1744
|
node.children.forEach((c) => {
|
|
1738
1745
|
if (t.isJSXElement(c)) collectComponentTags(c, imports, instanceTags, inMapCallback);
|
|
1739
1746
|
else if (t.isJSXFragment(c)) collectComponentTags(c, imports, instanceTags, inMapCallback);
|
|
@@ -2276,7 +2283,7 @@ function processChildren(children, parts, ctx, elementPath, dcCursor, directChil
|
|
|
2276
2283
|
pushString(parts, "");
|
|
2277
2284
|
}
|
|
2278
2285
|
if (expressionMayBeFalsy(rawExpr)) expr = t.logicalExpression("||", expr, t.stringLiteral(""));
|
|
2279
|
-
const safeExpr = childCallInfo || isChildrenPropAccess(rawExpr) || expressionContainsJSX(rawExpr) || ctx.inMapCallback || callsJSXReturningProperty(rawExpr, ctx.classBody) ? expr : jsExpr`geaEscapeHtml(
|
|
2286
|
+
const safeExpr = childCallInfo || isChildrenPropAccess(rawExpr) || expressionContainsJSX(rawExpr) || ctx.inMapCallback || callsJSXReturningProperty(rawExpr, ctx.classBody) ? expr : jsExpr`geaEscapeHtml(${expr})`;
|
|
2280
2287
|
parts.push({
|
|
2281
2288
|
type: "expression",
|
|
2282
2289
|
value: safeExpr
|
|
@@ -9687,7 +9694,9 @@ function transformRemainingJSX(ast, imports) {
|
|
|
9687
9694
|
const classMethod = path.findParent((p) => t.isClassMethod(p.node));
|
|
9688
9695
|
if (classMethod && t.isClassMethod(classMethod.node) && t.isIdentifier(classMethod.node.key) && classMethod.node.key.name === "template") return;
|
|
9689
9696
|
try {
|
|
9690
|
-
|
|
9697
|
+
let result = transformJSXToTemplate(path.node, { imports });
|
|
9698
|
+
if (t.isTemplateLiteral(result) && result.quasis.length === 2 && result.quasis[0].value.raw === "" && result.quasis[1].value.raw === "" && result.expressions.length === 1 && path.parent && t.isArrowFunctionExpression(path.parent) && path.parent.body === path.node) result = result.expressions[0];
|
|
9699
|
+
path.replaceWith(result);
|
|
9691
9700
|
} catch (err) {
|
|
9692
9701
|
console.warn("[gea] Failed to transform JSX element:", err instanceof Error ? err.message : err);
|
|
9693
9702
|
}
|
|
@@ -9717,39 +9726,41 @@ const legacyShouldProxyDep = (p) => /\.(js|ts)$/.test(p) && !p.match(/(store|sta
|
|
|
9717
9726
|
* Returns `true` when HMR code was added, `false` otherwise (no component
|
|
9718
9727
|
* class, or the file contains a `gea-auto-register plugin` comment).
|
|
9719
9728
|
*/
|
|
9720
|
-
function injectHMR(ast,
|
|
9729
|
+
function injectHMR(ast, componentClassNames, defaultExportClassName, componentImports, componentImportsUsedAsTags, hmrImportSource = "virtual:gea-hmr", shouldProxyDep, shouldSkipDepAccept) {
|
|
9721
9730
|
if (hasAutoRegisterComment(ast)) return false;
|
|
9731
|
+
if (componentClassNames.length === 0) return false;
|
|
9722
9732
|
const hmrStmts = [];
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
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) {
|
|
9743
9755
|
registerComponentInstance(this.constructor.name, this);
|
|
9744
|
-
return __origCreated.call(this, __geaProps);
|
|
9756
|
+
return ${id("__origCreated" + suffix)}.call(this, __geaProps);
|
|
9745
9757
|
};`);
|
|
9746
|
-
hmrStmts.push(js`const __origDispose = ${id(
|
|
9747
|
-
hmrStmts.push(js`${id(
|
|
9758
|
+
hmrStmts.push(js`const ${id("__origDispose" + suffix)} = ${id(cn)}.prototype.dispose;`);
|
|
9759
|
+
hmrStmts.push(js`${id(cn)}.prototype.dispose = function() {
|
|
9748
9760
|
unregisterComponentInstance(this.constructor.name, this);
|
|
9749
|
-
return __origDispose.call(this);
|
|
9761
|
+
return ${id("__origDispose" + suffix)}.call(this);
|
|
9750
9762
|
};`);
|
|
9751
9763
|
}
|
|
9752
|
-
if (hmrStmts.length === 0) return false;
|
|
9753
9764
|
ast.program.body.push(t.ifStatement(hot(), t.blockStatement(hmrStmts)));
|
|
9754
9765
|
return true;
|
|
9755
9766
|
}
|
|
@@ -9791,10 +9802,11 @@ function rewriteComponentDeps(ast, imports, proxyDep) {
|
|
|
9791
9802
|
* add `hot.accept(dep, () => hot.invalidate())` so the page reloads when
|
|
9792
9803
|
* stores/utils change.
|
|
9793
9804
|
*/
|
|
9794
|
-
function createAccepts(imports, proxyDep) {
|
|
9805
|
+
function createAccepts(imports, proxyDep, shouldSkip) {
|
|
9795
9806
|
const stmts = [];
|
|
9796
9807
|
for (const p of imports) {
|
|
9797
9808
|
if (!isRelative(p)) continue;
|
|
9809
|
+
if (shouldSkip?.(p)) continue;
|
|
9798
9810
|
if (!proxyDep(p)) stmts.push(js`${hot()}.accept(${p}, ${invalidateCb()});`);
|
|
9799
9811
|
}
|
|
9800
9812
|
return stmts;
|
|
@@ -9855,6 +9867,14 @@ function isComponentImportSource(source) {
|
|
|
9855
9867
|
if (source.startsWith("node:")) return false;
|
|
9856
9868
|
return true;
|
|
9857
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
|
+
*/
|
|
9858
9878
|
function transform(ctx) {
|
|
9859
9879
|
const { sourceFile, code, isServe, isSSR, hmrImportSource } = ctx;
|
|
9860
9880
|
if (!(code.includes("<") && code.includes(">"))) return null;
|
|
@@ -9880,7 +9900,6 @@ function transform(ctx) {
|
|
|
9880
9900
|
let transformed = false;
|
|
9881
9901
|
const componentImportSet = /* @__PURE__ */ new Set();
|
|
9882
9902
|
const componentImportsUsedAsTags = /* @__PURE__ */ new Set();
|
|
9883
|
-
let isDefaultExport = false;
|
|
9884
9903
|
imports.forEach((source) => {
|
|
9885
9904
|
if (!isComponentImportSource(source)) return;
|
|
9886
9905
|
componentImportSet.add(source);
|
|
@@ -9890,9 +9909,6 @@ function transform(ctx) {
|
|
|
9890
9909
|
const knownComponentImports = /* @__PURE__ */ new Set();
|
|
9891
9910
|
const namedImportSources = /* @__PURE__ */ new Map();
|
|
9892
9911
|
traverse(ast, {
|
|
9893
|
-
ExportDefaultDeclaration() {
|
|
9894
|
-
isDefaultExport = true;
|
|
9895
|
-
},
|
|
9896
9912
|
ImportDeclaration(path) {
|
|
9897
9913
|
const source = path.node.source.value;
|
|
9898
9914
|
if (!isComponentImportSource(source)) return;
|
|
@@ -9941,7 +9957,9 @@ function transform(ctx) {
|
|
|
9941
9957
|
}
|
|
9942
9958
|
} else transformed = transformNonComponentJSX(ast, imports);
|
|
9943
9959
|
}
|
|
9944
|
-
if (isServe &&
|
|
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;
|
|
9945
9963
|
const shouldProxyDep = (source) => {
|
|
9946
9964
|
if (!source.startsWith(".")) return false;
|
|
9947
9965
|
const resolved = ctx.resolveImportPath(sourceFile, source);
|
|
@@ -9950,7 +9968,13 @@ function transform(ctx) {
|
|
|
9950
9968
|
if (ctx.isComponentModule(resolved)) return true;
|
|
9951
9969
|
return false;
|
|
9952
9970
|
};
|
|
9953
|
-
|
|
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;
|
|
9954
9978
|
}
|
|
9955
9979
|
if (!transformed) return null;
|
|
9956
9980
|
ensureGeaCompilerSymbolImports(ast);
|
|
@@ -10029,6 +10053,13 @@ export function reconcile(oldC, newC) {
|
|
|
10029
10053
|
}
|
|
10030
10054
|
`;
|
|
10031
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');
|
|
10032
10063
|
var hot = import.meta.hot;
|
|
10033
10064
|
var componentInstances = hot && hot.data && hot.data.componentInstances || new Map();
|
|
10034
10065
|
if (hot) hot.data.componentInstances = componentInstances;
|
|
@@ -10340,7 +10371,7 @@ function geaPlugin() {
|
|
|
10340
10371
|
}
|
|
10341
10372
|
}
|
|
10342
10373
|
if (/\bclass\s+Component\s+extends\s+Store\b/.test(code)) return null;
|
|
10343
|
-
|
|
10374
|
+
const result = transform({
|
|
10344
10375
|
sourceFile: cleanId,
|
|
10345
10376
|
code,
|
|
10346
10377
|
isServe: isServeCommand,
|
|
@@ -10352,8 +10383,50 @@ function geaPlugin() {
|
|
|
10352
10383
|
registerStoreModule: (fp) => storeModules.add(fp),
|
|
10353
10384
|
registerComponentModule: (fp) => componentModules.add(fp)
|
|
10354
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;
|
|
10355
10395
|
}
|
|
10356
10396
|
};
|
|
10357
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
|
+
}
|
|
10358
10431
|
//#endregion
|
|
10359
10432
|
export { geaPlugin };
|