@shriyanss/js-recon 1.3.1 → 1.4.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/CLAUDE.md +45 -0
- package/README.md +1 -0
- package/build/cs_mast/index.js +122 -0
- package/build/cs_mast/index.js.map +1 -0
- package/build/globalConfig.js +2 -2
- package/build/globalConfig.js.map +1 -1
- package/build/index.js +67 -7
- package/build/index.js.map +1 -1
- package/build/lazyLoad/downloadLoadedJsUtil.js +6 -1
- package/build/lazyLoad/downloadLoadedJsUtil.js.map +1 -1
- package/build/lazyLoad/index.js +72 -38
- package/build/lazyLoad/index.js.map +1 -1
- package/build/lazyLoad/methodFilter.js +49 -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_GetLazyResourcesWebpackJs.js +6 -1
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.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/checkSvelte.js +17 -0
- package/build/lazyLoad/techDetect/checkSvelte.js.map +1 -1
- package/build/lazyLoad/techDetect/index.js +11 -4
- package/build/lazyLoad/techDetect/index.js.map +1 -1
- package/build/lazyLoad/vue/vue_discoverJsFiles.js +47 -25
- package/build/lazyLoad/vue/vue_discoverJsFiles.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/map/vue_js/vue_resolveHttpClient.js +2 -1
- package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -1
- package/build/map/vue_js/vue_resolveXhr.js +2 -1
- package/build/map/vue_js/vue_resolveXhr.js.map +1 -1
- package/build/refactor/index.js +133 -22
- package/build/refactor/index.js.map +1 -1
- package/build/refactor/next/helpers.js +214 -0
- package/build/refactor/next/helpers.js.map +1 -0
- package/build/refactor/next/index.js +40 -73
- package/build/refactor/next/index.js.map +1 -1
- package/build/refactor/next/transform.js +177 -0
- package/build/refactor/next/transform.js.map +1 -0
- package/build/refactor/next/validator.js +118 -0
- package/build/refactor/next/validator.js.map +1 -0
- package/build/refactor/react/helpers.js +93 -0
- package/build/refactor/react/helpers.js.map +1 -0
- package/build/refactor/react/index.js +186 -589
- package/build/refactor/react/index.js.map +1 -1
- package/build/refactor/react/library-classify.js +166 -0
- package/build/refactor/react/library-classify.js.map +1 -0
- package/build/refactor/react/transform.js +839 -0
- package/build/refactor/react/transform.js.map +1 -0
- package/build/refactor/react/validator.js +122 -0
- package/build/refactor/react/validator.js.map +1 -0
- package/build/utility/getChromiumPath.js +40 -0
- package/build/utility/getChromiumPath.js.map +1 -0
- package/build/utility/makeReq.js +11 -2
- package/build/utility/makeReq.js.map +1 -1
- package/package.json +3 -2
|
@@ -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,225 @@ 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;
|
|
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";
|
|
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 } from "./transform.js";
|
|
22
|
+
import { classifyLibraryModule, resolveReexportChains } from "./library-classify.js";
|
|
15
23
|
const traverse = _traverse.default;
|
|
16
|
-
const generate = _generator.default;
|
|
24
|
+
const generate = (_a = _generator.default) !== null && _a !== void 0 ? _a : _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 a VariableDeclarator's init is the webpack numeric module map
|
|
45
|
+
// object (every property has a NumericLiteral key and a function value).
|
|
46
|
+
const isModuleMapDeclarator = (d) => {
|
|
47
|
+
if (!t.isObjectExpression(d.init))
|
|
48
|
+
return false;
|
|
49
|
+
const props = d.init.properties;
|
|
50
|
+
if (props.length === 0)
|
|
51
|
+
return false;
|
|
52
|
+
return props.every((p) => {
|
|
53
|
+
if (t.isObjectProperty(p))
|
|
54
|
+
return (t.isNumericLiteral(p.key) && (t.isFunctionExpression(p.value) || t.isArrowFunctionExpression(p.value)));
|
|
55
|
+
if (t.isObjectMethod(p))
|
|
56
|
+
return t.isNumericLiteral(p.key);
|
|
57
|
+
return false;
|
|
58
|
+
});
|
|
59
|
+
};
|
|
17
60
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* Recognises the standard webpack 5 / React 18 production bundle shape:
|
|
21
|
-
* - An outer IIFE that holds a `{ <id>: function(module, exports, require) { ... } }`
|
|
22
|
-
* module map.
|
|
23
|
-
* - Each inner module function uses positional params named `(module, exports, require)`
|
|
24
|
-
* (minifier locals — names vary per chunk).
|
|
25
|
-
* - React's public surface lives in modules identifiable by content fingerprint
|
|
26
|
-
* (the public hooks delegate to `R.current.<hook>(...)`).
|
|
61
|
+
* Rewrites a webpack-bundled React chunk by splitting the numeric module map
|
|
62
|
+
* into individual ECMAScript module files.
|
|
27
63
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* Lossy — for human inspection only.
|
|
64
|
+
* Step 1 – Find the `var X = { <numId>: function(e,n,t){…}, … }` module map.
|
|
65
|
+
* Step 2 – For each module:
|
|
66
|
+
* a) Convert `<moduleParam>.exports = <requireParam>(N)` → `export * from "./N.js"`
|
|
67
|
+
* (and `<moduleParam>.exports = <expr>` → `export default <expr>`),
|
|
68
|
+
* including inside top-level sequence expressions.
|
|
69
|
+
* b) Convert `<exportsParam>.<propName> = <rhs>` → ECMAScript named exports
|
|
70
|
+
* (per MDN export reference) for any module that has an exports param.
|
|
71
|
+
* c) Hoist `var <name> = <requireParam>(N)` to `import * as <name> from "./N.js"`.
|
|
72
|
+
* d) Replace remaining inline `<requireParam>(N)` calls with a synthesized
|
|
73
|
+
* namespace import reference.
|
|
74
|
+
* e) Strip the outer function wrapper.
|
|
75
|
+
* Step 3 – Validate generated code with Babel; iteratively drop/downgrade statements
|
|
76
|
+
* that still cause parse errors.
|
|
77
|
+
* Step 4 – Collect all IIFE body statements that are NOT part of the module map and
|
|
78
|
+
* write them to `index.js` (entrypoint bootstrap, app component functions,
|
|
79
|
+
* the ReactDOM.render call, etc.).
|
|
46
80
|
*/
|
|
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
|
-
"act",
|
|
81
|
-
]);
|
|
82
|
-
const JSX_RUNTIME_NAMES = new Set(["jsx", "jsxs", "jsxDEV", "Fragment"]);
|
|
83
|
-
const REACT_DOM_CLIENT_NAMES = new Set(["createRoot", "hydrateRoot"]);
|
|
84
|
-
const refactorReact = (chunk) => __awaiter(void 0, void 0, void 0, function* () {
|
|
85
|
-
console.log(chalk.cyan(`[i] Refactoring React chunk: ${chunk.id}`));
|
|
81
|
+
const isLibrarySig = (sig, libSigs) => !!(sig && libSigs && libSigs.has(sig));
|
|
82
|
+
// Hash a single module's function body using cs_mast_init and look the signature
|
|
83
|
+
// up against the count=18 baseline set. Returns true when the module's body
|
|
84
|
+
// matches a baseline library signature.
|
|
85
|
+
const moduleIsLibrary = (mod, libSigs) => {
|
|
86
|
+
if (!libSigs || libSigs.size === 0)
|
|
87
|
+
return false;
|
|
88
|
+
const fnNode = mod.fnPath.node;
|
|
89
|
+
if (!t.isBlockStatement(fnNode.body))
|
|
90
|
+
return false;
|
|
91
|
+
const body = fnNode.body;
|
|
92
|
+
try {
|
|
93
|
+
const code = generate(body).code;
|
|
94
|
+
const tree = cs_mast_init(code, {
|
|
95
|
+
hash: "sha256",
|
|
96
|
+
scat: LIB_SIG_SCAT,
|
|
97
|
+
sinc: [],
|
|
98
|
+
lang: "js",
|
|
99
|
+
prsr: "@babel/parser",
|
|
100
|
+
});
|
|
101
|
+
for (const sig of tree._signatureMap.keys()) {
|
|
102
|
+
if (isLibrarySig(sig, libSigs))
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (_a) {
|
|
107
|
+
// unparseable / unhashable — fall through and treat as non-library
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
};
|
|
111
|
+
const refactorReact = (chunk, libSigs) => __awaiter(void 0, void 0, void 0, function* () {
|
|
112
|
+
var _a;
|
|
113
|
+
console.log(chalk.cyan(`[i] Processing React bundle: ${chunk.id}`));
|
|
86
114
|
const ast = parser.parse(chunk.code, {
|
|
87
115
|
sourceType: "unambiguous",
|
|
88
116
|
plugins: ["jsx", "typescript"],
|
|
89
117
|
errorRecovery: true,
|
|
90
118
|
});
|
|
91
119
|
const modules = [];
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
120
|
+
const captureProperty = (path) => {
|
|
121
|
+
if (!isInModuleMap(path))
|
|
94
122
|
return;
|
|
95
|
-
const
|
|
96
|
-
if (!t.
|
|
123
|
+
const key = path.node.key;
|
|
124
|
+
if (!t.isNumericLiteral(key)) {
|
|
125
|
+
if (t.isStringLiteral(key) && /[a-zA-Z]/.test(key.value)) {
|
|
126
|
+
console.log(chalk.yellow(`[!] Alphanumeric module ID "${key.value}" detected — not yet supported, skipping (please open a PR)`));
|
|
127
|
+
}
|
|
97
128
|
return;
|
|
98
|
-
|
|
129
|
+
}
|
|
130
|
+
const value = path.node.value;
|
|
131
|
+
if (!t.isFunctionExpression(value) && !t.isArrowFunctionExpression(value))
|
|
99
132
|
return;
|
|
133
|
+
const id = String(key.value);
|
|
134
|
+
const params = value.params;
|
|
135
|
+
if (params.length > 3) {
|
|
136
|
+
console.log(chalk.yellow(`[!] Module ${id} has ${params.length} params — not yet researched, skipping`));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const moduleParam = params[0] && t.isIdentifier(params[0]) ? params[0].name : "";
|
|
140
|
+
const exportsParam = params[1] && t.isIdentifier(params[1]) ? params[1].name : "";
|
|
141
|
+
const requireParam = params.length >= 3 && t.isIdentifier(params[2]) ? params[2].name : undefined;
|
|
100
142
|
modules.push({
|
|
101
143
|
id,
|
|
102
|
-
fnPath,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
144
|
+
fnPath: path.get("value"),
|
|
145
|
+
paramCount: params.length,
|
|
146
|
+
moduleParam,
|
|
147
|
+
exportsParam,
|
|
148
|
+
requireParam,
|
|
106
149
|
});
|
|
107
150
|
};
|
|
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
|
-
}
|
|
151
|
+
const captureMethod = (path) => {
|
|
152
|
+
if (!isInModuleMap(path))
|
|
153
|
+
return;
|
|
154
|
+
const key = path.node.key;
|
|
155
|
+
if (!t.isNumericLiteral(key)) {
|
|
156
|
+
if (t.isStringLiteral(key) && /[a-zA-Z]/.test(key.value)) {
|
|
157
|
+
console.log(chalk.yellow(`[!] Alphanumeric module ID "${key.value}" detected — not yet supported, skipping (please open a PR)`));
|
|
184
158
|
}
|
|
159
|
+
return;
|
|
185
160
|
}
|
|
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
|
-
}
|
|
161
|
+
const id = String(key.value);
|
|
162
|
+
const params = path.node.params;
|
|
163
|
+
if (params.length > 3) {
|
|
164
|
+
console.log(chalk.yellow(`[!] Module ${id} has ${params.length} params — not yet researched, skipping`));
|
|
165
|
+
return;
|
|
300
166
|
}
|
|
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
|
-
},
|
|
167
|
+
const moduleParam = params[0] && t.isIdentifier(params[0]) ? params[0].name : "";
|
|
168
|
+
const exportsParam = params[1] && t.isIdentifier(params[1]) ? params[1].name : "";
|
|
169
|
+
const requireParam = params.length >= 3 && t.isIdentifier(params[2]) ? params[2].name : undefined;
|
|
170
|
+
modules.push({
|
|
171
|
+
id,
|
|
172
|
+
fnPath: path,
|
|
173
|
+
paramCount: params.length,
|
|
174
|
+
moduleParam,
|
|
175
|
+
exportsParam,
|
|
176
|
+
requireParam,
|
|
415
177
|
});
|
|
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();
|
|
178
|
+
};
|
|
425
179
|
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
|
-
},
|
|
180
|
+
ObjectProperty: captureProperty,
|
|
181
|
+
ObjectMethod: captureMethod,
|
|
442
182
|
});
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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
|
-
});
|
|
504
|
-
}
|
|
505
|
-
let codeCopy = generate(ast).code;
|
|
506
|
-
const importLines = [];
|
|
507
|
-
if (reactImports.size > 0) {
|
|
508
|
-
importLines.push(`import { ${Array.from(reactImports).sort().join(", ")} } from "react";`);
|
|
509
|
-
}
|
|
510
|
-
if (jsxRuntimeImports.size > 0) {
|
|
511
|
-
importLines.push(`import { ${Array.from(jsxRuntimeImports).sort().join(", ")} } from "react/jsx-runtime";`);
|
|
512
|
-
}
|
|
513
|
-
if (reactDomImports.size > 0) {
|
|
514
|
-
importLines.push(`import { ${Array.from(reactDomImports).sort().join(", ")} } from "react-dom/client";`);
|
|
515
|
-
}
|
|
516
|
-
const header = importLines.length > 0 ? `${importLines.join("\n")}\n\n` : "";
|
|
517
|
-
const allExports = [];
|
|
518
|
-
for (const arr of exportCollections.values())
|
|
519
|
-
allExports.push(...arr);
|
|
520
|
-
const codeHasDefaultExport = /\bexport\s+default\b|\bas\s+default\b/.test(codeCopy);
|
|
521
|
-
let trailingExports = "";
|
|
522
|
-
if (allExports.length > 0) {
|
|
523
|
-
const usedKeys = new Set();
|
|
524
|
-
const usedLocals = new Set();
|
|
525
|
-
const named = [];
|
|
526
|
-
let collisionIndex = 0;
|
|
527
|
-
const defaultLines = [];
|
|
528
|
-
for (const entry of allExports) {
|
|
529
|
-
if (usedLocals.has(entry.local))
|
|
530
|
-
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
|
-
}
|
|
183
|
+
console.log(chalk.cyan(`[i] Found ${modules.length} modules`));
|
|
184
|
+
const results = {};
|
|
185
|
+
// moduleId → LibraryModuleInfo for modules that are library-classified
|
|
186
|
+
const libModuleMap = new Map();
|
|
187
|
+
let libraryCount = 0;
|
|
188
|
+
for (const mod of modules) {
|
|
189
|
+
if (moduleIsLibrary(mod, libSigs)) {
|
|
190
|
+
console.log(chalk.gray(`[-] Module ${mod.id} matches library baseline — skipping`));
|
|
191
|
+
libraryCount++;
|
|
192
|
+
// Classify the library module so index.js can use proper named imports
|
|
193
|
+
const info = classifyLibraryModule(mod);
|
|
194
|
+
libModuleMap.set(mod.id, info);
|
|
195
|
+
continue;
|
|
545
196
|
}
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
197
|
+
const statements = transformModule(mod);
|
|
198
|
+
const code = validateAndFix(statements, mod.id);
|
|
199
|
+
if (code === null) {
|
|
200
|
+
console.log(chalk.yellow(`[~] Module ${mod.id} skipped due to unresolvable syntax errors`));
|
|
201
|
+
continue;
|
|
551
202
|
}
|
|
552
|
-
|
|
553
|
-
if (parts.length > 0)
|
|
554
|
-
trailingExports = `\n\n${parts.join("\n")}`;
|
|
203
|
+
results[mod.id] = code;
|
|
555
204
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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};`;
|
|
205
|
+
if (libSigs && libSigs.size > 0) {
|
|
206
|
+
console.log(chalk.cyan(`[i] Library modules skipped: ${libraryCount}/${modules.length}`));
|
|
207
|
+
// Resolve re-export chains so shim modules (e.g. 540 → 287/React) get the right identity
|
|
208
|
+
resolveReexportChains(libModuleMap, modules);
|
|
577
209
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
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);
|
|
594
|
-
}
|
|
595
|
-
if (aliased.kind === "react-dom/client") {
|
|
596
|
-
for (const [canonical, minLocal] of aliased.entry.exportMap) {
|
|
597
|
-
if (minLocal === prop && REACT_DOM_CLIENT_NAMES.has(canonical)) {
|
|
598
|
-
reactDomImports.add(canonical);
|
|
599
|
-
return t.identifier(canonical);
|
|
600
|
-
}
|
|
210
|
+
// Collect everything in the IIFE body that is NOT the module-map variable into index.js.
|
|
211
|
+
const topLevel = (_a = findIifeBody(ast.program)) !== null && _a !== void 0 ? _a : ast.program.body;
|
|
212
|
+
const indexStatements = [];
|
|
213
|
+
for (const stmt of topLevel) {
|
|
214
|
+
if (t.isVariableDeclaration(stmt)) {
|
|
215
|
+
const remaining = stmt.declarations.filter((d) => !isModuleMapDeclarator(d));
|
|
216
|
+
if (remaining.length > 0)
|
|
217
|
+
indexStatements.push(t.variableDeclaration(stmt.kind, remaining));
|
|
601
218
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
};
|
|
605
|
-
const getStaticKey = (node) => {
|
|
606
|
-
if (t.isIdentifier(node))
|
|
607
|
-
return node.name;
|
|
608
|
-
if (t.isStringLiteral(node))
|
|
609
|
-
return node.value;
|
|
610
|
-
if (t.isNumericLiteral(node))
|
|
611
|
-
return String(node.value);
|
|
612
|
-
return null;
|
|
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
|
-
}
|
|
219
|
+
else {
|
|
220
|
+
indexStatements.push(stmt);
|
|
624
221
|
}
|
|
625
222
|
}
|
|
626
|
-
if (
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
223
|
+
if (indexStatements.length > 0) {
|
|
224
|
+
console.log(chalk.cyan(`[i] Writing ${indexStatements.length} non-module statements to index.js`));
|
|
225
|
+
const transformed = transformIndexStatements(indexStatements, libModuleMap.size > 0 ? libModuleMap : undefined);
|
|
226
|
+
const indexCode = validateAndFix(transformed, "index");
|
|
227
|
+
if (indexCode !== null)
|
|
228
|
+
results["index"] = indexCode;
|
|
632
229
|
}
|
|
633
|
-
return
|
|
634
|
-
};
|
|
230
|
+
return results;
|
|
231
|
+
});
|
|
635
232
|
export default refactorReact;
|
|
636
233
|
//# sourceMappingURL=index.js.map
|