@shriyanss/js-recon 1.4.1-alpha.4 → 1.4.1-alpha.6
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 +187 -1
- package/.github/workflows/promote-js-recon.yml +116 -0
- package/.github/workflows/publish-js-recon.yml +7 -114
- package/.github/workflows/rules-smoke-test.yaml +79 -0
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +208 -14
- package/Dockerfile.release +29 -0
- package/README.md +18 -17
- package/build/analyze/engine/csMastSEngine.js +129 -0
- package/build/analyze/engine/csMastSEngine.js.map +1 -0
- package/build/analyze/engine/index.js +15 -0
- package/build/analyze/engine/index.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/findDirectAssignment.js +2 -1
- package/build/analyze/helpers/engineHelpers/findDirectAssignment.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js +2 -1
- package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/resolveFunctionIdentifier.js +2 -1
- package/build/analyze/helpers/engineHelpers/resolveFunctionIdentifier.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/taintFlow.js +11 -2
- package/build/analyze/helpers/engineHelpers/taintFlow.js.map +1 -1
- package/build/analyze/helpers/schemas.js +5 -1
- package/build/analyze/helpers/schemas.js.map +1 -1
- package/build/analyze/helpers/validate.js +3 -3
- package/build/analyze/helpers/validate.js.map +1 -1
- package/build/completion/index.js +432 -0
- package/build/completion/index.js.map +1 -0
- package/build/cs_mast/index.js +94 -3
- package/build/cs_mast/index.js.map +1 -1
- package/build/endpoints/next_js/client_jsFilesHref.js +2 -1
- package/build/endpoints/next_js/client_jsFilesHref.js.map +1 -1
- package/build/endpoints/next_js/client_jsonParse.js +2 -1
- package/build/endpoints/next_js/client_jsonParse.js.map +1 -1
- package/build/endpoints/next_js/client_mappedJsonFile.js +2 -1
- package/build/endpoints/next_js/client_mappedJsonFile.js.map +1 -1
- package/build/endpoints/next_js/client_subsequentRequests.js +2 -1
- package/build/endpoints/next_js/client_subsequentRequests.js.map +1 -1
- package/build/fingerprint/index.js +56 -33
- package/build/fingerprint/index.js.map +1 -1
- package/build/globalConfig.js +1 -1
- package/build/index.js +67 -6
- package/build/index.js.map +1 -1
- package/build/lazyLoad/angular/angular_getFromMainJs.js +2 -1
- package/build/lazyLoad/angular/angular_getFromMainJs.js.map +1 -1
- package/build/lazyLoad/downloadFilesUtil.js +6 -5
- package/build/lazyLoad/downloadFilesUtil.js.map +1 -1
- package/build/lazyLoad/downloadLoadedJsUtil.js +56 -7
- package/build/lazyLoad/downloadLoadedJsUtil.js.map +1 -1
- package/build/lazyLoad/downloadQueue.js +9 -8
- package/build/lazyLoad/downloadQueue.js.map +1 -1
- package/build/lazyLoad/index.js +28 -1
- package/build/lazyLoad/index.js.map +1 -1
- package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js +36 -34
- package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js.map +1 -1
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +63 -15
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
- package/build/lazyLoad/next_js/next_parseLayoutJs.js +80 -68
- package/build/lazyLoad/next_js/next_parseLayoutJs.js.map +1 -1
- package/build/lazyLoad/next_js/next_promiseResolve.js +22 -16
- package/build/lazyLoad/next_js/next_promiseResolve.js.map +1 -1
- package/build/lazyLoad/nuxt_js/nuxt_astParse.js +49 -47
- package/build/lazyLoad/nuxt_js/nuxt_astParse.js.map +1 -1
- package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +3 -1
- package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js.map +1 -1
- package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +3 -2
- package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js.map +1 -1
- package/build/lazyLoad/react/react_followImports.js +12 -9
- package/build/lazyLoad/react/react_followImports.js.map +1 -1
- package/build/lazyLoad/react/react_webpackChunkPaths.js +175 -123
- package/build/lazyLoad/react/react_webpackChunkPaths.js.map +1 -1
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +3 -2
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -1
- package/build/lazyLoad/techDetect/checkAngularJS.js +27 -6
- package/build/lazyLoad/techDetect/checkAngularJS.js.map +1 -1
- package/build/lazyLoad/techDetect/checkReact.js +8 -0
- package/build/lazyLoad/techDetect/checkReact.js.map +1 -1
- package/build/lazyLoad/techDetect/index.js +23 -2
- package/build/lazyLoad/techDetect/index.js.map +1 -1
- package/build/lazyLoad/vue/vue_RuntimeJs.js +2 -1
- package/build/lazyLoad/vue/vue_RuntimeJs.js.map +1 -1
- package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js +2 -1
- package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js.map +1 -1
- package/build/lazyLoad/vue/vue_discoverJsFiles.js +22 -10
- package/build/lazyLoad/vue/vue_discoverJsFiles.js.map +1 -1
- package/build/lazyLoad/vue/vue_getClientSidePaths.js +2 -1
- package/build/lazyLoad/vue/vue_getClientSidePaths.js.map +1 -1
- package/build/lazyLoad/vue/vue_jsImports.js +2 -1
- package/build/lazyLoad/vue/vue_jsImports.js.map +1 -1
- package/build/lazyLoad/vue/vue_viteMapDeps.js +62 -54
- package/build/lazyLoad/vue/vue_viteMapDeps.js.map +1 -1
- package/build/map/angular_js/getAngularConnections.js +1 -1
- package/build/map/angular_js/getAngularConnections.js.map +1 -1
- package/build/map/graphql/resolveGraphql.js +2 -1
- package/build/map/graphql/resolveGraphql.js.map +1 -1
- package/build/map/next_js/getExports.js +2 -1
- package/build/map/next_js/getExports.js.map +1 -1
- package/build/map/next_js/getFetchInstances.js +2 -1
- package/build/map/next_js/getFetchInstances.js.map +1 -1
- package/build/map/next_js/getTurbopackConnections.js +2 -1
- package/build/map/next_js/getTurbopackConnections.js.map +1 -1
- package/build/map/next_js/getWebpackConnections.js +2 -1
- package/build/map/next_js/getWebpackConnections.js.map +1 -1
- package/build/map/next_js/resolveAxios.js +2 -1
- package/build/map/next_js/resolveAxios.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/directCallsWithoutAssignment.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/directCallsWithoutAssignment.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/interceptorHeaders.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/interceptorHeaders.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/traceBody.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/traceBody.js.map +1 -1
- package/build/map/next_js/resolveFetch.js +2 -1
- package/build/map/next_js/resolveFetch.js.map +1 -1
- package/build/map/next_js/resolveNewRequest.js +2 -1
- package/build/map/next_js/resolveNewRequest.js.map +1 -1
- package/build/map/next_js/resolveServerActions.js +2 -1
- package/build/map/next_js/resolveServerActions.js.map +1 -1
- package/build/map/next_js/utils.js +2 -1
- package/build/map/next_js/utils.js.map +1 -1
- package/build/map/react_js/getReactConnections.js +5 -4
- package/build/map/react_js/getReactConnections.js.map +1 -1
- package/build/map/vue_js/crossFileResolver.js +3 -2
- package/build/map/vue_js/crossFileResolver.js.map +1 -1
- package/build/map/vue_js/getViteConnections.js +5 -4
- package/build/map/vue_js/getViteConnections.js.map +1 -1
- package/build/map/vue_js/taint_utils.js +2 -1
- package/build/map/vue_js/taint_utils.js.map +1 -1
- package/build/map/vue_js/vue_resolveFetch.js +3 -2
- package/build/map/vue_js/vue_resolveFetch.js.map +1 -1
- package/build/map/vue_js/vue_resolveHttpClient.js +3 -2
- package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -1
- package/build/map/vue_js/vue_resolveXhr.js +3 -2
- package/build/map/vue_js/vue_resolveXhr.js.map +1 -1
- package/build/refactor/index.js +399 -60
- package/build/refactor/index.js.map +1 -1
- package/build/refactor/next/helpers.js +194 -13
- package/build/refactor/next/helpers.js.map +1 -1
- package/build/refactor/next/index.js +223 -36
- package/build/refactor/next/index.js.map +1 -1
- package/build/refactor/next/transform.js +905 -55
- package/build/refactor/next/transform.js.map +1 -1
- package/build/refactor/react/index.js +3 -3
- package/build/refactor/react/index.js.map +1 -1
- package/build/refactor/react/transform.js +2 -1
- package/build/refactor/react/transform.js.map +1 -1
- package/build/refactor/react/validator.js +2 -1
- package/build/refactor/react/validator.js.map +1 -1
- package/build/refactor/react-vite/index.js +39 -5
- package/build/refactor/react-vite/index.js.map +1 -1
- package/build/refactor/react-vite/vendor-analyze.js +3 -3
- package/build/refactor/react-vite/vendor-analyze.js.map +1 -1
- package/build/refactor/remote/config.js +17 -1
- package/build/refactor/remote/config.js.map +1 -1
- package/build/refactor/remote/hf-client.js +27 -2
- package/build/refactor/remote/hf-client.js.map +1 -1
- package/build/refactor/remote/version-detect.js +355 -0
- package/build/refactor/remote/version-detect.js.map +1 -0
- package/build/refactor/vue/index.js +176 -0
- package/build/refactor/vue/index.js.map +1 -0
- package/build/refactor/vue/vendor-analyze-vue.js +271 -0
- package/build/refactor/vue/vendor-analyze-vue.js.map +1 -0
- package/build/refactor/vue/vite.js +216 -0
- package/build/refactor/vue/vite.js.map +1 -0
- package/build/report/utility/genHtml.js +14 -3
- package/build/report/utility/genHtml.js.map +1 -1
- package/build/run/bundler-detect.js +140 -0
- package/build/run/bundler-detect.js.map +1 -0
- package/build/run/index.js +91 -5
- package/build/run/index.js.map +1 -1
- package/build/strings/index.js +6 -2
- package/build/strings/index.js.map +1 -1
- package/build/strings/trufflehog.js +71 -0
- package/build/strings/trufflehog.js.map +1 -0
- package/build/utility/makeReq.js +10 -9
- package/build/utility/makeReq.js.map +1 -1
- package/build/utility/postmanGenerator.js +2 -2
- package/build/utility/postmanGenerator.js.map +1 -1
- package/build/utility/puppeteerInstance.js +7 -1
- package/build/utility/puppeteerInstance.js.map +1 -1
- package/package.json +9 -5
- package/scripts/smoke-test.js +74 -0
package/build/refactor/index.js
CHANGED
|
@@ -13,15 +13,19 @@ import path from "path";
|
|
|
13
13
|
import { execSync } from "child_process";
|
|
14
14
|
import prettier from "prettier";
|
|
15
15
|
// Next.js
|
|
16
|
-
import refactorNext from "./next/index.js";
|
|
16
|
+
import refactorNext, { refactorNextWebpack } from "./next/index.js";
|
|
17
17
|
// React
|
|
18
18
|
import refactorReact from "./react/index.js";
|
|
19
19
|
// React (Vite)
|
|
20
20
|
import refactorVite from "./react-vite/index.js";
|
|
21
|
+
// Vue
|
|
22
|
+
import refactorVueWebpack from "./vue/index.js";
|
|
23
|
+
import { refactorVueVite } from "./vue/vite.js";
|
|
21
24
|
// Remote HuggingFace client + cache
|
|
22
|
-
import { TECH_TO_BRANCH, getHfRawUrl, fetchCollisionsJson, listCollisionsFiles, validateRemoteBranch, getSampleSize, getTechnology, } from "./remote/hf-client.js";
|
|
23
|
-
import { loadRefactorConfig, validateRefactorConfig } from "./remote/config.js";
|
|
25
|
+
import { TECH_TO_BRANCH, getHfRawUrl, fetchCollisionsJson, listCollisionsFiles, validateRemoteBranch, validateRemotePath, getSampleSize, getTechnology, } from "./remote/hf-client.js";
|
|
26
|
+
import { loadRefactorConfig, validateRefactorConfig, purgeDynamicVersionDetectionScatConfig, saveDynamicVersionDetectionScatConfig, } from "./remote/config.js";
|
|
24
27
|
import { loadListCache, saveListCache, shouldRefreshListCache, loadCachedSignature, saveSignatureToCache, isSignatureCacheFresh, validateCaches, } from "./remote/cache.js";
|
|
28
|
+
import { detectReactVersion, VERSION_TECH_TO_BUNDLER, listAvailableVersions, selectDynamicScatConfigs, validateStaticScatConfig, } from "./remote/version-detect.js";
|
|
25
29
|
/**
|
|
26
30
|
* Derives the assets directory from the URL embedded in any chunk's code comment.
|
|
27
31
|
* Chunks have a header like `// File Source: http://localhost:3001/assets/foo.js`.
|
|
@@ -69,17 +73,18 @@ function findVendorChunkFiles(chunks, assetsDir) {
|
|
|
69
73
|
const BASELINE_SCAT_DIR = {
|
|
70
74
|
"react-webpack": "lit-decl-loop-cond",
|
|
71
75
|
"react-vite": "lit-decl-loop-cond",
|
|
76
|
+
"next-webpack": "lit-decl-loop-cond",
|
|
72
77
|
};
|
|
73
78
|
// Canonical ordering of scat categories (matches ALL_SCAT_CATEGORIES in csmast.mjs).
|
|
74
79
|
// Used when converting a user-supplied --scat list to a bucket directory name.
|
|
75
80
|
const ALL_SCAT_CATEGORIES = ["lit", "id", "op", "decl", "loop", "cond", "name", "val", "op_name"];
|
|
76
81
|
// Converts a scat list to the bucket directory name, preserving canonical category order.
|
|
77
|
-
const scatToDir = (scat) => {
|
|
82
|
+
export const scatToDir = (scat) => {
|
|
78
83
|
const scatSet = new Set(scat);
|
|
79
84
|
return ALL_SCAT_CATEGORIES.filter((c) => scatSet.has(c)).join("-") || scat.join("-");
|
|
80
85
|
};
|
|
81
86
|
// Parses a collisions.json file and returns signatures whose count equals the maximum.
|
|
82
|
-
const loadCollisionsFile = (filePath) => {
|
|
87
|
+
export const loadCollisionsFile = (filePath) => {
|
|
83
88
|
const records = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
84
89
|
const maxCount = records.reduce((m, r) => (r.count > m ? r.count : m), 0);
|
|
85
90
|
return new Set(records.filter((r) => r.count >= maxCount).map((r) => r.signature));
|
|
@@ -156,9 +161,12 @@ const buildLibSigs = (input, tech, scatOverride) => {
|
|
|
156
161
|
};
|
|
157
162
|
// Loads library signatures from the remote HuggingFace dataset.
|
|
158
163
|
// Returns null if the branch is not configured for `tech` or if validation fails.
|
|
164
|
+
// When opts.remoteCollisions is provided it is used directly as the bucket path,
|
|
165
|
+
// bypassing the TECH_TO_BRANCH lookup and metadata-file validation.
|
|
166
|
+
// Exits with code 25 if opts.remoteCollisions is set but the path does not exist.
|
|
159
167
|
const loadRemoteLibSigs = (tech, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
160
|
-
var _a, _b, _c;
|
|
161
|
-
const branch = TECH_TO_BRANCH[tech];
|
|
168
|
+
var _a, _b, _c, _d;
|
|
169
|
+
const branch = (_a = opts.remoteCollisions) !== null && _a !== void 0 ? _a : TECH_TO_BRANCH[tech];
|
|
162
170
|
if (!branch)
|
|
163
171
|
return null;
|
|
164
172
|
const scatDir = opts.scat ? scatToDir(opts.scat) : BASELINE_SCAT_DIR[tech];
|
|
@@ -175,33 +183,60 @@ const loadRemoteLibSigs = (tech, opts) => __awaiter(void 0, void 0, void 0, func
|
|
|
175
183
|
for (const w of cacheWarnings) {
|
|
176
184
|
console.log(chalk.yellow(`[!] Cache validation warning: ${w} (will refresh)`));
|
|
177
185
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
186
|
+
if (opts.remoteCollisions) {
|
|
187
|
+
// User-supplied path: validate it exists in the HF bucket.
|
|
188
|
+
console.log(chalk.cyan(`[i] Validating remote dataset path "${branch}"...`));
|
|
189
|
+
const pathExists = yield validateRemotePath(branch);
|
|
190
|
+
if (!pathExists) {
|
|
191
|
+
console.error(chalk.red(`[!] Remote dataset path "${branch}" not found in the shriyanss/cs-mast-s-dataset bucket.`));
|
|
192
|
+
process.exit(25);
|
|
193
|
+
}
|
|
184
194
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
else {
|
|
196
|
+
// Auto-detected branch: validate metadata files exist.
|
|
197
|
+
console.log(chalk.cyan(`[i] Validating remote bucket prefix "${branch}"...`));
|
|
198
|
+
const branchOk = yield validateRemoteBranch(branch);
|
|
199
|
+
if (!branchOk) {
|
|
200
|
+
console.log(chalk.red(`[!] Remote bucket prefix "${branch}" is missing required metadata (sample_size / technology). Skipping remote signatures.`));
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
// Verify the bucket prefix technology matches.
|
|
204
|
+
const remoteTech = yield getTechnology(branch);
|
|
205
|
+
if (remoteTech !== tech) {
|
|
206
|
+
console.log(chalk.red(`[!] Remote bucket prefix "${branch}" is for technology "${remoteTech}", not "${tech}". Skipping remote signatures.`));
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Try to load the sample size for quality filtering.
|
|
211
|
+
// Custom paths may not have a sample_size file; fall back to 0 (skip quality filter).
|
|
212
|
+
let sampleSize = 0;
|
|
213
|
+
if (opts.remoteCollisions) {
|
|
214
|
+
try {
|
|
215
|
+
sampleSize = yield getSampleSize(branch);
|
|
216
|
+
}
|
|
217
|
+
catch (_e) {
|
|
218
|
+
// Custom path may not have sample_size; quality filter will be skipped (sampleSize=0)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
sampleSize = yield getSampleSize(branch);
|
|
190
223
|
}
|
|
191
|
-
const sampleSize = yield getSampleSize(branch);
|
|
192
224
|
// Build / refresh file list cache.
|
|
225
|
+
// Also refresh if the specific branch is not yet in the cache (e.g. first run
|
|
226
|
+
// with a new --remote-collisions path, or a new tech added after initial cache).
|
|
193
227
|
let listCache = cacheWarnings.length > 0 ? null : loadListCache();
|
|
194
|
-
|
|
228
|
+
const branchMissingFromCache = !(listCache === null || listCache === void 0 ? void 0 : listCache.branches[branch]);
|
|
229
|
+
if (shouldRefreshListCache(listCache, opts) || branchMissingFromCache) {
|
|
195
230
|
console.log(chalk.cyan(`[i] Refreshing remote file list cache for bucket prefix "${branch}"...`));
|
|
196
231
|
const paths = yield listCollisionsFiles(branch, scatDir);
|
|
197
232
|
const now = Date.now();
|
|
198
|
-
const branches = (
|
|
233
|
+
const branches = (_b = listCache === null || listCache === void 0 ? void 0 : listCache.branches) !== null && _b !== void 0 ? _b : {};
|
|
199
234
|
branches[branch] = paths;
|
|
200
235
|
listCache = { generatedAt: now, branches };
|
|
201
236
|
saveListCache(listCache);
|
|
202
237
|
console.log(chalk.cyan(`[i] File list cache updated: ${paths.length} files found`));
|
|
203
238
|
}
|
|
204
|
-
const filePaths = (
|
|
239
|
+
const filePaths = (_c = listCache === null || listCache === void 0 ? void 0 : listCache.branches[branch]) !== null && _c !== void 0 ? _c : [];
|
|
205
240
|
// Filter to only paths that match <something>/<scatDir>/collisions.json
|
|
206
241
|
const matchingPaths = filePaths.filter((p) => p.endsWith(`/${scatDir}/collisions.json`));
|
|
207
242
|
if (matchingPaths.length === 0) {
|
|
@@ -222,21 +257,34 @@ const loadRemoteLibSigs = (tech, opts) => __awaiter(void 0, void 0, void 0, func
|
|
|
222
257
|
const url = getHfRawUrl(branch, relPath);
|
|
223
258
|
records = yield fetchCollisionsJson(url);
|
|
224
259
|
if (records === null) {
|
|
225
|
-
// 404 or error
|
|
226
|
-
|
|
260
|
+
// 404 or network error.
|
|
261
|
+
// For user-specified --remote-collisions paths the list is stable;
|
|
262
|
+
// just skip this file and continue without poisoning the list cache.
|
|
263
|
+
// For auto-detected branches, invalidate the list cache so a stale
|
|
264
|
+
// entry (e.g. a deleted file) is not tried again on the next run.
|
|
265
|
+
if (!opts.remoteCollisions && !opts.skipCacheChecks) {
|
|
227
266
|
console.log(chalk.yellow(`[!] Could not fetch ${relPath} (404 or error) — refreshing list cache`));
|
|
228
267
|
const freshPaths = yield listCollisionsFiles(branch, scatDir);
|
|
229
|
-
const updatedBranches = (
|
|
268
|
+
const updatedBranches = (_d = listCache === null || listCache === void 0 ? void 0 : listCache.branches) !== null && _d !== void 0 ? _d : {};
|
|
230
269
|
updatedBranches[branch] = freshPaths;
|
|
231
270
|
saveListCache({ generatedAt: Date.now(), branches: updatedBranches });
|
|
232
271
|
listCache = { generatedAt: Date.now(), branches: updatedBranches };
|
|
233
272
|
}
|
|
273
|
+
else {
|
|
274
|
+
console.log(chalk.yellow(`[!] Could not fetch ${relPath} — skipping`));
|
|
275
|
+
}
|
|
234
276
|
continue;
|
|
235
277
|
}
|
|
236
278
|
saveSignatureToCache(branch, relPath, records, config.maxCacheSizeMb);
|
|
237
279
|
}
|
|
238
|
-
// Apply signature quality filter: (count / sampleSize) * 100 >= threshold
|
|
239
|
-
|
|
280
|
+
// Apply signature quality filter: (count / sampleSize) * 100 >= threshold.
|
|
281
|
+
// When sampleSize is 0 (no sample_size file on the remote path), skip quality
|
|
282
|
+
// filtering and include all records.
|
|
283
|
+
const filtered = sampleSize === 0 ? records : records.filter((r) => (r.count / sampleSize) * 100 >= opts.signatureQuality);
|
|
284
|
+
// Skip feature dirs with no records — they indicate missing/empty dataset
|
|
285
|
+
// entries and would collapse the intersection to zero if included.
|
|
286
|
+
if (filtered.length === 0)
|
|
287
|
+
continue;
|
|
240
288
|
// Intersect with previous sets.
|
|
241
289
|
const sigSet = new Set(filtered.map((r) => r.signature));
|
|
242
290
|
if (intersection === null) {
|
|
@@ -258,9 +306,12 @@ const loadRemoteLibSigs = (tech, opts) => __awaiter(void 0, void 0, void 0, func
|
|
|
258
306
|
return { sigs: intersection, desc: `remote:${branch} (${loadedCount} files, quality>=${opts.signatureQuality}%)` };
|
|
259
307
|
});
|
|
260
308
|
const availableTechs = {
|
|
261
|
-
next: "Next.js",
|
|
309
|
+
"next-turbopack": "Next.js (Turbopack)",
|
|
310
|
+
"next-webpack": "Next.js (webpack)",
|
|
262
311
|
"react-webpack": "React (webpack)",
|
|
263
312
|
"react-vite": "React (Vite)",
|
|
313
|
+
"vue-webpack": "Vue.js (webpack)",
|
|
314
|
+
"vue-vite": "Vue.js (Vite)",
|
|
264
315
|
};
|
|
265
316
|
/**
|
|
266
317
|
* Scaffolds a minimal webpack project in `outputDir` (package.json,
|
|
@@ -271,7 +322,7 @@ const availableTechs = {
|
|
|
271
322
|
* The entry file is the first written file that contains `createRoot(` — i.e.
|
|
272
323
|
* the app entry point. If no such file is found, the first written file is used.
|
|
273
324
|
*/
|
|
274
|
-
function runBuildCheck(outputDir, writtenFiles) {
|
|
325
|
+
function runBuildCheck(outputDir, writtenFiles, detectedVersion) {
|
|
275
326
|
var _a;
|
|
276
327
|
if (writtenFiles.length === 0)
|
|
277
328
|
return;
|
|
@@ -286,16 +337,23 @@ function runBuildCheck(outputDir, writtenFiles) {
|
|
|
286
337
|
})) !== null && _a !== void 0 ? _a : writtenFiles[0];
|
|
287
338
|
const entryRelative = `./${path.basename(entryFile)}`;
|
|
288
339
|
console.log(chalk.cyan(`[i] Setting up webpack build check in ${outputDir}/ (entry: ${entryRelative})`));
|
|
340
|
+
// Use detected React version when available, fall back to React 18.
|
|
341
|
+
const reactVersion = detectedVersion ? `^${detectedVersion.reactNpm}` : "^18.3.1";
|
|
342
|
+
const reactDomVersion = (detectedVersion === null || detectedVersion === void 0 ? void 0 : detectedVersion.reactDomNpm)
|
|
343
|
+
? `^${detectedVersion.reactDomNpm}`
|
|
344
|
+
: detectedVersion
|
|
345
|
+
? undefined // pre-0.14: no react-dom
|
|
346
|
+
: "^18.3.1";
|
|
347
|
+
if (detectedVersion) {
|
|
348
|
+
console.log(chalk.cyan(`[i] Using detected React version ${detectedVersion.versionKey} in package.json (react@${reactVersion})`));
|
|
349
|
+
}
|
|
350
|
+
const dependencies = Object.assign(Object.assign({ react: reactVersion }, (reactDomVersion ? { "react-dom": reactDomVersion } : {})), { "react-router-dom": "^6.27.0" });
|
|
289
351
|
// package.json — no "type": "module" so webpack.config.js can use CommonJS require
|
|
290
352
|
const pkg = {
|
|
291
353
|
name: "refactored-app",
|
|
292
354
|
version: "1.0.0",
|
|
293
355
|
scripts: { build: "webpack" },
|
|
294
|
-
dependencies
|
|
295
|
-
react: "^18.3.1",
|
|
296
|
-
"react-dom": "^18.3.1",
|
|
297
|
-
"react-router-dom": "^6.27.0",
|
|
298
|
-
},
|
|
356
|
+
dependencies,
|
|
299
357
|
devDependencies: {
|
|
300
358
|
"@babel/core": "^7.25.0",
|
|
301
359
|
"@babel/preset-env": "^7.25.0",
|
|
@@ -365,13 +423,17 @@ module.exports = {
|
|
|
365
423
|
*
|
|
366
424
|
* The entry file is the first written file that contains `createRoot(`.
|
|
367
425
|
*/
|
|
368
|
-
function runViteBuildCheck(outputDir, writtenFiles) {
|
|
426
|
+
function runViteBuildCheck(outputDir, writtenFiles, detectedVersion) {
|
|
369
427
|
var _a;
|
|
370
428
|
if (writtenFiles.length === 0)
|
|
371
429
|
return;
|
|
430
|
+
// Deduplicate — callers should not pass duplicates, but guard here too so a
|
|
431
|
+
// duplicate does not cause the renameSync below to throw ENOENT on the
|
|
432
|
+
// second occurrence of the same path.
|
|
433
|
+
const uniqueFiles = [...new Set(writtenFiles)];
|
|
372
434
|
// Rename .js files to .jsx — Vite's import analysis can't parse JSX in .js files,
|
|
373
435
|
// and refactored Vite chunks always contain JSX. The original source files were .jsx.
|
|
374
|
-
const jsxFiles =
|
|
436
|
+
const jsxFiles = uniqueFiles.map((f) => {
|
|
375
437
|
if (!f.endsWith(".js"))
|
|
376
438
|
return f;
|
|
377
439
|
const jsxPath = f.replace(/\.js$/, ".jsx");
|
|
@@ -400,16 +462,23 @@ function runViteBuildCheck(outputDir, writtenFiles) {
|
|
|
400
462
|
})) !== null && _a !== void 0 ? _a : jsxFiles[0];
|
|
401
463
|
const entryRelative = `./${path.basename(entryFile)}`;
|
|
402
464
|
console.log(chalk.cyan(`[i] Setting up Vite build check in ${outputDir}/ (entry: ${entryRelative})`));
|
|
465
|
+
// Use detected React version when available, fall back to React 18.
|
|
466
|
+
const reactVersion = detectedVersion ? `^${detectedVersion.reactNpm}` : "^18.3.1";
|
|
467
|
+
const reactDomVersion = (detectedVersion === null || detectedVersion === void 0 ? void 0 : detectedVersion.reactDomNpm)
|
|
468
|
+
? `^${detectedVersion.reactDomNpm}`
|
|
469
|
+
: detectedVersion
|
|
470
|
+
? undefined
|
|
471
|
+
: "^18.3.1";
|
|
472
|
+
if (detectedVersion) {
|
|
473
|
+
console.log(chalk.cyan(`[i] Using detected React version ${detectedVersion.versionKey} in package.json (react@${reactVersion})`));
|
|
474
|
+
}
|
|
475
|
+
const dependencies = Object.assign(Object.assign({ react: reactVersion }, (reactDomVersion ? { "react-dom": reactDomVersion } : {})), { "react-router-dom": "^6.27.0" });
|
|
403
476
|
const pkg = {
|
|
404
477
|
name: "refactored-app",
|
|
405
478
|
version: "1.0.0",
|
|
406
479
|
type: "module",
|
|
407
480
|
scripts: { build: "vite build" },
|
|
408
|
-
dependencies
|
|
409
|
-
react: "^18.3.1",
|
|
410
|
-
"react-dom": "^18.3.1",
|
|
411
|
-
"react-router-dom": "^6.27.0",
|
|
412
|
-
},
|
|
481
|
+
dependencies,
|
|
413
482
|
devDependencies: {
|
|
414
483
|
"@vitejs/plugin-react": "^4.3.1",
|
|
415
484
|
vite: "^5.4.0",
|
|
@@ -451,6 +520,83 @@ export default defineConfig({
|
|
|
451
520
|
console.log(chalk.red("[!] Vite build check failed — review output above"));
|
|
452
521
|
}
|
|
453
522
|
}
|
|
523
|
+
/**
|
|
524
|
+
* Resolves the scat dir list to use for version detection based on the user's options.
|
|
525
|
+
* Returns an empty array when version detection should be skipped (e.g., invalid static config).
|
|
526
|
+
*
|
|
527
|
+
* Dynamic mode (default):
|
|
528
|
+
* - Reads cached dynamic scat configs from ~/.js-recon/refactor/config.json.
|
|
529
|
+
* - If --detect-version-dynamic-conf-purge is set, clears the cache first.
|
|
530
|
+
* - If no cached configs exist, runs selectDynamicScatConfigs() and saves the result.
|
|
531
|
+
*
|
|
532
|
+
* Static mode (user passes comma-separated categories):
|
|
533
|
+
* - Validates that reliable_signatures.json is non-empty for all known versions.
|
|
534
|
+
* - Exits with code 26 if the config is invalid or empty for any version.
|
|
535
|
+
*/
|
|
536
|
+
function resolveVersionDetectionScatDirs(bundler, opts) {
|
|
537
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
538
|
+
var _a, _b;
|
|
539
|
+
const detectConfig = (_a = opts.detectVersionConfig) !== null && _a !== void 0 ? _a : "dynamic";
|
|
540
|
+
if (detectConfig === "dynamic") {
|
|
541
|
+
if (opts.detectVersionDynamicConfPurge) {
|
|
542
|
+
console.log(chalk.cyan("[i] Version detection: purging cached dynamic scat config..."));
|
|
543
|
+
purgeDynamicVersionDetectionScatConfig();
|
|
544
|
+
}
|
|
545
|
+
const cfg = loadRefactorConfig();
|
|
546
|
+
if (cfg.dynamicVersionDetectionScatConfig && cfg.dynamicVersionDetectionScatConfig.length > 0) {
|
|
547
|
+
console.log(chalk.cyan(`[i] Version detection: using cached dynamic scat config (${cfg.dynamicVersionDetectionScatConfig.join(", ")})`));
|
|
548
|
+
return cfg.dynamicVersionDetectionScatConfig;
|
|
549
|
+
}
|
|
550
|
+
// No cached config — discover versions and run dynamic selection.
|
|
551
|
+
let versions;
|
|
552
|
+
try {
|
|
553
|
+
versions = yield listAvailableVersions(bundler);
|
|
554
|
+
}
|
|
555
|
+
catch (e) {
|
|
556
|
+
console.log(chalk.yellow(`[!] Version detection: could not list versions (${String(e)})`));
|
|
557
|
+
return [];
|
|
558
|
+
}
|
|
559
|
+
if (versions.length === 0) {
|
|
560
|
+
console.log(chalk.yellow(`[!] Version detection: no version data found for ${bundler}`));
|
|
561
|
+
return [];
|
|
562
|
+
}
|
|
563
|
+
const threshold = Math.max(1, (_b = opts.detectVersionDynamicThreshold) !== null && _b !== void 0 ? _b : 3);
|
|
564
|
+
const selected = yield selectDynamicScatConfigs(bundler, versions, threshold);
|
|
565
|
+
if (selected.length > 0) {
|
|
566
|
+
saveDynamicVersionDetectionScatConfig(selected);
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
console.log(chalk.yellow(`[!] Version detection: dynamic selection found no valid scat configs`));
|
|
570
|
+
}
|
|
571
|
+
return selected;
|
|
572
|
+
}
|
|
573
|
+
// Static config: parse comma-separated scat categories and convert to bucket dir name.
|
|
574
|
+
const categories = detectConfig
|
|
575
|
+
.split(",")
|
|
576
|
+
.map((s) => s.trim())
|
|
577
|
+
.filter(Boolean);
|
|
578
|
+
const scatDir = scatToDir(categories);
|
|
579
|
+
if (!scatDir) {
|
|
580
|
+
console.log(chalk.red(`[!] --detect-version-config "${detectConfig}" produced an empty scat dir; check category names`));
|
|
581
|
+
process.exit(26);
|
|
582
|
+
}
|
|
583
|
+
// Validate by checking that reliable_signatures.json is non-empty for all versions.
|
|
584
|
+
let versions;
|
|
585
|
+
try {
|
|
586
|
+
versions = yield listAvailableVersions(bundler);
|
|
587
|
+
}
|
|
588
|
+
catch (e) {
|
|
589
|
+
console.log(chalk.yellow(`[!] Version detection: could not list versions to validate static config (${String(e)})`));
|
|
590
|
+
return [];
|
|
591
|
+
}
|
|
592
|
+
const valid = yield validateStaticScatConfig(bundler, versions, scatDir);
|
|
593
|
+
if (!valid) {
|
|
594
|
+
console.log(chalk.red(`[!] --detect-version-config "${detectConfig}" (scat dir: "${scatDir}") has empty reliable_signatures.json for one or more versions. Cannot use for version detection.`));
|
|
595
|
+
process.exit(26);
|
|
596
|
+
}
|
|
597
|
+
return [scatDir];
|
|
598
|
+
});
|
|
599
|
+
}
|
|
454
600
|
/**
|
|
455
601
|
* Refactors JavaScript code chunks based on technology-specific patterns.
|
|
456
602
|
*
|
|
@@ -465,7 +611,7 @@ export default defineConfig({
|
|
|
465
611
|
* @returns Promise that resolves when refactoring is complete
|
|
466
612
|
*/
|
|
467
613
|
const refactor = (mappedJson, outputDir, tech, list, collisionsFile, remoteOpts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
468
|
-
var _a, _b, _c, _d;
|
|
614
|
+
var _a, _b, _c, _d, _e;
|
|
469
615
|
console.log(chalk.cyan("[i] Loading refactor module..."));
|
|
470
616
|
if (list) {
|
|
471
617
|
console.log(chalk.cyan("[i] Listing available technologies"));
|
|
@@ -517,12 +663,15 @@ const refactor = (mappedJson, outputDir, tech, list, collisionsFile, remoteOpts)
|
|
|
517
663
|
libSigs = result.sigs;
|
|
518
664
|
console.log(chalk.cyan(`[i] Loaded ${libSigs.size} library signatures from ${result.desc}`));
|
|
519
665
|
}
|
|
520
|
-
else if (!(remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.noRemote) && TECH_TO_BRANCH[tech]) {
|
|
521
|
-
//
|
|
666
|
+
else if ((remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.remoteCollisions) || (!(remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.noRemote) && TECH_TO_BRANCH[tech])) {
|
|
667
|
+
// Load from remote HuggingFace dataset.
|
|
668
|
+
// --remote-collisions uses the explicit path; otherwise falls back to TECH_TO_BRANCH.
|
|
522
669
|
const opts = {
|
|
523
670
|
signatureQuality: (_a = remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.signatureQuality) !== null && _a !== void 0 ? _a : 100,
|
|
524
671
|
refreshCache: (_b = remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.refreshCache) !== null && _b !== void 0 ? _b : false,
|
|
525
672
|
skipCacheChecks: (_c = remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.skipCacheChecks) !== null && _c !== void 0 ? _c : false,
|
|
673
|
+
remoteCollisions: remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.remoteCollisions,
|
|
674
|
+
scat: remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.scat,
|
|
526
675
|
};
|
|
527
676
|
const result = yield loadRemoteLibSigs(tech, opts);
|
|
528
677
|
if (result) {
|
|
@@ -533,7 +682,7 @@ const refactor = (mappedJson, outputDir, tech, list, collisionsFile, remoteOpts)
|
|
|
533
682
|
}
|
|
534
683
|
}
|
|
535
684
|
// iterate through the chunks
|
|
536
|
-
if (tech === "next") {
|
|
685
|
+
if (tech === "next-turbopack") {
|
|
537
686
|
for (const [, value] of Object.entries(chunks)) {
|
|
538
687
|
const moduleFiles = yield refactorNext(value);
|
|
539
688
|
for (const [moduleId, rawCode] of Object.entries(moduleFiles)) {
|
|
@@ -542,8 +691,42 @@ const refactor = (mappedJson, outputDir, tech, list, collisionsFile, remoteOpts)
|
|
|
542
691
|
singleQuote: true,
|
|
543
692
|
trailingComma: "none",
|
|
544
693
|
});
|
|
545
|
-
|
|
546
|
-
|
|
694
|
+
// Skip writing empty files
|
|
695
|
+
if (formatted.trim().length === 0) {
|
|
696
|
+
console.log(chalk.gray(`[~] Module ${moduleId} is empty after stripping — skipping`));
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
const filePath = `${outputDir}/${moduleId}.js`;
|
|
700
|
+
fs.writeFileSync(filePath, formatted);
|
|
701
|
+
console.log(chalk.green(`[✓] Module ${moduleId} written to ${filePath}`));
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
else if (tech === "next-webpack") {
|
|
706
|
+
if (remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.scat) {
|
|
707
|
+
console.log(chalk.cyan(`[i] Using custom scat config: ${remoteOpts.scat.join(",")}`));
|
|
708
|
+
}
|
|
709
|
+
for (const [, value] of Object.entries(chunks)) {
|
|
710
|
+
const moduleFiles = yield refactorNextWebpack(value, libSigs, remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.scat);
|
|
711
|
+
for (const [moduleId, rawCode] of Object.entries(moduleFiles)) {
|
|
712
|
+
let formatted;
|
|
713
|
+
try {
|
|
714
|
+
formatted = yield prettier.format(rawCode, {
|
|
715
|
+
parser: "babel",
|
|
716
|
+
singleQuote: true,
|
|
717
|
+
trailingComma: "none",
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
catch (_f) {
|
|
721
|
+
formatted = rawCode;
|
|
722
|
+
}
|
|
723
|
+
if (formatted.trim().length === 0) {
|
|
724
|
+
console.log(chalk.gray(`[~] Module ${moduleId} is empty after stripping — skipping`));
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
const filePath = `${outputDir}/${moduleId}.js`;
|
|
728
|
+
fs.writeFileSync(filePath, formatted);
|
|
729
|
+
console.log(chalk.green(`[✓] Module ${moduleId} written to ${filePath}`));
|
|
547
730
|
}
|
|
548
731
|
}
|
|
549
732
|
}
|
|
@@ -618,43 +801,199 @@ const refactor = (mappedJson, outputDir, tech, list, collisionsFile, remoteOpts)
|
|
|
618
801
|
console.log(chalk.green(`[✓] Module ${moduleId} written to ${outputDir}/${moduleId}.js`));
|
|
619
802
|
}
|
|
620
803
|
}
|
|
804
|
+
// Version detection — run before build check so the detected version can be
|
|
805
|
+
// written into package.json by runBuildCheck.
|
|
806
|
+
let detectedVersion;
|
|
807
|
+
if ((remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.detectVersion) && VERSION_TECH_TO_BUNDLER[tech]) {
|
|
808
|
+
const bundler = VERSION_TECH_TO_BUNDLER[tech];
|
|
809
|
+
const vScatDirs = yield resolveVersionDetectionScatDirs(bundler, remoteOpts);
|
|
810
|
+
if (vScatDirs.length > 0) {
|
|
811
|
+
// Include vendor chunk file contents (e.g. vendor-react-dom.*.js) that hold
|
|
812
|
+
// React library code but are not present in mapped.json. Without these, the
|
|
813
|
+
// signature set contains only app-level code and misses version-specific React
|
|
814
|
+
// library signatures.
|
|
815
|
+
const vendorCodes = [];
|
|
816
|
+
const vDetectAssetsDir = findAssetsDir(chunks, mappedJson);
|
|
817
|
+
if (vDetectAssetsDir) {
|
|
818
|
+
for (const vendorFile of findVendorChunkFiles(chunks, vDetectAssetsDir)) {
|
|
819
|
+
try {
|
|
820
|
+
vendorCodes.push(fs.readFileSync(vendorFile, "utf8"));
|
|
821
|
+
}
|
|
822
|
+
catch (_g) {
|
|
823
|
+
// non-fatal
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
const vResult = yield detectReactVersion(chunks, tech, vScatDirs, vendorCodes.length > 0 ? vendorCodes : undefined);
|
|
828
|
+
if (vResult) {
|
|
829
|
+
console.log(chalk.green(`[✓] Detected React version: ${vResult.versionKey} (${vResult.matchCount} signature matches)`));
|
|
830
|
+
detectedVersion = vResult;
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
console.log(chalk.yellow(`[!] Version detection: no matching version found`));
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
621
837
|
// Build check — scaffold a minimal webpack project and verify the refactored
|
|
622
838
|
// code compiles.
|
|
623
|
-
runBuildCheck(outputDir, writtenFiles);
|
|
839
|
+
runBuildCheck(outputDir, writtenFiles, detectedVersion);
|
|
624
840
|
}
|
|
625
841
|
else if (tech === "react-vite") {
|
|
626
|
-
|
|
627
|
-
|
|
842
|
+
// Pre-scan vendor chunks from the assets directory that are not in mapped.json.
|
|
843
|
+
// Vendor chunks (vendor-react-*.js) are referenced by app chunks but are typically
|
|
844
|
+
// absent from mapped.json. Without them, vendorExportMaps is empty and vendor
|
|
845
|
+
// import statements are never rewritten — leaving unresolvable references in output.
|
|
846
|
+
const vitAssetsDir = findAssetsDir(chunks, mappedJson);
|
|
847
|
+
if (vitAssetsDir) {
|
|
848
|
+
const missingFiles = findVendorChunkFiles(chunks, vitAssetsDir);
|
|
849
|
+
for (const vendorFile of missingFiles) {
|
|
850
|
+
const basename = path.basename(vendorFile);
|
|
851
|
+
if (!/vendor[-_]react/i.test(basename) && !/rolldown[-_]runtime/i.test(basename))
|
|
852
|
+
continue;
|
|
853
|
+
console.log(chalk.cyan(`[i] Including missing vendor chunk: ${basename}`));
|
|
854
|
+
const key = basename.replace(/\.js$/, "");
|
|
855
|
+
chunks[key] = {
|
|
856
|
+
id: basename,
|
|
857
|
+
description: "",
|
|
858
|
+
loadedOn: [],
|
|
859
|
+
containsFetch: false,
|
|
860
|
+
isAxiosLibrary: false,
|
|
861
|
+
exports: [],
|
|
862
|
+
callStack: [],
|
|
863
|
+
code: fs.readFileSync(vendorFile, "utf8"),
|
|
864
|
+
imports: [],
|
|
865
|
+
file: `assets/${basename}`,
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
const viteFiles = yield refactorVite(chunks, libSigs, remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.scat);
|
|
870
|
+
// Group chunk outputs by their output file path before writing.
|
|
871
|
+
// Multiple mapped.json sub-chunks can originate from the same Vite source
|
|
872
|
+
// file (one per top-level function detected by `map`). Writing each
|
|
873
|
+
// sub-chunk independently would overwrite previous writes so only the last
|
|
874
|
+
// sub-chunk would survive. Grouping and concatenating avoids the overwrite:
|
|
875
|
+
// all sub-chunks for the same file are merged into one formatted output.
|
|
876
|
+
const fileGroups = new Map();
|
|
628
877
|
for (const [chunkKey, rawCode] of Object.entries(viteFiles)) {
|
|
878
|
+
// Use the original chunk basename so dynamic imports (which reference
|
|
879
|
+
// the original Vite filenames) resolve correctly in the build check.
|
|
880
|
+
const chunkInfo = chunks[chunkKey];
|
|
881
|
+
const originalFile = chunkInfo ? ((_d = chunkInfo.file) !== null && _d !== void 0 ? _d : chunkInfo.id) : null;
|
|
882
|
+
const outputBasename = originalFile
|
|
883
|
+
? path.basename(originalFile)
|
|
884
|
+
: chunkKey.replace(/[/\\]/g, "_").replace(/\.js$/, "") + ".js";
|
|
885
|
+
const filePath = `${outputDir}/${outputBasename}`;
|
|
886
|
+
if (!fileGroups.has(filePath)) {
|
|
887
|
+
fileGroups.set(filePath, { codes: [], keys: [] });
|
|
888
|
+
}
|
|
889
|
+
fileGroups.get(filePath).codes.push(rawCode);
|
|
890
|
+
fileGroups.get(filePath).keys.push(chunkKey);
|
|
891
|
+
}
|
|
892
|
+
const writtenFiles = [];
|
|
893
|
+
for (const [filePath, { codes, keys }] of fileGroups) {
|
|
894
|
+
// Put import-bearing code parts first so ESM import statements end up
|
|
895
|
+
// at the top of the concatenated file. Helper sub-chunks typically have
|
|
896
|
+
// no imports; the component sub-chunk that has peer imports (e.g.
|
|
897
|
+
// from ./shared-*.js) must come first or the combined file is invalid.
|
|
898
|
+
const importBearing = codes.filter((c) => /^import\s/m.test(c));
|
|
899
|
+
const rest = codes.filter((c) => !/^import\s/m.test(c));
|
|
900
|
+
const combined = [...importBearing, ...rest].join("\n\n");
|
|
629
901
|
let formatted;
|
|
630
902
|
try {
|
|
631
|
-
formatted = yield prettier.format(
|
|
903
|
+
formatted = yield prettier.format(combined, {
|
|
632
904
|
parser: "babel",
|
|
633
905
|
singleQuote: true,
|
|
634
906
|
trailingComma: "none",
|
|
635
907
|
});
|
|
636
908
|
}
|
|
637
|
-
catch (
|
|
638
|
-
formatted =
|
|
909
|
+
catch (_h) {
|
|
910
|
+
formatted = combined;
|
|
639
911
|
}
|
|
640
912
|
if (formatted.trim().length === 0) {
|
|
641
|
-
console.log(chalk.gray(`[~] Chunk ${
|
|
913
|
+
console.log(chalk.gray(`[~] Chunk ${keys.join("+")} is empty after refactoring — skipping`));
|
|
642
914
|
continue;
|
|
643
915
|
}
|
|
644
|
-
|
|
645
|
-
//
|
|
916
|
+
fs.writeFileSync(filePath, formatted);
|
|
917
|
+
// Push exactly once per output file — prevents duplicate entries in
|
|
918
|
+
// writtenFiles that would cause the rename loop in runViteBuildCheck
|
|
919
|
+
// to attempt renaming the same path twice (ENOENT on the second try).
|
|
920
|
+
writtenFiles.push(filePath);
|
|
921
|
+
for (const key of keys) {
|
|
922
|
+
console.log(chalk.green(`[✓] Chunk ${key} written to ${filePath}`));
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
// Version detection — run before build check so the detected version can be
|
|
926
|
+
// written into package.json by runViteBuildCheck.
|
|
927
|
+
let viteDetectedVersion;
|
|
928
|
+
if ((remoteOpts === null || remoteOpts === void 0 ? void 0 : remoteOpts.detectVersion) && VERSION_TECH_TO_BUNDLER[tech]) {
|
|
929
|
+
const bundler = VERSION_TECH_TO_BUNDLER[tech];
|
|
930
|
+
const vScatDirs = yield resolveVersionDetectionScatDirs(bundler, remoteOpts);
|
|
931
|
+
if (vScatDirs.length > 0) {
|
|
932
|
+
const vResult = yield detectReactVersion(chunks, tech, vScatDirs);
|
|
933
|
+
if (vResult) {
|
|
934
|
+
console.log(chalk.green(`[✓] Detected React version: ${vResult.versionKey} (${vResult.matchCount} signature matches)`));
|
|
935
|
+
viteDetectedVersion = vResult;
|
|
936
|
+
}
|
|
937
|
+
else {
|
|
938
|
+
console.log(chalk.yellow(`[!] Version detection: no matching version found`));
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
// Build check with Vite scaffold
|
|
943
|
+
runViteBuildCheck(outputDir, writtenFiles, viteDetectedVersion);
|
|
944
|
+
}
|
|
945
|
+
else if (tech === "vue-webpack") {
|
|
946
|
+
for (const [, value] of Object.entries(chunks)) {
|
|
947
|
+
const moduleFiles = yield refactorVueWebpack(value);
|
|
948
|
+
for (const [moduleId, rawCode] of Object.entries(moduleFiles)) {
|
|
949
|
+
let formatted;
|
|
950
|
+
try {
|
|
951
|
+
formatted = yield prettier.format(rawCode, {
|
|
952
|
+
parser: "babel",
|
|
953
|
+
singleQuote: true,
|
|
954
|
+
trailingComma: "none",
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
catch (_j) {
|
|
958
|
+
formatted = rawCode;
|
|
959
|
+
}
|
|
960
|
+
if (formatted.trim().length === 0) {
|
|
961
|
+
console.log(chalk.gray(`[~] Module ${moduleId} is empty after stripping — skipping`));
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
const filePath = `${outputDir}/${moduleId}.js`;
|
|
965
|
+
fs.writeFileSync(filePath, formatted);
|
|
966
|
+
console.log(chalk.green(`[✓] Module ${moduleId} written to ${filePath}`));
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
else if (tech === "vue-vite") {
|
|
971
|
+
const viteFiles = yield refactorVueVite(chunks);
|
|
972
|
+
for (const [chunkKey, rawCode] of Object.entries(viteFiles)) {
|
|
646
973
|
const chunkInfo = chunks[chunkKey];
|
|
647
|
-
const originalFile = chunkInfo ? ((
|
|
974
|
+
const originalFile = chunkInfo ? ((_e = chunkInfo.file) !== null && _e !== void 0 ? _e : chunkInfo.id) : null;
|
|
648
975
|
const outputBasename = originalFile
|
|
649
976
|
? path.basename(originalFile)
|
|
650
977
|
: chunkKey.replace(/[/\\]/g, "_").replace(/\.js$/, "") + ".js";
|
|
651
978
|
const filePath = `${outputDir}/${outputBasename}`;
|
|
979
|
+
let formatted;
|
|
980
|
+
try {
|
|
981
|
+
formatted = yield prettier.format(rawCode, {
|
|
982
|
+
parser: "babel",
|
|
983
|
+
singleQuote: true,
|
|
984
|
+
trailingComma: "none",
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
catch (_k) {
|
|
988
|
+
formatted = rawCode;
|
|
989
|
+
}
|
|
990
|
+
if (formatted.trim().length === 0) {
|
|
991
|
+
console.log(chalk.gray(`[~] Chunk ${chunkKey} is empty after refactoring — skipping`));
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
652
994
|
fs.writeFileSync(filePath, formatted);
|
|
653
|
-
writtenFiles.push(filePath);
|
|
654
995
|
console.log(chalk.green(`[✓] Chunk ${chunkKey} written to ${filePath}`));
|
|
655
996
|
}
|
|
656
|
-
// Build check with Vite scaffold
|
|
657
|
-
runViteBuildCheck(outputDir, writtenFiles);
|
|
658
997
|
}
|
|
659
998
|
console.log(chalk.green("[✓] Refactoring complete."));
|
|
660
999
|
});
|