@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.
Files changed (58) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/CLAUDE.md +45 -0
  3. package/README.md +1 -0
  4. package/build/cs_mast/index.js +122 -0
  5. package/build/cs_mast/index.js.map +1 -0
  6. package/build/globalConfig.js +2 -2
  7. package/build/globalConfig.js.map +1 -1
  8. package/build/index.js +67 -7
  9. package/build/index.js.map +1 -1
  10. package/build/lazyLoad/downloadLoadedJsUtil.js +6 -1
  11. package/build/lazyLoad/downloadLoadedJsUtil.js.map +1 -1
  12. package/build/lazyLoad/index.js +72 -38
  13. package/build/lazyLoad/index.js.map +1 -1
  14. package/build/lazyLoad/methodFilter.js +49 -0
  15. package/build/lazyLoad/methodFilter.js.map +1 -0
  16. package/build/lazyLoad/next_js/NextJsCrawler.js +69 -48
  17. package/build/lazyLoad/next_js/NextJsCrawler.js.map +1 -1
  18. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +6 -1
  19. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
  20. package/build/lazyLoad/techDetect/checkNextJS.js +24 -15
  21. package/build/lazyLoad/techDetect/checkNextJS.js.map +1 -1
  22. package/build/lazyLoad/techDetect/checkSvelte.js +17 -0
  23. package/build/lazyLoad/techDetect/checkSvelte.js.map +1 -1
  24. package/build/lazyLoad/techDetect/index.js +11 -4
  25. package/build/lazyLoad/techDetect/index.js.map +1 -1
  26. package/build/lazyLoad/vue/vue_discoverJsFiles.js +47 -25
  27. package/build/lazyLoad/vue/vue_discoverJsFiles.js.map +1 -1
  28. package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js +2 -2
  29. package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js.map +1 -1
  30. package/build/map/vue_js/vue_resolveHttpClient.js +2 -1
  31. package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -1
  32. package/build/map/vue_js/vue_resolveXhr.js +2 -1
  33. package/build/map/vue_js/vue_resolveXhr.js.map +1 -1
  34. package/build/refactor/index.js +133 -22
  35. package/build/refactor/index.js.map +1 -1
  36. package/build/refactor/next/helpers.js +214 -0
  37. package/build/refactor/next/helpers.js.map +1 -0
  38. package/build/refactor/next/index.js +40 -73
  39. package/build/refactor/next/index.js.map +1 -1
  40. package/build/refactor/next/transform.js +177 -0
  41. package/build/refactor/next/transform.js.map +1 -0
  42. package/build/refactor/next/validator.js +118 -0
  43. package/build/refactor/next/validator.js.map +1 -0
  44. package/build/refactor/react/helpers.js +93 -0
  45. package/build/refactor/react/helpers.js.map +1 -0
  46. package/build/refactor/react/index.js +186 -589
  47. package/build/refactor/react/index.js.map +1 -1
  48. package/build/refactor/react/library-classify.js +166 -0
  49. package/build/refactor/react/library-classify.js.map +1 -0
  50. package/build/refactor/react/transform.js +839 -0
  51. package/build/refactor/react/transform.js.map +1 -0
  52. package/build/refactor/react/validator.js +122 -0
  53. package/build/refactor/react/validator.js.map +1 -0
  54. package/build/utility/getChromiumPath.js +40 -0
  55. package/build/utility/getChromiumPath.js.map +1 -0
  56. package/build/utility/makeReq.js +11 -2
  57. package/build/utility/makeReq.js.map +1 -1
  58. 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
- * 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>(...)`).
61
+ * Rewrites a webpack-bundled React chunk by splitting the numeric module map
62
+ * into individual ECMAScript module files.
27
63
  *
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.
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 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}`));
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 captureModule = (id, fnPath, params) => {
93
- if (params.length < 2)
120
+ const captureProperty = (path) => {
121
+ if (!isInModuleMap(path))
94
122
  return;
95
- const m = params[0], e = params[1], r = params.length >= 3 ? params[2] : undefined;
96
- if (!t.isIdentifier(m) || !t.isIdentifier(e))
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
- if (r !== undefined && !t.isIdentifier(r))
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
- moduleParam: m.name,
104
- exportsParam: e.name,
105
- requireParam: r ? r.name : undefined,
144
+ fnPath: path.get("value"),
145
+ paramCount: params.length,
146
+ moduleParam,
147
+ exportsParam,
148
+ requireParam,
106
149
  });
107
150
  };
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
- }
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
- 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
- }
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
- 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
- },
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
- 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
- },
180
+ ObjectProperty: captureProperty,
181
+ ObjectMethod: captureMethod,
442
182
  });
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
- }
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 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} };`);
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
- parts.push(...defaultLines);
553
- if (parts.length > 0)
554
- trailingExports = `\n\n${parts.join("\n")}`;
203
+ results[mod.id] = code;
555
204
  }
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};`;
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
- 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
- }
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
- 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
- }
219
+ else {
220
+ indexStatements.push(stmt);
624
221
  }
625
222
  }
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
- }
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 null;
634
- };
230
+ return results;
231
+ });
635
232
  export default refactorReact;
636
233
  //# sourceMappingURL=index.js.map