@js-recon/js-recon 1.3.2 → 1.4.1-alpha.10
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/.github/workflows/build-and-prettify.yaml +207 -14
- package/.github/workflows/pr_checker.yml +2 -2
- package/.github/workflows/promote-js-recon.yml +104 -0
- package/.github/workflows/publish-js-recon.yml +22 -80
- package/.github/workflows/rules-smoke-test.yaml +79 -0
- package/CHANGELOG.md +177 -4
- package/CLAUDE.md +350 -16
- package/Dockerfile.release +29 -0
- package/README.md +48 -19
- package/build/analyze/engine/csMastSEngine.js +129 -0
- package/build/analyze/engine/csMastSEngine.js.map +1 -0
- package/build/analyze/engine/index.js +15 -0
- package/build/analyze/engine/index.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/findDirectAssignment.js +2 -1
- package/build/analyze/helpers/engineHelpers/findDirectAssignment.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js +2 -1
- package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/resolveFunctionIdentifier.js +2 -1
- package/build/analyze/helpers/engineHelpers/resolveFunctionIdentifier.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/taintFlow.js +11 -2
- package/build/analyze/helpers/engineHelpers/taintFlow.js.map +1 -1
- package/build/analyze/helpers/initRules.js.map +1 -1
- package/build/analyze/helpers/schemas.js +6 -2
- package/build/analyze/helpers/schemas.js.map +1 -1
- package/build/analyze/helpers/validate.js +3 -3
- package/build/analyze/helpers/validate.js.map +1 -1
- package/build/analyze/index.js +1 -0
- package/build/analyze/index.js.map +1 -1
- package/build/completion/index.js +432 -0
- package/build/completion/index.js.map +1 -0
- package/build/cs_mast/index.js +213 -0
- package/build/cs_mast/index.js.map +1 -0
- package/build/endpoints/next_js/client_jsFilesHref.js +2 -1
- package/build/endpoints/next_js/client_jsFilesHref.js.map +1 -1
- package/build/endpoints/next_js/client_jsonParse.js +2 -1
- package/build/endpoints/next_js/client_jsonParse.js.map +1 -1
- package/build/endpoints/next_js/client_mappedJsonFile.js +2 -1
- package/build/endpoints/next_js/client_mappedJsonFile.js.map +1 -1
- package/build/endpoints/next_js/client_subsequentRequests.js +2 -1
- package/build/endpoints/next_js/client_subsequentRequests.js.map +1 -1
- package/build/fingerprint/index.js +56 -33
- package/build/fingerprint/index.js.map +1 -1
- package/build/globalConfig.js +3 -3
- package/build/globalConfig.js.map +1 -1
- package/build/index.js +210 -10
- package/build/index.js.map +1 -1
- package/build/lazyLoad/angular/angular_getFromMainJs.js +2 -1
- package/build/lazyLoad/angular/angular_getFromMainJs.js.map +1 -1
- package/build/lazyLoad/downloadFilesUtil.js +6 -5
- package/build/lazyLoad/downloadFilesUtil.js.map +1 -1
- package/build/lazyLoad/downloadLoadedJsUtil.js +59 -5
- package/build/lazyLoad/downloadLoadedJsUtil.js.map +1 -1
- package/build/lazyLoad/downloadQueue.js +10 -9
- package/build/lazyLoad/downloadQueue.js.map +1 -1
- package/build/lazyLoad/index.js +127 -85
- package/build/lazyLoad/index.js.map +1 -1
- package/build/lazyLoad/methodFilter.js +50 -0
- package/build/lazyLoad/methodFilter.js.map +1 -0
- package/build/lazyLoad/next_js/NextJsCrawler.js +69 -48
- package/build/lazyLoad/next_js/NextJsCrawler.js.map +1 -1
- package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js +36 -34
- package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js.map +1 -1
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +66 -13
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
- package/build/lazyLoad/next_js/next_parseLayoutJs.js +80 -68
- package/build/lazyLoad/next_js/next_parseLayoutJs.js.map +1 -1
- package/build/lazyLoad/next_js/next_promiseResolve.js +22 -16
- package/build/lazyLoad/next_js/next_promiseResolve.js.map +1 -1
- package/build/lazyLoad/nuxt_js/nuxt_astParse.js +49 -47
- package/build/lazyLoad/nuxt_js/nuxt_astParse.js.map +1 -1
- package/build/lazyLoad/nuxt_js/nuxt_getBuildsManifest.js +64 -0
- package/build/lazyLoad/nuxt_js/nuxt_getBuildsManifest.js.map +1 -0
- package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +3 -1
- package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js.map +1 -1
- package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +3 -2
- package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js.map +1 -1
- package/build/lazyLoad/react/react_followImports.js +16 -7
- package/build/lazyLoad/react/react_followImports.js.map +1 -1
- package/build/lazyLoad/react/react_webpackChunkPaths.js +175 -123
- package/build/lazyLoad/react/react_webpackChunkPaths.js.map +1 -1
- package/build/lazyLoad/svelte/svelte_getFromPageSource.js +16 -0
- package/build/lazyLoad/svelte/svelte_getFromPageSource.js.map +1 -1
- package/build/lazyLoad/svelte/svelte_getVersionJson.js +40 -0
- package/build/lazyLoad/svelte/svelte_getVersionJson.js.map +1 -0
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +3 -2
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -1
- package/build/lazyLoad/techDetect/checkAngularJS.js +27 -6
- package/build/lazyLoad/techDetect/checkAngularJS.js.map +1 -1
- package/build/lazyLoad/techDetect/checkNextJS.js +24 -15
- package/build/lazyLoad/techDetect/checkNextJS.js.map +1 -1
- package/build/lazyLoad/techDetect/checkReact.js +8 -0
- package/build/lazyLoad/techDetect/checkReact.js.map +1 -1
- package/build/lazyLoad/techDetect/checkSvelte.js +17 -0
- package/build/lazyLoad/techDetect/checkSvelte.js.map +1 -1
- package/build/lazyLoad/techDetect/index.js +33 -5
- package/build/lazyLoad/techDetect/index.js.map +1 -1
- package/build/lazyLoad/vue/vue_RuntimeJs.js +2 -1
- package/build/lazyLoad/vue/vue_RuntimeJs.js.map +1 -1
- package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js +2 -1
- package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js.map +1 -1
- package/build/lazyLoad/vue/vue_discoverJsFiles.js +59 -25
- package/build/lazyLoad/vue/vue_discoverJsFiles.js.map +1 -1
- package/build/lazyLoad/vue/vue_getClientSidePaths.js +2 -1
- package/build/lazyLoad/vue/vue_getClientSidePaths.js.map +1 -1
- package/build/lazyLoad/vue/vue_jsImports.js +2 -1
- package/build/lazyLoad/vue/vue_jsImports.js.map +1 -1
- package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js +2 -2
- package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js.map +1 -1
- package/build/lazyLoad/vue/vue_viteMapDeps.js +62 -54
- package/build/lazyLoad/vue/vue_viteMapDeps.js.map +1 -1
- package/build/map/angular_js/getAngularConnections.js +88 -0
- package/build/map/angular_js/getAngularConnections.js.map +1 -0
- package/build/map/angular_js/interactive.js +4 -0
- package/build/map/angular_js/interactive.js.map +1 -0
- package/build/map/graphql/resolveGraphql.js +2 -1
- package/build/map/graphql/resolveGraphql.js.map +1 -1
- package/build/map/index.js +38 -0
- package/build/map/index.js.map +1 -1
- package/build/map/next_js/getExports.js +2 -1
- package/build/map/next_js/getExports.js.map +1 -1
- package/build/map/next_js/getFetchInstances.js +2 -1
- package/build/map/next_js/getFetchInstances.js.map +1 -1
- package/build/map/next_js/getTurbopackConnections.js +2 -1
- package/build/map/next_js/getTurbopackConnections.js.map +1 -1
- package/build/map/next_js/getWebpackConnections.js +2 -1
- package/build/map/next_js/getWebpackConnections.js.map +1 -1
- package/build/map/next_js/resolveAxios.js +2 -1
- package/build/map/next_js/resolveAxios.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/directCallsWithoutAssignment.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/directCallsWithoutAssignment.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/interceptorHeaders.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/interceptorHeaders.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/traceBody.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/traceBody.js.map +1 -1
- package/build/map/next_js/resolveFetch.js +2 -1
- package/build/map/next_js/resolveFetch.js.map +1 -1
- package/build/map/next_js/resolveNewRequest.js +2 -1
- package/build/map/next_js/resolveNewRequest.js.map +1 -1
- package/build/map/next_js/resolveServerActions.js +2 -1
- package/build/map/next_js/resolveServerActions.js.map +1 -1
- package/build/map/next_js/utils.js +2 -1
- package/build/map/next_js/utils.js.map +1 -1
- package/build/map/react_js/getReactConnections.js +5 -4
- package/build/map/react_js/getReactConnections.js.map +1 -1
- package/build/map/vue_js/crossFileResolver.js +3 -2
- package/build/map/vue_js/crossFileResolver.js.map +1 -1
- package/build/map/vue_js/getViteConnections.js +5 -4
- package/build/map/vue_js/getViteConnections.js.map +1 -1
- package/build/map/vue_js/taint_utils.js +2 -1
- package/build/map/vue_js/taint_utils.js.map +1 -1
- package/build/map/vue_js/vue_resolveFetch.js +3 -2
- package/build/map/vue_js/vue_resolveFetch.js.map +1 -1
- package/build/map/vue_js/vue_resolveHttpClient.js +5 -3
- package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -1
- package/build/map/vue_js/vue_resolveXhr.js +5 -3
- package/build/map/vue_js/vue_resolveXhr.js.map +1 -1
- package/build/mcp/cli.js.map +1 -1
- package/build/refactor/index.js +973 -27
- package/build/refactor/index.js.map +1 -1
- package/build/refactor/next/helpers.js +395 -0
- package/build/refactor/next/helpers.js.map +1 -0
- package/build/refactor/next/index.js +261 -71
- package/build/refactor/next/index.js.map +1 -1
- package/build/refactor/next/transform.js +1027 -0
- package/build/refactor/next/transform.js.map +1 -0
- package/build/refactor/next/validator.js +119 -0
- package/build/refactor/next/validator.js.map +1 -0
- package/build/refactor/react/helpers.js +106 -0
- package/build/refactor/react/helpers.js.map +1 -0
- package/build/refactor/react/index.js +290 -585
- package/build/refactor/react/index.js.map +1 -1
- package/build/refactor/react/library-classify.js +397 -0
- package/build/refactor/react/library-classify.js.map +1 -0
- package/build/refactor/react/transform.js +1852 -0
- package/build/refactor/react/transform.js.map +1 -0
- package/build/refactor/react/validator.js +123 -0
- package/build/refactor/react/validator.js.map +1 -0
- package/build/refactor/react-vite/index.js +652 -0
- package/build/refactor/react-vite/index.js.map +1 -0
- package/build/refactor/react-vite/vendor-analyze.js +336 -0
- package/build/refactor/react-vite/vendor-analyze.js.map +1 -0
- package/build/refactor/remote/cache.js +250 -0
- package/build/refactor/remote/cache.js.map +1 -0
- package/build/refactor/remote/config.js +63 -0
- package/build/refactor/remote/config.js.map +1 -0
- package/build/refactor/remote/hf-client.js +202 -0
- package/build/refactor/remote/hf-client.js.map +1 -0
- package/build/refactor/remote/version-detect.js +355 -0
- package/build/refactor/remote/version-detect.js.map +1 -0
- package/build/refactor/vue/index.js +176 -0
- package/build/refactor/vue/index.js.map +1 -0
- package/build/refactor/vue/vendor-analyze-vue.js +271 -0
- package/build/refactor/vue/vendor-analyze-vue.js.map +1 -0
- package/build/refactor/vue/vite.js +216 -0
- package/build/refactor/vue/vite.js.map +1 -0
- package/build/report/utility/genHtml.js +15 -4
- package/build/report/utility/genHtml.js.map +1 -1
- package/build/run/bundler-detect.js +154 -0
- package/build/run/bundler-detect.js.map +1 -0
- package/build/run/index.js +208 -16
- package/build/run/index.js.map +1 -1
- package/build/sourcemaps/index.js +89 -0
- package/build/sourcemaps/index.js.map +1 -0
- package/build/strings/index.js +6 -2
- package/build/strings/index.js.map +1 -1
- package/build/strings/trufflehog.js +71 -0
- package/build/strings/trufflehog.js.map +1 -0
- package/build/utility/banner.js +68 -0
- package/build/utility/banner.js.map +1 -0
- package/build/utility/getChromiumPath.js +40 -0
- package/build/utility/getChromiumPath.js.map +1 -0
- package/build/utility/globals.js +7 -0
- package/build/utility/globals.js.map +1 -1
- package/build/utility/makeReq.js +21 -11
- package/build/utility/makeReq.js.map +1 -1
- package/build/utility/postmanGenerator.js +2 -2
- package/build/utility/postmanGenerator.js.map +1 -1
- package/build/utility/puppeteerInstance.js +7 -1
- package/build/utility/puppeteerInstance.js.map +1 -1
- package/package.json +14 -8
- package/scripts/smoke-test.js +74 -0
|
@@ -0,0 +1,1852 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
import _traverse from "@babel/traverse";
|
|
3
|
+
import _generator from "@babel/generator";
|
|
4
|
+
import * as t from "@babel/types";
|
|
5
|
+
import { tryExtractModuleExportsAssignment, tryExtractExportsAssignment, tryExtractRequireCall, buildModuleExportStatement, makeNamedExportStatement, } from "./helpers.js";
|
|
6
|
+
import { REACT_CANONICAL, JSX_RUNTIME_CANONICAL, REACT_DOM_CLIENT_CANONICAL, REACT_ROUTER_DOM_CANONICAL, librarySource, } from "./library-classify.js";
|
|
7
|
+
const traverse = ((_a = _traverse.default) !== null && _a !== void 0 ? _a : _traverse);
|
|
8
|
+
const generate = _generator.default;
|
|
9
|
+
/**
|
|
10
|
+
* Transforms a single webpack module function into an array of ES module statements.
|
|
11
|
+
*
|
|
12
|
+
* Four passes (all top-level only to avoid placing exports inside nested functions):
|
|
13
|
+
* 1. `<moduleParam>.exports = <rhs>` → `export * from` or `export default`
|
|
14
|
+
* 2. `<exportsParam>.<prop> = <rhs>` → named export (all modules with an exportsParam)
|
|
15
|
+
* 3. Hoist `var x = <requireParam>(N)` to static `import * as x from "./N.js"`
|
|
16
|
+
* 4. Replace remaining inline `<requireParam>(N)` calls with the hoisted name
|
|
17
|
+
*/
|
|
18
|
+
export const transformModule = (mod) => {
|
|
19
|
+
const { fnPath, moduleParam, exportsParam, requireParam } = mod;
|
|
20
|
+
const body = fnPath.node.body;
|
|
21
|
+
if (!t.isBlockStatement(body))
|
|
22
|
+
return [];
|
|
23
|
+
// Pass 1 — handle `<moduleParam>.exports = <rhs>` at the top level only.
|
|
24
|
+
if (moduleParam) {
|
|
25
|
+
const next = [];
|
|
26
|
+
for (const stmt of body.body) {
|
|
27
|
+
if (!t.isExpressionStatement(stmt)) {
|
|
28
|
+
next.push(stmt);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const expr = stmt.expression;
|
|
32
|
+
const directRhs = tryExtractModuleExportsAssignment(expr, moduleParam);
|
|
33
|
+
if (directRhs) {
|
|
34
|
+
next.push(buildModuleExportStatement(directRhs, requireParam));
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (t.isSequenceExpression(expr)) {
|
|
38
|
+
let hadMatch = false;
|
|
39
|
+
const splitted = [];
|
|
40
|
+
for (const sub of expr.expressions) {
|
|
41
|
+
const rhs = tryExtractModuleExportsAssignment(sub, moduleParam);
|
|
42
|
+
if (rhs) {
|
|
43
|
+
hadMatch = true;
|
|
44
|
+
splitted.push(buildModuleExportStatement(rhs, requireParam));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
splitted.push(t.expressionStatement(sub));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (hadMatch) {
|
|
51
|
+
next.push(...splitted);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
next.push(stmt);
|
|
56
|
+
}
|
|
57
|
+
body.body = next;
|
|
58
|
+
}
|
|
59
|
+
// Pass 1.5 — handle webpack ES module registration patterns:
|
|
60
|
+
// `<requireParam>.r(<exportsParam>)` → drop (marks module as ES module, not needed in ESM)
|
|
61
|
+
// `<requireParam>.d(<exportsParam>, {name: ()=>binding, ...})` → named exports
|
|
62
|
+
// This handles the common lazy-chunk pattern where webpack emits r.r(t), r.d(t, {default:()=>X})
|
|
63
|
+
// at the top of the module body instead of the older t.exports = X form.
|
|
64
|
+
if (requireParam && exportsParam) {
|
|
65
|
+
const processWebpackRegExpr = (expr) => {
|
|
66
|
+
if (!t.isCallExpression(expr))
|
|
67
|
+
return null;
|
|
68
|
+
const callee = expr.callee;
|
|
69
|
+
if (!t.isMemberExpression(callee) || callee.computed)
|
|
70
|
+
return null;
|
|
71
|
+
if (!t.isIdentifier(callee.object, { name: requireParam }))
|
|
72
|
+
return null;
|
|
73
|
+
const method = t.isIdentifier(callee.property) ? callee.property.name : null;
|
|
74
|
+
if (!method)
|
|
75
|
+
return null;
|
|
76
|
+
// r.r(t) → drop
|
|
77
|
+
if (method === "r" &&
|
|
78
|
+
expr.arguments.length === 1 &&
|
|
79
|
+
t.isIdentifier(expr.arguments[0], { name: exportsParam })) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
// r.d(t, {name: ()=>binding, ...}) → named export statements
|
|
83
|
+
if (method === "d" &&
|
|
84
|
+
expr.arguments.length >= 2 &&
|
|
85
|
+
t.isIdentifier(expr.arguments[0], { name: exportsParam }) &&
|
|
86
|
+
t.isObjectExpression(expr.arguments[1])) {
|
|
87
|
+
const stmts = [];
|
|
88
|
+
for (const prop of expr.arguments[1].properties) {
|
|
89
|
+
if (!t.isObjectProperty(prop) || prop.computed)
|
|
90
|
+
continue;
|
|
91
|
+
const key = prop.key;
|
|
92
|
+
const val = prop.value;
|
|
93
|
+
let propName = null;
|
|
94
|
+
if (t.isIdentifier(key))
|
|
95
|
+
propName = key.name;
|
|
96
|
+
else if (t.isStringLiteral(key))
|
|
97
|
+
propName = key.value;
|
|
98
|
+
if (!propName)
|
|
99
|
+
continue;
|
|
100
|
+
// val is typically `() => binding` — unwrap the arrow function body
|
|
101
|
+
const exportedVal = t.isArrowFunctionExpression(val) && !t.isBlockStatement(val.body)
|
|
102
|
+
? val.body
|
|
103
|
+
: val;
|
|
104
|
+
stmts.push(makeNamedExportStatement(propName, exportedVal));
|
|
105
|
+
}
|
|
106
|
+
return stmts;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
};
|
|
110
|
+
const next = [];
|
|
111
|
+
for (const stmt of body.body) {
|
|
112
|
+
if (!t.isExpressionStatement(stmt)) {
|
|
113
|
+
next.push(stmt);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const expr = stmt.expression;
|
|
117
|
+
if (t.isSequenceExpression(expr)) {
|
|
118
|
+
let hadMatch = false;
|
|
119
|
+
const splitted = [];
|
|
120
|
+
for (const sub of expr.expressions) {
|
|
121
|
+
const handled = processWebpackRegExpr(sub);
|
|
122
|
+
if (handled !== null) {
|
|
123
|
+
hadMatch = true;
|
|
124
|
+
splitted.push(...handled);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
splitted.push(t.expressionStatement(sub));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (hadMatch) {
|
|
131
|
+
next.push(...splitted);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const handled = processWebpackRegExpr(expr);
|
|
136
|
+
if (handled !== null) {
|
|
137
|
+
next.push(...handled);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
next.push(stmt);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
body.body = next;
|
|
144
|
+
}
|
|
145
|
+
// Pass 2 — handle `<exportsParam>.<propName> = <rhs>` at the top level only.
|
|
146
|
+
if (exportsParam) {
|
|
147
|
+
const next = [];
|
|
148
|
+
for (const stmt of body.body) {
|
|
149
|
+
if (!t.isExpressionStatement(stmt)) {
|
|
150
|
+
next.push(stmt);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const expr = stmt.expression;
|
|
154
|
+
const direct = tryExtractExportsAssignment(expr, exportsParam);
|
|
155
|
+
if (direct) {
|
|
156
|
+
next.push(makeNamedExportStatement(direct.propName, direct.rhs));
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (t.isSequenceExpression(expr)) {
|
|
160
|
+
const splitted = [];
|
|
161
|
+
for (const sub of expr.expressions) {
|
|
162
|
+
const match = tryExtractExportsAssignment(sub, exportsParam);
|
|
163
|
+
if (match) {
|
|
164
|
+
splitted.push(makeNamedExportStatement(match.propName, match.rhs));
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
splitted.push(t.expressionStatement(sub));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
next.push(...splitted);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
next.push(stmt);
|
|
174
|
+
}
|
|
175
|
+
body.body = next;
|
|
176
|
+
}
|
|
177
|
+
// Pass 3 — hoist top-level `var <name> = <requireParam>(N)` to static imports.
|
|
178
|
+
// moduleSpec → importName
|
|
179
|
+
const hoistedImports = new Map();
|
|
180
|
+
// numId → canonical importName (used by Pass 4 for inline replacements)
|
|
181
|
+
const importNameByNumId = new Map();
|
|
182
|
+
if (requireParam) {
|
|
183
|
+
const next = [];
|
|
184
|
+
for (const stmt of body.body) {
|
|
185
|
+
if (!t.isVariableDeclaration(stmt)) {
|
|
186
|
+
next.push(stmt);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const kept = [];
|
|
190
|
+
for (const decl of stmt.declarations) {
|
|
191
|
+
if (!t.isIdentifier(decl.id) || !decl.init) {
|
|
192
|
+
kept.push(decl);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const numId = tryExtractRequireCall(decl.init, requireParam);
|
|
196
|
+
if (numId === null) {
|
|
197
|
+
kept.push(decl);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const spec = `./${numId}.js`;
|
|
201
|
+
if (!hoistedImports.has(spec)) {
|
|
202
|
+
hoistedImports.set(spec, decl.id.name);
|
|
203
|
+
importNameByNumId.set(numId, decl.id.name);
|
|
204
|
+
}
|
|
205
|
+
// declarator removed (it becomes the static import)
|
|
206
|
+
}
|
|
207
|
+
if (kept.length > 0) {
|
|
208
|
+
next.push(t.variableDeclaration(stmt.kind, kept));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
body.body = next;
|
|
212
|
+
}
|
|
213
|
+
// Pass 4 — replace any remaining inline `<requireParam>(N)` calls with a synthesized
|
|
214
|
+
// namespace import reference.
|
|
215
|
+
if (requireParam) {
|
|
216
|
+
fnPath.traverse({
|
|
217
|
+
CallExpression(p) {
|
|
218
|
+
const numId = tryExtractRequireCall(p.node, requireParam);
|
|
219
|
+
if (numId === null)
|
|
220
|
+
return;
|
|
221
|
+
let name = importNameByNumId.get(numId);
|
|
222
|
+
if (!name) {
|
|
223
|
+
name = `_jsr_module_${numId}`;
|
|
224
|
+
hoistedImports.set(`./${numId}.js`, name);
|
|
225
|
+
importNameByNumId.set(numId, name);
|
|
226
|
+
}
|
|
227
|
+
p.replaceWith(t.identifier(name));
|
|
228
|
+
p.skip();
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
// Pass 4.5 — convert webpack async chunk loading to a true dynamic import:
|
|
233
|
+
// requireParam.e(N).then(requireParam.bind(requireParam, N)) → import('./N.js')
|
|
234
|
+
// This pattern is emitted by webpack for React.lazy() code-split points. After the
|
|
235
|
+
// outer module wrapper is stripped, `requireParam` is no longer in scope, so the
|
|
236
|
+
// expression must be replaced before Step 5 removes it.
|
|
237
|
+
if (requireParam) {
|
|
238
|
+
fnPath.traverse({
|
|
239
|
+
CallExpression(callPath) {
|
|
240
|
+
const node = callPath.node;
|
|
241
|
+
// Outer call must be X.then(bindExpr) — non-computed member
|
|
242
|
+
if (!t.isMemberExpression(node.callee) || node.callee.computed)
|
|
243
|
+
return;
|
|
244
|
+
if (!t.isIdentifier(node.callee.property, { name: "then" }))
|
|
245
|
+
return;
|
|
246
|
+
// X must be requireParam.e(numId)
|
|
247
|
+
const eCallNode = node.callee.object;
|
|
248
|
+
if (!t.isCallExpression(eCallNode))
|
|
249
|
+
return;
|
|
250
|
+
const eCallee = eCallNode.callee;
|
|
251
|
+
if (!t.isMemberExpression(eCallee) || eCallee.computed)
|
|
252
|
+
return;
|
|
253
|
+
if (!t.isIdentifier(eCallee.object, { name: requireParam }))
|
|
254
|
+
return;
|
|
255
|
+
if (!t.isIdentifier(eCallee.property, { name: "e" }))
|
|
256
|
+
return;
|
|
257
|
+
const eArgs = eCallNode.arguments;
|
|
258
|
+
if (eArgs.length !== 1 || !t.isNumericLiteral(eArgs[0]))
|
|
259
|
+
return;
|
|
260
|
+
const numId = eArgs[0].value;
|
|
261
|
+
// The .then() argument must be requireParam.bind(requireParam, numId)
|
|
262
|
+
if (node.arguments.length !== 1 || !t.isCallExpression(node.arguments[0]))
|
|
263
|
+
return;
|
|
264
|
+
const bindNode = node.arguments[0];
|
|
265
|
+
if (!t.isMemberExpression(bindNode.callee) || bindNode.callee.computed)
|
|
266
|
+
return;
|
|
267
|
+
if (!t.isIdentifier(bindNode.callee.object, { name: requireParam }))
|
|
268
|
+
return;
|
|
269
|
+
if (!t.isIdentifier(bindNode.callee.property, { name: "bind" }))
|
|
270
|
+
return;
|
|
271
|
+
const bindArgs = bindNode.arguments;
|
|
272
|
+
if (bindArgs.length !== 2)
|
|
273
|
+
return;
|
|
274
|
+
if (!t.isIdentifier(bindArgs[0], { name: requireParam }))
|
|
275
|
+
return;
|
|
276
|
+
if (!t.isNumericLiteral(bindArgs[1]) || bindArgs[1].value !== numId)
|
|
277
|
+
return;
|
|
278
|
+
// Replace with import('./N.js') — a dynamic import expression
|
|
279
|
+
callPath.replaceWith(t.callExpression(t.import(), [t.stringLiteral(`./${numId}.js`)]));
|
|
280
|
+
callPath.skip();
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
// Step 5 — strip outer function wrapper, prepend hoisted static imports.
|
|
285
|
+
const importStmts = [];
|
|
286
|
+
for (const [spec, name] of hoistedImports) {
|
|
287
|
+
importStmts.push(t.importDeclaration([t.importNamespaceSpecifier(t.identifier(name))], t.stringLiteral(spec)));
|
|
288
|
+
}
|
|
289
|
+
return [...importStmts, ...body.body];
|
|
290
|
+
};
|
|
291
|
+
// Returns true when fn matches the webpack runtime require helper:
|
|
292
|
+
// function X(id) { … return (moduleMap[id](mod, mod.exports, X), mod.exports); }
|
|
293
|
+
// The key signal is the final return statement: a SequenceExpression whose first element
|
|
294
|
+
// is a computed-member CallExpression and whose second is a `<var>.exports` MemberExpression.
|
|
295
|
+
const isWebpackRequireHelper = (fn) => {
|
|
296
|
+
if (fn.params.length !== 1)
|
|
297
|
+
return false;
|
|
298
|
+
for (const stmt of fn.body.body) {
|
|
299
|
+
if (!t.isReturnStatement(stmt) || !stmt.argument)
|
|
300
|
+
continue;
|
|
301
|
+
const arg = stmt.argument;
|
|
302
|
+
if (!t.isSequenceExpression(arg) || arg.expressions.length !== 2)
|
|
303
|
+
continue;
|
|
304
|
+
const [first, second] = arg.expressions;
|
|
305
|
+
if (!t.isCallExpression(first))
|
|
306
|
+
continue;
|
|
307
|
+
if (!t.isMemberExpression(first.callee) || !first.callee.computed)
|
|
308
|
+
continue;
|
|
309
|
+
if (!t.isMemberExpression(second))
|
|
310
|
+
continue;
|
|
311
|
+
if (!t.isIdentifier(second.property, { name: "exports" }))
|
|
312
|
+
continue;
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
return false;
|
|
316
|
+
};
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
// Pass D helpers — library-aware call-site rewriting
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
/** Resolves the canonical name for an accessed property on a library-bound local var.
|
|
321
|
+
* Returns null if the var is not a known library or the prop has no canonical mapping. */
|
|
322
|
+
function resolveLibraryProp(varName, prop, varToLib) {
|
|
323
|
+
const info = varToLib.get(varName);
|
|
324
|
+
if (!info || info.type === "unknown")
|
|
325
|
+
return null;
|
|
326
|
+
// Helper: is this name actually a canonical export for this library type?
|
|
327
|
+
const isCanonical = (name) => {
|
|
328
|
+
if (info.type === "react")
|
|
329
|
+
return REACT_CANONICAL.has(name);
|
|
330
|
+
// Fragment is also exported by react/jsx-runtime but excluded from JSX_RUNTIME_CANONICAL
|
|
331
|
+
// (which is used only for module classification). Accept it explicitly here for rewriting.
|
|
332
|
+
if (info.type === "react-jsx-runtime")
|
|
333
|
+
return JSX_RUNTIME_CANONICAL.has(name) || name === "Fragment";
|
|
334
|
+
if (info.type === "react-dom-client")
|
|
335
|
+
return REACT_DOM_CLIENT_CANONICAL.has(name);
|
|
336
|
+
if (info.type === "react-router-dom")
|
|
337
|
+
return REACT_ROUTER_DOM_CANONICAL.has(name);
|
|
338
|
+
return false;
|
|
339
|
+
};
|
|
340
|
+
// Check exportMap: only trust the value if it IS a canonical name.
|
|
341
|
+
// (Avoids mapping e.g. n.jsx = i → canonical 'i' when i is just a local fn.)
|
|
342
|
+
const viaMap = info.exportMap.get(prop);
|
|
343
|
+
if (viaMap && isCanonical(viaMap))
|
|
344
|
+
return { canonical: viaMap, libType: info.type };
|
|
345
|
+
// Fall back to checking the prop name itself (handles cases where the exported
|
|
346
|
+
// property name is already the canonical name, e.g. n.jsx = <iife>).
|
|
347
|
+
if (isCanonical(prop))
|
|
348
|
+
return { canonical: prop, libType: info.type };
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
/** Extracts `varName` and `prop` from a `(0, varName.prop)` or `varName.prop` callee. */
|
|
352
|
+
function tryExtractMemberCallee(callee) {
|
|
353
|
+
// (0, X.Y) form
|
|
354
|
+
if (t.isSequenceExpression(callee) && callee.expressions.length === 2) {
|
|
355
|
+
const [first, second] = callee.expressions;
|
|
356
|
+
if (t.isNumericLiteral(first) && first.value === 0 && t.isMemberExpression(second)) {
|
|
357
|
+
const obj = second.object;
|
|
358
|
+
const prop = second.property;
|
|
359
|
+
if (t.isIdentifier(obj) && t.isIdentifier(prop) && !second.computed) {
|
|
360
|
+
return { varName: obj.name, prop: prop.name };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// X.Y form
|
|
365
|
+
if (t.isMemberExpression(callee) && !callee.computed) {
|
|
366
|
+
const obj = callee.object;
|
|
367
|
+
const prop = callee.property;
|
|
368
|
+
if (t.isIdentifier(obj) && t.isIdentifier(prop)) {
|
|
369
|
+
return { varName: obj.name, prop: prop.name };
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Pass D – rewrite library calls and collect which named exports are needed.
|
|
376
|
+
*
|
|
377
|
+
* Traverses all statements; for every `(0, X.Y)(args)` or `X.Y(args)` where X is a
|
|
378
|
+
* known library local var, replaces the callee with the canonical identifier and records
|
|
379
|
+
* the needed named import. Also rewrites bare member expressions `X.Y` that are NOT in a
|
|
380
|
+
* call position (e.g. passed as a value to another function).
|
|
381
|
+
*
|
|
382
|
+
* Returns the set of named exports needed per library type so the caller can build
|
|
383
|
+
* proper `import { ... } from "..."` declarations.
|
|
384
|
+
*/
|
|
385
|
+
function rewriteLibraryCalls(statements, varToLib) {
|
|
386
|
+
// libType → set of canonical export names actually used
|
|
387
|
+
const usedExports = new Map();
|
|
388
|
+
const record = (libType, name) => {
|
|
389
|
+
const src = librarySource(libType);
|
|
390
|
+
if (!src)
|
|
391
|
+
return;
|
|
392
|
+
if (!usedExports.has(src))
|
|
393
|
+
usedExports.set(src, new Set());
|
|
394
|
+
usedExports.get(src).add(name);
|
|
395
|
+
};
|
|
396
|
+
const syntheticFile = t.file(t.program(statements, [], "module"));
|
|
397
|
+
traverse(syntheticFile, {
|
|
398
|
+
CallExpression(p) {
|
|
399
|
+
const info = tryExtractMemberCallee(p.node.callee);
|
|
400
|
+
if (!info)
|
|
401
|
+
return;
|
|
402
|
+
const resolved = resolveLibraryProp(info.varName, info.prop, varToLib);
|
|
403
|
+
if (!resolved)
|
|
404
|
+
return;
|
|
405
|
+
record(resolved.libType, resolved.canonical);
|
|
406
|
+
// Replace (0, X.Y)(...) or X.Y(...) with canonicalName(...)
|
|
407
|
+
p.node.callee = t.identifier(resolved.canonical);
|
|
408
|
+
// Do NOT skip — arguments may contain further (0, X.Y)(...) or X.Y member refs.
|
|
409
|
+
},
|
|
410
|
+
MemberExpression(p) {
|
|
411
|
+
// Rewrite X.Y in non-call positions (e.g. passed as callback value)
|
|
412
|
+
if (p.parent && t.isCallExpression(p.parent) && p.parent.callee === p.node)
|
|
413
|
+
return; // handled above
|
|
414
|
+
const obj = p.node.object;
|
|
415
|
+
const prop = p.node.property;
|
|
416
|
+
if (!t.isIdentifier(obj) || !t.isIdentifier(prop) || p.node.computed)
|
|
417
|
+
return;
|
|
418
|
+
const resolved = resolveLibraryProp(obj.name, prop.name, varToLib);
|
|
419
|
+
if (!resolved)
|
|
420
|
+
return;
|
|
421
|
+
record(resolved.libType, resolved.canonical);
|
|
422
|
+
p.replaceWith(t.identifier(resolved.canonical));
|
|
423
|
+
p.skip();
|
|
424
|
+
},
|
|
425
|
+
// Rewrite JSX member expressions: <ns.Component> → <CanonicalName>
|
|
426
|
+
// These are JSXMemberExpression nodes (not MemberExpression), so the visitor above misses them.
|
|
427
|
+
JSXOpeningElement(p) {
|
|
428
|
+
const elem = p.node;
|
|
429
|
+
if (!t.isJSXMemberExpression(elem.name))
|
|
430
|
+
return;
|
|
431
|
+
const obj = elem.name.object;
|
|
432
|
+
const prop = elem.name.property;
|
|
433
|
+
if (!t.isJSXIdentifier(obj) || !t.isJSXIdentifier(prop))
|
|
434
|
+
return;
|
|
435
|
+
const resolved = resolveLibraryProp(obj.name, prop.name, varToLib);
|
|
436
|
+
if (!resolved)
|
|
437
|
+
return;
|
|
438
|
+
record(resolved.libType, resolved.canonical);
|
|
439
|
+
elem.name = t.jsxIdentifier(resolved.canonical);
|
|
440
|
+
},
|
|
441
|
+
JSXClosingElement(p) {
|
|
442
|
+
const elem = p.node;
|
|
443
|
+
if (!t.isJSXMemberExpression(elem.name))
|
|
444
|
+
return;
|
|
445
|
+
const obj = elem.name.object;
|
|
446
|
+
const prop = elem.name.property;
|
|
447
|
+
if (!t.isJSXIdentifier(obj) || !t.isJSXIdentifier(prop))
|
|
448
|
+
return;
|
|
449
|
+
const resolved = resolveLibraryProp(obj.name, prop.name, varToLib);
|
|
450
|
+
if (!resolved)
|
|
451
|
+
return;
|
|
452
|
+
// Don't record() again — already recorded from JSXOpeningElement.
|
|
453
|
+
elem.name = t.jsxIdentifier(resolved.canonical);
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
return usedExports;
|
|
457
|
+
}
|
|
458
|
+
// ---------------------------------------------------------------------------
|
|
459
|
+
// Pass E — Babel slicedToArray collapse
|
|
460
|
+
// ---------------------------------------------------------------------------
|
|
461
|
+
/** Returns true if the last arm of a `||` chain is an IIFE that throws TypeError. */
|
|
462
|
+
function isSlicedToArrayTail(node) {
|
|
463
|
+
if (!t.isCallExpression(node) || node.arguments.length !== 0)
|
|
464
|
+
return false;
|
|
465
|
+
const callee = node.callee;
|
|
466
|
+
if (!t.isFunctionExpression(callee) && !t.isArrowFunctionExpression(callee))
|
|
467
|
+
return false;
|
|
468
|
+
const body = callee.body;
|
|
469
|
+
if (!t.isBlockStatement(body))
|
|
470
|
+
return false;
|
|
471
|
+
return body.body.some((s) => t.isThrowStatement(s) &&
|
|
472
|
+
t.isNewExpression(s.argument) &&
|
|
473
|
+
t.isIdentifier(s.argument.callee, { name: "TypeError" }));
|
|
474
|
+
}
|
|
475
|
+
function hasSlicedToArrayTail(node) {
|
|
476
|
+
if (isSlicedToArrayTail(node))
|
|
477
|
+
return true;
|
|
478
|
+
if (t.isLogicalExpression(node) && node.operator === "||")
|
|
479
|
+
return hasSlicedToArrayTail(node.right);
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Tries to detect a `var <temps>, resultVar = (<seqExpr>), target0 = resultVar[0], ...`
|
|
484
|
+
* pattern produced by Babel's slicedToArray expansion.
|
|
485
|
+
*
|
|
486
|
+
* Returns `{ actualExpr, resultVar, targets }` on match, or null.
|
|
487
|
+
*/
|
|
488
|
+
function tryDetectSlicedToArray(decl) {
|
|
489
|
+
let resultVar = null;
|
|
490
|
+
let actualExpr = null;
|
|
491
|
+
for (const d of decl.declarations) {
|
|
492
|
+
if (!d.init || !t.isSequenceExpression(d.init))
|
|
493
|
+
continue;
|
|
494
|
+
const exprs = d.init.expressions;
|
|
495
|
+
if (exprs.length < 3)
|
|
496
|
+
continue;
|
|
497
|
+
// exprs[0]: tempVar = <actualCall>
|
|
498
|
+
if (!t.isAssignmentExpression(exprs[0]) || exprs[0].operator !== "=")
|
|
499
|
+
continue;
|
|
500
|
+
const firstAssign = exprs[0];
|
|
501
|
+
if (!t.isIdentifier(firstAssign.left))
|
|
502
|
+
continue;
|
|
503
|
+
// exprs[1]: countVar = <number>
|
|
504
|
+
if (!t.isAssignmentExpression(exprs[1]) || !t.isNumericLiteral(exprs[1].right))
|
|
505
|
+
continue;
|
|
506
|
+
// last expr: the TypeError IIFE (possibly nested in || chain)
|
|
507
|
+
if (!hasSlicedToArrayTail(exprs[exprs.length - 1]))
|
|
508
|
+
continue;
|
|
509
|
+
resultVar = t.isIdentifier(d.id) ? d.id.name : null;
|
|
510
|
+
if (!resultVar)
|
|
511
|
+
continue;
|
|
512
|
+
actualExpr = firstAssign.right;
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
if (!resultVar || !actualExpr)
|
|
516
|
+
return null;
|
|
517
|
+
const targets = [];
|
|
518
|
+
for (const d of decl.declarations) {
|
|
519
|
+
if (!d.init || !t.isMemberExpression(d.init))
|
|
520
|
+
continue;
|
|
521
|
+
if (!t.isIdentifier(d.init.object, { name: resultVar }))
|
|
522
|
+
continue;
|
|
523
|
+
if (!d.init.computed || !t.isNumericLiteral(d.init.property))
|
|
524
|
+
continue;
|
|
525
|
+
if (!t.isIdentifier(d.id))
|
|
526
|
+
continue;
|
|
527
|
+
targets.push({ id: d.id, index: d.init.property.value });
|
|
528
|
+
}
|
|
529
|
+
return targets.length > 0 ? { actualExpr, resultVar, targets } : null;
|
|
530
|
+
}
|
|
531
|
+
/** Collapse Babel slicedToArray expansions in a statement list (including nested blocks). */
|
|
532
|
+
function collapseSlicedToArray(statements) {
|
|
533
|
+
const out = [];
|
|
534
|
+
for (const stmt of statements) {
|
|
535
|
+
// Recurse into block statements (e.g. if/else, function bodies handled via traversal)
|
|
536
|
+
if (t.isBlockStatement(stmt)) {
|
|
537
|
+
stmt.body = collapseSlicedToArray(stmt.body);
|
|
538
|
+
out.push(stmt);
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (!t.isVariableDeclaration(stmt)) {
|
|
542
|
+
out.push(stmt);
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const match = tryDetectSlicedToArray(stmt);
|
|
546
|
+
if (!match) {
|
|
547
|
+
out.push(stmt);
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
const { actualExpr, resultVar, targets } = match;
|
|
551
|
+
// Sort targets by index so array pattern elements are in order
|
|
552
|
+
const sorted = [...targets].sort((a, b) => a.index - b.index);
|
|
553
|
+
const pattern = t.arrayPattern(sorted.map((tgt) => tgt.id));
|
|
554
|
+
out.push(t.variableDeclaration("const", [t.variableDeclarator(pattern, actualExpr)]));
|
|
555
|
+
}
|
|
556
|
+
return out;
|
|
557
|
+
}
|
|
558
|
+
// ---------------------------------------------------------------------------
|
|
559
|
+
// Pass E.2 — collapse named slicedToArray helper calls
|
|
560
|
+
// ---------------------------------------------------------------------------
|
|
561
|
+
/**
|
|
562
|
+
* Detects a named slicedToArray helper function by the TypeError throw at the
|
|
563
|
+
* end of its body. Shape: function(e, t) { return ... || (() => { throw new TypeError("...non-iterable...") })() }
|
|
564
|
+
*/
|
|
565
|
+
function isNamedSlicedToArrayHelper(stmt) {
|
|
566
|
+
if (!t.isFunctionDeclaration(stmt) || !stmt.id || stmt.params.length !== 2)
|
|
567
|
+
return null;
|
|
568
|
+
// Direct name match: _slicedToArray is always a Babel array-destructure helper.
|
|
569
|
+
if (stmt.id.name === "_slicedToArray" || stmt.id.name === "slicedToArray")
|
|
570
|
+
return stmt.id.name;
|
|
571
|
+
const body = stmt.body.body;
|
|
572
|
+
if (body.length === 0)
|
|
573
|
+
return null;
|
|
574
|
+
const last = body[body.length - 1];
|
|
575
|
+
if (!t.isReturnStatement(last) || !last.argument)
|
|
576
|
+
return null;
|
|
577
|
+
// The return value must (somewhere in its || chain) throw new TypeError
|
|
578
|
+
const hasTypeError = (node) => {
|
|
579
|
+
if (t.isCallExpression(node) &&
|
|
580
|
+
(t.isFunctionExpression(node.callee) || t.isArrowFunctionExpression(node.callee))) {
|
|
581
|
+
const innerBody = node.callee.body;
|
|
582
|
+
if (t.isBlockStatement(innerBody)) {
|
|
583
|
+
return innerBody.body.some((s) => t.isThrowStatement(s) &&
|
|
584
|
+
t.isNewExpression(s.argument) &&
|
|
585
|
+
t.isIdentifier(s.argument.callee, { name: "TypeError" }) &&
|
|
586
|
+
s.argument.arguments.some((a) => t.isStringLiteral(a) && a.value.includes("non-iterable")));
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (t.isLogicalExpression(node) && node.operator === "||")
|
|
590
|
+
return hasTypeError(node.right);
|
|
591
|
+
return false;
|
|
592
|
+
};
|
|
593
|
+
return hasTypeError(last.argument) ? stmt.id.name : null;
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Collapses `const temp = helper(expr, n), a = temp[0], b = temp[1]` into
|
|
597
|
+
* `const [a, b] = expr` for a given list of statements, knowing which function
|
|
598
|
+
* names are slicedToArray helpers (passed in as `helperNames`).
|
|
599
|
+
*/
|
|
600
|
+
function collapseSlicedToArrayCalls(stmts, helperNames) {
|
|
601
|
+
if (helperNames.size === 0)
|
|
602
|
+
return stmts;
|
|
603
|
+
const out = [];
|
|
604
|
+
for (const stmt of stmts) {
|
|
605
|
+
if (!t.isVariableDeclaration(stmt)) {
|
|
606
|
+
out.push(stmt);
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
const decls = stmt.declarations;
|
|
610
|
+
const newDecls = [];
|
|
611
|
+
let i = 0;
|
|
612
|
+
while (i < decls.length) {
|
|
613
|
+
const d = decls[i];
|
|
614
|
+
if (!t.isIdentifier(d.id) || !d.init || !t.isCallExpression(d.init)) {
|
|
615
|
+
newDecls.push(d);
|
|
616
|
+
i++;
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
const callExpr = d.init;
|
|
620
|
+
const callee = callExpr.callee;
|
|
621
|
+
if (!t.isIdentifier(callee) || !helperNames.has(callee.name)) {
|
|
622
|
+
newDecls.push(d);
|
|
623
|
+
i++;
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
const tempName = d.id.name;
|
|
627
|
+
let actualExpr = callExpr.arguments[0];
|
|
628
|
+
// If actualExpr references the immediately preceding declarator, inline its init
|
|
629
|
+
// so we get `const [a, b] = actualCall` instead of `const _x = actualCall, [a, b] = _x`.
|
|
630
|
+
if (t.isIdentifier(actualExpr) &&
|
|
631
|
+
newDecls.length > 0 &&
|
|
632
|
+
t.isIdentifier(newDecls[newDecls.length - 1].id) &&
|
|
633
|
+
newDecls[newDecls.length - 1].id.name === actualExpr.name &&
|
|
634
|
+
newDecls[newDecls.length - 1].init != null) {
|
|
635
|
+
actualExpr = newDecls[newDecls.length - 1].init;
|
|
636
|
+
newDecls.pop(); // remove the now-inlined declarator
|
|
637
|
+
}
|
|
638
|
+
const targets = [];
|
|
639
|
+
let j = i + 1;
|
|
640
|
+
while (j < decls.length) {
|
|
641
|
+
const nd = decls[j];
|
|
642
|
+
if (!t.isIdentifier(nd.id) ||
|
|
643
|
+
!nd.init ||
|
|
644
|
+
!t.isMemberExpression(nd.init) ||
|
|
645
|
+
!nd.init.computed ||
|
|
646
|
+
!t.isIdentifier(nd.init.object, { name: tempName }) ||
|
|
647
|
+
!t.isNumericLiteral(nd.init.property))
|
|
648
|
+
break;
|
|
649
|
+
targets.push({
|
|
650
|
+
id: nd.id,
|
|
651
|
+
index: nd.init.property.value,
|
|
652
|
+
});
|
|
653
|
+
j++;
|
|
654
|
+
}
|
|
655
|
+
if (targets.length > 0) {
|
|
656
|
+
const sorted = [...targets].sort((a, b) => a.index - b.index);
|
|
657
|
+
const pattern = t.arrayPattern(sorted.map((tgt) => tgt.id));
|
|
658
|
+
newDecls.push(t.variableDeclarator(pattern, actualExpr));
|
|
659
|
+
i = j;
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
newDecls.push(d);
|
|
663
|
+
i++;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
if (newDecls.length > 0)
|
|
667
|
+
out.push(t.variableDeclaration(stmt.kind, newDecls));
|
|
668
|
+
}
|
|
669
|
+
return out;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Given a list of statements, finds any named slicedToArray helpers, then:
|
|
673
|
+
* 1. Collapses `const temp = helper(expr, n), a = temp[0], b = temp[1]` → `const [a, b] = expr`
|
|
674
|
+
* 2. Rewrites inline `helper(expr, n)[k]` → `expr[k]` (single-element extraction with chained access)
|
|
675
|
+
* 3. Removes the helper declarations once they are no longer referenced.
|
|
676
|
+
*/
|
|
677
|
+
function collapseNamedSlicedToArray(statements) {
|
|
678
|
+
const helperNames = new Set();
|
|
679
|
+
for (const stmt of statements) {
|
|
680
|
+
const name = isNamedSlicedToArrayHelper(stmt);
|
|
681
|
+
if (name)
|
|
682
|
+
helperNames.add(name);
|
|
683
|
+
}
|
|
684
|
+
if (helperNames.size === 0)
|
|
685
|
+
return statements;
|
|
686
|
+
// Remove helper declarations and collapse top-level declarator-sequence usages
|
|
687
|
+
const withoutHelpers = statements.filter((s) => !(t.isFunctionDeclaration(s) && s.id && helperNames.has(s.id.name)));
|
|
688
|
+
const topCollapsed = collapseSlicedToArrayCalls(withoutHelpers, helperNames);
|
|
689
|
+
// Recurse into nested function bodies via traverse — collapse declarator sequences
|
|
690
|
+
const syntheticFile = t.file(t.program(topCollapsed, [], "module"));
|
|
691
|
+
traverse(syntheticFile, {
|
|
692
|
+
BlockStatement(p) {
|
|
693
|
+
const collapsed = collapseSlicedToArrayCalls(p.node.body, helperNames);
|
|
694
|
+
if (collapsed !== p.node.body)
|
|
695
|
+
p.node.body = collapsed;
|
|
696
|
+
},
|
|
697
|
+
// Rewrite inline `helper(expr, n)[k]` → `expr[k]`
|
|
698
|
+
MemberExpression(p) {
|
|
699
|
+
const node = p.node;
|
|
700
|
+
if (!node.computed)
|
|
701
|
+
return;
|
|
702
|
+
if (!t.isNumericLiteral(node.property))
|
|
703
|
+
return;
|
|
704
|
+
if (!t.isCallExpression(node.object))
|
|
705
|
+
return;
|
|
706
|
+
const call = node.object;
|
|
707
|
+
if (!t.isIdentifier(call.callee) || !helperNames.has(call.callee.name))
|
|
708
|
+
return;
|
|
709
|
+
const actualExpr = call.arguments[0];
|
|
710
|
+
if (!actualExpr)
|
|
711
|
+
return;
|
|
712
|
+
p.replaceWith(t.memberExpression(actualExpr, node.property, true));
|
|
713
|
+
p.skip();
|
|
714
|
+
},
|
|
715
|
+
});
|
|
716
|
+
return topCollapsed;
|
|
717
|
+
}
|
|
718
|
+
/** Traverse all function bodies in a statement list and collapse slicedToArray inside them. */
|
|
719
|
+
function collapseSlicedToArrayDeep(statements) {
|
|
720
|
+
// First collapse named helpers (module-level factored-out slicedToArray function).
|
|
721
|
+
// This also recurses into all nested function bodies via traverse internally.
|
|
722
|
+
const afterNamed = collapseNamedSlicedToArray(statements);
|
|
723
|
+
// Collapse the inline expansion pattern at top level
|
|
724
|
+
const top = collapseSlicedToArray(afterNamed);
|
|
725
|
+
// Then recurse into function bodies via a traverse for the inline pattern
|
|
726
|
+
const syntheticFile = t.file(t.program(top, [], "module"));
|
|
727
|
+
traverse(syntheticFile, {
|
|
728
|
+
BlockStatement(p) {
|
|
729
|
+
const collapsed = collapseSlicedToArray(p.node.body);
|
|
730
|
+
if (collapsed !== p.node.body) {
|
|
731
|
+
p.node.body = collapsed;
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
});
|
|
735
|
+
return top;
|
|
736
|
+
}
|
|
737
|
+
// ---------------------------------------------------------------------------
|
|
738
|
+
// Pass F — JSX recovery
|
|
739
|
+
// ---------------------------------------------------------------------------
|
|
740
|
+
function exprToJsxName(expr) {
|
|
741
|
+
var _a;
|
|
742
|
+
if (t.isStringLiteral(expr))
|
|
743
|
+
return t.jsxIdentifier(expr.value);
|
|
744
|
+
// Handle template literals with no expressions: `div` → "div"
|
|
745
|
+
if (t.isTemplateLiteral(expr) && expr.expressions.length === 0 && expr.quasis.length === 1) {
|
|
746
|
+
const raw = (_a = expr.quasis[0].value.cooked) !== null && _a !== void 0 ? _a : expr.quasis[0].value.raw;
|
|
747
|
+
if (raw)
|
|
748
|
+
return t.jsxIdentifier(raw);
|
|
749
|
+
}
|
|
750
|
+
if (t.isIdentifier(expr))
|
|
751
|
+
return t.jsxIdentifier(expr.name);
|
|
752
|
+
if (t.isMemberExpression(expr) && !expr.computed && t.isIdentifier(expr.property)) {
|
|
753
|
+
const obj = exprToJsxName(expr.object);
|
|
754
|
+
if (obj)
|
|
755
|
+
return t.jsxMemberExpression(obj, t.jsxIdentifier(expr.property.name));
|
|
756
|
+
}
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
function exprToJsxAttrValue(expr) {
|
|
760
|
+
if (t.isStringLiteral(expr))
|
|
761
|
+
return expr;
|
|
762
|
+
return t.jsxExpressionContainer(expr);
|
|
763
|
+
}
|
|
764
|
+
function childToJsxChild(child) {
|
|
765
|
+
var _a;
|
|
766
|
+
if (t.isStringLiteral(child))
|
|
767
|
+
return t.jsxText(child.value);
|
|
768
|
+
// Template literal with no expressions: `Dashboard` → JSXText
|
|
769
|
+
if (t.isTemplateLiteral(child) && child.expressions.length === 0 && child.quasis.length === 1) {
|
|
770
|
+
const raw = (_a = child.quasis[0].value.cooked) !== null && _a !== void 0 ? _a : child.quasis[0].value.raw;
|
|
771
|
+
if (raw !== undefined)
|
|
772
|
+
return t.jsxText(raw);
|
|
773
|
+
}
|
|
774
|
+
if (t.isJSXElement(child) || t.isJSXFragment(child))
|
|
775
|
+
return child;
|
|
776
|
+
// Recursively convert nested jsx(...) calls into JSX elements.
|
|
777
|
+
if (t.isCallExpression(child)) {
|
|
778
|
+
const converted = tryConvertToJSX(child);
|
|
779
|
+
if (converted)
|
|
780
|
+
return converted;
|
|
781
|
+
}
|
|
782
|
+
return t.jsxExpressionContainer(child);
|
|
783
|
+
}
|
|
784
|
+
// Detect Babel's compiled object-spread IIFE:
|
|
785
|
+
// (function(target) { for(var i=1; i<arguments.length; i++){...} return target; })(base, spread1, ...)
|
|
786
|
+
// Returns { base, spreads } if matched, null otherwise.
|
|
787
|
+
function tryUnpackSpreadIIFE(expr) {
|
|
788
|
+
if (!t.isCallExpression(expr))
|
|
789
|
+
return null;
|
|
790
|
+
const callee = expr.callee;
|
|
791
|
+
if (!t.isFunctionExpression(callee))
|
|
792
|
+
return null;
|
|
793
|
+
if (callee.params.length !== 1 || !t.isIdentifier(callee.params[0]))
|
|
794
|
+
return null;
|
|
795
|
+
const paramName = callee.params[0].name;
|
|
796
|
+
const body = callee.body.body;
|
|
797
|
+
if (body.length < 2)
|
|
798
|
+
return null;
|
|
799
|
+
// Must contain a for loop that iterates `arguments.length`
|
|
800
|
+
const hasArgumentsLoop = body.some((s) => {
|
|
801
|
+
if (!t.isForStatement(s) || !s.test || !t.isBinaryExpression(s.test))
|
|
802
|
+
return false;
|
|
803
|
+
const right = s.test.right;
|
|
804
|
+
return (s.test.operator === "<" &&
|
|
805
|
+
t.isMemberExpression(right) &&
|
|
806
|
+
t.isIdentifier(right.object, { name: "arguments" }) &&
|
|
807
|
+
t.isIdentifier(right.property, { name: "length" }));
|
|
808
|
+
});
|
|
809
|
+
if (!hasArgumentsLoop)
|
|
810
|
+
return null;
|
|
811
|
+
// Must return the first param
|
|
812
|
+
const hasReturn = body.some((s) => t.isReturnStatement(s) && s.argument && t.isIdentifier(s.argument, { name: paramName }));
|
|
813
|
+
if (!hasReturn)
|
|
814
|
+
return null;
|
|
815
|
+
const args = expr.arguments;
|
|
816
|
+
if (args.length < 1)
|
|
817
|
+
return null;
|
|
818
|
+
return { base: args[0], spreads: args.slice(1) };
|
|
819
|
+
}
|
|
820
|
+
/** Build JSXAttributes + children from an ObjectExpression or a spread-IIFE propsArg. */
|
|
821
|
+
function propsArgToAttrsAndChildren(propsArg) {
|
|
822
|
+
const attrs = [];
|
|
823
|
+
const childExprs = [];
|
|
824
|
+
const processObjectExpr = (obj) => {
|
|
825
|
+
for (const prop of obj.properties) {
|
|
826
|
+
if (t.isSpreadElement(prop)) {
|
|
827
|
+
attrs.push(t.jsxSpreadAttribute(prop.argument));
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
if (!t.isObjectProperty(prop) || prop.computed)
|
|
831
|
+
continue;
|
|
832
|
+
const keyNode = prop.key;
|
|
833
|
+
const valNode = prop.value;
|
|
834
|
+
const keyName = t.isIdentifier(keyNode) ? keyNode.name : t.isStringLiteral(keyNode) ? keyNode.value : null;
|
|
835
|
+
if (!keyName)
|
|
836
|
+
continue;
|
|
837
|
+
if (keyName === "children") {
|
|
838
|
+
if (t.isArrayExpression(valNode)) {
|
|
839
|
+
for (const el of valNode.elements) {
|
|
840
|
+
if (el)
|
|
841
|
+
childExprs.push(el);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
childExprs.push(valNode);
|
|
846
|
+
}
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
attrs.push(t.jsxAttribute(t.jsxIdentifier(keyName), exprToJsxAttrValue(valNode)));
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
if (t.isObjectExpression(propsArg)) {
|
|
853
|
+
processObjectExpr(propsArg);
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
// Try to unpack Babel's compiled object-spread IIFE
|
|
857
|
+
const unpacked = tryUnpackSpreadIIFE(propsArg);
|
|
858
|
+
if (unpacked) {
|
|
859
|
+
if (t.isObjectExpression(unpacked.base)) {
|
|
860
|
+
processObjectExpr(unpacked.base);
|
|
861
|
+
}
|
|
862
|
+
else {
|
|
863
|
+
attrs.push(t.jsxSpreadAttribute(unpacked.base));
|
|
864
|
+
}
|
|
865
|
+
for (const spreadExpr of unpacked.spreads) {
|
|
866
|
+
attrs.push(t.jsxSpreadAttribute(spreadExpr));
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
// Fallback: emit the entire props argument as a single JSX spread
|
|
871
|
+
attrs.push(t.jsxSpreadAttribute(propsArg));
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return { attrs, childExprs };
|
|
875
|
+
}
|
|
876
|
+
const JSX_METHOD_NAMES = new Set(["jsx", "jsxs", "jsxDEV"]);
|
|
877
|
+
// Extract the jsx method name from a callee that may be:
|
|
878
|
+
// - bare identifier: `jsx`
|
|
879
|
+
// - member expression: `ns.jsx`
|
|
880
|
+
// - sequence (0, ns.jsx) after a `(0, ...)()` call
|
|
881
|
+
// Returns the method name string, or null if not a jsx call.
|
|
882
|
+
function getJsxMethodName(callee) {
|
|
883
|
+
if (t.isIdentifier(callee) && JSX_METHOD_NAMES.has(callee.name)) {
|
|
884
|
+
return callee.name;
|
|
885
|
+
}
|
|
886
|
+
if (t.isMemberExpression(callee) && !callee.computed) {
|
|
887
|
+
const prop = callee.property;
|
|
888
|
+
if (t.isIdentifier(prop) && JSX_METHOD_NAMES.has(prop.name)) {
|
|
889
|
+
return prop.name;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
// (0, ns.jsx) sequence expression
|
|
893
|
+
if (t.isSequenceExpression(callee)) {
|
|
894
|
+
const exprs = callee.expressions;
|
|
895
|
+
const last = exprs[exprs.length - 1];
|
|
896
|
+
if (last && t.isMemberExpression(last) && !last.computed) {
|
|
897
|
+
const prop = last.property;
|
|
898
|
+
if (t.isIdentifier(prop) && JSX_METHOD_NAMES.has(prop.name)) {
|
|
899
|
+
return prop.name;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
function tryConvertToJSX(call) {
|
|
906
|
+
const callee = call.callee;
|
|
907
|
+
const methodName = getJsxMethodName(callee);
|
|
908
|
+
if (!methodName)
|
|
909
|
+
return null;
|
|
910
|
+
if (call.arguments.length < 2)
|
|
911
|
+
return null;
|
|
912
|
+
const tagArg = call.arguments[0];
|
|
913
|
+
const propsArg = call.arguments[1];
|
|
914
|
+
const jsxName = exprToJsxName(tagArg);
|
|
915
|
+
if (!jsxName)
|
|
916
|
+
return null;
|
|
917
|
+
const { attrs, childExprs } = propsArgToAttrsAndChildren(propsArg);
|
|
918
|
+
const children = childExprs.map((e) => childToJsxChild(e)).filter(Boolean);
|
|
919
|
+
// Fragment identifier → JSXFragment shorthand (<>...</>)
|
|
920
|
+
if (t.isJSXIdentifier(jsxName) && jsxName.name === "Fragment") {
|
|
921
|
+
return t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), children);
|
|
922
|
+
}
|
|
923
|
+
const jsxAttrs = attrs.filter((a) => t.isJSXAttribute(a) || t.isJSXSpreadAttribute(a));
|
|
924
|
+
const selfClosing = children.length === 0;
|
|
925
|
+
const openingElement = t.jsxOpeningElement(jsxName, jsxAttrs, selfClosing);
|
|
926
|
+
const closingElement = selfClosing ? null : t.jsxClosingElement(jsxName);
|
|
927
|
+
return t.jsxElement(openingElement, closingElement, children, selfClosing);
|
|
928
|
+
}
|
|
929
|
+
/** Traverse statements and replace jsx()/jsxs() calls with JSXElement nodes. */
|
|
930
|
+
function recoverJSX(statements) {
|
|
931
|
+
const syntheticFile = t.file(t.program(statements, [], "module"));
|
|
932
|
+
traverse(syntheticFile, {
|
|
933
|
+
CallExpression(p) {
|
|
934
|
+
const jsxEl = tryConvertToJSX(p.node);
|
|
935
|
+
if (!jsxEl)
|
|
936
|
+
return;
|
|
937
|
+
p.replaceWith(jsxEl);
|
|
938
|
+
// Do NOT skip — nested jsx calls inside lambdas/expression containers need visiting.
|
|
939
|
+
},
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
// ---------------------------------------------------------------------------
|
|
943
|
+
// Pass G — remove top-level Babel helpers and webpack internals
|
|
944
|
+
// ---------------------------------------------------------------------------
|
|
945
|
+
function isBabelArrayLikeToArrayHelper(stmt) {
|
|
946
|
+
if (!t.isFunctionDeclaration(stmt) || !stmt.id)
|
|
947
|
+
return false;
|
|
948
|
+
const body = stmt.body.body;
|
|
949
|
+
if (body.length < 2 || body.length > 4)
|
|
950
|
+
return false;
|
|
951
|
+
// Key signal: contains `for (var ... = Array(n); ...)` and a return of the array
|
|
952
|
+
const hasArrayOf = body.some((s) => {
|
|
953
|
+
if (!t.isForStatement(s))
|
|
954
|
+
return false;
|
|
955
|
+
const init = s.init;
|
|
956
|
+
if (!t.isVariableDeclaration(init))
|
|
957
|
+
return false;
|
|
958
|
+
return init.declarations.some((d) => d.init &&
|
|
959
|
+
t.isCallExpression(d.init) &&
|
|
960
|
+
t.isIdentifier(d.init.callee, { name: "Array" }));
|
|
961
|
+
});
|
|
962
|
+
return hasArrayOf;
|
|
963
|
+
}
|
|
964
|
+
// Detect `_typeof` — Babel's lazy-init typeof polyfill.
|
|
965
|
+
// Shape: 1-param function whose sole body statement is
|
|
966
|
+
// return ((fnName = <conditional>), fnName(arg))
|
|
967
|
+
function isBabelTypeofHelper(stmt) {
|
|
968
|
+
if (!t.isFunctionDeclaration(stmt) || !stmt.id || stmt.params.length !== 1)
|
|
969
|
+
return false;
|
|
970
|
+
const body = stmt.body.body;
|
|
971
|
+
if (body.length !== 1 || !t.isReturnStatement(body[0]))
|
|
972
|
+
return false;
|
|
973
|
+
const arg = body[0].argument;
|
|
974
|
+
if (!arg || !t.isSequenceExpression(arg))
|
|
975
|
+
return false;
|
|
976
|
+
const exprs = arg.expressions;
|
|
977
|
+
if (exprs.length !== 2)
|
|
978
|
+
return false;
|
|
979
|
+
const first = exprs[0];
|
|
980
|
+
return (t.isAssignmentExpression(first) &&
|
|
981
|
+
t.isIdentifier(first.left, { name: stmt.id.name }));
|
|
982
|
+
}
|
|
983
|
+
// Detect `_defineProperty` / `_toPropertyKey`+`_defineProperty` combos.
|
|
984
|
+
// Shape: 3-param function whose body contains an Object.defineProperty call with
|
|
985
|
+
// { value, enumerable, configurable, writable } descriptor.
|
|
986
|
+
function isBabelDefinePropertyHelper(stmt) {
|
|
987
|
+
if (!t.isFunctionDeclaration(stmt) || !stmt.id || stmt.params.length !== 3)
|
|
988
|
+
return false;
|
|
989
|
+
const bodyCode = stmt.body.body;
|
|
990
|
+
const hasDefProp = (node) => {
|
|
991
|
+
if (t.isCallExpression(node) &&
|
|
992
|
+
t.isMemberExpression(node.callee) &&
|
|
993
|
+
t.isIdentifier(node.callee.object, { name: "Object" }) &&
|
|
994
|
+
t.isIdentifier(node.callee.property, {
|
|
995
|
+
name: "defineProperty",
|
|
996
|
+
})) {
|
|
997
|
+
const args = node.arguments;
|
|
998
|
+
if (args.length >= 3 && t.isObjectExpression(args[2])) {
|
|
999
|
+
const props = args[2].properties;
|
|
1000
|
+
const keys = props
|
|
1001
|
+
.filter((p) => t.isObjectProperty(p) && t.isIdentifier(p.key))
|
|
1002
|
+
.map((p) => p.key.name);
|
|
1003
|
+
return keys.includes("enumerable") && keys.includes("configurable") && keys.includes("writable");
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return false;
|
|
1007
|
+
};
|
|
1008
|
+
// Walk statements for the defineProperty call (may be nested in return/conditional)
|
|
1009
|
+
const walk = (node) => {
|
|
1010
|
+
if (hasDefProp(node))
|
|
1011
|
+
return true;
|
|
1012
|
+
for (const key of Object.keys(node)) {
|
|
1013
|
+
const child = node[key];
|
|
1014
|
+
if (!child || typeof child !== "object")
|
|
1015
|
+
continue;
|
|
1016
|
+
if (Array.isArray(child)) {
|
|
1017
|
+
if (child.some((c) => c && typeof c === "object" && "type" in c && walk(c)))
|
|
1018
|
+
return true;
|
|
1019
|
+
}
|
|
1020
|
+
else if ("type" in child) {
|
|
1021
|
+
if (walk(child))
|
|
1022
|
+
return true;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
return false;
|
|
1026
|
+
};
|
|
1027
|
+
return bodyCode.some((s) => walk(s));
|
|
1028
|
+
}
|
|
1029
|
+
// Detect `_objectSpreadPropsHelper` / `_objectKeys`.
|
|
1030
|
+
// Shape: 2-param function whose first statement declares a variable via Object.keys(param0)
|
|
1031
|
+
// and whose body references getOwnPropertySymbols.
|
|
1032
|
+
function isBabelObjectSpreadHelper(stmt) {
|
|
1033
|
+
if (!t.isFunctionDeclaration(stmt) || !stmt.id || stmt.params.length !== 2)
|
|
1034
|
+
return false;
|
|
1035
|
+
const body = stmt.body.body;
|
|
1036
|
+
if (body.length < 2)
|
|
1037
|
+
return false;
|
|
1038
|
+
// First statement: var t = Object.keys(e)
|
|
1039
|
+
const first = body[0];
|
|
1040
|
+
if (!t.isVariableDeclaration(first))
|
|
1041
|
+
return false;
|
|
1042
|
+
const firstIsObjectKeys = first.declarations.some((d) => {
|
|
1043
|
+
if (!d.init || !t.isCallExpression(d.init))
|
|
1044
|
+
return false;
|
|
1045
|
+
const callee = d.init.callee;
|
|
1046
|
+
return (t.isMemberExpression(callee) &&
|
|
1047
|
+
t.isIdentifier(callee.object, { name: "Object" }) &&
|
|
1048
|
+
t.isIdentifier(callee.property, { name: "keys" }));
|
|
1049
|
+
});
|
|
1050
|
+
if (!firstIsObjectKeys)
|
|
1051
|
+
return false;
|
|
1052
|
+
// Body references getOwnPropertySymbols
|
|
1053
|
+
const bodyStr = JSON.stringify(body);
|
|
1054
|
+
return bodyStr.includes('"getOwnPropertySymbols"');
|
|
1055
|
+
}
|
|
1056
|
+
// Detect `_objectSpread2` — Babel's 1-param spread helper that uses arguments.length
|
|
1057
|
+
// and Object.defineProperties to merge source objects into the target.
|
|
1058
|
+
function isBabelObjectSpread2Helper(stmt) {
|
|
1059
|
+
if (!t.isFunctionDeclaration(stmt))
|
|
1060
|
+
return false;
|
|
1061
|
+
const params = stmt.params;
|
|
1062
|
+
if (params.length !== 1)
|
|
1063
|
+
return false;
|
|
1064
|
+
const body = stmt.body.body;
|
|
1065
|
+
// Must have a for loop that reads arguments.length, then calls Object.defineProperties
|
|
1066
|
+
const hasArgumentsLength = JSON.stringify(body).includes('"arguments"');
|
|
1067
|
+
const hasDefineProperties = JSON.stringify(body).includes('"defineProperties"') || JSON.stringify(body).includes('"defineProperty"');
|
|
1068
|
+
return hasArgumentsLength && hasDefineProperties;
|
|
1069
|
+
}
|
|
1070
|
+
// Detect `_ownKeys` / `ownKeys` — companion helper to _objectSpread2.
|
|
1071
|
+
// Shape: 1- or 2-param function whose first statement declares a var via Object.keys(param)
|
|
1072
|
+
// and whose body references getOwnPropertySymbols.
|
|
1073
|
+
function isBabelOwnKeysHelper(stmt) {
|
|
1074
|
+
if (!t.isFunctionDeclaration(stmt))
|
|
1075
|
+
return false;
|
|
1076
|
+
const params = stmt.params;
|
|
1077
|
+
if (params.length < 1 || params.length > 2)
|
|
1078
|
+
return false;
|
|
1079
|
+
const body = stmt.body.body;
|
|
1080
|
+
if (body.length === 0)
|
|
1081
|
+
return false;
|
|
1082
|
+
// First statement declares a var via Object.keys(param)
|
|
1083
|
+
const first = body[0];
|
|
1084
|
+
if (!t.isVariableDeclaration(first))
|
|
1085
|
+
return false;
|
|
1086
|
+
const bodyStr = JSON.stringify(body);
|
|
1087
|
+
return bodyStr.includes('"keys"') && bodyStr.includes('"getOwnPropertySymbols"');
|
|
1088
|
+
}
|
|
1089
|
+
// Named Babel array-destructure companion helpers that appear as FunctionDeclarations.
|
|
1090
|
+
const NAMED_BABEL_ARRAY_HELPERS = new Set([
|
|
1091
|
+
"_arrayWithHoles",
|
|
1092
|
+
"_arrayLikeToArray",
|
|
1093
|
+
"_iterableToArrayLimit",
|
|
1094
|
+
"_unsupportedIterableToArray",
|
|
1095
|
+
"_nonIterableRest",
|
|
1096
|
+
]);
|
|
1097
|
+
function isNamedBabelArrayHelper(stmt) {
|
|
1098
|
+
return t.isFunctionDeclaration(stmt) && stmt.id != null && NAMED_BABEL_ARRAY_HELPERS.has(stmt.id.name);
|
|
1099
|
+
}
|
|
1100
|
+
/** Remove `var x = {}` declarations where every declarator has an empty-object init.
|
|
1101
|
+
* These are webpack module-cache variables. */
|
|
1102
|
+
function dropEmptyObjectVars(stmts) {
|
|
1103
|
+
return stmts.filter((stmt) => {
|
|
1104
|
+
if (!t.isVariableDeclaration(stmt))
|
|
1105
|
+
return true;
|
|
1106
|
+
return !stmt.declarations.every((d) => d.init && t.isObjectExpression(d.init) && d.init.properties.length === 0);
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
// ---------------------------------------------------------------------------
|
|
1110
|
+
// Pass H — prune unused named imports (e.g. jsx/jsxs after JSX recovery)
|
|
1111
|
+
// ---------------------------------------------------------------------------
|
|
1112
|
+
/** Collect all free identifier references in the given statements. */
|
|
1113
|
+
function collectReferencedNames(stmts) {
|
|
1114
|
+
const names = new Set();
|
|
1115
|
+
const syntheticFile = t.file(t.program(stmts, [], "module"));
|
|
1116
|
+
traverse(syntheticFile, {
|
|
1117
|
+
Identifier(p) {
|
|
1118
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1119
|
+
// Skip identifier nodes that are property names or binding declarations
|
|
1120
|
+
if ((_a = p.parentPath) === null || _a === void 0 ? void 0 : _a.isImportSpecifier())
|
|
1121
|
+
return;
|
|
1122
|
+
if ((_b = p.parentPath) === null || _b === void 0 ? void 0 : _b.isImportDefaultSpecifier())
|
|
1123
|
+
return;
|
|
1124
|
+
if ((_c = p.parentPath) === null || _c === void 0 ? void 0 : _c.isImportNamespaceSpecifier())
|
|
1125
|
+
return;
|
|
1126
|
+
if (((_d = p.parentPath) === null || _d === void 0 ? void 0 : _d.isMemberExpression()) &&
|
|
1127
|
+
!p.parent.computed &&
|
|
1128
|
+
p.parentPath.get("property") === p)
|
|
1129
|
+
return;
|
|
1130
|
+
if (((_e = p.parentPath) === null || _e === void 0 ? void 0 : _e.isObjectProperty()) &&
|
|
1131
|
+
!p.parent.computed &&
|
|
1132
|
+
p.parentPath.get("key") === p)
|
|
1133
|
+
return;
|
|
1134
|
+
if ((_f = p.parentPath) === null || _f === void 0 ? void 0 : _f.isJSXAttribute())
|
|
1135
|
+
return;
|
|
1136
|
+
if (((_g = p.parentPath) === null || _g === void 0 ? void 0 : _g.isJSXOpeningElement()) || ((_h = p.parentPath) === null || _h === void 0 ? void 0 : _h.isJSXClosingElement()))
|
|
1137
|
+
return;
|
|
1138
|
+
names.add(p.node.name);
|
|
1139
|
+
},
|
|
1140
|
+
JSXIdentifier(p) {
|
|
1141
|
+
// JSX element names (e.g. <Fragment>) are JSXIdentifier nodes, not Identifier.
|
|
1142
|
+
// Add them so their import specifiers survive Pass H pruning.
|
|
1143
|
+
names.add(p.node.name);
|
|
1144
|
+
},
|
|
1145
|
+
});
|
|
1146
|
+
return names;
|
|
1147
|
+
}
|
|
1148
|
+
/** Remove named import specifiers whose local name is not referenced in `bodyStmts`. */
|
|
1149
|
+
function pruneUnusedNamedImports(importStmts, bodyStmts) {
|
|
1150
|
+
const refs = collectReferencedNames(bodyStmts);
|
|
1151
|
+
return importStmts
|
|
1152
|
+
.map((stmt) => {
|
|
1153
|
+
if (!t.isImportDeclaration(stmt))
|
|
1154
|
+
return stmt;
|
|
1155
|
+
const prunedSpecifiers = stmt.specifiers.filter((spec) => {
|
|
1156
|
+
if (t.isImportNamespaceSpecifier(spec))
|
|
1157
|
+
return true; // always keep namespace imports
|
|
1158
|
+
if (t.isImportDefaultSpecifier(spec))
|
|
1159
|
+
return true;
|
|
1160
|
+
if (t.isImportSpecifier(spec)) {
|
|
1161
|
+
const localName = t.isIdentifier(spec.local) ? spec.local.name : null;
|
|
1162
|
+
return localName ? refs.has(localName) : true;
|
|
1163
|
+
}
|
|
1164
|
+
return true;
|
|
1165
|
+
});
|
|
1166
|
+
if (prunedSpecifiers.length === 0)
|
|
1167
|
+
return null; // drop empty imports
|
|
1168
|
+
if (prunedSpecifiers.length === stmt.specifiers.length)
|
|
1169
|
+
return stmt; // unchanged
|
|
1170
|
+
return t.importDeclaration(prunedSpecifiers, stmt.source);
|
|
1171
|
+
})
|
|
1172
|
+
.filter(Boolean);
|
|
1173
|
+
}
|
|
1174
|
+
// ---------------------------------------------------------------------------
|
|
1175
|
+
// Pass D for module files — library-aware import rewriting
|
|
1176
|
+
// ---------------------------------------------------------------------------
|
|
1177
|
+
/**
|
|
1178
|
+
* Applies Pass D to an already-transformed module file whose imports are in the
|
|
1179
|
+
* form `import * as ns from './N.js'`. Looks up each module ID `N` in
|
|
1180
|
+
* `libModuleMap`, rewrites call-sites from `(0, ns.hook)(...)` to `hook(...)`,
|
|
1181
|
+
* replaces namespace imports with named imports from the real library path, and
|
|
1182
|
+
* prunes unused specifiers.
|
|
1183
|
+
*
|
|
1184
|
+
* Returns the rewritten statement list (imports first, then body).
|
|
1185
|
+
* If `libModuleMap` is empty / not provided, returns `statements` unchanged.
|
|
1186
|
+
*/
|
|
1187
|
+
export const applyLibraryImportRewriting = (statements, libModuleMap) => {
|
|
1188
|
+
if (!libModuleMap || libModuleMap.size === 0)
|
|
1189
|
+
return statements;
|
|
1190
|
+
// Separate existing import declarations from body statements
|
|
1191
|
+
const importDecls = [];
|
|
1192
|
+
const bodyStmts = [];
|
|
1193
|
+
for (const stmt of statements) {
|
|
1194
|
+
if (t.isImportDeclaration(stmt))
|
|
1195
|
+
importDecls.push(stmt);
|
|
1196
|
+
else
|
|
1197
|
+
bodyStmts.push(stmt);
|
|
1198
|
+
}
|
|
1199
|
+
// Build varName → moduleId from `import * as ns from './N.js'`
|
|
1200
|
+
const varToModuleId = new Map(); // ns → numericModuleId
|
|
1201
|
+
for (const decl of importDecls) {
|
|
1202
|
+
const source = decl.source.value;
|
|
1203
|
+
const match = source.match(/^\.\/(\d+)\.js$/);
|
|
1204
|
+
if (!match)
|
|
1205
|
+
continue;
|
|
1206
|
+
const numId = parseInt(match[1], 10);
|
|
1207
|
+
for (const spec of decl.specifiers) {
|
|
1208
|
+
if (t.isImportNamespaceSpecifier(spec)) {
|
|
1209
|
+
varToModuleId.set(spec.local.name, numId);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
// Build varName → LibraryModuleInfo
|
|
1214
|
+
const varToLib = new Map();
|
|
1215
|
+
for (const [varName, numId] of varToModuleId) {
|
|
1216
|
+
const info = libModuleMap.get(String(numId));
|
|
1217
|
+
if (info)
|
|
1218
|
+
varToLib.set(varName, info);
|
|
1219
|
+
}
|
|
1220
|
+
if (varToLib.size === 0)
|
|
1221
|
+
return statements;
|
|
1222
|
+
// --- CSS injection stripping ---
|
|
1223
|
+
// Collect CSS-typed import vars (style-loader, css-module)
|
|
1224
|
+
const cssImportVars = new Set();
|
|
1225
|
+
for (const [varName, info] of varToLib) {
|
|
1226
|
+
if (info.type === "style-loader" || info.type === "css-module") {
|
|
1227
|
+
cssImportVars.add(varName);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
if (cssImportVars.size > 0) {
|
|
1231
|
+
const CSS_INJECT_PROPS = new Set([
|
|
1232
|
+
"styleTagTransform",
|
|
1233
|
+
"setAttributes",
|
|
1234
|
+
"insert",
|
|
1235
|
+
"domAPI",
|
|
1236
|
+
"insertStyleElement",
|
|
1237
|
+
]);
|
|
1238
|
+
// Find CSS wrapper vars: var x = <expr>.n(libVar) for any library import.
|
|
1239
|
+
// webpack's .n() (interopRequireDefault) only appears in the CSS injection
|
|
1240
|
+
// setup in React apps; it's safe to strip all library-import .n() wrappers.
|
|
1241
|
+
const allLibVars = new Set(varToLib.keys());
|
|
1242
|
+
const cssWrapperVars = new Set();
|
|
1243
|
+
for (const stmt of bodyStmts) {
|
|
1244
|
+
if (!t.isVariableDeclaration(stmt))
|
|
1245
|
+
continue;
|
|
1246
|
+
for (const decl of stmt.declarations) {
|
|
1247
|
+
if (!t.isIdentifier(decl.id) || !decl.init)
|
|
1248
|
+
continue;
|
|
1249
|
+
if (!t.isCallExpression(decl.init))
|
|
1250
|
+
continue;
|
|
1251
|
+
const callee = decl.init.callee;
|
|
1252
|
+
const args = decl.init.arguments;
|
|
1253
|
+
if (t.isMemberExpression(callee) &&
|
|
1254
|
+
!callee.computed &&
|
|
1255
|
+
t.isIdentifier(callee.property, { name: "n" }) &&
|
|
1256
|
+
args.length === 1 &&
|
|
1257
|
+
t.isIdentifier(args[0]) &&
|
|
1258
|
+
allLibVars.has(args[0].name)) {
|
|
1259
|
+
cssWrapperVars.add(decl.id.name);
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
// Find CSS config object vars: var g = {} where g is assigned CSS injection props
|
|
1264
|
+
const cssConfigObjVars = new Set();
|
|
1265
|
+
for (const stmt of bodyStmts) {
|
|
1266
|
+
if (!t.isExpressionStatement(stmt))
|
|
1267
|
+
continue;
|
|
1268
|
+
const expr = stmt.expression;
|
|
1269
|
+
if (!t.isAssignmentExpression(expr))
|
|
1270
|
+
continue;
|
|
1271
|
+
const lhs = expr.left;
|
|
1272
|
+
if (!t.isMemberExpression(lhs) || lhs.computed)
|
|
1273
|
+
continue;
|
|
1274
|
+
const prop = lhs.property;
|
|
1275
|
+
if (t.isIdentifier(prop) && CSS_INJECT_PROPS.has(prop.name)) {
|
|
1276
|
+
if (t.isIdentifier(lhs.object))
|
|
1277
|
+
cssConfigObjVars.add(lhs.object.name);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
const isCssMemberRef = (e) => {
|
|
1281
|
+
if (!t.isMemberExpression(e) || e.computed)
|
|
1282
|
+
return false;
|
|
1283
|
+
return (t.isIdentifier(e.object) &&
|
|
1284
|
+
cssImportVars.has(e.object.name));
|
|
1285
|
+
};
|
|
1286
|
+
const hasLogicalCssRef = (e) => {
|
|
1287
|
+
if (isCssMemberRef(e))
|
|
1288
|
+
return true;
|
|
1289
|
+
if (t.isLogicalExpression(e))
|
|
1290
|
+
return (hasLogicalCssRef(e.left) ||
|
|
1291
|
+
hasLogicalCssRef(e.right));
|
|
1292
|
+
return false;
|
|
1293
|
+
};
|
|
1294
|
+
const isCssInjectionStmt = (stmt) => {
|
|
1295
|
+
if (!t.isExpressionStatement(stmt))
|
|
1296
|
+
return false;
|
|
1297
|
+
const expr = stmt.expression;
|
|
1298
|
+
// g.styleTagTransform = ..., g.setAttributes = ..., etc.
|
|
1299
|
+
if (t.isAssignmentExpression(expr) &&
|
|
1300
|
+
t.isMemberExpression(expr.left) &&
|
|
1301
|
+
!expr.left.computed &&
|
|
1302
|
+
t.isIdentifier(expr.left.property) &&
|
|
1303
|
+
CSS_INJECT_PROPS.has(expr.left.property.name))
|
|
1304
|
+
return true;
|
|
1305
|
+
// cssWrapperVar()(cssModuleVar.A, configObj)
|
|
1306
|
+
if (t.isCallExpression(expr) && t.isCallExpression(expr.callee)) {
|
|
1307
|
+
const innerCallee = expr.callee.callee;
|
|
1308
|
+
if (t.isIdentifier(innerCallee) && cssWrapperVars.has(innerCallee.name))
|
|
1309
|
+
return true;
|
|
1310
|
+
}
|
|
1311
|
+
// cssModuleVar.A && cssModuleVar.A.locals && cssModuleVar.A.locals
|
|
1312
|
+
if (t.isLogicalExpression(expr) && hasLogicalCssRef(expr))
|
|
1313
|
+
return true;
|
|
1314
|
+
return false;
|
|
1315
|
+
};
|
|
1316
|
+
const filteredStmts = [];
|
|
1317
|
+
for (const stmt of bodyStmts) {
|
|
1318
|
+
if (isCssInjectionStmt(stmt))
|
|
1319
|
+
continue;
|
|
1320
|
+
if (t.isVariableDeclaration(stmt)) {
|
|
1321
|
+
const remaining = stmt.declarations.filter((decl) => {
|
|
1322
|
+
if (!t.isIdentifier(decl.id))
|
|
1323
|
+
return true;
|
|
1324
|
+
const name = decl.id.name;
|
|
1325
|
+
if (cssWrapperVars.has(name))
|
|
1326
|
+
return false;
|
|
1327
|
+
if (cssConfigObjVars.has(name) &&
|
|
1328
|
+
decl.init &&
|
|
1329
|
+
t.isObjectExpression(decl.init) &&
|
|
1330
|
+
decl.init.properties.length === 0)
|
|
1331
|
+
return false;
|
|
1332
|
+
return true;
|
|
1333
|
+
});
|
|
1334
|
+
if (remaining.length === 0)
|
|
1335
|
+
continue;
|
|
1336
|
+
filteredStmts.push(remaining.length < stmt.declarations.length ? t.variableDeclaration(stmt.kind, remaining) : stmt);
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
filteredStmts.push(stmt);
|
|
1340
|
+
}
|
|
1341
|
+
bodyStmts.splice(0, bodyStmts.length, ...filteredStmts);
|
|
1342
|
+
}
|
|
1343
|
+
// Rewrite call-sites in bodyStmts; collect used exports per library
|
|
1344
|
+
const usedExports = rewriteLibraryCalls(bodyStmts, varToLib);
|
|
1345
|
+
// Build replacement import declarations
|
|
1346
|
+
const finalImportStmts = [];
|
|
1347
|
+
const handledSpecs = new Set();
|
|
1348
|
+
for (const [varName, info] of varToLib) {
|
|
1349
|
+
const src = librarySource(info.type);
|
|
1350
|
+
if (!src)
|
|
1351
|
+
continue;
|
|
1352
|
+
if (!handledSpecs.has(src)) {
|
|
1353
|
+
handledSpecs.add(src);
|
|
1354
|
+
const used = usedExports.get(src);
|
|
1355
|
+
if (used && used.size > 0) {
|
|
1356
|
+
const specifiers = [...used]
|
|
1357
|
+
.sort()
|
|
1358
|
+
.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
|
|
1359
|
+
finalImportStmts.push(t.importDeclaration(specifiers, t.stringLiteral(src)));
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
// Keep non-library namespace imports unchanged.
|
|
1364
|
+
// For library namespace imports: keep only if the namespace variable is still used as
|
|
1365
|
+
// the object of a MemberExpression or JSXMemberExpression (i.e., namespace access).
|
|
1366
|
+
// Using collectReferencedNames would falsely keep imports when a local variable in a
|
|
1367
|
+
// closure happens to share the namespace name (e.g. `const n = e.toLowerCase()`).
|
|
1368
|
+
const namespaceObjRefs = new Set();
|
|
1369
|
+
{
|
|
1370
|
+
const syntheticFile = t.file(t.program(bodyStmts, [], "module"));
|
|
1371
|
+
traverse(syntheticFile, {
|
|
1372
|
+
MemberExpression(p) {
|
|
1373
|
+
if (!p.node.computed && t.isIdentifier(p.node.object)) {
|
|
1374
|
+
namespaceObjRefs.add(p.node.object.name);
|
|
1375
|
+
}
|
|
1376
|
+
},
|
|
1377
|
+
JSXMemberExpression(p) {
|
|
1378
|
+
if (t.isJSXIdentifier(p.node.object)) {
|
|
1379
|
+
namespaceObjRefs.add(p.node.object.name);
|
|
1380
|
+
}
|
|
1381
|
+
},
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
for (const decl of importDecls) {
|
|
1385
|
+
const source = decl.source.value;
|
|
1386
|
+
const match = source.match(/^\.\/(\d+)\.js$/);
|
|
1387
|
+
if (!match) {
|
|
1388
|
+
finalImportStmts.push(decl);
|
|
1389
|
+
continue;
|
|
1390
|
+
}
|
|
1391
|
+
const numId = parseInt(match[1], 10);
|
|
1392
|
+
const isLibrary = [...varToModuleId.entries()].some(([vn, id]) => id === numId && varToLib.has(vn));
|
|
1393
|
+
if (!isLibrary) {
|
|
1394
|
+
finalImportStmts.push(decl);
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
// Library module: keep the namespace import only if the namespace var is
|
|
1398
|
+
// still used as a namespace object (i.e. some exports were not resolved).
|
|
1399
|
+
const hasRemainingRefs = decl.specifiers.some((spec) => t.isImportNamespaceSpecifier(spec) && namespaceObjRefs.has(spec.local.name));
|
|
1400
|
+
if (hasRemainingRefs)
|
|
1401
|
+
finalImportStmts.push(decl);
|
|
1402
|
+
}
|
|
1403
|
+
// Pass H — prune named imports that are no longer referenced
|
|
1404
|
+
const prunedImports = pruneUnusedNamedImports(finalImportStmts, bodyStmts);
|
|
1405
|
+
return [...prunedImports, ...bodyStmts];
|
|
1406
|
+
};
|
|
1407
|
+
// ---------------------------------------------------------------------------
|
|
1408
|
+
// Module cleanup passes — applies E/F/G standalone (no webpack require context needed)
|
|
1409
|
+
// ---------------------------------------------------------------------------
|
|
1410
|
+
/**
|
|
1411
|
+
* Applies JSX recovery, slicedToArray collapse, and Babel helper removal to
|
|
1412
|
+
* any list of statements. Used for both individual module files and index.js.
|
|
1413
|
+
* Does NOT require a webpack require-function name or library module map.
|
|
1414
|
+
*/
|
|
1415
|
+
export const applyModuleCleanupPasses = (statements) => {
|
|
1416
|
+
// Pass E — collapse Babel slicedToArray expansions.
|
|
1417
|
+
const afterE = collapseSlicedToArrayDeep(statements);
|
|
1418
|
+
// Pass F — JSX recovery.
|
|
1419
|
+
recoverJSX(afterE);
|
|
1420
|
+
// Pass G — remove top-level Babel helper functions and webpack module-cache vars.
|
|
1421
|
+
const afterG = dropEmptyObjectVars(afterE.filter((stmt) => !isBabelArrayLikeToArrayHelper(stmt) &&
|
|
1422
|
+
!isBabelTypeofHelper(stmt) &&
|
|
1423
|
+
!isBabelDefinePropertyHelper(stmt) &&
|
|
1424
|
+
!isBabelObjectSpreadHelper(stmt) &&
|
|
1425
|
+
!isBabelObjectSpread2Helper(stmt) &&
|
|
1426
|
+
!isBabelOwnKeysHelper(stmt) &&
|
|
1427
|
+
!isNamedBabelArrayHelper(stmt)));
|
|
1428
|
+
return afterG;
|
|
1429
|
+
};
|
|
1430
|
+
// ---------------------------------------------------------------------------
|
|
1431
|
+
// Route-aware component renaming
|
|
1432
|
+
// ---------------------------------------------------------------------------
|
|
1433
|
+
/** Derives a component name from a React Router path string. */
|
|
1434
|
+
function pathToComponentName(fullPath) {
|
|
1435
|
+
if (fullPath === "/" || fullPath === "")
|
|
1436
|
+
return "Home";
|
|
1437
|
+
const isIndex = fullPath.endsWith("/index");
|
|
1438
|
+
const base = isIndex ? fullPath.slice(0, -"/index".length) : fullPath;
|
|
1439
|
+
const segments = base
|
|
1440
|
+
.replace(/^\//, "")
|
|
1441
|
+
.split("/")
|
|
1442
|
+
.filter((s) => s && !s.startsWith(":"));
|
|
1443
|
+
if (segments.length === 0)
|
|
1444
|
+
return "Home";
|
|
1445
|
+
const name = segments
|
|
1446
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1).replace(/-(\w)/g, (_, c) => c.toUpperCase()))
|
|
1447
|
+
.join("");
|
|
1448
|
+
return isIndex ? `${name}Dashboard` : name;
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Post-pass that renames minified component variables to meaningful names derived
|
|
1452
|
+
* from React Router route paths. Intended to run after JSX recovery (Pass F) so
|
|
1453
|
+
* that `<Route path="..." element={<X />} />` patterns are visible as JSX.
|
|
1454
|
+
*
|
|
1455
|
+
* Also renames:
|
|
1456
|
+
* - The root App component (the one that contains `<Routes>`)
|
|
1457
|
+
* - The Suspense fallback component (if it's a locally-defined function)
|
|
1458
|
+
*/
|
|
1459
|
+
export const renameRouteComponents = (statements) => {
|
|
1460
|
+
// Step 1: collect lazy-import variable names → spec (e.g. '_' → './45.js')
|
|
1461
|
+
const lazyVars = new Map(); // varName → importSpec
|
|
1462
|
+
for (const stmt of statements) {
|
|
1463
|
+
if (!t.isVariableDeclaration(stmt))
|
|
1464
|
+
continue;
|
|
1465
|
+
for (const decl of stmt.declarations) {
|
|
1466
|
+
if (!t.isIdentifier(decl.id) || !decl.init)
|
|
1467
|
+
continue;
|
|
1468
|
+
if (!t.isCallExpression(decl.init))
|
|
1469
|
+
continue;
|
|
1470
|
+
const callExpr = decl.init;
|
|
1471
|
+
if (!t.isIdentifier(callExpr.callee, { name: "lazy" }))
|
|
1472
|
+
continue;
|
|
1473
|
+
if (callExpr.arguments.length !== 1)
|
|
1474
|
+
continue;
|
|
1475
|
+
const arg = callExpr.arguments[0];
|
|
1476
|
+
if (!t.isArrowFunctionExpression(arg) && !t.isFunctionExpression(arg))
|
|
1477
|
+
continue;
|
|
1478
|
+
const body = arg.body;
|
|
1479
|
+
// body is `import('./N.js')` — a CallExpression with an Import callee
|
|
1480
|
+
const importCall = t.isBlockStatement(body)
|
|
1481
|
+
? body.body.length === 1 &&
|
|
1482
|
+
t.isReturnStatement(body.body[0])
|
|
1483
|
+
? body.body[0].argument
|
|
1484
|
+
: null
|
|
1485
|
+
: body;
|
|
1486
|
+
if (!importCall || !t.isCallExpression(importCall))
|
|
1487
|
+
continue;
|
|
1488
|
+
if (!t.isImport(importCall.callee))
|
|
1489
|
+
continue;
|
|
1490
|
+
const importArgs = importCall.arguments;
|
|
1491
|
+
if (importArgs.length !== 1 || !t.isStringLiteral(importArgs[0]))
|
|
1492
|
+
continue;
|
|
1493
|
+
lazyVars.set(decl.id.name, importArgs[0].value);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
if (lazyVars.size === 0)
|
|
1497
|
+
return statements;
|
|
1498
|
+
// Step 2: traverse JSX Route elements, tracking parent route paths for full-path assembly.
|
|
1499
|
+
const varToPath = new Map(); // varName → full route path
|
|
1500
|
+
const syntheticFile = t.file(t.program(statements, [], "module"));
|
|
1501
|
+
const pathStack = [];
|
|
1502
|
+
traverse(syntheticFile, {
|
|
1503
|
+
JSXElement: {
|
|
1504
|
+
enter(p) {
|
|
1505
|
+
const opening = p.node.openingElement;
|
|
1506
|
+
if (!t.isJSXIdentifier(opening.name, { name: "Route" }))
|
|
1507
|
+
return;
|
|
1508
|
+
let routePath = null;
|
|
1509
|
+
let isIndex = false;
|
|
1510
|
+
let elementVarName = null;
|
|
1511
|
+
for (const attr of opening.attributes) {
|
|
1512
|
+
if (!t.isJSXAttribute(attr))
|
|
1513
|
+
continue;
|
|
1514
|
+
const keyName = t.isJSXIdentifier(attr.name) ? attr.name.name : null;
|
|
1515
|
+
if (keyName === "path" && t.isStringLiteral(attr.value)) {
|
|
1516
|
+
routePath = attr.value.value;
|
|
1517
|
+
}
|
|
1518
|
+
else if (keyName === "index") {
|
|
1519
|
+
isIndex = true;
|
|
1520
|
+
}
|
|
1521
|
+
else if (keyName === "element") {
|
|
1522
|
+
const val = attr.value;
|
|
1523
|
+
const jsxEl = t.isJSXExpressionContainer(val)
|
|
1524
|
+
? t.isJSXElement(val.expression)
|
|
1525
|
+
? val.expression
|
|
1526
|
+
: null
|
|
1527
|
+
: t.isJSXElement(val)
|
|
1528
|
+
? val
|
|
1529
|
+
: null;
|
|
1530
|
+
if (jsxEl) {
|
|
1531
|
+
const tagName = jsxEl.openingElement.name;
|
|
1532
|
+
if (t.isJSXIdentifier(tagName))
|
|
1533
|
+
elementVarName = tagName.name;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
const parentPath = pathStack.length > 0 ? pathStack[pathStack.length - 1] : "";
|
|
1538
|
+
let fullPath;
|
|
1539
|
+
if (routePath !== null) {
|
|
1540
|
+
fullPath = routePath.startsWith("/") ? routePath : `${parentPath}/${routePath}`;
|
|
1541
|
+
}
|
|
1542
|
+
else if (isIndex) {
|
|
1543
|
+
fullPath = `${parentPath}/index`;
|
|
1544
|
+
}
|
|
1545
|
+
else {
|
|
1546
|
+
fullPath = parentPath;
|
|
1547
|
+
}
|
|
1548
|
+
pathStack.push(fullPath);
|
|
1549
|
+
if (elementVarName && lazyVars.has(elementVarName)) {
|
|
1550
|
+
varToPath.set(elementVarName, fullPath);
|
|
1551
|
+
}
|
|
1552
|
+
},
|
|
1553
|
+
exit(p) {
|
|
1554
|
+
if (t.isJSXIdentifier(p.node.openingElement.name, { name: "Route" })) {
|
|
1555
|
+
pathStack.pop();
|
|
1556
|
+
}
|
|
1557
|
+
},
|
|
1558
|
+
},
|
|
1559
|
+
});
|
|
1560
|
+
// Step 3: generate semantic names from route paths
|
|
1561
|
+
const renames = new Map(); // oldName → newName
|
|
1562
|
+
const usedNames = new Set();
|
|
1563
|
+
for (const [varName, fullPath] of varToPath) {
|
|
1564
|
+
let name = pathToComponentName(fullPath);
|
|
1565
|
+
if (usedNames.has(name)) {
|
|
1566
|
+
let i = 2;
|
|
1567
|
+
while (usedNames.has(`${name}${i}`))
|
|
1568
|
+
i++;
|
|
1569
|
+
name = `${name}${i}`;
|
|
1570
|
+
}
|
|
1571
|
+
usedNames.add(name);
|
|
1572
|
+
renames.set(varName, name);
|
|
1573
|
+
}
|
|
1574
|
+
// Step 4: find the root App component — the function whose JSX body contains <Routes>
|
|
1575
|
+
traverse(syntheticFile, {
|
|
1576
|
+
JSXElement(p) {
|
|
1577
|
+
if (!t.isJSXIdentifier(p.node.openingElement.name, { name: "Routes" }))
|
|
1578
|
+
return;
|
|
1579
|
+
let ancestor = p.parentPath;
|
|
1580
|
+
while (ancestor) {
|
|
1581
|
+
if (ancestor.isFunctionDeclaration() ||
|
|
1582
|
+
ancestor.isFunctionExpression() ||
|
|
1583
|
+
ancestor.isArrowFunctionExpression()) {
|
|
1584
|
+
if (ancestor.isFunctionDeclaration()) {
|
|
1585
|
+
const id = ancestor.node.id;
|
|
1586
|
+
if (id && !renames.has(id.name))
|
|
1587
|
+
renames.set(id.name, "App");
|
|
1588
|
+
}
|
|
1589
|
+
else {
|
|
1590
|
+
const par = ancestor.parentPath;
|
|
1591
|
+
if (par === null || par === void 0 ? void 0 : par.isVariableDeclarator()) {
|
|
1592
|
+
const declId = par.node.id;
|
|
1593
|
+
if (t.isIdentifier(declId) && !renames.has(declId.name)) {
|
|
1594
|
+
renames.set(declId.name, "App");
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
break;
|
|
1599
|
+
}
|
|
1600
|
+
if (!ancestor.parentPath)
|
|
1601
|
+
break;
|
|
1602
|
+
ancestor = ancestor.parentPath;
|
|
1603
|
+
}
|
|
1604
|
+
p.skip();
|
|
1605
|
+
},
|
|
1606
|
+
});
|
|
1607
|
+
// Step 5: find the Suspense fallback component
|
|
1608
|
+
traverse(syntheticFile, {
|
|
1609
|
+
JSXAttribute(p) {
|
|
1610
|
+
if (!t.isJSXIdentifier(p.node.name, { name: "fallback" }))
|
|
1611
|
+
return;
|
|
1612
|
+
const val = p.node.value;
|
|
1613
|
+
if (!t.isJSXExpressionContainer(val))
|
|
1614
|
+
return;
|
|
1615
|
+
const expr = val.expression;
|
|
1616
|
+
if (!t.isJSXElement(expr))
|
|
1617
|
+
return;
|
|
1618
|
+
const tagName = expr.openingElement.name;
|
|
1619
|
+
if (!t.isJSXIdentifier(tagName))
|
|
1620
|
+
return;
|
|
1621
|
+
const varName = tagName.name;
|
|
1622
|
+
if (!renames.has(varName) && !lazyVars.has(varName)) {
|
|
1623
|
+
renames.set(varName, "Loading");
|
|
1624
|
+
}
|
|
1625
|
+
},
|
|
1626
|
+
});
|
|
1627
|
+
if (renames.size === 0)
|
|
1628
|
+
return statements;
|
|
1629
|
+
// Step 6: rename bindings via scope analysis, then patch any remaining JSXIdentifiers
|
|
1630
|
+
traverse(syntheticFile, {
|
|
1631
|
+
Program(p) {
|
|
1632
|
+
for (const [oldName, newName] of renames) {
|
|
1633
|
+
if (p.scope.hasBinding(oldName)) {
|
|
1634
|
+
p.scope.rename(oldName, newName);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
},
|
|
1638
|
+
});
|
|
1639
|
+
// JSXIdentifier nodes may not be tracked by scope — patch them explicitly as a fallback
|
|
1640
|
+
traverse(syntheticFile, {
|
|
1641
|
+
JSXIdentifier(p) {
|
|
1642
|
+
const newName = renames.get(p.node.name);
|
|
1643
|
+
if (newName)
|
|
1644
|
+
p.node.name = newName;
|
|
1645
|
+
},
|
|
1646
|
+
});
|
|
1647
|
+
return statements;
|
|
1648
|
+
};
|
|
1649
|
+
// ---------------------------------------------------------------------------
|
|
1650
|
+
// Main export
|
|
1651
|
+
// ---------------------------------------------------------------------------
|
|
1652
|
+
/**
|
|
1653
|
+
* Transforms the non-module-map IIFE statements that become `index.js`.
|
|
1654
|
+
*
|
|
1655
|
+
* Passes:
|
|
1656
|
+
* A – Remove webpack require helper function.
|
|
1657
|
+
* B – Hoist top-level `var x = requireFn(N)` to static `import * as x from "./N.js"`.
|
|
1658
|
+
* C – Replace remaining inline `requireFn(N)` calls.
|
|
1659
|
+
* D – Rewrite library module calls using identity map (e.g. `l.H(...)` → `createRoot(...)`)
|
|
1660
|
+
* and replace namespace imports with named imports from proper library paths.
|
|
1661
|
+
* E – Collapse Babel slicedToArray expansions to `const [a, b] = expr`.
|
|
1662
|
+
* F – Recover JSX from `jsx(tag, props)` / `jsxs(tag, props)` calls.
|
|
1663
|
+
* G – Remove Babel array-helper functions (e.g. `arrayLikeToArray`).
|
|
1664
|
+
*/
|
|
1665
|
+
export const transformIndexStatements = (statements, libModuleMap) => {
|
|
1666
|
+
// Pass A — detect and remove webpack require helper(s).
|
|
1667
|
+
const requireFnNames = new Set();
|
|
1668
|
+
const afterA = [];
|
|
1669
|
+
for (const stmt of statements) {
|
|
1670
|
+
if (t.isFunctionDeclaration(stmt) && stmt.id && isWebpackRequireHelper(stmt)) {
|
|
1671
|
+
requireFnNames.add(stmt.id.name);
|
|
1672
|
+
continue; // drop
|
|
1673
|
+
}
|
|
1674
|
+
afterA.push(stmt);
|
|
1675
|
+
}
|
|
1676
|
+
if (requireFnNames.size === 0)
|
|
1677
|
+
return statements; // nothing to do
|
|
1678
|
+
// Pass A.1 — strip webpack runtime bootstrap statements.
|
|
1679
|
+
// Any statement that references requireFn.X (assignments, calls, or var inits)
|
|
1680
|
+
// is webpack infrastructure. Also strip the runtime temp-var declarations that
|
|
1681
|
+
// have no init / empty-collection init and 3+ declarators.
|
|
1682
|
+
const containsRequireFnMember = (node, fnName) => {
|
|
1683
|
+
if (t.isMemberExpression(node) && t.isIdentifier(node.object, { name: fnName }))
|
|
1684
|
+
return true;
|
|
1685
|
+
for (const key of Object.keys(node)) {
|
|
1686
|
+
const child = node[key];
|
|
1687
|
+
if (child && typeof child === "object") {
|
|
1688
|
+
if (Array.isArray(child)) {
|
|
1689
|
+
for (const item of child) {
|
|
1690
|
+
if (item &&
|
|
1691
|
+
typeof item.type === "string" &&
|
|
1692
|
+
containsRequireFnMember(item, fnName))
|
|
1693
|
+
return true;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
else if (typeof child.type === "string") {
|
|
1697
|
+
if (containsRequireFnMember(child, fnName))
|
|
1698
|
+
return true;
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
return false;
|
|
1703
|
+
};
|
|
1704
|
+
const isRuntimeTempVarDecl = (stmt) => {
|
|
1705
|
+
if (!t.isVariableDeclaration(stmt) || stmt.declarations.length < 3)
|
|
1706
|
+
return false;
|
|
1707
|
+
return stmt.declarations.every((decl) => {
|
|
1708
|
+
if (!decl.init)
|
|
1709
|
+
return true;
|
|
1710
|
+
if (t.isObjectExpression(decl.init) && decl.init.properties.length === 0)
|
|
1711
|
+
return true;
|
|
1712
|
+
if (t.isArrayExpression(decl.init) && decl.init.elements.length === 0)
|
|
1713
|
+
return true;
|
|
1714
|
+
if (t.isUnaryExpression(decl.init) && decl.init.operator === "void")
|
|
1715
|
+
return true;
|
|
1716
|
+
return false;
|
|
1717
|
+
});
|
|
1718
|
+
};
|
|
1719
|
+
const afterA1 = [];
|
|
1720
|
+
for (const stmt of afterA) {
|
|
1721
|
+
let isRuntime = isRuntimeTempVarDecl(stmt);
|
|
1722
|
+
if (!isRuntime) {
|
|
1723
|
+
for (const fnName of requireFnNames) {
|
|
1724
|
+
if (containsRequireFnMember(stmt, fnName)) {
|
|
1725
|
+
isRuntime = true;
|
|
1726
|
+
break;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
if (!isRuntime)
|
|
1731
|
+
afterA1.push(stmt);
|
|
1732
|
+
}
|
|
1733
|
+
const hoistedImports = new Map();
|
|
1734
|
+
const importNameByNumId = new Map();
|
|
1735
|
+
// Pass B — hoist top-level `var x = requireFn(N)` to static imports.
|
|
1736
|
+
// Also build varName → moduleId so Pass D can look up library identity.
|
|
1737
|
+
const varToModuleId = new Map(); // localVarName → numericModuleId
|
|
1738
|
+
const afterB = [];
|
|
1739
|
+
for (const stmt of afterA1) {
|
|
1740
|
+
if (!t.isVariableDeclaration(stmt)) {
|
|
1741
|
+
afterB.push(stmt);
|
|
1742
|
+
continue;
|
|
1743
|
+
}
|
|
1744
|
+
const kept = [];
|
|
1745
|
+
for (const decl of stmt.declarations) {
|
|
1746
|
+
if (!t.isIdentifier(decl.id) || !decl.init) {
|
|
1747
|
+
kept.push(decl);
|
|
1748
|
+
continue;
|
|
1749
|
+
}
|
|
1750
|
+
let matched = false;
|
|
1751
|
+
for (const fnName of requireFnNames) {
|
|
1752
|
+
const numId = tryExtractRequireCall(decl.init, fnName);
|
|
1753
|
+
if (numId === null)
|
|
1754
|
+
continue;
|
|
1755
|
+
const spec = `./${numId}.js`;
|
|
1756
|
+
const localName = decl.id.name;
|
|
1757
|
+
if (!hoistedImports.has(spec)) {
|
|
1758
|
+
hoistedImports.set(spec, localName);
|
|
1759
|
+
importNameByNumId.set(numId, localName);
|
|
1760
|
+
}
|
|
1761
|
+
varToModuleId.set(localName, numId);
|
|
1762
|
+
matched = true;
|
|
1763
|
+
break;
|
|
1764
|
+
}
|
|
1765
|
+
if (!matched)
|
|
1766
|
+
kept.push(decl);
|
|
1767
|
+
}
|
|
1768
|
+
if (kept.length > 0)
|
|
1769
|
+
afterB.push(t.variableDeclaration(stmt.kind, kept));
|
|
1770
|
+
}
|
|
1771
|
+
// Pass C — replace remaining inline requireFn(N) calls recursively.
|
|
1772
|
+
const syntheticFile = t.file(t.program(afterB, [], "module"));
|
|
1773
|
+
traverse(syntheticFile, {
|
|
1774
|
+
CallExpression(p) {
|
|
1775
|
+
for (const fnName of requireFnNames) {
|
|
1776
|
+
const numId = tryExtractRequireCall(p.node, fnName);
|
|
1777
|
+
if (numId === null)
|
|
1778
|
+
continue;
|
|
1779
|
+
let name = importNameByNumId.get(numId);
|
|
1780
|
+
if (!name) {
|
|
1781
|
+
name = `_jsr_module_${numId}`;
|
|
1782
|
+
hoistedImports.set(`./${numId}.js`, name);
|
|
1783
|
+
importNameByNumId.set(numId, name);
|
|
1784
|
+
}
|
|
1785
|
+
p.replaceWith(t.identifier(name));
|
|
1786
|
+
p.skip();
|
|
1787
|
+
break;
|
|
1788
|
+
}
|
|
1789
|
+
},
|
|
1790
|
+
});
|
|
1791
|
+
// Pass D — library-aware import rewriting (only when libModuleMap is supplied).
|
|
1792
|
+
// Build varName → LibraryModuleInfo, then rewrite calls and imports.
|
|
1793
|
+
const finalImportStmts = [];
|
|
1794
|
+
if (libModuleMap && libModuleMap.size > 0) {
|
|
1795
|
+
// Map local var names to their library identity
|
|
1796
|
+
const varToLib = new Map();
|
|
1797
|
+
for (const [varName, numId] of varToModuleId) {
|
|
1798
|
+
const info = libModuleMap.get(String(numId));
|
|
1799
|
+
if (info)
|
|
1800
|
+
varToLib.set(varName, info);
|
|
1801
|
+
}
|
|
1802
|
+
if (varToLib.size > 0) {
|
|
1803
|
+
// Rewrite call-sites; collect which named exports are actually used
|
|
1804
|
+
const usedExports = rewriteLibraryCalls(afterB, varToLib);
|
|
1805
|
+
// Build import declarations for library modules (named imports).
|
|
1806
|
+
// handledSpecs guards only the import-declaration emit (avoid duplicates when
|
|
1807
|
+
// two local vars map to the same library source). The namespace-import deletion
|
|
1808
|
+
// must happen for EVERY var that maps to a library, regardless of dedup.
|
|
1809
|
+
const handledSpecs = new Set();
|
|
1810
|
+
for (const [varName, info] of varToLib) {
|
|
1811
|
+
const src = librarySource(info.type);
|
|
1812
|
+
if (!src)
|
|
1813
|
+
continue;
|
|
1814
|
+
if (!handledSpecs.has(src)) {
|
|
1815
|
+
handledSpecs.add(src);
|
|
1816
|
+
const used = usedExports.get(src);
|
|
1817
|
+
if (used && used.size > 0) {
|
|
1818
|
+
const specifiers = [...used]
|
|
1819
|
+
.sort()
|
|
1820
|
+
.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
|
|
1821
|
+
finalImportStmts.push(t.importDeclaration(specifiers, t.stringLiteral(src)));
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
// Always remove the namespace import for this var, even if src was already handled.
|
|
1825
|
+
const numId = varToModuleId.get(varName);
|
|
1826
|
+
if (numId !== undefined)
|
|
1827
|
+
hoistedImports.delete(`./${numId}.js`);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
// Remaining (non-library) namespace imports
|
|
1832
|
+
for (const [spec, name] of hoistedImports) {
|
|
1833
|
+
finalImportStmts.push(t.importDeclaration([t.importNamespaceSpecifier(t.identifier(name))], t.stringLiteral(spec)));
|
|
1834
|
+
}
|
|
1835
|
+
// Pass E — collapse Babel slicedToArray expansions.
|
|
1836
|
+
const afterE = collapseSlicedToArrayDeep(afterB);
|
|
1837
|
+
// Pass F — JSX recovery.
|
|
1838
|
+
recoverJSX(afterE);
|
|
1839
|
+
// Pass G — remove top-level Babel helper functions and webpack module-cache vars.
|
|
1840
|
+
const afterG = dropEmptyObjectVars(afterE.filter((stmt) => !isBabelArrayLikeToArrayHelper(stmt) &&
|
|
1841
|
+
!isBabelTypeofHelper(stmt) &&
|
|
1842
|
+
!isBabelDefinePropertyHelper(stmt) &&
|
|
1843
|
+
!isBabelObjectSpreadHelper(stmt) &&
|
|
1844
|
+
!isBabelObjectSpread2Helper(stmt) &&
|
|
1845
|
+
!isBabelOwnKeysHelper(stmt) &&
|
|
1846
|
+
!isNamedBabelArrayHelper(stmt)));
|
|
1847
|
+
// Pass H — prune named imports whose local name is no longer referenced
|
|
1848
|
+
// (e.g. jsx/jsxs after JSX recovery, namespace imports that were cleared).
|
|
1849
|
+
const prunedImports = pruneUnusedNamedImports(finalImportStmts, afterG);
|
|
1850
|
+
return [...prunedImports, ...afterG];
|
|
1851
|
+
};
|
|
1852
|
+
//# sourceMappingURL=transform.js.map
|