@shriyanss/js-recon 1.3.1-alpha.2 → 1.3.1-alpha.4
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 +15 -1
- package/.github/workflows/pr_checker.yml +9 -8
- package/.github/workflows/publish-js-recon.yml +13 -1
- package/CHANGELOG.md +96 -0
- package/CLAUDE.md +225 -0
- package/README.md +2 -0
- package/build/analyze/engine/astEngine.js +57 -29
- package/build/analyze/engine/astEngine.js.map +1 -1
- package/build/analyze/engine/index.js +2 -2
- package/build/analyze/engine/index.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/taintFlow.js +32 -0
- package/build/analyze/helpers/engineHelpers/taintFlow.js.map +1 -1
- package/build/analyze/helpers/initRules.js +9 -0
- package/build/analyze/helpers/initRules.js.map +1 -1
- package/build/analyze/helpers/schemas.js +6 -1
- package/build/analyze/helpers/schemas.js.map +1 -1
- package/build/analyze/helpers/validate.js +49 -9
- package/build/analyze/helpers/validate.js.map +1 -1
- package/build/analyze/index.js +10 -4
- package/build/analyze/index.js.map +1 -1
- package/build/endpoints/next_js/client_mappedJsonFile.js +12 -5
- package/build/endpoints/next_js/client_mappedJsonFile.js.map +1 -1
- package/build/fingerprint/index.js +123 -0
- package/build/fingerprint/index.js.map +1 -0
- package/build/globalConfig.js +1 -1
- package/build/index.js +72 -5
- package/build/index.js.map +1 -1
- package/build/lazyLoad/downloadFilesUtil.js +3 -3
- package/build/lazyLoad/downloadFilesUtil.js.map +1 -1
- package/build/lazyLoad/downloadQueue.js +16 -11
- package/build/lazyLoad/downloadQueue.js.map +1 -1
- package/build/lazyLoad/index.js +260 -163
- package/build/lazyLoad/index.js.map +1 -1
- package/build/lazyLoad/next_js/NextJsCrawler.js +58 -16
- package/build/lazyLoad/next_js/NextJsCrawler.js.map +1 -1
- package/build/lazyLoad/next_js/next_GetJSScript.js +8 -4
- package/build/lazyLoad/next_js/next_GetJSScript.js.map +1 -1
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +149 -139
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
- package/build/lazyLoad/next_js/next_SubsequentRequests.js +25 -4
- package/build/lazyLoad/next_js/next_SubsequentRequests.js.map +1 -1
- package/build/lazyLoad/next_js/next_scriptTagsSubsequentRequests.js +13 -5
- package/build/lazyLoad/next_js/next_scriptTagsSubsequentRequests.js.map +1 -1
- package/build/lazyLoad/react/react_followImports.js +105 -0
- package/build/lazyLoad/react/react_followImports.js.map +1 -0
- package/build/lazyLoad/react/react_getScriptTags.js +28 -5
- package/build/lazyLoad/react/react_getScriptTags.js.map +1 -1
- package/build/lazyLoad/svelte/svelte_discoverPagesFromJs.js +162 -0
- package/build/lazyLoad/svelte/svelte_discoverPagesFromJs.js.map +1 -0
- package/build/lazyLoad/svelte/svelte_getFromPageSource.js +15 -0
- package/build/lazyLoad/svelte/svelte_getFromPageSource.js.map +1 -1
- package/build/lazyLoad/svelte/svelte_recursivePageCrawl.js +180 -0
- package/build/lazyLoad/svelte/svelte_recursivePageCrawl.js.map +1 -0
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +15 -1
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -1
- package/build/lazyLoad/techDetect/checkReact.js +67 -36
- package/build/lazyLoad/techDetect/checkReact.js.map +1 -1
- package/build/lazyLoad/techDetect/checkSvelte.js +35 -35
- package/build/lazyLoad/techDetect/checkSvelte.js.map +1 -1
- package/build/lazyLoad/techDetect/index.js +31 -25
- package/build/lazyLoad/techDetect/index.js.map +1 -1
- package/build/lazyLoad/vue/vue_getClientSidePaths.js +6 -0
- package/build/lazyLoad/vue/vue_getClientSidePaths.js.map +1 -1
- package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js +6 -0
- package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js.map +1 -1
- package/build/lazyLoad/vue/vue_stringJsFiles.js +6 -0
- package/build/lazyLoad/vue/vue_stringJsFiles.js.map +1 -1
- package/build/load/index.js +316 -0
- package/build/load/index.js.map +1 -0
- package/build/map/graphql/resolveGraphql.js +296 -0
- package/build/map/graphql/resolveGraphql.js.map +1 -0
- package/build/map/index.js +104 -6
- package/build/map/index.js.map +1 -1
- package/build/map/next_js/interactive.js +30 -0
- package/build/map/next_js/interactive.js.map +1 -1
- package/build/map/next_js/interactive_helpers/commandHandler.js +26 -0
- package/build/map/next_js/interactive_helpers/commandHandler.js.map +1 -1
- package/build/map/next_js/interactive_helpers/commandHelpers.js +28 -0
- package/build/map/next_js/interactive_helpers/commandHelpers.js.map +1 -1
- package/build/map/next_js/interactive_helpers/esqueryGen.js +370 -0
- package/build/map/next_js/interactive_helpers/esqueryGen.js.map +1 -0
- package/build/map/next_js/interactive_helpers/helpMenu.js +2 -1
- package/build/map/next_js/interactive_helpers/helpMenu.js.map +1 -1
- package/build/map/next_js/interactive_helpers/inputPatch.js +207 -0
- package/build/map/next_js/interactive_helpers/inputPatch.js.map +1 -0
- package/build/map/next_js/interactive_helpers/ui.js +0 -1
- package/build/map/next_js/interactive_helpers/ui.js.map +1 -1
- package/build/map/next_js/resolveServerActions.js +449 -0
- package/build/map/next_js/resolveServerActions.js.map +1 -0
- package/build/map/next_js/utils.js +89 -2
- package/build/map/next_js/utils.js.map +1 -1
- package/build/map/react_js/getReactConnections.js +298 -0
- package/build/map/react_js/getReactConnections.js.map +1 -0
- package/build/map/react_js/interactive.js +4 -0
- package/build/map/react_js/interactive.js.map +1 -0
- package/build/map/react_js/react_resolveFetch.js +6 -0
- package/build/map/react_js/react_resolveFetch.js.map +1 -0
- package/build/map/svelte_js/interactive.js +58 -0
- package/build/map/svelte_js/interactive.js.map +1 -0
- package/build/map/svelte_js/interactive_helpers/commandHandler.js +4 -0
- package/build/map/svelte_js/interactive_helpers/commandHandler.js.map +1 -0
- package/build/map/vue_js/bodyResolver.js +477 -0
- package/build/map/vue_js/bodyResolver.js.map +1 -0
- package/build/map/vue_js/crossFileResolver.js +438 -0
- package/build/map/vue_js/crossFileResolver.js.map +1 -0
- package/build/map/vue_js/getViteConnections.js +151 -106
- package/build/map/vue_js/getViteConnections.js.map +1 -1
- package/build/map/vue_js/interactive.js +28 -0
- package/build/map/vue_js/interactive.js.map +1 -1
- package/build/map/vue_js/interactive_helpers/commandHandler.js +22 -0
- package/build/map/vue_js/interactive_helpers/commandHandler.js.map +1 -1
- package/build/map/vue_js/interactive_helpers/helpMenu.js +1 -0
- package/build/map/vue_js/interactive_helpers/helpMenu.js.map +1 -1
- package/build/map/vue_js/taint_utils.js +621 -0
- package/build/map/vue_js/taint_utils.js.map +1 -0
- package/build/map/vue_js/vue_resolveFetch.js +279 -25
- package/build/map/vue_js/vue_resolveFetch.js.map +1 -1
- package/build/map/vue_js/vue_resolveHttpClient.js +733 -0
- package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -0
- package/build/map/vue_js/vue_resolveXhr.js +279 -0
- package/build/map/vue_js/vue_resolveXhr.js.map +1 -0
- package/build/mcp/chatOneShot.js +101 -0
- package/build/mcp/chatOneShot.js.map +1 -0
- package/build/mcp/claudeCodeCreds.js +150 -0
- package/build/mcp/claudeCodeCreds.js.map +1 -0
- package/build/mcp/cli.js +140 -149
- package/build/mcp/cli.js.map +1 -1
- package/build/mcp/commands.js +80 -0
- package/build/mcp/commands.js.map +1 -1
- package/build/mcp/index.js +30 -9
- package/build/mcp/index.js.map +1 -1
- package/build/mcp/intent.js +204 -0
- package/build/mcp/intent.js.map +1 -0
- package/build/mcp/jobs.js +199 -0
- package/build/mcp/jobs.js.map +1 -0
- package/build/mcp/mcpServer.js +241 -0
- package/build/mcp/mcpServer.js.map +1 -0
- package/build/mcp/providers.js +18 -1
- package/build/mcp/providers.js.map +1 -1
- package/build/mcp/skills.js +115 -0
- package/build/mcp/skills.js.map +1 -0
- package/build/refactor/index.js +6 -0
- package/build/refactor/index.js.map +1 -1
- package/build/refactor/react/index.js +636 -0
- package/build/refactor/react/index.js.map +1 -0
- package/build/report/utility/populateDb/populateAnalysisFindings.js +1 -1
- package/build/report/utility/populateDb/populateAnalysisFindings.js.map +1 -1
- package/build/run/index.js +277 -60
- package/build/run/index.js.map +1 -1
- package/build/run/interruptHandler.js +93 -0
- package/build/run/interruptHandler.js.map +1 -0
- package/build/utility/globals.js +38 -0
- package/build/utility/globals.js.map +1 -1
- package/build/utility/makeReq.js +39 -5
- package/build/utility/makeReq.js.map +1 -1
- package/build/utility/openapiGenerator.js +61 -17
- package/build/utility/openapiGenerator.js.map +1 -1
- package/build/utility/postmanGenerator.js +69 -16
- package/build/utility/postmanGenerator.js.map +1 -1
- package/build/utility/progressLog.js +50 -0
- package/build/utility/progressLog.js.map +1 -0
- package/package.json +7 -4
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
import parser from "@babel/parser";
|
|
12
|
+
import _traverse from "@babel/traverse";
|
|
13
|
+
import _generator from "@babel/generator";
|
|
14
|
+
import * as t from "@babel/types";
|
|
15
|
+
const traverse = _traverse.default;
|
|
16
|
+
const generate = _generator.default;
|
|
17
|
+
/**
|
|
18
|
+
* Refactors a webpack-bundled React chunk into a more readable form.
|
|
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>(...)`).
|
|
27
|
+
*
|
|
28
|
+
* Two layers of rewrites happen:
|
|
29
|
+
*
|
|
30
|
+
* 1. Webpack-level:
|
|
31
|
+
* - `<require>(<n>)` → `require("./<n>.js")`
|
|
32
|
+
* - `<require>.d(<exports>, { k: () => local })` → captured into export map
|
|
33
|
+
* - `Object.defineProperty(<exports>, "k", { ... })` → captured into export map
|
|
34
|
+
*
|
|
35
|
+
* 2. React-level:
|
|
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.
|
|
46
|
+
*/
|
|
47
|
+
const REACT_HOOK_NAMES = new Set([
|
|
48
|
+
"useState",
|
|
49
|
+
"useEffect",
|
|
50
|
+
"useRef",
|
|
51
|
+
"useContext",
|
|
52
|
+
"useReducer",
|
|
53
|
+
"useMemo",
|
|
54
|
+
"useCallback",
|
|
55
|
+
"useId",
|
|
56
|
+
"useTransition",
|
|
57
|
+
"useLayoutEffect",
|
|
58
|
+
"useDeferredValue",
|
|
59
|
+
"useImperativeHandle",
|
|
60
|
+
"useDebugValue",
|
|
61
|
+
"useSyncExternalStore",
|
|
62
|
+
"useInsertionEffect",
|
|
63
|
+
"useOptimistic",
|
|
64
|
+
"useActionState",
|
|
65
|
+
"useFormStatus",
|
|
66
|
+
]);
|
|
67
|
+
const REACT_TOP_LEVEL_API_NAMES = new Set([
|
|
68
|
+
"createContext",
|
|
69
|
+
"createElement",
|
|
70
|
+
"createRef",
|
|
71
|
+
"forwardRef",
|
|
72
|
+
"memo",
|
|
73
|
+
"lazy",
|
|
74
|
+
"startTransition",
|
|
75
|
+
"Fragment",
|
|
76
|
+
"Children",
|
|
77
|
+
"cloneElement",
|
|
78
|
+
"isValidElement",
|
|
79
|
+
"use",
|
|
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}`));
|
|
86
|
+
const ast = parser.parse(chunk.code, {
|
|
87
|
+
sourceType: "unambiguous",
|
|
88
|
+
plugins: ["jsx", "typescript"],
|
|
89
|
+
errorRecovery: true,
|
|
90
|
+
});
|
|
91
|
+
const modules = [];
|
|
92
|
+
const captureModule = (id, fnPath, params) => {
|
|
93
|
+
if (params.length < 2)
|
|
94
|
+
return;
|
|
95
|
+
const m = params[0], e = params[1], r = params.length >= 3 ? params[2] : undefined;
|
|
96
|
+
if (!t.isIdentifier(m) || !t.isIdentifier(e))
|
|
97
|
+
return;
|
|
98
|
+
if (r !== undefined && !t.isIdentifier(r))
|
|
99
|
+
return;
|
|
100
|
+
modules.push({
|
|
101
|
+
id,
|
|
102
|
+
fnPath,
|
|
103
|
+
moduleParam: m.name,
|
|
104
|
+
exportsParam: e.name,
|
|
105
|
+
requireParam: r ? r.name : undefined,
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
// Only capture entries whose key is numeric AND whose grandparent is an
|
|
109
|
+
// ObjectExpression at module level (the webpack module map). This filters
|
|
110
|
+
// out the many `useImperativeHandle(e, n, t) { ... }` ObjectMethods inside
|
|
111
|
+
// React's own source, which happen to share the 3-param shape.
|
|
112
|
+
const isInModuleMap = (path) => {
|
|
113
|
+
const objectParent = path.parentPath;
|
|
114
|
+
if (!objectParent || !objectParent.isObjectExpression())
|
|
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
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (reExportTarget) {
|
|
187
|
+
reExportTargetOf.set(mod.id, reExportTarget);
|
|
188
|
+
}
|
|
189
|
+
mod.fnPath.traverse({
|
|
190
|
+
CallExpression(p) {
|
|
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
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const reactImports = new Set();
|
|
303
|
+
const jsxRuntimeImports = new Set();
|
|
304
|
+
const reactDomImports = new Set();
|
|
305
|
+
for (const mod of modules) {
|
|
306
|
+
if (!mod.requireParam)
|
|
307
|
+
continue; // 2-param modules can't have require()
|
|
308
|
+
const localAliases = new Map();
|
|
309
|
+
mod.fnPath.traverse({
|
|
310
|
+
VariableDeclarator(p) {
|
|
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
|
+
},
|
|
415
|
+
});
|
|
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();
|
|
425
|
+
traverse(ast, {
|
|
426
|
+
VariableDeclarator(p) {
|
|
427
|
+
const id = p.node.id;
|
|
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
|
+
},
|
|
442
|
+
});
|
|
443
|
+
if (globalAliases.size > 0) {
|
|
444
|
+
traverse(ast, {
|
|
445
|
+
CallExpression: {
|
|
446
|
+
exit(p) {
|
|
447
|
+
const node = p.node;
|
|
448
|
+
if (t.isSequenceExpression(node.callee) &&
|
|
449
|
+
node.callee.expressions.length === 2 &&
|
|
450
|
+
t.isNumericLiteral(node.callee.expressions[0], { value: 0 }) &&
|
|
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
|
+
});
|
|
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
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const parts = [];
|
|
547
|
+
if (named.length > 0) {
|
|
548
|
+
const namedList = named.map(({ key, local }) => (key === local ? key : `${local} as ${key}`)).join(", ");
|
|
549
|
+
parts.push(`/* webpack-derived exports — keys may collide across modules in the chunk */`);
|
|
550
|
+
parts.push(`export { ${namedList} };`);
|
|
551
|
+
}
|
|
552
|
+
parts.push(...defaultLines);
|
|
553
|
+
if (parts.length > 0)
|
|
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);
|
|
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
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return member;
|
|
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
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
if (t.isFunctionExpression(node) && t.isBlockStatement(node.body)) {
|
|
627
|
+
for (const stmt of node.body.body) {
|
|
628
|
+
if (t.isReturnStatement(stmt) && stmt.argument && t.isIdentifier(stmt.argument)) {
|
|
629
|
+
return stmt.argument.name;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return null;
|
|
634
|
+
};
|
|
635
|
+
export default refactorReact;
|
|
636
|
+
//# sourceMappingURL=index.js.map
|