@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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// ECMAScript export reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
|
|
2
|
+
// ECMAScript import reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
|
|
1
3
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
4
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
5
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -7,630 +9,333 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
9
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
10
|
});
|
|
9
11
|
};
|
|
12
|
+
var _a, _b;
|
|
10
13
|
import chalk from "chalk";
|
|
11
14
|
import parser from "@babel/parser";
|
|
12
15
|
import _traverse from "@babel/traverse";
|
|
13
16
|
import _generator from "@babel/generator";
|
|
14
17
|
import * as t from "@babel/types";
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
import { cs_mast_init } from "@shriyanss/cs-mast";
|
|
19
|
+
import { isInModuleMap } from "./helpers.js";
|
|
20
|
+
import { validateAndFix } from "./validator.js";
|
|
21
|
+
import { transformModule, transformIndexStatements, applyModuleCleanupPasses, applyLibraryImportRewriting, renameRouteComponents, } from "./transform.js";
|
|
22
|
+
import { classifyLibraryModule, resolveReexportChains } from "./library-classify.js";
|
|
23
|
+
const traverse = ((_a = _traverse.default) !== null && _a !== void 0 ? _a : _traverse);
|
|
24
|
+
const generate = (_b = _generator.default) !== null && _b !== void 0 ? _b : _generator;
|
|
25
|
+
// scat config used when computing experiment-baseline signatures (matches
|
|
26
|
+
// refactor_observations/feature-signatures/<feature>/lit-decl-loop-cond/collisions.json).
|
|
27
|
+
const LIB_SIG_SCAT = ["lit", "decl", "loop", "cond"];
|
|
28
|
+
// Returns the body statements of a top-level IIFE (e.g. `(() => { … })()`), or null.
|
|
29
|
+
const findIifeBody = (program) => {
|
|
30
|
+
for (const stmt of program.body) {
|
|
31
|
+
if (!t.isExpressionStatement(stmt))
|
|
32
|
+
continue;
|
|
33
|
+
const expr = stmt.expression;
|
|
34
|
+
if (!t.isCallExpression(expr) || expr.arguments.length !== 0)
|
|
35
|
+
continue;
|
|
36
|
+
const callee = expr.callee;
|
|
37
|
+
if ((t.isArrowFunctionExpression(callee) || t.isFunctionExpression(callee)) &&
|
|
38
|
+
t.isBlockStatement(callee.body)) {
|
|
39
|
+
return callee.body.body;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
};
|
|
44
|
+
// Returns true when stmt is the webpack lazy-chunk push call:
|
|
45
|
+
// (self.webpackChunk...||[]).push([[chunkId,...], {moduleId: fn, ...}])
|
|
46
|
+
const isLazyChunkPushStmt = (stmt) => {
|
|
47
|
+
if (!t.isExpressionStatement(stmt))
|
|
48
|
+
return false;
|
|
49
|
+
const expr = stmt.expression;
|
|
50
|
+
if (!t.isCallExpression(expr))
|
|
51
|
+
return false;
|
|
52
|
+
const callee = expr.callee;
|
|
53
|
+
return t.isMemberExpression(callee) && t.isIdentifier(callee.property, { name: "push" });
|
|
54
|
+
};
|
|
55
|
+
// Returns true when a VariableDeclarator's init is the webpack numeric module map
|
|
56
|
+
// object (every property has a NumericLiteral key and a function value).
|
|
57
|
+
const isModuleMapDeclarator = (d) => {
|
|
58
|
+
if (!t.isObjectExpression(d.init))
|
|
59
|
+
return false;
|
|
60
|
+
const props = d.init.properties;
|
|
61
|
+
if (props.length === 0)
|
|
62
|
+
return false;
|
|
63
|
+
return props.every((p) => {
|
|
64
|
+
if (t.isObjectProperty(p))
|
|
65
|
+
return (t.isNumericLiteral(p.key) && (t.isFunctionExpression(p.value) || t.isArrowFunctionExpression(p.value)));
|
|
66
|
+
if (t.isObjectMethod(p))
|
|
67
|
+
return t.isNumericLiteral(p.key);
|
|
68
|
+
return false;
|
|
69
|
+
});
|
|
70
|
+
};
|
|
17
71
|
/**
|
|
18
|
-
*
|
|
72
|
+
* Rewrites a webpack-bundled React chunk by splitting the numeric module map
|
|
73
|
+
* into individual ECMAScript module files.
|
|
19
74
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* - Catalog every inner module by content fingerprint to learn which are
|
|
37
|
-
* `react`, `react/jsx-runtime`, `react-dom/client`.
|
|
38
|
-
* - In each module body, find `var <local> = <require>(<n>)` to learn which
|
|
39
|
-
* local aliases which React module within that scope.
|
|
40
|
-
* - Rewrite `(0, <reactLocal>.<hook>)(args)` → `<hook>(args)` and collect
|
|
41
|
-
* `<hook>` into the file-level react import set. Similarly for jsx-runtime
|
|
42
|
-
* and react-dom/client.
|
|
43
|
-
* - Any remaining `(0, X.Y)(args)` flattens to `X.Y(args)` (always safe).
|
|
44
|
-
*
|
|
45
|
-
* Lossy — for human inspection only.
|
|
75
|
+
* Step 1 – Find the `var X = { <numId>: function(e,n,t){…}, … }` module map.
|
|
76
|
+
* Step 2 – For each module:
|
|
77
|
+
* a) Convert `<moduleParam>.exports = <requireParam>(N)` → `export * from "./N.js"`
|
|
78
|
+
* (and `<moduleParam>.exports = <expr>` → `export default <expr>`),
|
|
79
|
+
* including inside top-level sequence expressions.
|
|
80
|
+
* b) Convert `<exportsParam>.<propName> = <rhs>` → ECMAScript named exports
|
|
81
|
+
* (per MDN export reference) for any module that has an exports param.
|
|
82
|
+
* c) Hoist `var <name> = <requireParam>(N)` to `import * as <name> from "./N.js"`.
|
|
83
|
+
* d) Replace remaining inline `<requireParam>(N)` calls with a synthesized
|
|
84
|
+
* namespace import reference.
|
|
85
|
+
* e) Strip the outer function wrapper.
|
|
86
|
+
* Step 3 – Validate generated code with Babel; iteratively drop/downgrade statements
|
|
87
|
+
* that still cause parse errors.
|
|
88
|
+
* Step 4 – Collect all IIFE body statements that are NOT part of the module map and
|
|
89
|
+
* write them to `index.js` (entrypoint bootstrap, app component functions,
|
|
90
|
+
* the ReactDOM.render call, etc.).
|
|
46
91
|
*/
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
const isLibrarySig = (sig, libSigs) => !!(sig && libSigs && libSigs.has(sig));
|
|
93
|
+
// Minimum fraction of a module's sub-tree signatures that must match the library
|
|
94
|
+
// baseline before the module is classified as library code. A value of 1.0 means
|
|
95
|
+
// every signature must match (too strict — misses partial-lib modules). A value
|
|
96
|
+
// of 0.0 means any single match suffices (too loose — false-positives on app modules
|
|
97
|
+
// that happen to share a helper with library code). 0.5 is a reasonable default:
|
|
98
|
+
// pure library modules score close to 1.0 while app modules with inline CSS helpers
|
|
99
|
+
// or a stray shared utility typically score well below 0.5.
|
|
100
|
+
const LIB_CLASSIFICATION_THRESHOLD = 0.51;
|
|
101
|
+
// Hash a single module's function body using cs_mast_init and look the signature
|
|
102
|
+
// up against the count=18 baseline set. Returns true when the fraction of the
|
|
103
|
+
// module's sub-tree signatures that match the baseline exceeds LIB_CLASSIFICATION_THRESHOLD.
|
|
104
|
+
const moduleIsLibrary = (mod, libSigs, scatOverride) => {
|
|
105
|
+
if (!libSigs || libSigs.size === 0)
|
|
106
|
+
return false;
|
|
107
|
+
const fnNode = mod.fnPath.node;
|
|
108
|
+
if (!t.isBlockStatement(fnNode.body))
|
|
109
|
+
return false;
|
|
110
|
+
const body = fnNode.body;
|
|
111
|
+
try {
|
|
112
|
+
const code = generate(body).code;
|
|
113
|
+
const tree = cs_mast_init(code, {
|
|
114
|
+
hash: "sha256",
|
|
115
|
+
scat: scatOverride !== null && scatOverride !== void 0 ? scatOverride : LIB_SIG_SCAT,
|
|
116
|
+
sinc: [],
|
|
117
|
+
lang: "js",
|
|
118
|
+
prsr: "@babel/parser",
|
|
119
|
+
});
|
|
120
|
+
const sigs = [...tree._signatureMap.keys()];
|
|
121
|
+
if (sigs.length === 0)
|
|
122
|
+
return false;
|
|
123
|
+
const matchCount = sigs.filter((sig) => isLibrarySig(sig, libSigs)).length;
|
|
124
|
+
const fraction = matchCount / sigs.length;
|
|
125
|
+
return fraction >= LIB_CLASSIFICATION_THRESHOLD;
|
|
126
|
+
}
|
|
127
|
+
catch (_a) {
|
|
128
|
+
// unparseable / unhashable — fall through and treat as non-library
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
};
|
|
132
|
+
// Returns true when a 1-param module looks like a style-loader runtime helper.
|
|
133
|
+
// Matches the addStyles module (contains domAPI + update) and the
|
|
134
|
+
// styleTagTransform module (sets cssText or calls createTextNode).
|
|
135
|
+
const isStyleLoaderModule = (mod) => {
|
|
136
|
+
if (mod.paramCount !== 1)
|
|
137
|
+
return false;
|
|
138
|
+
const fnNode = mod.fnPath.node;
|
|
139
|
+
if (!t.isBlockStatement(fnNode.body))
|
|
140
|
+
return false;
|
|
141
|
+
const body = fnNode.body;
|
|
142
|
+
try {
|
|
143
|
+
const code = generate(body).code;
|
|
144
|
+
if (code.includes("domAPI") && code.includes("update"))
|
|
145
|
+
return true;
|
|
146
|
+
if (code.includes("styleSheet") && code.includes("cssText"))
|
|
147
|
+
return true;
|
|
148
|
+
if (code.includes("styleTagTransform") || code.includes("insertStyleElement"))
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
catch (_a) {
|
|
152
|
+
// unparseable
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
};
|
|
156
|
+
// Returns true when a 3-param module is a CSS content module (pushes a CSS array
|
|
157
|
+
// with an id + CSS string via requireParam.d).
|
|
158
|
+
const isCssModuleEntry = (mod) => {
|
|
159
|
+
if (mod.paramCount !== 3)
|
|
160
|
+
return false;
|
|
161
|
+
const fnNode = mod.fnPath.node;
|
|
162
|
+
if (!t.isBlockStatement(fnNode.body))
|
|
163
|
+
return false;
|
|
164
|
+
const body = fnNode.body;
|
|
165
|
+
try {
|
|
166
|
+
const code = generate(body).code;
|
|
167
|
+
// CSS content modules push an array of [moduleId, cssString, ""] and register via .d
|
|
168
|
+
if (code.includes(".push(") && code.includes(".id,") && code.includes(".d("))
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
catch (_a) {
|
|
172
|
+
// unparseable
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
};
|
|
176
|
+
const refactorReact = (chunk, libSigs, externalLibModuleMap, classifyAllAsLibrary, scatOverride) => __awaiter(void 0, void 0, void 0, function* () {
|
|
177
|
+
console.log(chalk.cyan(`[i] Processing React bundle: ${chunk.id}`));
|
|
86
178
|
const ast = parser.parse(chunk.code, {
|
|
87
179
|
sourceType: "unambiguous",
|
|
88
180
|
plugins: ["jsx", "typescript"],
|
|
89
181
|
errorRecovery: true,
|
|
90
182
|
});
|
|
91
183
|
const modules = [];
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
184
|
+
const captureProperty = (path) => {
|
|
185
|
+
if (!isInModuleMap(path))
|
|
186
|
+
return;
|
|
187
|
+
const key = path.node.key;
|
|
188
|
+
if (!t.isNumericLiteral(key)) {
|
|
189
|
+
if (t.isStringLiteral(key) && /[a-zA-Z]/.test(key.value)) {
|
|
190
|
+
console.log(chalk.yellow(`[!] Alphanumeric module ID "${key.value}" detected — not yet supported, skipping (please open a PR)`));
|
|
191
|
+
}
|
|
94
192
|
return;
|
|
95
|
-
|
|
96
|
-
|
|
193
|
+
}
|
|
194
|
+
const value = path.node.value;
|
|
195
|
+
if (!t.isFunctionExpression(value) && !t.isArrowFunctionExpression(value))
|
|
97
196
|
return;
|
|
98
|
-
|
|
197
|
+
const id = String(key.value);
|
|
198
|
+
const params = value.params;
|
|
199
|
+
if (params.length > 3) {
|
|
200
|
+
console.log(chalk.yellow(`[!] Module ${id} has ${params.length} params — not yet researched, skipping`));
|
|
99
201
|
return;
|
|
202
|
+
}
|
|
203
|
+
const moduleParam = params[0] && t.isIdentifier(params[0]) ? params[0].name : "";
|
|
204
|
+
const exportsParam = params[1] && t.isIdentifier(params[1]) ? params[1].name : "";
|
|
205
|
+
const requireParam = params.length >= 3 && t.isIdentifier(params[2]) ? params[2].name : undefined;
|
|
100
206
|
modules.push({
|
|
101
207
|
id,
|
|
102
|
-
fnPath,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
208
|
+
fnPath: path.get("value"),
|
|
209
|
+
paramCount: params.length,
|
|
210
|
+
moduleParam,
|
|
211
|
+
exportsParam,
|
|
212
|
+
requireParam,
|
|
106
213
|
});
|
|
107
214
|
};
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return false;
|
|
116
|
-
// The ObjectExpression's parent should be the module-map assignment:
|
|
117
|
-
// var e = { 540: fn, 338: fn, ... }
|
|
118
|
-
// i.e. a VariableDeclarator whose init is the object.
|
|
119
|
-
const objHolder = objectParent.parentPath;
|
|
120
|
-
if (!objHolder)
|
|
121
|
-
return false;
|
|
122
|
-
if (objHolder.isVariableDeclarator())
|
|
123
|
-
return true;
|
|
124
|
-
// Some bundles place the map directly inside the IIFE arrow body's
|
|
125
|
-
// return / argument position — accept those too.
|
|
126
|
-
if (objHolder.isAssignmentExpression())
|
|
127
|
-
return true;
|
|
128
|
-
return false;
|
|
129
|
-
};
|
|
130
|
-
traverse(ast, {
|
|
131
|
-
ObjectProperty(path) {
|
|
132
|
-
if (!t.isNumericLiteral(path.node.key))
|
|
133
|
-
return;
|
|
134
|
-
if (!isInModuleMap(path))
|
|
135
|
-
return;
|
|
136
|
-
const value = path.node.value;
|
|
137
|
-
if (!t.isFunctionExpression(value) && !t.isArrowFunctionExpression(value))
|
|
138
|
-
return;
|
|
139
|
-
const id = String(path.node.key.value);
|
|
140
|
-
const valuePath = path.get("value");
|
|
141
|
-
captureModule(id, valuePath, value.params);
|
|
142
|
-
},
|
|
143
|
-
ObjectMethod(path) {
|
|
144
|
-
if (!t.isNumericLiteral(path.node.key))
|
|
145
|
-
return;
|
|
146
|
-
if (!isInModuleMap(path))
|
|
147
|
-
return;
|
|
148
|
-
const id = String(path.node.key.value);
|
|
149
|
-
captureModule(id, path, path.node.params);
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
const catalog = new Map();
|
|
153
|
-
const exportCollections = new Map();
|
|
154
|
-
// Track which modules are pure re-exporters: body of form
|
|
155
|
-
// e.exports = t(N)
|
|
156
|
-
// → reExportTargetOf[mod.id] = "N"
|
|
157
|
-
const reExportTargetOf = new Map();
|
|
158
|
-
for (const mod of modules) {
|
|
159
|
-
const exportsMap = new Map();
|
|
160
|
-
const exportsParamAssignKeys = new Set();
|
|
161
|
-
let dispatchCallCount = 0;
|
|
162
|
-
let reExportTarget = null;
|
|
163
|
-
// Quick re-export detection: a module whose ONLY non-trivial statement
|
|
164
|
-
// is `<module>.exports = <require>(<n>)`.
|
|
165
|
-
const body = mod.fnPath.node.body;
|
|
166
|
-
if (t.isBlockStatement(body)) {
|
|
167
|
-
for (const stmt of body.body) {
|
|
168
|
-
if (!t.isExpressionStatement(stmt))
|
|
169
|
-
continue;
|
|
170
|
-
const expr = stmt.expression;
|
|
171
|
-
if (t.isAssignmentExpression(expr) &&
|
|
172
|
-
t.isMemberExpression(expr.left) &&
|
|
173
|
-
t.isIdentifier(expr.left.object, { name: mod.moduleParam }) &&
|
|
174
|
-
t.isIdentifier(expr.left.property, { name: "exports" }) &&
|
|
175
|
-
t.isCallExpression(expr.right) &&
|
|
176
|
-
t.isIdentifier(expr.right.callee) &&
|
|
177
|
-
mod.requireParam &&
|
|
178
|
-
expr.right.callee.name === mod.requireParam &&
|
|
179
|
-
expr.right.arguments.length === 1 &&
|
|
180
|
-
t.isNumericLiteral(expr.right.arguments[0])) {
|
|
181
|
-
reExportTarget = String(expr.right.arguments[0].value);
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
215
|
+
const captureMethod = (path) => {
|
|
216
|
+
if (!isInModuleMap(path))
|
|
217
|
+
return;
|
|
218
|
+
const key = path.node.key;
|
|
219
|
+
if (!t.isNumericLiteral(key)) {
|
|
220
|
+
if (t.isStringLiteral(key) && /[a-zA-Z]/.test(key.value)) {
|
|
221
|
+
console.log(chalk.yellow(`[!] Alphanumeric module ID "${key.value}" detected — not yet supported, skipping (please open a PR)`));
|
|
184
222
|
}
|
|
223
|
+
return;
|
|
185
224
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const node = p.node;
|
|
192
|
-
// <require>.d(<exports>, { ... })
|
|
193
|
-
if (mod.requireParam &&
|
|
194
|
-
t.isMemberExpression(node.callee) &&
|
|
195
|
-
t.isIdentifier(node.callee.object, { name: mod.requireParam }) &&
|
|
196
|
-
t.isIdentifier(node.callee.property, { name: "d" }) &&
|
|
197
|
-
node.arguments.length === 2 &&
|
|
198
|
-
t.isIdentifier(node.arguments[0], { name: mod.exportsParam }) &&
|
|
199
|
-
t.isObjectExpression(node.arguments[1])) {
|
|
200
|
-
for (const prop of node.arguments[1].properties) {
|
|
201
|
-
if (!t.isObjectProperty(prop))
|
|
202
|
-
continue;
|
|
203
|
-
const k = getStaticKey(prop.key);
|
|
204
|
-
if (!k)
|
|
205
|
-
continue;
|
|
206
|
-
const local = extractGetterLocal(prop.value);
|
|
207
|
-
if (!local)
|
|
208
|
-
continue;
|
|
209
|
-
exportsMap.set(k, local);
|
|
210
|
-
}
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
// Object.defineProperty(<exports>, "k", { get: ... })
|
|
214
|
-
if (t.isMemberExpression(node.callee) &&
|
|
215
|
-
t.isIdentifier(node.callee.object, { name: "Object" }) &&
|
|
216
|
-
t.isIdentifier(node.callee.property, { name: "defineProperty" }) &&
|
|
217
|
-
node.arguments.length === 3 &&
|
|
218
|
-
t.isIdentifier(node.arguments[0], { name: mod.exportsParam }) &&
|
|
219
|
-
t.isStringLiteral(node.arguments[1]) &&
|
|
220
|
-
t.isObjectExpression(node.arguments[2])) {
|
|
221
|
-
const k = node.arguments[1].value;
|
|
222
|
-
for (const prop of node.arguments[2].properties) {
|
|
223
|
-
if (!t.isObjectProperty(prop))
|
|
224
|
-
continue;
|
|
225
|
-
if (getStaticKey(prop.key) !== "get")
|
|
226
|
-
continue;
|
|
227
|
-
const local = extractGetterLocal(prop.value);
|
|
228
|
-
if (!local)
|
|
229
|
-
continue;
|
|
230
|
-
exportsMap.set(k, local);
|
|
231
|
-
}
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
// Call shape: <X>.current.<hookName>(args) — react dispatch
|
|
235
|
-
if (t.isMemberExpression(node.callee) &&
|
|
236
|
-
t.isMemberExpression(node.callee.object) &&
|
|
237
|
-
t.isIdentifier(node.callee.object.property, {
|
|
238
|
-
name: "current",
|
|
239
|
-
}) &&
|
|
240
|
-
t.isIdentifier(node.callee.property) &&
|
|
241
|
-
REACT_HOOK_NAMES.has(node.callee.property.name)) {
|
|
242
|
-
dispatchCallCount++;
|
|
243
|
-
}
|
|
244
|
-
},
|
|
245
|
-
AssignmentExpression(p) {
|
|
246
|
-
// <exportsParam>.K = ... (module-scope assignment to exports)
|
|
247
|
-
const left = p.node.left;
|
|
248
|
-
if (t.isMemberExpression(left) &&
|
|
249
|
-
t.isIdentifier(left.object, { name: mod.exportsParam }) &&
|
|
250
|
-
t.isIdentifier(left.property)) {
|
|
251
|
-
const minLocal = left.property.name;
|
|
252
|
-
exportsParamAssignKeys.add(minLocal);
|
|
253
|
-
// If the RHS is `<ident>.<canonicalName>`, learn that the
|
|
254
|
-
// exports name `minLocal` corresponds to that canonical
|
|
255
|
-
// name. e.g. `n.H = r.createRoot` ⇒ exportsMap["createRoot"] = "H".
|
|
256
|
-
const right = p.node.right;
|
|
257
|
-
if (t.isMemberExpression(right) && t.isIdentifier(right.property)) {
|
|
258
|
-
const canonical = right.property.name;
|
|
259
|
-
if (!exportsMap.has(canonical)) {
|
|
260
|
-
exportsMap.set(canonical, minLocal);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
},
|
|
265
|
-
});
|
|
266
|
-
let kind = null;
|
|
267
|
-
if (dispatchCallCount > 0) {
|
|
268
|
-
kind = "react";
|
|
269
|
-
}
|
|
270
|
-
else if ((exportsParamAssignKeys.has("jsx") && exportsParamAssignKeys.has("jsxs")) ||
|
|
271
|
-
(exportsMap.has("jsx") && exportsMap.has("jsxs"))) {
|
|
272
|
-
kind = "react/jsx-runtime";
|
|
273
|
-
}
|
|
274
|
-
else if (exportsParamAssignKeys.has("createRoot") || exportsMap.has("createRoot")) {
|
|
275
|
-
kind = "react-dom/client";
|
|
276
|
-
}
|
|
277
|
-
if (kind) {
|
|
278
|
-
catalog.set(mod.id, { kind, exportMap: exportsMap });
|
|
279
|
-
}
|
|
280
|
-
if (exportsMap.size > 0) {
|
|
281
|
-
const arr = [];
|
|
282
|
-
for (const [k, v] of exportsMap)
|
|
283
|
-
arr.push({ key: k, local: v });
|
|
284
|
-
exportCollections.set(mod.id, arr);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
// Resolve re-export chains: if module M is `e.exports = t(N)` and N is
|
|
288
|
-
// classified, M inherits N's kind. Iterate until no new classifications.
|
|
289
|
-
let changed = true;
|
|
290
|
-
while (changed) {
|
|
291
|
-
changed = false;
|
|
292
|
-
for (const [source, target] of reExportTargetOf) {
|
|
293
|
-
if (catalog.has(source))
|
|
294
|
-
continue;
|
|
295
|
-
const tgt = catalog.get(target);
|
|
296
|
-
if (tgt) {
|
|
297
|
-
catalog.set(source, { kind: tgt.kind, exportMap: tgt.exportMap });
|
|
298
|
-
changed = true;
|
|
299
|
-
}
|
|
225
|
+
const id = String(key.value);
|
|
226
|
+
const params = path.node.params;
|
|
227
|
+
if (params.length > 3) {
|
|
228
|
+
console.log(chalk.yellow(`[!] Module ${id} has ${params.length} params — not yet researched, skipping`));
|
|
229
|
+
return;
|
|
300
230
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const id = p.node.id;
|
|
312
|
-
const init = p.node.init;
|
|
313
|
-
if (t.isIdentifier(id) &&
|
|
314
|
-
init &&
|
|
315
|
-
t.isCallExpression(init) &&
|
|
316
|
-
t.isIdentifier(init.callee, { name: mod.requireParam }) &&
|
|
317
|
-
init.arguments.length === 1 &&
|
|
318
|
-
t.isNumericLiteral(init.arguments[0])) {
|
|
319
|
-
const requireId = String(init.arguments[0].value);
|
|
320
|
-
const entry = catalog.get(requireId);
|
|
321
|
-
if (entry) {
|
|
322
|
-
localAliases.set(id.name, { kind: entry.kind, entry });
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
},
|
|
326
|
-
});
|
|
327
|
-
mod.fnPath.traverse({
|
|
328
|
-
CallExpression: {
|
|
329
|
-
exit(p) {
|
|
330
|
-
const node = p.node;
|
|
331
|
-
if (t.isIdentifier(node.callee, { name: mod.requireParam }) &&
|
|
332
|
-
node.arguments.length === 1 &&
|
|
333
|
-
t.isNumericLiteral(node.arguments[0])) {
|
|
334
|
-
const numId = node.arguments[0].value;
|
|
335
|
-
p.replaceWith(t.callExpression(t.identifier("require"), [t.stringLiteral(`./${numId}.js`)]));
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
if (t.isMemberExpression(node.callee) &&
|
|
339
|
-
t.isIdentifier(node.callee.object, { name: mod.requireParam }) &&
|
|
340
|
-
t.isIdentifier(node.callee.property, { name: "d" }) &&
|
|
341
|
-
node.arguments.length === 2 &&
|
|
342
|
-
t.isIdentifier(node.arguments[0], { name: mod.exportsParam })) {
|
|
343
|
-
p.replaceWith(t.identifier("void 0"));
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
if (t.isMemberExpression(node.callee) &&
|
|
347
|
-
t.isIdentifier(node.callee.object, { name: "Object" }) &&
|
|
348
|
-
t.isIdentifier(node.callee.property, { name: "defineProperty" }) &&
|
|
349
|
-
node.arguments.length === 3 &&
|
|
350
|
-
t.isIdentifier(node.arguments[0], { name: mod.exportsParam })) {
|
|
351
|
-
p.replaceWith(t.identifier("void 0"));
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
if (t.isSequenceExpression(node.callee) &&
|
|
355
|
-
node.callee.expressions.length === 2 &&
|
|
356
|
-
t.isNumericLiteral(node.callee.expressions[0], { value: 0 }) &&
|
|
357
|
-
(t.isMemberExpression(node.callee.expressions[1]) || t.isIdentifier(node.callee.expressions[1]))) {
|
|
358
|
-
const inner = node.callee.expressions[1];
|
|
359
|
-
// If the MemberExpression visitor already rewrote the
|
|
360
|
-
// member to a bare Identifier, just strip the `(0, …)`
|
|
361
|
-
// wrapper. Otherwise pass through the recognition pass.
|
|
362
|
-
const callee = t.isMemberExpression(inner)
|
|
363
|
-
? rewriteRecognisedMember(inner, localAliases, reactImports, jsxRuntimeImports, reactDomImports)
|
|
364
|
-
: inner;
|
|
365
|
-
p.replaceWith(t.callExpression(callee, node.arguments));
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
|
-
if (t.isMemberExpression(node.callee)) {
|
|
369
|
-
const member = node.callee;
|
|
370
|
-
if (t.isIdentifier(member.object) && localAliases.has(member.object.name)) {
|
|
371
|
-
const newCallee = rewriteRecognisedMember(member, localAliases, reactImports, jsxRuntimeImports, reactDomImports);
|
|
372
|
-
// Only replace when the callee changed; otherwise
|
|
373
|
-
// we'd rebuild the same node and traverse would loop.
|
|
374
|
-
if (newCallee !== member) {
|
|
375
|
-
p.replaceWith(t.callExpression(newCallee, node.arguments));
|
|
376
|
-
}
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
},
|
|
381
|
-
},
|
|
382
|
-
MemberExpression: {
|
|
383
|
-
exit(p) {
|
|
384
|
-
if (p.parent && t.isCallExpression(p.parent) && p.parent.callee === p.node)
|
|
385
|
-
return;
|
|
386
|
-
if (!t.isIdentifier(p.node.object) || !t.isIdentifier(p.node.property))
|
|
387
|
-
return;
|
|
388
|
-
const aliased = localAliases.get(p.node.object.name);
|
|
389
|
-
if (!aliased)
|
|
390
|
-
return;
|
|
391
|
-
const prop = p.node.property.name;
|
|
392
|
-
if (aliased.kind === "react/jsx-runtime" && JSX_RUNTIME_NAMES.has(prop)) {
|
|
393
|
-
jsxRuntimeImports.add(prop);
|
|
394
|
-
p.replaceWith(t.identifier(prop));
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
if (aliased.kind === "react") {
|
|
398
|
-
if (REACT_HOOK_NAMES.has(prop) || REACT_TOP_LEVEL_API_NAMES.has(prop)) {
|
|
399
|
-
reactImports.add(prop);
|
|
400
|
-
p.replaceWith(t.identifier(prop));
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
if (aliased.kind === "react-dom/client") {
|
|
405
|
-
for (const [canonical, minLocal] of aliased.entry.exportMap) {
|
|
406
|
-
if (minLocal === prop && REACT_DOM_CLIENT_NAMES.has(canonical)) {
|
|
407
|
-
reactDomImports.add(canonical);
|
|
408
|
-
p.replaceWith(t.identifier(canonical));
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
},
|
|
414
|
-
},
|
|
231
|
+
const moduleParam = params[0] && t.isIdentifier(params[0]) ? params[0].name : "";
|
|
232
|
+
const exportsParam = params[1] && t.isIdentifier(params[1]) ? params[1].name : "";
|
|
233
|
+
const requireParam = params.length >= 3 && t.isIdentifier(params[2]) ? params[2].name : undefined;
|
|
234
|
+
modules.push({
|
|
235
|
+
id,
|
|
236
|
+
fnPath: path,
|
|
237
|
+
paramCount: params.length,
|
|
238
|
+
moduleParam,
|
|
239
|
+
exportsParam,
|
|
240
|
+
requireParam,
|
|
415
241
|
});
|
|
416
|
-
}
|
|
417
|
-
// ──────────────────────────────────────────────────────────────────
|
|
418
|
-
// Pass 4 (global): catch IIFE-level `var X = <any>(<numericId>)` aliases
|
|
419
|
-
// and rewrite the user-entry callsites at the outermost scope, which the
|
|
420
|
-
// per-module pass cannot reach because the entry-IIFE wrapper has 0 params
|
|
421
|
-
// (no positional require). The catalog match keeps this safe — only known
|
|
422
|
-
// React-family module IDs trigger alias substitution.
|
|
423
|
-
// ──────────────────────────────────────────────────────────────────
|
|
424
|
-
const globalAliases = new Map();
|
|
242
|
+
};
|
|
425
243
|
traverse(ast, {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const init = p.node.init;
|
|
429
|
-
if (t.isIdentifier(id) &&
|
|
430
|
-
init &&
|
|
431
|
-
t.isCallExpression(init) &&
|
|
432
|
-
t.isIdentifier(init.callee) &&
|
|
433
|
-
init.arguments.length === 1 &&
|
|
434
|
-
t.isNumericLiteral(init.arguments[0])) {
|
|
435
|
-
const requireId = String(init.arguments[0].value);
|
|
436
|
-
const entry = catalog.get(requireId);
|
|
437
|
-
if (entry) {
|
|
438
|
-
globalAliases.set(id.name, { kind: entry.kind, entry });
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
},
|
|
244
|
+
ObjectProperty: captureProperty,
|
|
245
|
+
ObjectMethod: captureMethod,
|
|
442
246
|
});
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
(t.isMemberExpression(node.callee.expressions[1]) || t.isIdentifier(node.callee.expressions[1]))) {
|
|
452
|
-
const inner = node.callee.expressions[1];
|
|
453
|
-
const callee = t.isMemberExpression(inner)
|
|
454
|
-
? rewriteRecognisedMember(inner, globalAliases, reactImports, jsxRuntimeImports, reactDomImports)
|
|
455
|
-
: inner;
|
|
456
|
-
p.replaceWith(t.callExpression(callee, node.arguments));
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
if (t.isMemberExpression(node.callee)) {
|
|
460
|
-
const member = node.callee;
|
|
461
|
-
if (t.isIdentifier(member.object) && globalAliases.has(member.object.name)) {
|
|
462
|
-
const newCallee = rewriteRecognisedMember(member, globalAliases, reactImports, jsxRuntimeImports, reactDomImports);
|
|
463
|
-
if (newCallee !== member) {
|
|
464
|
-
p.replaceWith(t.callExpression(newCallee, node.arguments));
|
|
465
|
-
}
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
},
|
|
470
|
-
},
|
|
471
|
-
MemberExpression: {
|
|
472
|
-
exit(p) {
|
|
473
|
-
if (p.parent && t.isCallExpression(p.parent) && p.parent.callee === p.node)
|
|
474
|
-
return;
|
|
475
|
-
if (!t.isIdentifier(p.node.object) || !t.isIdentifier(p.node.property))
|
|
476
|
-
return;
|
|
477
|
-
const aliased = globalAliases.get(p.node.object.name);
|
|
478
|
-
if (!aliased)
|
|
479
|
-
return;
|
|
480
|
-
const prop = p.node.property.name;
|
|
481
|
-
if (aliased.kind === "react/jsx-runtime" && JSX_RUNTIME_NAMES.has(prop)) {
|
|
482
|
-
jsxRuntimeImports.add(prop);
|
|
483
|
-
p.replaceWith(t.identifier(prop));
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
if (aliased.kind === "react" &&
|
|
487
|
-
(REACT_HOOK_NAMES.has(prop) || REACT_TOP_LEVEL_API_NAMES.has(prop))) {
|
|
488
|
-
reactImports.add(prop);
|
|
489
|
-
p.replaceWith(t.identifier(prop));
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
if (aliased.kind === "react-dom/client") {
|
|
493
|
-
for (const [canonical, minLocal] of aliased.entry.exportMap) {
|
|
494
|
-
if (minLocal === prop && REACT_DOM_CLIENT_NAMES.has(canonical)) {
|
|
495
|
-
reactDomImports.add(canonical);
|
|
496
|
-
p.replaceWith(t.identifier(canonical));
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
},
|
|
502
|
-
},
|
|
503
|
-
});
|
|
247
|
+
console.log(chalk.cyan(`[i] Found ${modules.length} modules`));
|
|
248
|
+
// Detect lazy chunk bundles: no IIFE wrapper, program body is entirely push() calls.
|
|
249
|
+
// Modules in lazy chunks are always application code — skip library classification for them.
|
|
250
|
+
// Also don't generate index.js from the push statement (it contains no bootstrap logic).
|
|
251
|
+
const iifeBody = findIifeBody(ast.program);
|
|
252
|
+
const isLazyBundle = iifeBody === null && ast.program.body.length > 0 && ast.program.body.every(isLazyChunkPushStmt);
|
|
253
|
+
if (isLazyBundle) {
|
|
254
|
+
console.log(chalk.cyan(`[i] Detected lazy chunk format — skipping library classification`));
|
|
504
255
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const defaultLines = [];
|
|
528
|
-
for (const entry of allExports) {
|
|
529
|
-
if (usedLocals.has(entry.local))
|
|
256
|
+
const files = {};
|
|
257
|
+
// moduleId → LibraryModuleInfo for modules that are library-classified (in this chunk)
|
|
258
|
+
const libModuleMap = new Map();
|
|
259
|
+
let libraryCount = 0;
|
|
260
|
+
// Collect non-library module statements before applying Pass D, so we can use the
|
|
261
|
+
// fully-resolved libModuleMap (including re-export chains) when rewriting imports.
|
|
262
|
+
const pendingModules = [];
|
|
263
|
+
for (const mod of modules) {
|
|
264
|
+
// classifyAllAsLibrary: used for vendor chunks outside mapped.json where we want
|
|
265
|
+
// to extract library export maps without generating any output files.
|
|
266
|
+
const isLib = classifyAllAsLibrary || (!isLazyBundle && moduleIsLibrary(mod, libSigs, scatOverride));
|
|
267
|
+
// Detect style-loader and CSS content modules regardless of baseline signatures.
|
|
268
|
+
if (!classifyAllAsLibrary && !isLib && !isLazyBundle) {
|
|
269
|
+
const styleLoaderType = isStyleLoaderModule(mod)
|
|
270
|
+
? "style-loader"
|
|
271
|
+
: isCssModuleEntry(mod)
|
|
272
|
+
? "css-module"
|
|
273
|
+
: null;
|
|
274
|
+
if (styleLoaderType !== null) {
|
|
275
|
+
console.log(chalk.gray(`[-] Module ${mod.id} detected as ${styleLoaderType} — skipping`));
|
|
276
|
+
libModuleMap.set(mod.id, { type: styleLoaderType, exportMap: new Map() });
|
|
277
|
+
libraryCount++;
|
|
530
278
|
continue;
|
|
531
|
-
usedLocals.add(entry.local);
|
|
532
|
-
let key = entry.key;
|
|
533
|
-
if (usedKeys.has(key)) {
|
|
534
|
-
collisionIndex += 1;
|
|
535
|
-
key = `${entry.key}_${collisionIndex}`;
|
|
536
|
-
}
|
|
537
|
-
usedKeys.add(key);
|
|
538
|
-
if (key === "default") {
|
|
539
|
-
if (!codeHasDefaultExport)
|
|
540
|
-
defaultLines.push(`export default ${entry.local};`);
|
|
541
|
-
}
|
|
542
|
-
else {
|
|
543
|
-
named.push({ key, local: entry.local });
|
|
544
279
|
}
|
|
545
280
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
281
|
+
if (isLib) {
|
|
282
|
+
console.log(chalk.gray(`[-] Module ${mod.id} matches library baseline — skipping`));
|
|
283
|
+
libraryCount++;
|
|
284
|
+
// Classify the library module so import rewriting can use proper named imports
|
|
285
|
+
const info = classifyLibraryModule(mod);
|
|
286
|
+
libModuleMap.set(mod.id, info);
|
|
287
|
+
continue;
|
|
551
288
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
trailingExports = `\n\n${parts.join("\n")}`;
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
let functionName = null;
|
|
558
|
-
traverse(ast, {
|
|
559
|
-
FunctionDeclaration(path) {
|
|
560
|
-
if (path.parent.type === "Program" && path.node.id) {
|
|
561
|
-
functionName = path.node.id.name;
|
|
562
|
-
path.stop();
|
|
563
|
-
}
|
|
564
|
-
},
|
|
565
|
-
VariableDeclarator(path) {
|
|
566
|
-
if (path.parentPath.parent.type === "Program" &&
|
|
567
|
-
path.node.init &&
|
|
568
|
-
path.node.init.type === "ArrowFunctionExpression" &&
|
|
569
|
-
path.node.id.type === "Identifier") {
|
|
570
|
-
functionName = path.node.id.name;
|
|
571
|
-
path.stop();
|
|
572
|
-
}
|
|
573
|
-
},
|
|
574
|
-
});
|
|
575
|
-
if (functionName && !codeHasDefaultExport)
|
|
576
|
-
trailingExports = `\n\nexport default ${functionName};`;
|
|
577
|
-
}
|
|
578
|
-
return `${header}${codeCopy}${trailingExports}`;
|
|
579
|
-
});
|
|
580
|
-
const rewriteRecognisedMember = (member, localAliases, reactImports, jsxImports, reactDomImports) => {
|
|
581
|
-
if (!t.isIdentifier(member.object) || !t.isIdentifier(member.property))
|
|
582
|
-
return member;
|
|
583
|
-
const aliased = localAliases.get(member.object.name);
|
|
584
|
-
if (!aliased)
|
|
585
|
-
return member;
|
|
586
|
-
const prop = member.property.name;
|
|
587
|
-
if (aliased.kind === "react" && (REACT_HOOK_NAMES.has(prop) || REACT_TOP_LEVEL_API_NAMES.has(prop))) {
|
|
588
|
-
reactImports.add(prop);
|
|
589
|
-
return t.identifier(prop);
|
|
590
|
-
}
|
|
591
|
-
if (aliased.kind === "react/jsx-runtime" && JSX_RUNTIME_NAMES.has(prop)) {
|
|
592
|
-
jsxImports.add(prop);
|
|
593
|
-
return t.identifier(prop);
|
|
289
|
+
const statements = applyModuleCleanupPasses(transformModule(mod));
|
|
290
|
+
pendingModules.push({ id: mod.id, statements });
|
|
594
291
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
292
|
+
// Resolve re-export chains (e.g. 338 → 247/react-dom-client, 540 → 287/React) so shim modules
|
|
293
|
+
// get the right library type. Always run for vendor pre-scans (classifyAllAsLibrary), and for
|
|
294
|
+
// normal non-lazy chunks when a library baseline is provided.
|
|
295
|
+
if (classifyAllAsLibrary || (!isLazyBundle && libSigs && libSigs.size > 0)) {
|
|
296
|
+
if (!isLazyBundle && libSigs && libSigs.size > 0) {
|
|
297
|
+
console.log(chalk.cyan(`[i] Library modules skipped: ${libraryCount}/${modules.length}`));
|
|
601
298
|
}
|
|
299
|
+
resolveReexportChains(libModuleMap, modules);
|
|
602
300
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const extractGetterLocal = (node) => {
|
|
615
|
-
if (t.isArrowFunctionExpression(node)) {
|
|
616
|
-
if (t.isIdentifier(node.body))
|
|
617
|
-
return node.body.name;
|
|
618
|
-
if (t.isBlockStatement(node.body)) {
|
|
619
|
-
for (const stmt of node.body.body) {
|
|
620
|
-
if (t.isReturnStatement(stmt) && stmt.argument && t.isIdentifier(stmt.argument)) {
|
|
621
|
-
return stmt.argument.name;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
301
|
+
// Merged map: prefer locally-classified entries over external ones (local classification
|
|
302
|
+
// is more precise for this specific bundle version).
|
|
303
|
+
const mergedLibMap = new Map([...(externalLibModuleMap !== null && externalLibModuleMap !== void 0 ? externalLibModuleMap : []), ...libModuleMap]);
|
|
304
|
+
// Apply Pass D (library-aware import rewriting) using the merged map, then validate.
|
|
305
|
+
for (const { id, statements } of pendingModules) {
|
|
306
|
+
const afterD = mergedLibMap.size > 0 ? applyLibraryImportRewriting(statements, mergedLibMap) : statements;
|
|
307
|
+
const afterRename = renameRouteComponents(afterD);
|
|
308
|
+
const code = validateAndFix(afterRename, id);
|
|
309
|
+
if (code === null) {
|
|
310
|
+
console.log(chalk.yellow(`[~] Module ${id} skipped due to unresolvable syntax errors`));
|
|
311
|
+
continue;
|
|
624
312
|
}
|
|
313
|
+
files[id] = code;
|
|
625
314
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
315
|
+
// Collect everything in the IIFE body that is NOT the module-map variable into index.js.
|
|
316
|
+
// Skip this step for lazy chunks — their push statement contains no bootstrap logic.
|
|
317
|
+
if (!isLazyBundle) {
|
|
318
|
+
const topLevel = iifeBody !== null && iifeBody !== void 0 ? iifeBody : ast.program.body;
|
|
319
|
+
const indexStatements = [];
|
|
320
|
+
for (const stmt of topLevel) {
|
|
321
|
+
if (t.isVariableDeclaration(stmt)) {
|
|
322
|
+
const remaining = stmt.declarations.filter((d) => !isModuleMapDeclarator(d));
|
|
323
|
+
if (remaining.length > 0)
|
|
324
|
+
indexStatements.push(t.variableDeclaration(stmt.kind, remaining));
|
|
630
325
|
}
|
|
326
|
+
else {
|
|
327
|
+
indexStatements.push(stmt);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (indexStatements.length > 0) {
|
|
331
|
+
console.log(chalk.cyan(`[i] Writing ${indexStatements.length} non-module statements to index.js`));
|
|
332
|
+
const transformed = transformIndexStatements(indexStatements, mergedLibMap.size > 0 ? mergedLibMap : undefined);
|
|
333
|
+
const indexCode = validateAndFix(transformed, "index");
|
|
334
|
+
if (indexCode !== null)
|
|
335
|
+
files["index"] = indexCode;
|
|
631
336
|
}
|
|
632
337
|
}
|
|
633
|
-
return
|
|
634
|
-
};
|
|
338
|
+
return { files, libModuleMap };
|
|
339
|
+
});
|
|
635
340
|
export default refactorReact;
|
|
636
341
|
//# sourceMappingURL=index.js.map
|