pruny 1.43.9 → 1.44.0
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/dist/index.js +691 -31
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -12564,7 +12564,7 @@ var source_default = chalk;
|
|
|
12564
12564
|
// src/index.ts
|
|
12565
12565
|
var import_prompts = __toESM(require_prompts3(), 1);
|
|
12566
12566
|
import { rmSync, existsSync as existsSync11, readdirSync, lstatSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
12567
|
-
import { dirname as
|
|
12567
|
+
import { dirname as dirname7, join as join11, relative as relative5, resolve as resolve5 } from "node:path";
|
|
12568
12568
|
|
|
12569
12569
|
// src/scanner.ts
|
|
12570
12570
|
var import_fast_glob10 = __toESM(require_out4(), 1);
|
|
@@ -15886,16 +15886,16 @@ async function scanUnusedServices(config) {
|
|
|
15886
15886
|
|
|
15887
15887
|
// src/scanners/broken-links.ts
|
|
15888
15888
|
var import_fast_glob9 = __toESM(require_out4(), 1);
|
|
15889
|
-
import { readFileSync as readFileSync9, existsSync as existsSync7 } from "node:fs";
|
|
15890
|
-
import { join as join7 } from "node:path";
|
|
15889
|
+
import { readFileSync as readFileSync9, existsSync as existsSync7, statSync as statSync2 } from "node:fs";
|
|
15890
|
+
import { dirname as dirname5, join as join7, resolve as resolve3 } from "node:path";
|
|
15891
15891
|
var LINK_PATTERNS = [
|
|
15892
|
-
/<Link\s+[^>]*href\s*=\s*['"`](\/[^'"`\s
|
|
15893
|
-
/router\.(push|replace)\s*\(\s*['"`](\/[^'"`\s
|
|
15894
|
-
/(?:redirect|permanentRedirect)\s*\(\s*['"`](\/[^'"`\s
|
|
15895
|
-
/href\s*:\s*['"`](\/[^'"`\s
|
|
15896
|
-
/<a\s+[^>]*href\s*=\s*['"`](\/[^'"`\s
|
|
15897
|
-
/revalidatePath\s*\(\s*['"`](\/[^'"`\s
|
|
15898
|
-
/pathname\s*===?\s*['"`](\/[^'"`\s
|
|
15892
|
+
/<Link\s+[^>]*href\s*=\s*(?:\{\s*)?['"`](\/[^'"`\s]+)['"`](?:\s*\})?/g,
|
|
15893
|
+
/router\.(push|replace)\s*\(\s*['"`](\/[^'"`\s]+)['"`]/g,
|
|
15894
|
+
/(?:redirect|permanentRedirect)\s*\(\s*['"`](\/[^'"`\s]+)['"`]/g,
|
|
15895
|
+
/href\s*:\s*['"`](\/[^'"`\s]+)['"`]/g,
|
|
15896
|
+
/<a\s+[^>]*href\s*=\s*(?:\{\s*)?['"`](\/[^'"`\s]+)['"`](?:\s*\})?/g,
|
|
15897
|
+
/revalidatePath\s*\(\s*['"`](\/[^'"`\s]+)['"`]/g,
|
|
15898
|
+
/pathname\s*===?\s*['"`](\/[^'"`\s]+)['"`]/g
|
|
15899
15899
|
];
|
|
15900
15900
|
function extractPath(match2) {
|
|
15901
15901
|
if (match2[2] && match2[2].startsWith("/"))
|
|
@@ -15904,6 +15904,11 @@ function extractPath(match2) {
|
|
|
15904
15904
|
return match2[1];
|
|
15905
15905
|
return null;
|
|
15906
15906
|
}
|
|
15907
|
+
function normalizePath(raw) {
|
|
15908
|
+
let out = raw.replace(/\$\{[^}]*\}/g, "[id]");
|
|
15909
|
+
out = out.replace(/[{}]/g, "");
|
|
15910
|
+
return out;
|
|
15911
|
+
}
|
|
15907
15912
|
function shouldSkipPath(path2) {
|
|
15908
15913
|
if (/^https?:\/\//.test(path2))
|
|
15909
15914
|
return true;
|
|
@@ -15939,15 +15944,15 @@ function filePathToRoute(filePath) {
|
|
|
15939
15944
|
});
|
|
15940
15945
|
return "/" + segments.join("/");
|
|
15941
15946
|
}
|
|
15942
|
-
function matchesRoute(refPath, routes,
|
|
15947
|
+
function matchesRoute(refPath, routes, routes_) {
|
|
15943
15948
|
const cleaned = cleanPath(refPath);
|
|
15944
15949
|
if (routes.has(cleaned))
|
|
15945
15950
|
return true;
|
|
15946
15951
|
const refSegments = cleaned.split("/").filter(Boolean);
|
|
15947
|
-
for (const
|
|
15948
|
-
if (matchSegments(refSegments,
|
|
15952
|
+
for (const route of routes_) {
|
|
15953
|
+
if (matchSegments(refSegments, route.segments, route.params))
|
|
15949
15954
|
return true;
|
|
15950
|
-
if (matchesDynamicSuffix(refSegments,
|
|
15955
|
+
if (matchesDynamicSuffix(refSegments, route.segments))
|
|
15951
15956
|
return true;
|
|
15952
15957
|
}
|
|
15953
15958
|
return false;
|
|
@@ -15965,14 +15970,29 @@ function matchesDynamicSuffix(refSegments, routeSegments) {
|
|
|
15965
15970
|
return false;
|
|
15966
15971
|
return matchSegments(refSegments, tail);
|
|
15967
15972
|
}
|
|
15968
|
-
function matchSegments(refSegments, routeSegments) {
|
|
15973
|
+
function matchSegments(refSegments, routeSegments, params) {
|
|
15969
15974
|
let ri = 0;
|
|
15970
15975
|
let si = 0;
|
|
15971
15976
|
while (ri < refSegments.length && si < routeSegments.length) {
|
|
15972
15977
|
const routeSeg = routeSegments[si];
|
|
15973
15978
|
if (/^\[\[?\.\.\./.test(routeSeg))
|
|
15974
15979
|
return true;
|
|
15975
|
-
|
|
15980
|
+
const dynMatch = /^\[(.+)\]$/.exec(routeSeg);
|
|
15981
|
+
if (dynMatch) {
|
|
15982
|
+
const paramName = dynMatch[1];
|
|
15983
|
+
if (params && params[paramName]) {
|
|
15984
|
+
const ref = refSegments[ri];
|
|
15985
|
+
const allowed = params[paramName];
|
|
15986
|
+
let ok = false;
|
|
15987
|
+
for (const v of allowed) {
|
|
15988
|
+
if (v.toLowerCase() === ref.toLowerCase()) {
|
|
15989
|
+
ok = true;
|
|
15990
|
+
break;
|
|
15991
|
+
}
|
|
15992
|
+
}
|
|
15993
|
+
if (!ok)
|
|
15994
|
+
return false;
|
|
15995
|
+
}
|
|
15976
15996
|
ri++;
|
|
15977
15997
|
si++;
|
|
15978
15998
|
continue;
|
|
@@ -15997,7 +16017,7 @@ function isGitignoredPublicFile(appDir, linkPath) {
|
|
|
15997
16017
|
continue;
|
|
15998
16018
|
try {
|
|
15999
16019
|
const patterns = readFileSync9(gitignorePath, "utf-8").split(`
|
|
16000
|
-
`).map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
16020
|
+
`).map((l) => l.trim()).filter((l) => l && !l.startsWith("#") && !l.startsWith("!"));
|
|
16001
16021
|
for (const pattern of patterns) {
|
|
16002
16022
|
if (minimatch(publicRelPath, pattern, { dot: true }) || minimatch(linkPath.slice(1), pattern, { dot: true }) || minimatch(`**/public${linkPath}`, pattern, { dot: true })) {
|
|
16003
16023
|
return true;
|
|
@@ -16025,12 +16045,21 @@ async function scanBrokenLinks(config) {
|
|
|
16025
16045
|
const knownRoutes = new Set;
|
|
16026
16046
|
const routeSegmentsList = [];
|
|
16027
16047
|
knownRoutes.add("/");
|
|
16048
|
+
const aliasMap = parseTsConfigPaths(appDir);
|
|
16028
16049
|
for (const file of pageFiles) {
|
|
16029
16050
|
const route = filePathToRoute(file);
|
|
16030
16051
|
knownRoutes.add(route);
|
|
16031
16052
|
const segments = route.split("/").filter(Boolean);
|
|
16032
16053
|
if (segments.some((s) => s.startsWith("["))) {
|
|
16033
|
-
|
|
16054
|
+
const absFile = join7(appDir, file);
|
|
16055
|
+
const params = resolveStaticParams(absFile, appDir, aliasMap);
|
|
16056
|
+
routeSegmentsList.push({ segments, params: params ?? undefined });
|
|
16057
|
+
if (process.env.DEBUG_PRUNY) {
|
|
16058
|
+
if (params) {
|
|
16059
|
+
const summary = Object.entries(params).map(([k, v]) => `${k}=${v.size}`).join(", ");
|
|
16060
|
+
console.log(`[DEBUG] Static params for ${route}: ${summary}`);
|
|
16061
|
+
}
|
|
16062
|
+
}
|
|
16034
16063
|
}
|
|
16035
16064
|
}
|
|
16036
16065
|
if (process.env.DEBUG_PRUNY) {
|
|
@@ -16073,9 +16102,10 @@ async function scanBrokenLinks(config) {
|
|
|
16073
16102
|
pattern.lastIndex = 0;
|
|
16074
16103
|
let match2;
|
|
16075
16104
|
while ((match2 = pattern.exec(content)) !== null) {
|
|
16076
|
-
const
|
|
16077
|
-
if (!
|
|
16105
|
+
const extracted = extractPath(match2);
|
|
16106
|
+
if (!extracted)
|
|
16078
16107
|
continue;
|
|
16108
|
+
const rawPath = normalizePath(extracted);
|
|
16079
16109
|
if (shouldSkipPath(rawPath))
|
|
16080
16110
|
continue;
|
|
16081
16111
|
const cleaned = cleanPath(rawPath);
|
|
@@ -16123,6 +16153,632 @@ async function scanBrokenLinks(config) {
|
|
|
16123
16153
|
links
|
|
16124
16154
|
};
|
|
16125
16155
|
}
|
|
16156
|
+
var TS_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
16157
|
+
var JSON_EXT = ".json";
|
|
16158
|
+
function resolveStaticParams(routeFile, appDir, aliasMap) {
|
|
16159
|
+
let content;
|
|
16160
|
+
try {
|
|
16161
|
+
content = readFileSync9(routeFile, "utf-8");
|
|
16162
|
+
} catch {
|
|
16163
|
+
return null;
|
|
16164
|
+
}
|
|
16165
|
+
const body = extractFunctionBody(content, "generateStaticParams");
|
|
16166
|
+
if (!body)
|
|
16167
|
+
return null;
|
|
16168
|
+
const returnExpr = extractReturnExpression(body);
|
|
16169
|
+
if (!returnExpr)
|
|
16170
|
+
return null;
|
|
16171
|
+
return resolveParamsExpression(returnExpr, content, routeFile, appDir, aliasMap, new Set, 0);
|
|
16172
|
+
}
|
|
16173
|
+
function extractFunctionBody(source, name) {
|
|
16174
|
+
const fnRe = new RegExp(`(?:export\\s+)?(?:async\\s+)?function\\s+${name}\\s*\\([^)]*\\)\\s*\\{`, "g");
|
|
16175
|
+
let m = fnRe.exec(source);
|
|
16176
|
+
if (m) {
|
|
16177
|
+
return readBalanced(source, m.index + m[0].length - 1);
|
|
16178
|
+
}
|
|
16179
|
+
const arrowRe = new RegExp(`(?:export\\s+)?const\\s+${name}\\s*=\\s*(?:async\\s*)?\\([^)]*\\)\\s*=>\\s*\\{`, "g");
|
|
16180
|
+
m = arrowRe.exec(source);
|
|
16181
|
+
if (m) {
|
|
16182
|
+
return readBalanced(source, m.index + m[0].length - 1);
|
|
16183
|
+
}
|
|
16184
|
+
return null;
|
|
16185
|
+
}
|
|
16186
|
+
function readBalanced(source, start) {
|
|
16187
|
+
if (source[start] !== "{")
|
|
16188
|
+
return null;
|
|
16189
|
+
let depth = 0;
|
|
16190
|
+
let i = start;
|
|
16191
|
+
let inString = null;
|
|
16192
|
+
let inLineComment = false;
|
|
16193
|
+
let inBlockComment = false;
|
|
16194
|
+
for (;i < source.length; i++) {
|
|
16195
|
+
const c = source[i];
|
|
16196
|
+
const next = source[i + 1];
|
|
16197
|
+
if (inLineComment) {
|
|
16198
|
+
if (c === `
|
|
16199
|
+
`)
|
|
16200
|
+
inLineComment = false;
|
|
16201
|
+
continue;
|
|
16202
|
+
}
|
|
16203
|
+
if (inBlockComment) {
|
|
16204
|
+
if (c === "*" && next === "/") {
|
|
16205
|
+
inBlockComment = false;
|
|
16206
|
+
i++;
|
|
16207
|
+
}
|
|
16208
|
+
continue;
|
|
16209
|
+
}
|
|
16210
|
+
if (inString) {
|
|
16211
|
+
if (c === "\\") {
|
|
16212
|
+
i++;
|
|
16213
|
+
continue;
|
|
16214
|
+
}
|
|
16215
|
+
if (c === inString)
|
|
16216
|
+
inString = null;
|
|
16217
|
+
continue;
|
|
16218
|
+
}
|
|
16219
|
+
if (c === "/" && next === "/") {
|
|
16220
|
+
inLineComment = true;
|
|
16221
|
+
i++;
|
|
16222
|
+
continue;
|
|
16223
|
+
}
|
|
16224
|
+
if (c === "/" && next === "*") {
|
|
16225
|
+
inBlockComment = true;
|
|
16226
|
+
i++;
|
|
16227
|
+
continue;
|
|
16228
|
+
}
|
|
16229
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
16230
|
+
inString = c;
|
|
16231
|
+
continue;
|
|
16232
|
+
}
|
|
16233
|
+
if (c === "{")
|
|
16234
|
+
depth++;
|
|
16235
|
+
else if (c === "}") {
|
|
16236
|
+
depth--;
|
|
16237
|
+
if (depth === 0)
|
|
16238
|
+
return source.slice(start + 1, i);
|
|
16239
|
+
}
|
|
16240
|
+
}
|
|
16241
|
+
return null;
|
|
16242
|
+
}
|
|
16243
|
+
function extractReturnExpression(body) {
|
|
16244
|
+
const re = /\breturn\s+([\s\S]+?)(?:;|$)/g;
|
|
16245
|
+
let last = null;
|
|
16246
|
+
let m;
|
|
16247
|
+
while ((m = re.exec(body)) !== null)
|
|
16248
|
+
last = m;
|
|
16249
|
+
if (!last)
|
|
16250
|
+
return null;
|
|
16251
|
+
return last[1].trim();
|
|
16252
|
+
}
|
|
16253
|
+
function resolveParamsExpression(expr, fileContent, filePath, appDir, aliasMap, visited, depth) {
|
|
16254
|
+
if (depth > 4)
|
|
16255
|
+
return null;
|
|
16256
|
+
expr = expr.trim();
|
|
16257
|
+
if (expr.startsWith("[")) {
|
|
16258
|
+
const arrText = sliceTopLevelArray(expr);
|
|
16259
|
+
if (arrText) {
|
|
16260
|
+
const tail = expr.slice(arrText.length).trim();
|
|
16261
|
+
if (!tail) {
|
|
16262
|
+
const arr = parseObjectArray(arrText);
|
|
16263
|
+
if (arr)
|
|
16264
|
+
return mergeParams(arr);
|
|
16265
|
+
}
|
|
16266
|
+
if (tail.startsWith(".map")) {
|
|
16267
|
+
const stringArr = parseStringArray(arrText);
|
|
16268
|
+
if (stringArr) {
|
|
16269
|
+
const paramName = inferParamFromMap(expr) ?? "slug";
|
|
16270
|
+
return { [paramName]: new Set(stringArr) };
|
|
16271
|
+
}
|
|
16272
|
+
const objArr = parseObjectArray(arrText);
|
|
16273
|
+
if (objArr) {
|
|
16274
|
+
const projection = extractMapProjection(expr);
|
|
16275
|
+
if (!projection)
|
|
16276
|
+
return null;
|
|
16277
|
+
const out = {};
|
|
16278
|
+
for (const [param, source] of Object.entries(projection)) {
|
|
16279
|
+
const parts = source.split(".");
|
|
16280
|
+
parts.shift();
|
|
16281
|
+
const values = [];
|
|
16282
|
+
for (const obj of objArr) {
|
|
16283
|
+
let v = obj;
|
|
16284
|
+
for (const p of parts) {
|
|
16285
|
+
if (v && typeof v === "object" && p in v) {
|
|
16286
|
+
v = v[p];
|
|
16287
|
+
} else {
|
|
16288
|
+
v = undefined;
|
|
16289
|
+
break;
|
|
16290
|
+
}
|
|
16291
|
+
}
|
|
16292
|
+
if (typeof v === "string")
|
|
16293
|
+
values.push(v);
|
|
16294
|
+
}
|
|
16295
|
+
if (values.length > 0)
|
|
16296
|
+
out[param] = new Set(values);
|
|
16297
|
+
}
|
|
16298
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
16299
|
+
}
|
|
16300
|
+
}
|
|
16301
|
+
}
|
|
16302
|
+
}
|
|
16303
|
+
const objKeysM = /^Object\.(keys|entries)\s*\(\s*([A-Za-z_$][\w$]*)\s*\)/.exec(expr);
|
|
16304
|
+
if (objKeysM) {
|
|
16305
|
+
const ident = objKeysM[2];
|
|
16306
|
+
const keys = resolveIdentifierKeys(ident, fileContent, filePath, appDir, aliasMap, visited, depth);
|
|
16307
|
+
if (!keys)
|
|
16308
|
+
return null;
|
|
16309
|
+
const paramName = inferParamFromMap(expr) ?? guessFirstParamName(expr) ?? "slug";
|
|
16310
|
+
return { [paramName]: new Set(keys) };
|
|
16311
|
+
}
|
|
16312
|
+
const identMapM = /^([A-Za-z_$][\w$]*)\s*\.\s*map\s*\(/.exec(expr);
|
|
16313
|
+
if (identMapM) {
|
|
16314
|
+
const ident = identMapM[1];
|
|
16315
|
+
const resolved = resolveIdentifierAsArray(ident, fileContent, filePath, appDir, aliasMap, visited, depth);
|
|
16316
|
+
if (!resolved)
|
|
16317
|
+
return null;
|
|
16318
|
+
if (resolved.kind === "strings") {
|
|
16319
|
+
const paramName = inferParamFromMap(expr) ?? "slug";
|
|
16320
|
+
return { [paramName]: new Set(resolved.values) };
|
|
16321
|
+
}
|
|
16322
|
+
if (resolved.kind === "objects") {
|
|
16323
|
+
const projection = extractMapProjection(expr);
|
|
16324
|
+
if (!projection)
|
|
16325
|
+
return null;
|
|
16326
|
+
const out = {};
|
|
16327
|
+
for (const [param, source] of Object.entries(projection)) {
|
|
16328
|
+
const values = [];
|
|
16329
|
+
for (const obj of resolved.values) {
|
|
16330
|
+
const parts = source.split(".");
|
|
16331
|
+
parts.shift();
|
|
16332
|
+
let v = obj;
|
|
16333
|
+
for (const p of parts) {
|
|
16334
|
+
if (v && typeof v === "object" && p in v) {
|
|
16335
|
+
v = v[p];
|
|
16336
|
+
} else {
|
|
16337
|
+
v = undefined;
|
|
16338
|
+
break;
|
|
16339
|
+
}
|
|
16340
|
+
}
|
|
16341
|
+
if (typeof v === "string")
|
|
16342
|
+
values.push(v);
|
|
16343
|
+
}
|
|
16344
|
+
if (values.length > 0)
|
|
16345
|
+
out[param] = new Set(values);
|
|
16346
|
+
}
|
|
16347
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
16348
|
+
}
|
|
16349
|
+
if (resolved.kind === "objectKeys") {
|
|
16350
|
+
const paramName = inferParamFromMap(expr) ?? "slug";
|
|
16351
|
+
return { [paramName]: new Set(resolved.values) };
|
|
16352
|
+
}
|
|
16353
|
+
}
|
|
16354
|
+
return null;
|
|
16355
|
+
}
|
|
16356
|
+
function extractMapProjection(expr) {
|
|
16357
|
+
const bodyMatch = /\.map\s*\(\s*(?:\(([^)]*)\)|([A-Za-z_$][\w$]*))\s*=>\s*\(?\s*\{([^}]+)\}/.exec(expr);
|
|
16358
|
+
if (!bodyMatch)
|
|
16359
|
+
return null;
|
|
16360
|
+
const iter = (bodyMatch[1] ?? bodyMatch[2] ?? "").trim().split(",")[0].trim() || "item";
|
|
16361
|
+
const objBody = bodyMatch[3];
|
|
16362
|
+
const out = {};
|
|
16363
|
+
const propRe = /([A-Za-z_$][\w$]*)\s*(?::\s*([A-Za-z_$][\w$.]*))?/g;
|
|
16364
|
+
let m;
|
|
16365
|
+
while ((m = propRe.exec(objBody)) !== null) {
|
|
16366
|
+
const key = m[1];
|
|
16367
|
+
const value = m[2] ?? key;
|
|
16368
|
+
out[key] = value.startsWith(iter + ".") || value === iter ? value : `${iter}.${value}`;
|
|
16369
|
+
}
|
|
16370
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
16371
|
+
}
|
|
16372
|
+
function inferParamFromMap(expr) {
|
|
16373
|
+
const m = /=>\s*\(?\s*\{\s*([A-Za-z_$][\w$]*)\s*[:}]/.exec(expr);
|
|
16374
|
+
return m ? m[1] : null;
|
|
16375
|
+
}
|
|
16376
|
+
function guessFirstParamName(expr) {
|
|
16377
|
+
const m = /\{\s*([A-Za-z_$][\w$]*)\s*\}/.exec(expr);
|
|
16378
|
+
return m ? m[1] : null;
|
|
16379
|
+
}
|
|
16380
|
+
function mergeParams(items) {
|
|
16381
|
+
const out = {};
|
|
16382
|
+
for (const item of items) {
|
|
16383
|
+
for (const [k, v] of Object.entries(item)) {
|
|
16384
|
+
if (typeof v !== "string")
|
|
16385
|
+
continue;
|
|
16386
|
+
if (!out[k])
|
|
16387
|
+
out[k] = new Set;
|
|
16388
|
+
out[k].add(v);
|
|
16389
|
+
}
|
|
16390
|
+
}
|
|
16391
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
16392
|
+
}
|
|
16393
|
+
function parseObjectArray(expr) {
|
|
16394
|
+
const arrText = sliceTopLevelArray(expr);
|
|
16395
|
+
if (!arrText)
|
|
16396
|
+
return null;
|
|
16397
|
+
const inner = arrText.slice(1, -1).trim();
|
|
16398
|
+
if (!inner)
|
|
16399
|
+
return [];
|
|
16400
|
+
const items = [];
|
|
16401
|
+
let depth = 0;
|
|
16402
|
+
let buf = "";
|
|
16403
|
+
const chunks = [];
|
|
16404
|
+
let inString = null;
|
|
16405
|
+
for (let i = 0;i < inner.length; i++) {
|
|
16406
|
+
const c = inner[i];
|
|
16407
|
+
if (inString) {
|
|
16408
|
+
if (c === "\\") {
|
|
16409
|
+
buf += c + inner[++i];
|
|
16410
|
+
continue;
|
|
16411
|
+
}
|
|
16412
|
+
if (c === inString)
|
|
16413
|
+
inString = null;
|
|
16414
|
+
buf += c;
|
|
16415
|
+
continue;
|
|
16416
|
+
}
|
|
16417
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
16418
|
+
inString = c;
|
|
16419
|
+
buf += c;
|
|
16420
|
+
continue;
|
|
16421
|
+
}
|
|
16422
|
+
if (c === "{" || c === "[" || c === "(")
|
|
16423
|
+
depth++;
|
|
16424
|
+
else if (c === "}" || c === "]" || c === ")")
|
|
16425
|
+
depth--;
|
|
16426
|
+
if (c === "," && depth === 0) {
|
|
16427
|
+
chunks.push(buf);
|
|
16428
|
+
buf = "";
|
|
16429
|
+
continue;
|
|
16430
|
+
}
|
|
16431
|
+
buf += c;
|
|
16432
|
+
}
|
|
16433
|
+
if (buf.trim())
|
|
16434
|
+
chunks.push(buf);
|
|
16435
|
+
for (const chunk of chunks) {
|
|
16436
|
+
const obj = parseSimpleObject(chunk.trim());
|
|
16437
|
+
if (!obj)
|
|
16438
|
+
return null;
|
|
16439
|
+
items.push(obj);
|
|
16440
|
+
}
|
|
16441
|
+
return items;
|
|
16442
|
+
}
|
|
16443
|
+
function parseStringArray(arrText) {
|
|
16444
|
+
if (!arrText.startsWith("[") || !arrText.endsWith("]"))
|
|
16445
|
+
return null;
|
|
16446
|
+
const inner = arrText.slice(1, -1).trim();
|
|
16447
|
+
if (!inner)
|
|
16448
|
+
return [];
|
|
16449
|
+
if (/[{[]/.test(inner))
|
|
16450
|
+
return null;
|
|
16451
|
+
const out = [];
|
|
16452
|
+
const re = /["'`]([^"'`]*)["'`]/g;
|
|
16453
|
+
let m;
|
|
16454
|
+
let count = 0;
|
|
16455
|
+
while ((m = re.exec(inner)) !== null) {
|
|
16456
|
+
out.push(m[1]);
|
|
16457
|
+
count++;
|
|
16458
|
+
}
|
|
16459
|
+
const chunks = inner.split(",").filter((s) => s.trim()).length;
|
|
16460
|
+
if (count !== chunks)
|
|
16461
|
+
return null;
|
|
16462
|
+
return out;
|
|
16463
|
+
}
|
|
16464
|
+
function sliceTopLevelArray(expr) {
|
|
16465
|
+
if (expr[0] !== "[")
|
|
16466
|
+
return null;
|
|
16467
|
+
let depth = 0;
|
|
16468
|
+
let inString = null;
|
|
16469
|
+
for (let i = 0;i < expr.length; i++) {
|
|
16470
|
+
const c = expr[i];
|
|
16471
|
+
if (inString) {
|
|
16472
|
+
if (c === "\\") {
|
|
16473
|
+
i++;
|
|
16474
|
+
continue;
|
|
16475
|
+
}
|
|
16476
|
+
if (c === inString)
|
|
16477
|
+
inString = null;
|
|
16478
|
+
continue;
|
|
16479
|
+
}
|
|
16480
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
16481
|
+
inString = c;
|
|
16482
|
+
continue;
|
|
16483
|
+
}
|
|
16484
|
+
if (c === "[")
|
|
16485
|
+
depth++;
|
|
16486
|
+
else if (c === "]") {
|
|
16487
|
+
depth--;
|
|
16488
|
+
if (depth === 0)
|
|
16489
|
+
return expr.slice(0, i + 1);
|
|
16490
|
+
}
|
|
16491
|
+
}
|
|
16492
|
+
return null;
|
|
16493
|
+
}
|
|
16494
|
+
function parseSimpleObject(text) {
|
|
16495
|
+
if (!text.startsWith("{") || !text.endsWith("}"))
|
|
16496
|
+
return null;
|
|
16497
|
+
const body = text.slice(1, -1);
|
|
16498
|
+
const out = {};
|
|
16499
|
+
const re = /([A-Za-z_$][\w$]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|`([^`]*)`)/g;
|
|
16500
|
+
let m;
|
|
16501
|
+
let count = 0;
|
|
16502
|
+
while ((m = re.exec(body)) !== null) {
|
|
16503
|
+
out[m[1]] = m[2] ?? m[3] ?? m[4] ?? "";
|
|
16504
|
+
count++;
|
|
16505
|
+
}
|
|
16506
|
+
return count > 0 ? out : null;
|
|
16507
|
+
}
|
|
16508
|
+
function resolveIdentifierKeys(ident, fileContent, filePath, appDir, aliasMap, visited, depth) {
|
|
16509
|
+
const r = resolveIdentifier(ident, fileContent, filePath, appDir, aliasMap, visited, depth);
|
|
16510
|
+
if (!r)
|
|
16511
|
+
return null;
|
|
16512
|
+
if (r.kind === "objectKeys")
|
|
16513
|
+
return r.values;
|
|
16514
|
+
if (r.kind === "objects") {
|
|
16515
|
+
return null;
|
|
16516
|
+
}
|
|
16517
|
+
if (r.kind === "strings")
|
|
16518
|
+
return r.values;
|
|
16519
|
+
return null;
|
|
16520
|
+
}
|
|
16521
|
+
function resolveIdentifierAsArray(ident, fileContent, filePath, appDir, aliasMap, visited, depth) {
|
|
16522
|
+
return resolveIdentifier(ident, fileContent, filePath, appDir, aliasMap, visited, depth);
|
|
16523
|
+
}
|
|
16524
|
+
function resolveIdentifier(ident, fileContent, filePath, appDir, aliasMap, visited, depth) {
|
|
16525
|
+
if (depth > 5)
|
|
16526
|
+
return null;
|
|
16527
|
+
const visitKey = `${filePath}::${ident}`;
|
|
16528
|
+
if (visited.has(visitKey))
|
|
16529
|
+
return null;
|
|
16530
|
+
visited.add(visitKey);
|
|
16531
|
+
const localValue = findLocalConst(ident, fileContent);
|
|
16532
|
+
if (localValue) {
|
|
16533
|
+
const parsed = parseLiteralValue(localValue);
|
|
16534
|
+
if (parsed)
|
|
16535
|
+
return parsed;
|
|
16536
|
+
}
|
|
16537
|
+
const importInfo = findImport(ident, fileContent);
|
|
16538
|
+
if (!importInfo)
|
|
16539
|
+
return null;
|
|
16540
|
+
const resolvedPath = resolveModulePath(importInfo.path, filePath, appDir, aliasMap);
|
|
16541
|
+
if (!resolvedPath)
|
|
16542
|
+
return null;
|
|
16543
|
+
if (resolvedPath.endsWith(JSON_EXT)) {
|
|
16544
|
+
try {
|
|
16545
|
+
const data = JSON.parse(readFileSync9(resolvedPath, "utf-8"));
|
|
16546
|
+
return literalToResolution(data);
|
|
16547
|
+
} catch {
|
|
16548
|
+
return null;
|
|
16549
|
+
}
|
|
16550
|
+
}
|
|
16551
|
+
let nextContent;
|
|
16552
|
+
try {
|
|
16553
|
+
nextContent = readFileSync9(resolvedPath, "utf-8");
|
|
16554
|
+
} catch {
|
|
16555
|
+
return null;
|
|
16556
|
+
}
|
|
16557
|
+
const nextIdent = importInfo.kind === "default" ? findDefaultExportIdentifier(nextContent) ?? ident : importInfo.imported;
|
|
16558
|
+
return resolveIdentifier(nextIdent, nextContent, resolvedPath, appDir, aliasMap, visited, depth + 1);
|
|
16559
|
+
}
|
|
16560
|
+
function findImport(ident, source) {
|
|
16561
|
+
const defRe = new RegExp(`import\\s+${ident}\\s+from\\s+['"]([^'"]+)['"]`, "g");
|
|
16562
|
+
let m;
|
|
16563
|
+
if ((m = defRe.exec(source)) !== null) {
|
|
16564
|
+
return { kind: "default", imported: ident, path: m[1] };
|
|
16565
|
+
}
|
|
16566
|
+
const namedRe = /import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]([^'"]+)['"]/g;
|
|
16567
|
+
while ((m = namedRe.exec(source)) !== null) {
|
|
16568
|
+
const specifiers = m[1].split(",").map((s) => s.trim());
|
|
16569
|
+
for (const spec of specifiers) {
|
|
16570
|
+
const aliasMatch = /^([A-Za-z_$][\w$]*)\s+as\s+([A-Za-z_$][\w$]*)$/.exec(spec);
|
|
16571
|
+
if (aliasMatch) {
|
|
16572
|
+
if (aliasMatch[2] === ident) {
|
|
16573
|
+
return { kind: "named", imported: aliasMatch[1], path: m[2] };
|
|
16574
|
+
}
|
|
16575
|
+
} else if (spec === ident) {
|
|
16576
|
+
return { kind: "named", imported: ident, path: m[2] };
|
|
16577
|
+
}
|
|
16578
|
+
}
|
|
16579
|
+
}
|
|
16580
|
+
return null;
|
|
16581
|
+
}
|
|
16582
|
+
function resolveModulePath(spec, fromFile, appDir, aliasMap) {
|
|
16583
|
+
let candidates = [];
|
|
16584
|
+
if (spec.startsWith(".")) {
|
|
16585
|
+
candidates.push(resolve3(dirname5(fromFile), spec));
|
|
16586
|
+
} else if (aliasMap.size > 0) {
|
|
16587
|
+
for (const [prefix, targets] of aliasMap.entries()) {
|
|
16588
|
+
if (spec === prefix.replace(/\/$/, "") || spec.startsWith(prefix)) {
|
|
16589
|
+
const sub = spec.slice(prefix.length);
|
|
16590
|
+
for (const t of targets) {
|
|
16591
|
+
candidates.push(sub ? join7(t, sub) : t);
|
|
16592
|
+
}
|
|
16593
|
+
}
|
|
16594
|
+
}
|
|
16595
|
+
} else {
|
|
16596
|
+
candidates.push(join7(appDir, spec));
|
|
16597
|
+
}
|
|
16598
|
+
for (const cand of candidates) {
|
|
16599
|
+
if (existsSync7(cand)) {
|
|
16600
|
+
try {
|
|
16601
|
+
if (statSync2(cand).isFile())
|
|
16602
|
+
return cand;
|
|
16603
|
+
} catch {}
|
|
16604
|
+
}
|
|
16605
|
+
for (const ext2 of [...TS_EXTS, JSON_EXT]) {
|
|
16606
|
+
if (existsSync7(cand + ext2))
|
|
16607
|
+
return cand + ext2;
|
|
16608
|
+
}
|
|
16609
|
+
for (const ext2 of TS_EXTS) {
|
|
16610
|
+
const idx = join7(cand, "index" + ext2);
|
|
16611
|
+
if (existsSync7(idx))
|
|
16612
|
+
return idx;
|
|
16613
|
+
}
|
|
16614
|
+
}
|
|
16615
|
+
return null;
|
|
16616
|
+
}
|
|
16617
|
+
function findLocalConst(ident, source) {
|
|
16618
|
+
const re = new RegExp(`(?:export\\s+)?const\\s+${ident}\\s*(?::[^=]+)?=\\s*`, "g");
|
|
16619
|
+
const m = re.exec(source);
|
|
16620
|
+
if (!m)
|
|
16621
|
+
return null;
|
|
16622
|
+
const start = m.index + m[0].length;
|
|
16623
|
+
return readExpression(source, start);
|
|
16624
|
+
}
|
|
16625
|
+
function readExpression(source, start) {
|
|
16626
|
+
let depth = 0;
|
|
16627
|
+
let inString = null;
|
|
16628
|
+
let i = start;
|
|
16629
|
+
for (;i < source.length; i++) {
|
|
16630
|
+
const c = source[i];
|
|
16631
|
+
if (inString) {
|
|
16632
|
+
if (c === "\\") {
|
|
16633
|
+
i++;
|
|
16634
|
+
continue;
|
|
16635
|
+
}
|
|
16636
|
+
if (c === inString)
|
|
16637
|
+
inString = null;
|
|
16638
|
+
continue;
|
|
16639
|
+
}
|
|
16640
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
16641
|
+
inString = c;
|
|
16642
|
+
continue;
|
|
16643
|
+
}
|
|
16644
|
+
if (c === "{" || c === "[" || c === "(")
|
|
16645
|
+
depth++;
|
|
16646
|
+
else if (c === "}" || c === "]" || c === ")")
|
|
16647
|
+
depth--;
|
|
16648
|
+
if (depth === 0 && (c === ";" || c === `
|
|
16649
|
+
`))
|
|
16650
|
+
break;
|
|
16651
|
+
}
|
|
16652
|
+
return source.slice(start, i).trim().replace(/[;]+$/, "").trim();
|
|
16653
|
+
}
|
|
16654
|
+
function findDefaultExportIdentifier(source) {
|
|
16655
|
+
const m = /export\s+default\s+([A-Za-z_$][\w$]*)\s*;?/.exec(source);
|
|
16656
|
+
return m ? m[1] : null;
|
|
16657
|
+
}
|
|
16658
|
+
function parseLiteralValue(expr) {
|
|
16659
|
+
expr = expr.trim();
|
|
16660
|
+
if (expr.startsWith("{")) {
|
|
16661
|
+
const keys = extractTopLevelKeys(expr);
|
|
16662
|
+
if (keys.length > 0)
|
|
16663
|
+
return { kind: "objectKeys", values: keys };
|
|
16664
|
+
return null;
|
|
16665
|
+
}
|
|
16666
|
+
if (expr.startsWith("[")) {
|
|
16667
|
+
const arrText = sliceTopLevelArray(expr);
|
|
16668
|
+
if (!arrText)
|
|
16669
|
+
return null;
|
|
16670
|
+
const stringRe = /["'`]([^"'`]*)["'`]/g;
|
|
16671
|
+
const inner = arrText.slice(1, -1);
|
|
16672
|
+
if (/^\s*\{/.test(inner)) {
|
|
16673
|
+
const objs = parseObjectArray(arrText);
|
|
16674
|
+
if (objs)
|
|
16675
|
+
return { kind: "objects", values: objs };
|
|
16676
|
+
return null;
|
|
16677
|
+
}
|
|
16678
|
+
const values = [];
|
|
16679
|
+
let m;
|
|
16680
|
+
while ((m = stringRe.exec(inner)) !== null)
|
|
16681
|
+
values.push(m[1]);
|
|
16682
|
+
if (values.length > 0)
|
|
16683
|
+
return { kind: "strings", values };
|
|
16684
|
+
}
|
|
16685
|
+
return null;
|
|
16686
|
+
}
|
|
16687
|
+
function extractTopLevelKeys(expr) {
|
|
16688
|
+
const inner = expr.slice(1, expr.length - 1);
|
|
16689
|
+
const keys = [];
|
|
16690
|
+
let depth = 0;
|
|
16691
|
+
let i = 0;
|
|
16692
|
+
let inString = null;
|
|
16693
|
+
let atKeyPos = true;
|
|
16694
|
+
while (i < inner.length) {
|
|
16695
|
+
const c = inner[i];
|
|
16696
|
+
if (inString) {
|
|
16697
|
+
if (c === "\\") {
|
|
16698
|
+
i += 2;
|
|
16699
|
+
continue;
|
|
16700
|
+
}
|
|
16701
|
+
if (c === inString)
|
|
16702
|
+
inString = null;
|
|
16703
|
+
i++;
|
|
16704
|
+
continue;
|
|
16705
|
+
}
|
|
16706
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
16707
|
+
if (atKeyPos && depth === 0) {
|
|
16708
|
+
const quote = c;
|
|
16709
|
+
const end = inner.indexOf(quote, i + 1);
|
|
16710
|
+
if (end === -1)
|
|
16711
|
+
break;
|
|
16712
|
+
keys.push(inner.slice(i + 1, end));
|
|
16713
|
+
i = end + 1;
|
|
16714
|
+
while (i < inner.length && /\s/.test(inner[i]))
|
|
16715
|
+
i++;
|
|
16716
|
+
if (inner[i] === ":") {
|
|
16717
|
+
i++;
|
|
16718
|
+
atKeyPos = false;
|
|
16719
|
+
}
|
|
16720
|
+
continue;
|
|
16721
|
+
}
|
|
16722
|
+
inString = c;
|
|
16723
|
+
i++;
|
|
16724
|
+
continue;
|
|
16725
|
+
}
|
|
16726
|
+
if (c === "{" || c === "[" || c === "(") {
|
|
16727
|
+
depth++;
|
|
16728
|
+
i++;
|
|
16729
|
+
continue;
|
|
16730
|
+
}
|
|
16731
|
+
if (c === "}" || c === "]" || c === ")") {
|
|
16732
|
+
depth--;
|
|
16733
|
+
i++;
|
|
16734
|
+
continue;
|
|
16735
|
+
}
|
|
16736
|
+
if (depth === 0 && c === ",") {
|
|
16737
|
+
atKeyPos = true;
|
|
16738
|
+
i++;
|
|
16739
|
+
continue;
|
|
16740
|
+
}
|
|
16741
|
+
if (depth === 0 && c === ":") {
|
|
16742
|
+
atKeyPos = false;
|
|
16743
|
+
i++;
|
|
16744
|
+
continue;
|
|
16745
|
+
}
|
|
16746
|
+
if (atKeyPos && depth === 0 && /[A-Za-z_$]/.test(c)) {
|
|
16747
|
+
let j = i;
|
|
16748
|
+
while (j < inner.length && /[\w$]/.test(inner[j]))
|
|
16749
|
+
j++;
|
|
16750
|
+
const key = inner.slice(i, j);
|
|
16751
|
+
let k = j;
|
|
16752
|
+
while (k < inner.length && /\s/.test(inner[k]))
|
|
16753
|
+
k++;
|
|
16754
|
+
if (inner[k] === ":") {
|
|
16755
|
+
keys.push(key);
|
|
16756
|
+
i = k + 1;
|
|
16757
|
+
atKeyPos = false;
|
|
16758
|
+
continue;
|
|
16759
|
+
}
|
|
16760
|
+
i = j;
|
|
16761
|
+
continue;
|
|
16762
|
+
}
|
|
16763
|
+
i++;
|
|
16764
|
+
}
|
|
16765
|
+
return keys;
|
|
16766
|
+
}
|
|
16767
|
+
function literalToResolution(data) {
|
|
16768
|
+
if (Array.isArray(data)) {
|
|
16769
|
+
if (data.every((v) => typeof v === "string")) {
|
|
16770
|
+
return { kind: "strings", values: data };
|
|
16771
|
+
}
|
|
16772
|
+
if (data.every((v) => v && typeof v === "object")) {
|
|
16773
|
+
return { kind: "objects", values: data };
|
|
16774
|
+
}
|
|
16775
|
+
return null;
|
|
16776
|
+
}
|
|
16777
|
+
if (data && typeof data === "object") {
|
|
16778
|
+
return { kind: "objectKeys", values: Object.keys(data) };
|
|
16779
|
+
}
|
|
16780
|
+
return null;
|
|
16781
|
+
}
|
|
16126
16782
|
|
|
16127
16783
|
// src/scanner.ts
|
|
16128
16784
|
function extractRoutePath(filePath) {
|
|
@@ -16628,7 +17284,7 @@ async function scan(config) {
|
|
|
16628
17284
|
// src/config.ts
|
|
16629
17285
|
var import_fast_glob11 = __toESM(require_out4(), 1);
|
|
16630
17286
|
import { existsSync as existsSync9, readFileSync as readFileSync11 } from "node:fs";
|
|
16631
|
-
import { join as join9, resolve as
|
|
17287
|
+
import { join as join9, resolve as resolve4, relative as relative4, dirname as dirname6 } from "node:path";
|
|
16632
17288
|
var DEFAULT_CONFIG = {
|
|
16633
17289
|
dir: "./",
|
|
16634
17290
|
ignore: {
|
|
@@ -16666,7 +17322,7 @@ function loadConfig(options) {
|
|
|
16666
17322
|
absolute: true
|
|
16667
17323
|
});
|
|
16668
17324
|
if (options.config && existsSync9(options.config)) {
|
|
16669
|
-
const absConfig =
|
|
17325
|
+
const absConfig = resolve4(cwd, options.config);
|
|
16670
17326
|
if (!configFiles.includes(absConfig)) {
|
|
16671
17327
|
configFiles.push(absConfig);
|
|
16672
17328
|
}
|
|
@@ -16689,7 +17345,7 @@ function loadConfig(options) {
|
|
|
16689
17345
|
try {
|
|
16690
17346
|
const content = readFileSync11(configPath, "utf-8");
|
|
16691
17347
|
const config = JSON.parse(content);
|
|
16692
|
-
const configDir =
|
|
17348
|
+
const configDir = dirname6(configPath);
|
|
16693
17349
|
const relDir = relative4(cwd, configDir);
|
|
16694
17350
|
const prefixPattern = (p) => {
|
|
16695
17351
|
if (p.startsWith("**/") || p.startsWith("/") || !relDir)
|
|
@@ -16804,7 +17460,7 @@ program2.action(async (options) => {
|
|
|
16804
17460
|
let isMonorepo = existsSync11(appsDir) && lstatSync(appsDir).isDirectory();
|
|
16805
17461
|
if (!isMonorepo) {
|
|
16806
17462
|
let current = absoluteDir;
|
|
16807
|
-
while (current !==
|
|
17463
|
+
while (current !== dirname7(current)) {
|
|
16808
17464
|
const potentialApps = join11(current, "apps");
|
|
16809
17465
|
if (existsSync11(potentialApps) && lstatSync(potentialApps).isDirectory()) {
|
|
16810
17466
|
monorepoRoot = current;
|
|
@@ -16812,7 +17468,7 @@ program2.action(async (options) => {
|
|
|
16812
17468
|
isMonorepo = true;
|
|
16813
17469
|
break;
|
|
16814
17470
|
}
|
|
16815
|
-
current =
|
|
17471
|
+
current = dirname7(current);
|
|
16816
17472
|
}
|
|
16817
17473
|
}
|
|
16818
17474
|
if (isMonorepo && monorepoRoot !== absoluteDir) {
|
|
@@ -16913,6 +17569,10 @@ program2.action(async (options) => {
|
|
|
16913
17569
|
if (options.json) {
|
|
16914
17570
|
console.log(JSON.stringify(result, null, 2));
|
|
16915
17571
|
} else if (options.fix) {
|
|
17572
|
+
if (isScanAll && !hasUnusedItems(result)) {
|
|
17573
|
+
console.log(source_default.green(` ✅ Nothing to fix in ${appLabel}`));
|
|
17574
|
+
continue;
|
|
17575
|
+
}
|
|
16916
17576
|
const fixResult = await handleFixes(result, currentConfig, options, isMonorepo);
|
|
16917
17577
|
if (fixResult === "back") {
|
|
16918
17578
|
requestedBack = true;
|
|
@@ -17155,7 +17815,7 @@ Analyzing cascading impact...`));
|
|
|
17155
17815
|
const routeFiles = new Set;
|
|
17156
17816
|
const rootDir = config.appSpecificScan ? config.appSpecificScan.rootDir : config.dir;
|
|
17157
17817
|
for (const r of [...unusedRoutes, ...partiallyRoutes]) {
|
|
17158
|
-
routeFiles.add(
|
|
17818
|
+
routeFiles.add(resolve5(rootDir, r.filePath));
|
|
17159
17819
|
}
|
|
17160
17820
|
const title = `Unused API Routes (${totalRoutesIssues} items in ${routeFiles.size} files)`;
|
|
17161
17821
|
choices.push({ title, value: "routes" });
|
|
@@ -17452,7 +18112,7 @@ Analyzing cascading impact...`));
|
|
|
17452
18112
|
const fullPath = resolveFilePath(filePath, config);
|
|
17453
18113
|
const route = fileRoutes[0];
|
|
17454
18114
|
if (route.type === "nextjs" && (filePath.includes("app/api") || filePath.includes("pages/api"))) {
|
|
17455
|
-
filesToUnlink.add(
|
|
18115
|
+
filesToUnlink.add(dirname7(fullPath));
|
|
17456
18116
|
} else if (route.type === "nestjs" && (result.unusedFiles?.files.some((f) => f.path === filePath) || filePath.includes("api/"))) {
|
|
17457
18117
|
filesToUnlink.add(fullPath);
|
|
17458
18118
|
} else {
|
|
@@ -17506,7 +18166,7 @@ Analyzing cascading impact...`));
|
|
|
17506
18166
|
}
|
|
17507
18167
|
const actuallyFixed = new Set;
|
|
17508
18168
|
for (const [absPath, removals] of removalsByFile) {
|
|
17509
|
-
if (actuallyDeleted.has(absPath) || actuallyDeleted.has(
|
|
18169
|
+
if (actuallyDeleted.has(absPath) || actuallyDeleted.has(dirname7(absPath)))
|
|
17510
18170
|
continue;
|
|
17511
18171
|
if (!existsSync11(absPath))
|
|
17512
18172
|
continue;
|
|
@@ -17521,7 +18181,7 @@ Analyzing cascading impact...`));
|
|
|
17521
18181
|
}
|
|
17522
18182
|
result.routes = result.routes.filter((r) => {
|
|
17523
18183
|
const fullPath = resolveFilePath(r.filePath, config);
|
|
17524
|
-
if (actuallyDeleted.has(fullPath) || actuallyDeleted.has(
|
|
18184
|
+
if (actuallyDeleted.has(fullPath) || actuallyDeleted.has(dirname7(fullPath)))
|
|
17525
18185
|
return false;
|
|
17526
18186
|
if (actuallyFixed.has(fullPath)) {
|
|
17527
18187
|
r.unusedMethods = [];
|
|
@@ -17993,11 +18653,11 @@ function printTable(summary) {
|
|
|
17993
18653
|
}
|
|
17994
18654
|
function findGitRoot(startDir) {
|
|
17995
18655
|
let currentDir = startDir;
|
|
17996
|
-
while (currentDir !==
|
|
18656
|
+
while (currentDir !== dirname7(currentDir)) {
|
|
17997
18657
|
if (existsSync11(join11(currentDir, ".git"))) {
|
|
17998
18658
|
return true;
|
|
17999
18659
|
}
|
|
18000
|
-
currentDir =
|
|
18660
|
+
currentDir = dirname7(currentDir);
|
|
18001
18661
|
}
|
|
18002
18662
|
return false;
|
|
18003
18663
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pruny",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.0",
|
|
4
4
|
"description": "Find and remove unused Next.js API routes & Nest.js Controllers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
37
|
-
"url": "https://github.com/
|
|
37
|
+
"url": "https://github.com/Navibyte-Innovations-Pvt-Ltd/pruny"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"chalk": "^5.3.0",
|