@js-recon/js-recon 1.3.2 → 1.4.1-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/.github/workflows/build-and-prettify.yaml +207 -14
  2. package/.github/workflows/pr_checker.yml +2 -2
  3. package/.github/workflows/promote-js-recon.yml +104 -0
  4. package/.github/workflows/publish-js-recon.yml +22 -80
  5. package/.github/workflows/rules-smoke-test.yaml +79 -0
  6. package/CHANGELOG.md +177 -4
  7. package/CLAUDE.md +350 -16
  8. package/Dockerfile.release +29 -0
  9. package/README.md +48 -19
  10. package/build/analyze/engine/csMastSEngine.js +129 -0
  11. package/build/analyze/engine/csMastSEngine.js.map +1 -0
  12. package/build/analyze/engine/index.js +15 -0
  13. package/build/analyze/engine/index.js.map +1 -1
  14. package/build/analyze/helpers/engineHelpers/findDirectAssignment.js +2 -1
  15. package/build/analyze/helpers/engineHelpers/findDirectAssignment.js.map +1 -1
  16. package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js +2 -1
  17. package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js.map +1 -1
  18. package/build/analyze/helpers/engineHelpers/resolveFunctionIdentifier.js +2 -1
  19. package/build/analyze/helpers/engineHelpers/resolveFunctionIdentifier.js.map +1 -1
  20. package/build/analyze/helpers/engineHelpers/taintFlow.js +11 -2
  21. package/build/analyze/helpers/engineHelpers/taintFlow.js.map +1 -1
  22. package/build/analyze/helpers/initRules.js.map +1 -1
  23. package/build/analyze/helpers/schemas.js +6 -2
  24. package/build/analyze/helpers/schemas.js.map +1 -1
  25. package/build/analyze/helpers/validate.js +3 -3
  26. package/build/analyze/helpers/validate.js.map +1 -1
  27. package/build/analyze/index.js +1 -0
  28. package/build/analyze/index.js.map +1 -1
  29. package/build/completion/index.js +432 -0
  30. package/build/completion/index.js.map +1 -0
  31. package/build/cs_mast/index.js +213 -0
  32. package/build/cs_mast/index.js.map +1 -0
  33. package/build/endpoints/next_js/client_jsFilesHref.js +2 -1
  34. package/build/endpoints/next_js/client_jsFilesHref.js.map +1 -1
  35. package/build/endpoints/next_js/client_jsonParse.js +2 -1
  36. package/build/endpoints/next_js/client_jsonParse.js.map +1 -1
  37. package/build/endpoints/next_js/client_mappedJsonFile.js +2 -1
  38. package/build/endpoints/next_js/client_mappedJsonFile.js.map +1 -1
  39. package/build/endpoints/next_js/client_subsequentRequests.js +2 -1
  40. package/build/endpoints/next_js/client_subsequentRequests.js.map +1 -1
  41. package/build/fingerprint/index.js +56 -33
  42. package/build/fingerprint/index.js.map +1 -1
  43. package/build/globalConfig.js +3 -3
  44. package/build/globalConfig.js.map +1 -1
  45. package/build/index.js +210 -10
  46. package/build/index.js.map +1 -1
  47. package/build/lazyLoad/angular/angular_getFromMainJs.js +2 -1
  48. package/build/lazyLoad/angular/angular_getFromMainJs.js.map +1 -1
  49. package/build/lazyLoad/downloadFilesUtil.js +6 -5
  50. package/build/lazyLoad/downloadFilesUtil.js.map +1 -1
  51. package/build/lazyLoad/downloadLoadedJsUtil.js +59 -5
  52. package/build/lazyLoad/downloadLoadedJsUtil.js.map +1 -1
  53. package/build/lazyLoad/downloadQueue.js +10 -9
  54. package/build/lazyLoad/downloadQueue.js.map +1 -1
  55. package/build/lazyLoad/index.js +127 -85
  56. package/build/lazyLoad/index.js.map +1 -1
  57. package/build/lazyLoad/methodFilter.js +50 -0
  58. package/build/lazyLoad/methodFilter.js.map +1 -0
  59. package/build/lazyLoad/next_js/NextJsCrawler.js +69 -48
  60. package/build/lazyLoad/next_js/NextJsCrawler.js.map +1 -1
  61. package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js +36 -34
  62. package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js.map +1 -1
  63. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +66 -13
  64. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
  65. package/build/lazyLoad/next_js/next_parseLayoutJs.js +80 -68
  66. package/build/lazyLoad/next_js/next_parseLayoutJs.js.map +1 -1
  67. package/build/lazyLoad/next_js/next_promiseResolve.js +22 -16
  68. package/build/lazyLoad/next_js/next_promiseResolve.js.map +1 -1
  69. package/build/lazyLoad/nuxt_js/nuxt_astParse.js +49 -47
  70. package/build/lazyLoad/nuxt_js/nuxt_astParse.js.map +1 -1
  71. package/build/lazyLoad/nuxt_js/nuxt_getBuildsManifest.js +64 -0
  72. package/build/lazyLoad/nuxt_js/nuxt_getBuildsManifest.js.map +1 -0
  73. package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +3 -1
  74. package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js.map +1 -1
  75. package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +3 -2
  76. package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js.map +1 -1
  77. package/build/lazyLoad/react/react_followImports.js +16 -7
  78. package/build/lazyLoad/react/react_followImports.js.map +1 -1
  79. package/build/lazyLoad/react/react_webpackChunkPaths.js +175 -123
  80. package/build/lazyLoad/react/react_webpackChunkPaths.js.map +1 -1
  81. package/build/lazyLoad/svelte/svelte_getFromPageSource.js +16 -0
  82. package/build/lazyLoad/svelte/svelte_getFromPageSource.js.map +1 -1
  83. package/build/lazyLoad/svelte/svelte_getVersionJson.js +40 -0
  84. package/build/lazyLoad/svelte/svelte_getVersionJson.js.map +1 -0
  85. package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +3 -2
  86. package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -1
  87. package/build/lazyLoad/techDetect/checkAngularJS.js +27 -6
  88. package/build/lazyLoad/techDetect/checkAngularJS.js.map +1 -1
  89. package/build/lazyLoad/techDetect/checkNextJS.js +24 -15
  90. package/build/lazyLoad/techDetect/checkNextJS.js.map +1 -1
  91. package/build/lazyLoad/techDetect/checkReact.js +8 -0
  92. package/build/lazyLoad/techDetect/checkReact.js.map +1 -1
  93. package/build/lazyLoad/techDetect/checkSvelte.js +17 -0
  94. package/build/lazyLoad/techDetect/checkSvelte.js.map +1 -1
  95. package/build/lazyLoad/techDetect/index.js +33 -5
  96. package/build/lazyLoad/techDetect/index.js.map +1 -1
  97. package/build/lazyLoad/vue/vue_RuntimeJs.js +2 -1
  98. package/build/lazyLoad/vue/vue_RuntimeJs.js.map +1 -1
  99. package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js +2 -1
  100. package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js.map +1 -1
  101. package/build/lazyLoad/vue/vue_discoverJsFiles.js +59 -25
  102. package/build/lazyLoad/vue/vue_discoverJsFiles.js.map +1 -1
  103. package/build/lazyLoad/vue/vue_getClientSidePaths.js +2 -1
  104. package/build/lazyLoad/vue/vue_getClientSidePaths.js.map +1 -1
  105. package/build/lazyLoad/vue/vue_jsImports.js +2 -1
  106. package/build/lazyLoad/vue/vue_jsImports.js.map +1 -1
  107. package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js +2 -2
  108. package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js.map +1 -1
  109. package/build/lazyLoad/vue/vue_viteMapDeps.js +62 -54
  110. package/build/lazyLoad/vue/vue_viteMapDeps.js.map +1 -1
  111. package/build/map/angular_js/getAngularConnections.js +88 -0
  112. package/build/map/angular_js/getAngularConnections.js.map +1 -0
  113. package/build/map/angular_js/interactive.js +4 -0
  114. package/build/map/angular_js/interactive.js.map +1 -0
  115. package/build/map/graphql/resolveGraphql.js +2 -1
  116. package/build/map/graphql/resolveGraphql.js.map +1 -1
  117. package/build/map/index.js +38 -0
  118. package/build/map/index.js.map +1 -1
  119. package/build/map/next_js/getExports.js +2 -1
  120. package/build/map/next_js/getExports.js.map +1 -1
  121. package/build/map/next_js/getFetchInstances.js +2 -1
  122. package/build/map/next_js/getFetchInstances.js.map +1 -1
  123. package/build/map/next_js/getTurbopackConnections.js +2 -1
  124. package/build/map/next_js/getTurbopackConnections.js.map +1 -1
  125. package/build/map/next_js/getWebpackConnections.js +2 -1
  126. package/build/map/next_js/getWebpackConnections.js.map +1 -1
  127. package/build/map/next_js/resolveAxios.js +2 -1
  128. package/build/map/next_js/resolveAxios.js.map +1 -1
  129. package/build/map/next_js/resolveAxiosHelpers/directCallsWithoutAssignment.js +2 -1
  130. package/build/map/next_js/resolveAxiosHelpers/directCallsWithoutAssignment.js.map +1 -1
  131. package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js +2 -1
  132. package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js.map +1 -1
  133. package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js +2 -1
  134. package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js.map +1 -1
  135. package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js +2 -1
  136. package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js.map +1 -1
  137. package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js +2 -1
  138. package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js.map +1 -1
  139. package/build/map/next_js/resolveAxiosHelpers/interceptorHeaders.js +2 -1
  140. package/build/map/next_js/resolveAxiosHelpers/interceptorHeaders.js.map +1 -1
  141. package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js +2 -1
  142. package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js.map +1 -1
  143. package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js +2 -1
  144. package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js.map +1 -1
  145. package/build/map/next_js/resolveAxiosHelpers/traceBody.js +2 -1
  146. package/build/map/next_js/resolveAxiosHelpers/traceBody.js.map +1 -1
  147. package/build/map/next_js/resolveFetch.js +2 -1
  148. package/build/map/next_js/resolveFetch.js.map +1 -1
  149. package/build/map/next_js/resolveNewRequest.js +2 -1
  150. package/build/map/next_js/resolveNewRequest.js.map +1 -1
  151. package/build/map/next_js/resolveServerActions.js +2 -1
  152. package/build/map/next_js/resolveServerActions.js.map +1 -1
  153. package/build/map/next_js/utils.js +2 -1
  154. package/build/map/next_js/utils.js.map +1 -1
  155. package/build/map/react_js/getReactConnections.js +5 -4
  156. package/build/map/react_js/getReactConnections.js.map +1 -1
  157. package/build/map/vue_js/crossFileResolver.js +3 -2
  158. package/build/map/vue_js/crossFileResolver.js.map +1 -1
  159. package/build/map/vue_js/getViteConnections.js +5 -4
  160. package/build/map/vue_js/getViteConnections.js.map +1 -1
  161. package/build/map/vue_js/taint_utils.js +2 -1
  162. package/build/map/vue_js/taint_utils.js.map +1 -1
  163. package/build/map/vue_js/vue_resolveFetch.js +3 -2
  164. package/build/map/vue_js/vue_resolveFetch.js.map +1 -1
  165. package/build/map/vue_js/vue_resolveHttpClient.js +5 -3
  166. package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -1
  167. package/build/map/vue_js/vue_resolveXhr.js +5 -3
  168. package/build/map/vue_js/vue_resolveXhr.js.map +1 -1
  169. package/build/mcp/cli.js.map +1 -1
  170. package/build/refactor/index.js +973 -27
  171. package/build/refactor/index.js.map +1 -1
  172. package/build/refactor/next/helpers.js +395 -0
  173. package/build/refactor/next/helpers.js.map +1 -0
  174. package/build/refactor/next/index.js +261 -71
  175. package/build/refactor/next/index.js.map +1 -1
  176. package/build/refactor/next/transform.js +1027 -0
  177. package/build/refactor/next/transform.js.map +1 -0
  178. package/build/refactor/next/validator.js +119 -0
  179. package/build/refactor/next/validator.js.map +1 -0
  180. package/build/refactor/react/helpers.js +106 -0
  181. package/build/refactor/react/helpers.js.map +1 -0
  182. package/build/refactor/react/index.js +290 -585
  183. package/build/refactor/react/index.js.map +1 -1
  184. package/build/refactor/react/library-classify.js +397 -0
  185. package/build/refactor/react/library-classify.js.map +1 -0
  186. package/build/refactor/react/transform.js +1852 -0
  187. package/build/refactor/react/transform.js.map +1 -0
  188. package/build/refactor/react/validator.js +123 -0
  189. package/build/refactor/react/validator.js.map +1 -0
  190. package/build/refactor/react-vite/index.js +652 -0
  191. package/build/refactor/react-vite/index.js.map +1 -0
  192. package/build/refactor/react-vite/vendor-analyze.js +336 -0
  193. package/build/refactor/react-vite/vendor-analyze.js.map +1 -0
  194. package/build/refactor/remote/cache.js +250 -0
  195. package/build/refactor/remote/cache.js.map +1 -0
  196. package/build/refactor/remote/config.js +63 -0
  197. package/build/refactor/remote/config.js.map +1 -0
  198. package/build/refactor/remote/hf-client.js +202 -0
  199. package/build/refactor/remote/hf-client.js.map +1 -0
  200. package/build/refactor/remote/version-detect.js +355 -0
  201. package/build/refactor/remote/version-detect.js.map +1 -0
  202. package/build/refactor/vue/index.js +176 -0
  203. package/build/refactor/vue/index.js.map +1 -0
  204. package/build/refactor/vue/vendor-analyze-vue.js +271 -0
  205. package/build/refactor/vue/vendor-analyze-vue.js.map +1 -0
  206. package/build/refactor/vue/vite.js +216 -0
  207. package/build/refactor/vue/vite.js.map +1 -0
  208. package/build/report/utility/genHtml.js +15 -4
  209. package/build/report/utility/genHtml.js.map +1 -1
  210. package/build/run/bundler-detect.js +154 -0
  211. package/build/run/bundler-detect.js.map +1 -0
  212. package/build/run/index.js +208 -16
  213. package/build/run/index.js.map +1 -1
  214. package/build/sourcemaps/index.js +89 -0
  215. package/build/sourcemaps/index.js.map +1 -0
  216. package/build/strings/index.js +6 -2
  217. package/build/strings/index.js.map +1 -1
  218. package/build/strings/trufflehog.js +71 -0
  219. package/build/strings/trufflehog.js.map +1 -0
  220. package/build/utility/banner.js +68 -0
  221. package/build/utility/banner.js.map +1 -0
  222. package/build/utility/getChromiumPath.js +40 -0
  223. package/build/utility/getChromiumPath.js.map +1 -0
  224. package/build/utility/globals.js +7 -0
  225. package/build/utility/globals.js.map +1 -1
  226. package/build/utility/makeReq.js +21 -11
  227. package/build/utility/makeReq.js.map +1 -1
  228. package/build/utility/postmanGenerator.js +2 -2
  229. package/build/utility/postmanGenerator.js.map +1 -1
  230. package/build/utility/puppeteerInstance.js +7 -1
  231. package/build/utility/puppeteerInstance.js.map +1 -1
  232. package/package.json +14 -8
  233. package/scripts/smoke-test.js +74 -0
@@ -1,3 +1,5 @@
1
+ // ECMAScript export reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
2
+ // ECMAScript import reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
1
3
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
4
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
5
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -7,630 +9,333 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
9
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
10
  });
9
11
  };
12
+ var _a, _b;
10
13
  import chalk from "chalk";
11
14
  import parser from "@babel/parser";
12
15
  import _traverse from "@babel/traverse";
13
16
  import _generator from "@babel/generator";
14
17
  import * as t from "@babel/types";
15
- const traverse = _traverse.default;
16
- const generate = _generator.default;
18
+ import { cs_mast_init } from "@shriyanss/cs-mast";
19
+ import { isInModuleMap } from "./helpers.js";
20
+ import { validateAndFix } from "./validator.js";
21
+ import { transformModule, transformIndexStatements, applyModuleCleanupPasses, applyLibraryImportRewriting, renameRouteComponents, } from "./transform.js";
22
+ import { classifyLibraryModule, resolveReexportChains } from "./library-classify.js";
23
+ const traverse = ((_a = _traverse.default) !== null && _a !== void 0 ? _a : _traverse);
24
+ const generate = (_b = _generator.default) !== null && _b !== void 0 ? _b : _generator;
25
+ // scat config used when computing experiment-baseline signatures (matches
26
+ // refactor_observations/feature-signatures/<feature>/lit-decl-loop-cond/collisions.json).
27
+ const LIB_SIG_SCAT = ["lit", "decl", "loop", "cond"];
28
+ // Returns the body statements of a top-level IIFE (e.g. `(() => { … })()`), or null.
29
+ const findIifeBody = (program) => {
30
+ for (const stmt of program.body) {
31
+ if (!t.isExpressionStatement(stmt))
32
+ continue;
33
+ const expr = stmt.expression;
34
+ if (!t.isCallExpression(expr) || expr.arguments.length !== 0)
35
+ continue;
36
+ const callee = expr.callee;
37
+ if ((t.isArrowFunctionExpression(callee) || t.isFunctionExpression(callee)) &&
38
+ t.isBlockStatement(callee.body)) {
39
+ return callee.body.body;
40
+ }
41
+ }
42
+ return null;
43
+ };
44
+ // Returns true when stmt is the webpack lazy-chunk push call:
45
+ // (self.webpackChunk...||[]).push([[chunkId,...], {moduleId: fn, ...}])
46
+ const isLazyChunkPushStmt = (stmt) => {
47
+ if (!t.isExpressionStatement(stmt))
48
+ return false;
49
+ const expr = stmt.expression;
50
+ if (!t.isCallExpression(expr))
51
+ return false;
52
+ const callee = expr.callee;
53
+ return t.isMemberExpression(callee) && t.isIdentifier(callee.property, { name: "push" });
54
+ };
55
+ // Returns true when a VariableDeclarator's init is the webpack numeric module map
56
+ // object (every property has a NumericLiteral key and a function value).
57
+ const isModuleMapDeclarator = (d) => {
58
+ if (!t.isObjectExpression(d.init))
59
+ return false;
60
+ const props = d.init.properties;
61
+ if (props.length === 0)
62
+ return false;
63
+ return props.every((p) => {
64
+ if (t.isObjectProperty(p))
65
+ return (t.isNumericLiteral(p.key) && (t.isFunctionExpression(p.value) || t.isArrowFunctionExpression(p.value)));
66
+ if (t.isObjectMethod(p))
67
+ return t.isNumericLiteral(p.key);
68
+ return false;
69
+ });
70
+ };
17
71
  /**
18
- * Refactors a webpack-bundled React chunk into a more readable form.
72
+ * Rewrites a webpack-bundled React chunk by splitting the numeric module map
73
+ * into individual ECMAScript module files.
19
74
  *
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.
75
+ * Step 1 – Find the `var X = { <numId>: function(e,n,t){…}, … }` module map.
76
+ * Step 2 – For each module:
77
+ * a) Convert `<moduleParam>.exports = <requireParam>(N)` → `export * from "./N.js"`
78
+ * (and `<moduleParam>.exports = <expr>` → `export default <expr>`),
79
+ * including inside top-level sequence expressions.
80
+ * b) Convert `<exportsParam>.<propName> = <rhs>` → ECMAScript named exports
81
+ * (per MDN export reference) for any module that has an exports param.
82
+ * c) Hoist `var <name> = <requireParam>(N)` to `import * as <name> from "./N.js"`.
83
+ * d) Replace remaining inline `<requireParam>(N)` calls with a synthesized
84
+ * namespace import reference.
85
+ * e) Strip the outer function wrapper.
86
+ * Step 3 – Validate generated code with Babel; iteratively drop/downgrade statements
87
+ * that still cause parse errors.
88
+ * Step 4 – Collect all IIFE body statements that are NOT part of the module map and
89
+ * write them to `index.js` (entrypoint bootstrap, app component functions,
90
+ * the ReactDOM.render call, etc.).
46
91
  */
47
- const 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}`));
92
+ const isLibrarySig = (sig, libSigs) => !!(sig && libSigs && libSigs.has(sig));
93
+ // Minimum fraction of a module's sub-tree signatures that must match the library
94
+ // baseline before the module is classified as library code. A value of 1.0 means
95
+ // every signature must match (too strict — misses partial-lib modules). A value
96
+ // of 0.0 means any single match suffices (too loose — false-positives on app modules
97
+ // that happen to share a helper with library code). 0.5 is a reasonable default:
98
+ // pure library modules score close to 1.0 while app modules with inline CSS helpers
99
+ // or a stray shared utility typically score well below 0.5.
100
+ const LIB_CLASSIFICATION_THRESHOLD = 0.51;
101
+ // Hash a single module's function body using cs_mast_init and look the signature
102
+ // up against the count=18 baseline set. Returns true when the fraction of the
103
+ // module's sub-tree signatures that match the baseline exceeds LIB_CLASSIFICATION_THRESHOLD.
104
+ const moduleIsLibrary = (mod, libSigs, scatOverride) => {
105
+ if (!libSigs || libSigs.size === 0)
106
+ return false;
107
+ const fnNode = mod.fnPath.node;
108
+ if (!t.isBlockStatement(fnNode.body))
109
+ return false;
110
+ const body = fnNode.body;
111
+ try {
112
+ const code = generate(body).code;
113
+ const tree = cs_mast_init(code, {
114
+ hash: "sha256",
115
+ scat: scatOverride !== null && scatOverride !== void 0 ? scatOverride : LIB_SIG_SCAT,
116
+ sinc: [],
117
+ lang: "js",
118
+ prsr: "@babel/parser",
119
+ });
120
+ const sigs = [...tree._signatureMap.keys()];
121
+ if (sigs.length === 0)
122
+ return false;
123
+ const matchCount = sigs.filter((sig) => isLibrarySig(sig, libSigs)).length;
124
+ const fraction = matchCount / sigs.length;
125
+ return fraction >= LIB_CLASSIFICATION_THRESHOLD;
126
+ }
127
+ catch (_a) {
128
+ // unparseable / unhashable — fall through and treat as non-library
129
+ }
130
+ return false;
131
+ };
132
+ // Returns true when a 1-param module looks like a style-loader runtime helper.
133
+ // Matches the addStyles module (contains domAPI + update) and the
134
+ // styleTagTransform module (sets cssText or calls createTextNode).
135
+ const isStyleLoaderModule = (mod) => {
136
+ if (mod.paramCount !== 1)
137
+ return false;
138
+ const fnNode = mod.fnPath.node;
139
+ if (!t.isBlockStatement(fnNode.body))
140
+ return false;
141
+ const body = fnNode.body;
142
+ try {
143
+ const code = generate(body).code;
144
+ if (code.includes("domAPI") && code.includes("update"))
145
+ return true;
146
+ if (code.includes("styleSheet") && code.includes("cssText"))
147
+ return true;
148
+ if (code.includes("styleTagTransform") || code.includes("insertStyleElement"))
149
+ return true;
150
+ }
151
+ catch (_a) {
152
+ // unparseable
153
+ }
154
+ return false;
155
+ };
156
+ // Returns true when a 3-param module is a CSS content module (pushes a CSS array
157
+ // with an id + CSS string via requireParam.d).
158
+ const isCssModuleEntry = (mod) => {
159
+ if (mod.paramCount !== 3)
160
+ return false;
161
+ const fnNode = mod.fnPath.node;
162
+ if (!t.isBlockStatement(fnNode.body))
163
+ return false;
164
+ const body = fnNode.body;
165
+ try {
166
+ const code = generate(body).code;
167
+ // CSS content modules push an array of [moduleId, cssString, ""] and register via .d
168
+ if (code.includes(".push(") && code.includes(".id,") && code.includes(".d("))
169
+ return true;
170
+ }
171
+ catch (_a) {
172
+ // unparseable
173
+ }
174
+ return false;
175
+ };
176
+ const refactorReact = (chunk, libSigs, externalLibModuleMap, classifyAllAsLibrary, scatOverride) => __awaiter(void 0, void 0, void 0, function* () {
177
+ console.log(chalk.cyan(`[i] Processing React bundle: ${chunk.id}`));
86
178
  const ast = parser.parse(chunk.code, {
87
179
  sourceType: "unambiguous",
88
180
  plugins: ["jsx", "typescript"],
89
181
  errorRecovery: true,
90
182
  });
91
183
  const modules = [];
92
- const captureModule = (id, fnPath, params) => {
93
- if (params.length < 2)
184
+ const captureProperty = (path) => {
185
+ if (!isInModuleMap(path))
186
+ return;
187
+ const key = path.node.key;
188
+ if (!t.isNumericLiteral(key)) {
189
+ if (t.isStringLiteral(key) && /[a-zA-Z]/.test(key.value)) {
190
+ console.log(chalk.yellow(`[!] Alphanumeric module ID "${key.value}" detected — not yet supported, skipping (please open a PR)`));
191
+ }
94
192
  return;
95
- const m = params[0], e = params[1], r = params.length >= 3 ? params[2] : undefined;
96
- if (!t.isIdentifier(m) || !t.isIdentifier(e))
193
+ }
194
+ const value = path.node.value;
195
+ if (!t.isFunctionExpression(value) && !t.isArrowFunctionExpression(value))
97
196
  return;
98
- if (r !== undefined && !t.isIdentifier(r))
197
+ const id = String(key.value);
198
+ const params = value.params;
199
+ if (params.length > 3) {
200
+ console.log(chalk.yellow(`[!] Module ${id} has ${params.length} params — not yet researched, skipping`));
99
201
  return;
202
+ }
203
+ const moduleParam = params[0] && t.isIdentifier(params[0]) ? params[0].name : "";
204
+ const exportsParam = params[1] && t.isIdentifier(params[1]) ? params[1].name : "";
205
+ const requireParam = params.length >= 3 && t.isIdentifier(params[2]) ? params[2].name : undefined;
100
206
  modules.push({
101
207
  id,
102
- fnPath,
103
- moduleParam: m.name,
104
- exportsParam: e.name,
105
- requireParam: r ? r.name : undefined,
208
+ fnPath: path.get("value"),
209
+ paramCount: params.length,
210
+ moduleParam,
211
+ exportsParam,
212
+ requireParam,
106
213
  });
107
214
  };
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
- }
215
+ const captureMethod = (path) => {
216
+ if (!isInModuleMap(path))
217
+ return;
218
+ const key = path.node.key;
219
+ if (!t.isNumericLiteral(key)) {
220
+ if (t.isStringLiteral(key) && /[a-zA-Z]/.test(key.value)) {
221
+ console.log(chalk.yellow(`[!] Alphanumeric module ID "${key.value}" detected — not yet supported, skipping (please open a PR)`));
184
222
  }
223
+ return;
185
224
  }
186
- 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
- }
225
+ const id = String(key.value);
226
+ const params = path.node.params;
227
+ if (params.length > 3) {
228
+ console.log(chalk.yellow(`[!] Module ${id} has ${params.length} params — not yet researched, skipping`));
229
+ return;
300
230
  }
301
- }
302
- 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
- },
231
+ const moduleParam = params[0] && t.isIdentifier(params[0]) ? params[0].name : "";
232
+ const exportsParam = params[1] && t.isIdentifier(params[1]) ? params[1].name : "";
233
+ const requireParam = params.length >= 3 && t.isIdentifier(params[2]) ? params[2].name : undefined;
234
+ modules.push({
235
+ id,
236
+ fnPath: path,
237
+ paramCount: params.length,
238
+ moduleParam,
239
+ exportsParam,
240
+ requireParam,
415
241
  });
416
- }
417
- // ──────────────────────────────────────────────────────────────────
418
- // Pass 4 (global): catch IIFE-level `var X = <any>(<numericId>)` aliases
419
- // and rewrite the user-entry callsites at the outermost scope, which the
420
- // per-module pass cannot reach because the entry-IIFE wrapper has 0 params
421
- // (no positional require). The catalog match keeps this safe — only known
422
- // React-family module IDs trigger alias substitution.
423
- // ──────────────────────────────────────────────────────────────────
424
- const globalAliases = new Map();
242
+ };
425
243
  traverse(ast, {
426
- 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
- },
244
+ ObjectProperty: captureProperty,
245
+ ObjectMethod: captureMethod,
442
246
  });
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
- });
247
+ console.log(chalk.cyan(`[i] Found ${modules.length} modules`));
248
+ // Detect lazy chunk bundles: no IIFE wrapper, program body is entirely push() calls.
249
+ // Modules in lazy chunks are always application code — skip library classification for them.
250
+ // Also don't generate index.js from the push statement (it contains no bootstrap logic).
251
+ const iifeBody = findIifeBody(ast.program);
252
+ const isLazyBundle = iifeBody === null && ast.program.body.length > 0 && ast.program.body.every(isLazyChunkPushStmt);
253
+ if (isLazyBundle) {
254
+ console.log(chalk.cyan(`[i] Detected lazy chunk format — skipping library classification`));
504
255
  }
505
- 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))
256
+ const files = {};
257
+ // moduleId → LibraryModuleInfo for modules that are library-classified (in this chunk)
258
+ const libModuleMap = new Map();
259
+ let libraryCount = 0;
260
+ // Collect non-library module statements before applying Pass D, so we can use the
261
+ // fully-resolved libModuleMap (including re-export chains) when rewriting imports.
262
+ const pendingModules = [];
263
+ for (const mod of modules) {
264
+ // classifyAllAsLibrary: used for vendor chunks outside mapped.json where we want
265
+ // to extract library export maps without generating any output files.
266
+ const isLib = classifyAllAsLibrary || (!isLazyBundle && moduleIsLibrary(mod, libSigs, scatOverride));
267
+ // Detect style-loader and CSS content modules regardless of baseline signatures.
268
+ if (!classifyAllAsLibrary && !isLib && !isLazyBundle) {
269
+ const styleLoaderType = isStyleLoaderModule(mod)
270
+ ? "style-loader"
271
+ : isCssModuleEntry(mod)
272
+ ? "css-module"
273
+ : null;
274
+ if (styleLoaderType !== null) {
275
+ console.log(chalk.gray(`[-] Module ${mod.id} detected as ${styleLoaderType} — skipping`));
276
+ libModuleMap.set(mod.id, { type: styleLoaderType, exportMap: new Map() });
277
+ libraryCount++;
530
278
  continue;
531
- usedLocals.add(entry.local);
532
- let key = entry.key;
533
- if (usedKeys.has(key)) {
534
- collisionIndex += 1;
535
- key = `${entry.key}_${collisionIndex}`;
536
- }
537
- usedKeys.add(key);
538
- if (key === "default") {
539
- if (!codeHasDefaultExport)
540
- defaultLines.push(`export default ${entry.local};`);
541
- }
542
- else {
543
- named.push({ key, local: entry.local });
544
279
  }
545
280
  }
546
- 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} };`);
281
+ if (isLib) {
282
+ console.log(chalk.gray(`[-] Module ${mod.id} matches library baseline — skipping`));
283
+ libraryCount++;
284
+ // Classify the library module so import rewriting can use proper named imports
285
+ const info = classifyLibraryModule(mod);
286
+ libModuleMap.set(mod.id, info);
287
+ continue;
551
288
  }
552
- 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);
289
+ const statements = applyModuleCleanupPasses(transformModule(mod));
290
+ pendingModules.push({ id: mod.id, statements });
594
291
  }
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
- }
292
+ // Resolve re-export chains (e.g. 338 → 247/react-dom-client, 540 → 287/React) so shim modules
293
+ // get the right library type. Always run for vendor pre-scans (classifyAllAsLibrary), and for
294
+ // normal non-lazy chunks when a library baseline is provided.
295
+ if (classifyAllAsLibrary || (!isLazyBundle && libSigs && libSigs.size > 0)) {
296
+ if (!isLazyBundle && libSigs && libSigs.size > 0) {
297
+ console.log(chalk.cyan(`[i] Library modules skipped: ${libraryCount}/${modules.length}`));
601
298
  }
299
+ resolveReexportChains(libModuleMap, modules);
602
300
  }
603
- 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
- }
301
+ // Merged map: prefer locally-classified entries over external ones (local classification
302
+ // is more precise for this specific bundle version).
303
+ const mergedLibMap = new Map([...(externalLibModuleMap !== null && externalLibModuleMap !== void 0 ? externalLibModuleMap : []), ...libModuleMap]);
304
+ // Apply Pass D (library-aware import rewriting) using the merged map, then validate.
305
+ for (const { id, statements } of pendingModules) {
306
+ const afterD = mergedLibMap.size > 0 ? applyLibraryImportRewriting(statements, mergedLibMap) : statements;
307
+ const afterRename = renameRouteComponents(afterD);
308
+ const code = validateAndFix(afterRename, id);
309
+ if (code === null) {
310
+ console.log(chalk.yellow(`[~] Module ${id} skipped due to unresolvable syntax errors`));
311
+ continue;
624
312
  }
313
+ files[id] = code;
625
314
  }
626
- 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;
315
+ // Collect everything in the IIFE body that is NOT the module-map variable into index.js.
316
+ // Skip this step for lazy chunks — their push statement contains no bootstrap logic.
317
+ if (!isLazyBundle) {
318
+ const topLevel = iifeBody !== null && iifeBody !== void 0 ? iifeBody : ast.program.body;
319
+ const indexStatements = [];
320
+ for (const stmt of topLevel) {
321
+ if (t.isVariableDeclaration(stmt)) {
322
+ const remaining = stmt.declarations.filter((d) => !isModuleMapDeclarator(d));
323
+ if (remaining.length > 0)
324
+ indexStatements.push(t.variableDeclaration(stmt.kind, remaining));
630
325
  }
326
+ else {
327
+ indexStatements.push(stmt);
328
+ }
329
+ }
330
+ if (indexStatements.length > 0) {
331
+ console.log(chalk.cyan(`[i] Writing ${indexStatements.length} non-module statements to index.js`));
332
+ const transformed = transformIndexStatements(indexStatements, mergedLibMap.size > 0 ? mergedLibMap : undefined);
333
+ const indexCode = validateAndFix(transformed, "index");
334
+ if (indexCode !== null)
335
+ files["index"] = indexCode;
631
336
  }
632
337
  }
633
- return null;
634
- };
338
+ return { files, libModuleMap };
339
+ });
635
340
  export default refactorReact;
636
341
  //# sourceMappingURL=index.js.map