pruny 1.43.10 → 1.44.1
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 +709 -22
- 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,8 +15886,8 @@ 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
15892
|
/<Link\s+[^>]*href\s*=\s*(?:\{\s*)?['"`](\/[^'"`\s]+)['"`](?:\s*\})?/g,
|
|
15893
15893
|
/router\.(push|replace)\s*\(\s*['"`](\/[^'"`\s]+)['"`]/g,
|
|
@@ -15944,15 +15944,15 @@ function filePathToRoute(filePath) {
|
|
|
15944
15944
|
});
|
|
15945
15945
|
return "/" + segments.join("/");
|
|
15946
15946
|
}
|
|
15947
|
-
function matchesRoute(refPath, routes,
|
|
15947
|
+
function matchesRoute(refPath, routes, routes_) {
|
|
15948
15948
|
const cleaned = cleanPath(refPath);
|
|
15949
15949
|
if (routes.has(cleaned))
|
|
15950
15950
|
return true;
|
|
15951
15951
|
const refSegments = cleaned.split("/").filter(Boolean);
|
|
15952
|
-
for (const
|
|
15953
|
-
if (matchSegments(refSegments,
|
|
15952
|
+
for (const route of routes_) {
|
|
15953
|
+
if (matchSegments(refSegments, route.segments, route.params))
|
|
15954
15954
|
return true;
|
|
15955
|
-
if (matchesDynamicSuffix(refSegments,
|
|
15955
|
+
if (matchesDynamicSuffix(refSegments, route.segments))
|
|
15956
15956
|
return true;
|
|
15957
15957
|
}
|
|
15958
15958
|
return false;
|
|
@@ -15970,25 +15970,71 @@ function matchesDynamicSuffix(refSegments, routeSegments) {
|
|
|
15970
15970
|
return false;
|
|
15971
15971
|
return matchSegments(refSegments, tail);
|
|
15972
15972
|
}
|
|
15973
|
-
function matchSegments(refSegments, routeSegments) {
|
|
15973
|
+
function matchSegments(refSegments, routeSegments, params) {
|
|
15974
15974
|
let ri = 0;
|
|
15975
15975
|
let si = 0;
|
|
15976
15976
|
while (ri < refSegments.length && si < routeSegments.length) {
|
|
15977
15977
|
const routeSeg = routeSegments[si];
|
|
15978
15978
|
if (/^\[\[?\.\.\./.test(routeSeg))
|
|
15979
15979
|
return true;
|
|
15980
|
-
|
|
15980
|
+
const dynMatch = /^\[(.+)\]$/.exec(routeSeg);
|
|
15981
|
+
if (dynMatch) {
|
|
15982
|
+
const paramName = dynMatch[1];
|
|
15983
|
+
const ref = refSegments[ri];
|
|
15984
|
+
const isPlaceholder = /^\[.+\]$/.test(ref);
|
|
15985
|
+
if (params && params[paramName] && !isPlaceholder) {
|
|
15986
|
+
const allowed = params[paramName];
|
|
15987
|
+
let ok = false;
|
|
15988
|
+
for (const v of allowed) {
|
|
15989
|
+
if (v.toLowerCase() === ref.toLowerCase()) {
|
|
15990
|
+
ok = true;
|
|
15991
|
+
break;
|
|
15992
|
+
}
|
|
15993
|
+
}
|
|
15994
|
+
if (!ok)
|
|
15995
|
+
return false;
|
|
15996
|
+
}
|
|
15981
15997
|
ri++;
|
|
15982
15998
|
si++;
|
|
15983
15999
|
continue;
|
|
15984
16000
|
}
|
|
15985
|
-
|
|
16001
|
+
const refSeg = refSegments[ri];
|
|
16002
|
+
if (/^\[.+\]$/.test(refSeg)) {
|
|
16003
|
+
ri++;
|
|
16004
|
+
si++;
|
|
16005
|
+
continue;
|
|
16006
|
+
}
|
|
16007
|
+
if (refSeg.toLowerCase() !== routeSeg.toLowerCase())
|
|
15986
16008
|
return false;
|
|
15987
16009
|
ri++;
|
|
15988
16010
|
si++;
|
|
15989
16011
|
}
|
|
15990
16012
|
return ri === refSegments.length && si === routeSegments.length;
|
|
15991
16013
|
}
|
|
16014
|
+
function isRuntimeGeneratedPublicAsset(appDir, linkPath) {
|
|
16015
|
+
if (/^\/(sitemap(?:[-_].+)?\.xml|robots\.txt|manifest\.(?:json|webmanifest)|favicon\.ico|sw\.js|service-worker\.js)$/.test(linkPath)) {
|
|
16016
|
+
return true;
|
|
16017
|
+
}
|
|
16018
|
+
const generatorConfigs = [
|
|
16019
|
+
"next-sitemap.config.js",
|
|
16020
|
+
"next-sitemap.config.mjs",
|
|
16021
|
+
"next-sitemap.config.cjs",
|
|
16022
|
+
"next-sitemap.config.ts"
|
|
16023
|
+
];
|
|
16024
|
+
if (linkPath.startsWith("/sitemap")) {
|
|
16025
|
+
for (const cfg of generatorConfigs) {
|
|
16026
|
+
if (existsSync7(join7(appDir, cfg)))
|
|
16027
|
+
return true;
|
|
16028
|
+
}
|
|
16029
|
+
for (const ext2 of ["ts", "tsx", "js", "jsx"]) {
|
|
16030
|
+
if (existsSync7(join7(appDir, "app", `sitemap.${ext2}`)))
|
|
16031
|
+
return true;
|
|
16032
|
+
if (existsSync7(join7(appDir, "src/app", `sitemap.${ext2}`)))
|
|
16033
|
+
return true;
|
|
16034
|
+
}
|
|
16035
|
+
}
|
|
16036
|
+
return false;
|
|
16037
|
+
}
|
|
15992
16038
|
function isGitignoredPublicFile(appDir, linkPath) {
|
|
15993
16039
|
const publicRelPath = `public${linkPath}`;
|
|
15994
16040
|
const dirsToCheck = [appDir];
|
|
@@ -16030,12 +16076,21 @@ async function scanBrokenLinks(config) {
|
|
|
16030
16076
|
const knownRoutes = new Set;
|
|
16031
16077
|
const routeSegmentsList = [];
|
|
16032
16078
|
knownRoutes.add("/");
|
|
16079
|
+
const aliasMap = parseTsConfigPaths(appDir);
|
|
16033
16080
|
for (const file of pageFiles) {
|
|
16034
16081
|
const route = filePathToRoute(file);
|
|
16035
16082
|
knownRoutes.add(route);
|
|
16036
16083
|
const segments = route.split("/").filter(Boolean);
|
|
16037
16084
|
if (segments.some((s) => s.startsWith("["))) {
|
|
16038
|
-
|
|
16085
|
+
const absFile = join7(appDir, file);
|
|
16086
|
+
const params = resolveStaticParams(absFile, appDir, aliasMap);
|
|
16087
|
+
routeSegmentsList.push({ segments, params: params ?? undefined });
|
|
16088
|
+
if (process.env.DEBUG_PRUNY) {
|
|
16089
|
+
if (params) {
|
|
16090
|
+
const summary = Object.entries(params).map(([k, v]) => `${k}=${v.size}`).join(", ");
|
|
16091
|
+
console.log(`[DEBUG] Static params for ${route}: ${summary}`);
|
|
16092
|
+
}
|
|
16093
|
+
}
|
|
16039
16094
|
}
|
|
16040
16095
|
}
|
|
16041
16096
|
if (process.env.DEBUG_PRUNY) {
|
|
@@ -16094,6 +16149,8 @@ async function scanBrokenLinks(config) {
|
|
|
16094
16149
|
continue;
|
|
16095
16150
|
if (isGitignoredPublicFile(appDir, cleaned))
|
|
16096
16151
|
continue;
|
|
16152
|
+
if (isRuntimeGeneratedPublicAsset(appDir, cleaned))
|
|
16153
|
+
continue;
|
|
16097
16154
|
const ignorePatterns = [
|
|
16098
16155
|
...config.ignore.links || [],
|
|
16099
16156
|
...config.ignore.routes
|
|
@@ -16129,6 +16186,632 @@ async function scanBrokenLinks(config) {
|
|
|
16129
16186
|
links
|
|
16130
16187
|
};
|
|
16131
16188
|
}
|
|
16189
|
+
var TS_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
16190
|
+
var JSON_EXT = ".json";
|
|
16191
|
+
function resolveStaticParams(routeFile, appDir, aliasMap) {
|
|
16192
|
+
let content;
|
|
16193
|
+
try {
|
|
16194
|
+
content = readFileSync9(routeFile, "utf-8");
|
|
16195
|
+
} catch {
|
|
16196
|
+
return null;
|
|
16197
|
+
}
|
|
16198
|
+
const body = extractFunctionBody(content, "generateStaticParams");
|
|
16199
|
+
if (!body)
|
|
16200
|
+
return null;
|
|
16201
|
+
const returnExpr = extractReturnExpression(body);
|
|
16202
|
+
if (!returnExpr)
|
|
16203
|
+
return null;
|
|
16204
|
+
return resolveParamsExpression(returnExpr, content, routeFile, appDir, aliasMap, new Set, 0);
|
|
16205
|
+
}
|
|
16206
|
+
function extractFunctionBody(source, name) {
|
|
16207
|
+
const fnRe = new RegExp(`(?:export\\s+)?(?:async\\s+)?function\\s+${name}\\s*\\([^)]*\\)\\s*\\{`, "g");
|
|
16208
|
+
let m = fnRe.exec(source);
|
|
16209
|
+
if (m) {
|
|
16210
|
+
return readBalanced(source, m.index + m[0].length - 1);
|
|
16211
|
+
}
|
|
16212
|
+
const arrowRe = new RegExp(`(?:export\\s+)?const\\s+${name}\\s*=\\s*(?:async\\s*)?\\([^)]*\\)\\s*=>\\s*\\{`, "g");
|
|
16213
|
+
m = arrowRe.exec(source);
|
|
16214
|
+
if (m) {
|
|
16215
|
+
return readBalanced(source, m.index + m[0].length - 1);
|
|
16216
|
+
}
|
|
16217
|
+
return null;
|
|
16218
|
+
}
|
|
16219
|
+
function readBalanced(source, start) {
|
|
16220
|
+
if (source[start] !== "{")
|
|
16221
|
+
return null;
|
|
16222
|
+
let depth = 0;
|
|
16223
|
+
let i = start;
|
|
16224
|
+
let inString = null;
|
|
16225
|
+
let inLineComment = false;
|
|
16226
|
+
let inBlockComment = false;
|
|
16227
|
+
for (;i < source.length; i++) {
|
|
16228
|
+
const c = source[i];
|
|
16229
|
+
const next = source[i + 1];
|
|
16230
|
+
if (inLineComment) {
|
|
16231
|
+
if (c === `
|
|
16232
|
+
`)
|
|
16233
|
+
inLineComment = false;
|
|
16234
|
+
continue;
|
|
16235
|
+
}
|
|
16236
|
+
if (inBlockComment) {
|
|
16237
|
+
if (c === "*" && next === "/") {
|
|
16238
|
+
inBlockComment = false;
|
|
16239
|
+
i++;
|
|
16240
|
+
}
|
|
16241
|
+
continue;
|
|
16242
|
+
}
|
|
16243
|
+
if (inString) {
|
|
16244
|
+
if (c === "\\") {
|
|
16245
|
+
i++;
|
|
16246
|
+
continue;
|
|
16247
|
+
}
|
|
16248
|
+
if (c === inString)
|
|
16249
|
+
inString = null;
|
|
16250
|
+
continue;
|
|
16251
|
+
}
|
|
16252
|
+
if (c === "/" && next === "/") {
|
|
16253
|
+
inLineComment = true;
|
|
16254
|
+
i++;
|
|
16255
|
+
continue;
|
|
16256
|
+
}
|
|
16257
|
+
if (c === "/" && next === "*") {
|
|
16258
|
+
inBlockComment = true;
|
|
16259
|
+
i++;
|
|
16260
|
+
continue;
|
|
16261
|
+
}
|
|
16262
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
16263
|
+
inString = c;
|
|
16264
|
+
continue;
|
|
16265
|
+
}
|
|
16266
|
+
if (c === "{")
|
|
16267
|
+
depth++;
|
|
16268
|
+
else if (c === "}") {
|
|
16269
|
+
depth--;
|
|
16270
|
+
if (depth === 0)
|
|
16271
|
+
return source.slice(start + 1, i);
|
|
16272
|
+
}
|
|
16273
|
+
}
|
|
16274
|
+
return null;
|
|
16275
|
+
}
|
|
16276
|
+
function extractReturnExpression(body) {
|
|
16277
|
+
const re = /\breturn\s+([\s\S]+?)(?:;|$)/g;
|
|
16278
|
+
let last = null;
|
|
16279
|
+
let m;
|
|
16280
|
+
while ((m = re.exec(body)) !== null)
|
|
16281
|
+
last = m;
|
|
16282
|
+
if (!last)
|
|
16283
|
+
return null;
|
|
16284
|
+
return last[1].trim();
|
|
16285
|
+
}
|
|
16286
|
+
function resolveParamsExpression(expr, fileContent, filePath, appDir, aliasMap, visited, depth) {
|
|
16287
|
+
if (depth > 4)
|
|
16288
|
+
return null;
|
|
16289
|
+
expr = expr.trim();
|
|
16290
|
+
if (expr.startsWith("[")) {
|
|
16291
|
+
const arrText = sliceTopLevelArray(expr);
|
|
16292
|
+
if (arrText) {
|
|
16293
|
+
const tail = expr.slice(arrText.length).trim();
|
|
16294
|
+
if (!tail) {
|
|
16295
|
+
const arr = parseObjectArray(arrText);
|
|
16296
|
+
if (arr)
|
|
16297
|
+
return mergeParams(arr);
|
|
16298
|
+
}
|
|
16299
|
+
if (tail.startsWith(".map")) {
|
|
16300
|
+
const stringArr = parseStringArray(arrText);
|
|
16301
|
+
if (stringArr) {
|
|
16302
|
+
const paramName = inferParamFromMap(expr) ?? "slug";
|
|
16303
|
+
return { [paramName]: new Set(stringArr) };
|
|
16304
|
+
}
|
|
16305
|
+
const objArr = parseObjectArray(arrText);
|
|
16306
|
+
if (objArr) {
|
|
16307
|
+
const projection = extractMapProjection(expr);
|
|
16308
|
+
if (!projection)
|
|
16309
|
+
return null;
|
|
16310
|
+
const out = {};
|
|
16311
|
+
for (const [param, source] of Object.entries(projection)) {
|
|
16312
|
+
const parts = source.split(".");
|
|
16313
|
+
parts.shift();
|
|
16314
|
+
const values = [];
|
|
16315
|
+
for (const obj of objArr) {
|
|
16316
|
+
let v = obj;
|
|
16317
|
+
for (const p of parts) {
|
|
16318
|
+
if (v && typeof v === "object" && p in v) {
|
|
16319
|
+
v = v[p];
|
|
16320
|
+
} else {
|
|
16321
|
+
v = undefined;
|
|
16322
|
+
break;
|
|
16323
|
+
}
|
|
16324
|
+
}
|
|
16325
|
+
if (typeof v === "string")
|
|
16326
|
+
values.push(v);
|
|
16327
|
+
}
|
|
16328
|
+
if (values.length > 0)
|
|
16329
|
+
out[param] = new Set(values);
|
|
16330
|
+
}
|
|
16331
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
16332
|
+
}
|
|
16333
|
+
}
|
|
16334
|
+
}
|
|
16335
|
+
}
|
|
16336
|
+
const objKeysM = /^Object\.(keys|entries)\s*\(\s*([A-Za-z_$][\w$]*)\s*\)/.exec(expr);
|
|
16337
|
+
if (objKeysM) {
|
|
16338
|
+
const ident = objKeysM[2];
|
|
16339
|
+
const keys = resolveIdentifierKeys(ident, fileContent, filePath, appDir, aliasMap, visited, depth);
|
|
16340
|
+
if (!keys)
|
|
16341
|
+
return null;
|
|
16342
|
+
const paramName = inferParamFromMap(expr) ?? guessFirstParamName(expr) ?? "slug";
|
|
16343
|
+
return { [paramName]: new Set(keys) };
|
|
16344
|
+
}
|
|
16345
|
+
const identMapM = /^([A-Za-z_$][\w$]*)\s*\.\s*map\s*\(/.exec(expr);
|
|
16346
|
+
if (identMapM) {
|
|
16347
|
+
const ident = identMapM[1];
|
|
16348
|
+
const resolved = resolveIdentifierAsArray(ident, fileContent, filePath, appDir, aliasMap, visited, depth);
|
|
16349
|
+
if (!resolved)
|
|
16350
|
+
return null;
|
|
16351
|
+
if (resolved.kind === "strings") {
|
|
16352
|
+
const paramName = inferParamFromMap(expr) ?? "slug";
|
|
16353
|
+
return { [paramName]: new Set(resolved.values) };
|
|
16354
|
+
}
|
|
16355
|
+
if (resolved.kind === "objects") {
|
|
16356
|
+
const projection = extractMapProjection(expr);
|
|
16357
|
+
if (!projection)
|
|
16358
|
+
return null;
|
|
16359
|
+
const out = {};
|
|
16360
|
+
for (const [param, source] of Object.entries(projection)) {
|
|
16361
|
+
const values = [];
|
|
16362
|
+
for (const obj of resolved.values) {
|
|
16363
|
+
const parts = source.split(".");
|
|
16364
|
+
parts.shift();
|
|
16365
|
+
let v = obj;
|
|
16366
|
+
for (const p of parts) {
|
|
16367
|
+
if (v && typeof v === "object" && p in v) {
|
|
16368
|
+
v = v[p];
|
|
16369
|
+
} else {
|
|
16370
|
+
v = undefined;
|
|
16371
|
+
break;
|
|
16372
|
+
}
|
|
16373
|
+
}
|
|
16374
|
+
if (typeof v === "string")
|
|
16375
|
+
values.push(v);
|
|
16376
|
+
}
|
|
16377
|
+
if (values.length > 0)
|
|
16378
|
+
out[param] = new Set(values);
|
|
16379
|
+
}
|
|
16380
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
16381
|
+
}
|
|
16382
|
+
if (resolved.kind === "objectKeys") {
|
|
16383
|
+
const paramName = inferParamFromMap(expr) ?? "slug";
|
|
16384
|
+
return { [paramName]: new Set(resolved.values) };
|
|
16385
|
+
}
|
|
16386
|
+
}
|
|
16387
|
+
return null;
|
|
16388
|
+
}
|
|
16389
|
+
function extractMapProjection(expr) {
|
|
16390
|
+
const bodyMatch = /\.map\s*\(\s*(?:\(([^)]*)\)|([A-Za-z_$][\w$]*))\s*=>\s*\(?\s*\{([^}]+)\}/.exec(expr);
|
|
16391
|
+
if (!bodyMatch)
|
|
16392
|
+
return null;
|
|
16393
|
+
const iter = (bodyMatch[1] ?? bodyMatch[2] ?? "").trim().split(",")[0].trim() || "item";
|
|
16394
|
+
const objBody = bodyMatch[3];
|
|
16395
|
+
const out = {};
|
|
16396
|
+
const propRe = /([A-Za-z_$][\w$]*)\s*(?::\s*([A-Za-z_$][\w$.]*))?/g;
|
|
16397
|
+
let m;
|
|
16398
|
+
while ((m = propRe.exec(objBody)) !== null) {
|
|
16399
|
+
const key = m[1];
|
|
16400
|
+
const value = m[2] ?? key;
|
|
16401
|
+
out[key] = value.startsWith(iter + ".") || value === iter ? value : `${iter}.${value}`;
|
|
16402
|
+
}
|
|
16403
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
16404
|
+
}
|
|
16405
|
+
function inferParamFromMap(expr) {
|
|
16406
|
+
const m = /=>\s*\(?\s*\{\s*([A-Za-z_$][\w$]*)\s*[:}]/.exec(expr);
|
|
16407
|
+
return m ? m[1] : null;
|
|
16408
|
+
}
|
|
16409
|
+
function guessFirstParamName(expr) {
|
|
16410
|
+
const m = /\{\s*([A-Za-z_$][\w$]*)\s*\}/.exec(expr);
|
|
16411
|
+
return m ? m[1] : null;
|
|
16412
|
+
}
|
|
16413
|
+
function mergeParams(items) {
|
|
16414
|
+
const out = {};
|
|
16415
|
+
for (const item of items) {
|
|
16416
|
+
for (const [k, v] of Object.entries(item)) {
|
|
16417
|
+
if (typeof v !== "string")
|
|
16418
|
+
continue;
|
|
16419
|
+
if (!out[k])
|
|
16420
|
+
out[k] = new Set;
|
|
16421
|
+
out[k].add(v);
|
|
16422
|
+
}
|
|
16423
|
+
}
|
|
16424
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
16425
|
+
}
|
|
16426
|
+
function parseObjectArray(expr) {
|
|
16427
|
+
const arrText = sliceTopLevelArray(expr);
|
|
16428
|
+
if (!arrText)
|
|
16429
|
+
return null;
|
|
16430
|
+
const inner = arrText.slice(1, -1).trim();
|
|
16431
|
+
if (!inner)
|
|
16432
|
+
return [];
|
|
16433
|
+
const items = [];
|
|
16434
|
+
let depth = 0;
|
|
16435
|
+
let buf = "";
|
|
16436
|
+
const chunks = [];
|
|
16437
|
+
let inString = null;
|
|
16438
|
+
for (let i = 0;i < inner.length; i++) {
|
|
16439
|
+
const c = inner[i];
|
|
16440
|
+
if (inString) {
|
|
16441
|
+
if (c === "\\") {
|
|
16442
|
+
buf += c + inner[++i];
|
|
16443
|
+
continue;
|
|
16444
|
+
}
|
|
16445
|
+
if (c === inString)
|
|
16446
|
+
inString = null;
|
|
16447
|
+
buf += c;
|
|
16448
|
+
continue;
|
|
16449
|
+
}
|
|
16450
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
16451
|
+
inString = c;
|
|
16452
|
+
buf += c;
|
|
16453
|
+
continue;
|
|
16454
|
+
}
|
|
16455
|
+
if (c === "{" || c === "[" || c === "(")
|
|
16456
|
+
depth++;
|
|
16457
|
+
else if (c === "}" || c === "]" || c === ")")
|
|
16458
|
+
depth--;
|
|
16459
|
+
if (c === "," && depth === 0) {
|
|
16460
|
+
chunks.push(buf);
|
|
16461
|
+
buf = "";
|
|
16462
|
+
continue;
|
|
16463
|
+
}
|
|
16464
|
+
buf += c;
|
|
16465
|
+
}
|
|
16466
|
+
if (buf.trim())
|
|
16467
|
+
chunks.push(buf);
|
|
16468
|
+
for (const chunk of chunks) {
|
|
16469
|
+
const obj = parseSimpleObject(chunk.trim());
|
|
16470
|
+
if (!obj)
|
|
16471
|
+
return null;
|
|
16472
|
+
items.push(obj);
|
|
16473
|
+
}
|
|
16474
|
+
return items;
|
|
16475
|
+
}
|
|
16476
|
+
function parseStringArray(arrText) {
|
|
16477
|
+
if (!arrText.startsWith("[") || !arrText.endsWith("]"))
|
|
16478
|
+
return null;
|
|
16479
|
+
const inner = arrText.slice(1, -1).trim();
|
|
16480
|
+
if (!inner)
|
|
16481
|
+
return [];
|
|
16482
|
+
if (/[{[]/.test(inner))
|
|
16483
|
+
return null;
|
|
16484
|
+
const out = [];
|
|
16485
|
+
const re = /["'`]([^"'`]*)["'`]/g;
|
|
16486
|
+
let m;
|
|
16487
|
+
let count = 0;
|
|
16488
|
+
while ((m = re.exec(inner)) !== null) {
|
|
16489
|
+
out.push(m[1]);
|
|
16490
|
+
count++;
|
|
16491
|
+
}
|
|
16492
|
+
const chunks = inner.split(",").filter((s) => s.trim()).length;
|
|
16493
|
+
if (count !== chunks)
|
|
16494
|
+
return null;
|
|
16495
|
+
return out;
|
|
16496
|
+
}
|
|
16497
|
+
function sliceTopLevelArray(expr) {
|
|
16498
|
+
if (expr[0] !== "[")
|
|
16499
|
+
return null;
|
|
16500
|
+
let depth = 0;
|
|
16501
|
+
let inString = null;
|
|
16502
|
+
for (let i = 0;i < expr.length; i++) {
|
|
16503
|
+
const c = expr[i];
|
|
16504
|
+
if (inString) {
|
|
16505
|
+
if (c === "\\") {
|
|
16506
|
+
i++;
|
|
16507
|
+
continue;
|
|
16508
|
+
}
|
|
16509
|
+
if (c === inString)
|
|
16510
|
+
inString = null;
|
|
16511
|
+
continue;
|
|
16512
|
+
}
|
|
16513
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
16514
|
+
inString = c;
|
|
16515
|
+
continue;
|
|
16516
|
+
}
|
|
16517
|
+
if (c === "[")
|
|
16518
|
+
depth++;
|
|
16519
|
+
else if (c === "]") {
|
|
16520
|
+
depth--;
|
|
16521
|
+
if (depth === 0)
|
|
16522
|
+
return expr.slice(0, i + 1);
|
|
16523
|
+
}
|
|
16524
|
+
}
|
|
16525
|
+
return null;
|
|
16526
|
+
}
|
|
16527
|
+
function parseSimpleObject(text) {
|
|
16528
|
+
if (!text.startsWith("{") || !text.endsWith("}"))
|
|
16529
|
+
return null;
|
|
16530
|
+
const body = text.slice(1, -1);
|
|
16531
|
+
const out = {};
|
|
16532
|
+
const re = /([A-Za-z_$][\w$]*)\s*:\s*(?:"([^"]*)"|'([^']*)'|`([^`]*)`)/g;
|
|
16533
|
+
let m;
|
|
16534
|
+
let count = 0;
|
|
16535
|
+
while ((m = re.exec(body)) !== null) {
|
|
16536
|
+
out[m[1]] = m[2] ?? m[3] ?? m[4] ?? "";
|
|
16537
|
+
count++;
|
|
16538
|
+
}
|
|
16539
|
+
return count > 0 ? out : null;
|
|
16540
|
+
}
|
|
16541
|
+
function resolveIdentifierKeys(ident, fileContent, filePath, appDir, aliasMap, visited, depth) {
|
|
16542
|
+
const r = resolveIdentifier(ident, fileContent, filePath, appDir, aliasMap, visited, depth);
|
|
16543
|
+
if (!r)
|
|
16544
|
+
return null;
|
|
16545
|
+
if (r.kind === "objectKeys")
|
|
16546
|
+
return r.values;
|
|
16547
|
+
if (r.kind === "objects") {
|
|
16548
|
+
return null;
|
|
16549
|
+
}
|
|
16550
|
+
if (r.kind === "strings")
|
|
16551
|
+
return r.values;
|
|
16552
|
+
return null;
|
|
16553
|
+
}
|
|
16554
|
+
function resolveIdentifierAsArray(ident, fileContent, filePath, appDir, aliasMap, visited, depth) {
|
|
16555
|
+
return resolveIdentifier(ident, fileContent, filePath, appDir, aliasMap, visited, depth);
|
|
16556
|
+
}
|
|
16557
|
+
function resolveIdentifier(ident, fileContent, filePath, appDir, aliasMap, visited, depth) {
|
|
16558
|
+
if (depth > 5)
|
|
16559
|
+
return null;
|
|
16560
|
+
const visitKey = `${filePath}::${ident}`;
|
|
16561
|
+
if (visited.has(visitKey))
|
|
16562
|
+
return null;
|
|
16563
|
+
visited.add(visitKey);
|
|
16564
|
+
const localValue = findLocalConst(ident, fileContent);
|
|
16565
|
+
if (localValue) {
|
|
16566
|
+
const parsed = parseLiteralValue(localValue);
|
|
16567
|
+
if (parsed)
|
|
16568
|
+
return parsed;
|
|
16569
|
+
}
|
|
16570
|
+
const importInfo = findImport(ident, fileContent);
|
|
16571
|
+
if (!importInfo)
|
|
16572
|
+
return null;
|
|
16573
|
+
const resolvedPath = resolveModulePath(importInfo.path, filePath, appDir, aliasMap);
|
|
16574
|
+
if (!resolvedPath)
|
|
16575
|
+
return null;
|
|
16576
|
+
if (resolvedPath.endsWith(JSON_EXT)) {
|
|
16577
|
+
try {
|
|
16578
|
+
const data = JSON.parse(readFileSync9(resolvedPath, "utf-8"));
|
|
16579
|
+
return literalToResolution(data);
|
|
16580
|
+
} catch {
|
|
16581
|
+
return null;
|
|
16582
|
+
}
|
|
16583
|
+
}
|
|
16584
|
+
let nextContent;
|
|
16585
|
+
try {
|
|
16586
|
+
nextContent = readFileSync9(resolvedPath, "utf-8");
|
|
16587
|
+
} catch {
|
|
16588
|
+
return null;
|
|
16589
|
+
}
|
|
16590
|
+
const nextIdent = importInfo.kind === "default" ? findDefaultExportIdentifier(nextContent) ?? ident : importInfo.imported;
|
|
16591
|
+
return resolveIdentifier(nextIdent, nextContent, resolvedPath, appDir, aliasMap, visited, depth + 1);
|
|
16592
|
+
}
|
|
16593
|
+
function findImport(ident, source) {
|
|
16594
|
+
const defRe = new RegExp(`import\\s+${ident}\\s+from\\s+['"]([^'"]+)['"]`, "g");
|
|
16595
|
+
let m;
|
|
16596
|
+
if ((m = defRe.exec(source)) !== null) {
|
|
16597
|
+
return { kind: "default", imported: ident, path: m[1] };
|
|
16598
|
+
}
|
|
16599
|
+
const namedRe = /import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]([^'"]+)['"]/g;
|
|
16600
|
+
while ((m = namedRe.exec(source)) !== null) {
|
|
16601
|
+
const specifiers = m[1].split(",").map((s) => s.trim());
|
|
16602
|
+
for (const spec of specifiers) {
|
|
16603
|
+
const aliasMatch = /^([A-Za-z_$][\w$]*)\s+as\s+([A-Za-z_$][\w$]*)$/.exec(spec);
|
|
16604
|
+
if (aliasMatch) {
|
|
16605
|
+
if (aliasMatch[2] === ident) {
|
|
16606
|
+
return { kind: "named", imported: aliasMatch[1], path: m[2] };
|
|
16607
|
+
}
|
|
16608
|
+
} else if (spec === ident) {
|
|
16609
|
+
return { kind: "named", imported: ident, path: m[2] };
|
|
16610
|
+
}
|
|
16611
|
+
}
|
|
16612
|
+
}
|
|
16613
|
+
return null;
|
|
16614
|
+
}
|
|
16615
|
+
function resolveModulePath(spec, fromFile, appDir, aliasMap) {
|
|
16616
|
+
let candidates = [];
|
|
16617
|
+
if (spec.startsWith(".")) {
|
|
16618
|
+
candidates.push(resolve3(dirname5(fromFile), spec));
|
|
16619
|
+
} else if (aliasMap.size > 0) {
|
|
16620
|
+
for (const [prefix, targets] of aliasMap.entries()) {
|
|
16621
|
+
if (spec === prefix.replace(/\/$/, "") || spec.startsWith(prefix)) {
|
|
16622
|
+
const sub = spec.slice(prefix.length);
|
|
16623
|
+
for (const t of targets) {
|
|
16624
|
+
candidates.push(sub ? join7(t, sub) : t);
|
|
16625
|
+
}
|
|
16626
|
+
}
|
|
16627
|
+
}
|
|
16628
|
+
} else {
|
|
16629
|
+
candidates.push(join7(appDir, spec));
|
|
16630
|
+
}
|
|
16631
|
+
for (const cand of candidates) {
|
|
16632
|
+
if (existsSync7(cand)) {
|
|
16633
|
+
try {
|
|
16634
|
+
if (statSync2(cand).isFile())
|
|
16635
|
+
return cand;
|
|
16636
|
+
} catch {}
|
|
16637
|
+
}
|
|
16638
|
+
for (const ext2 of [...TS_EXTS, JSON_EXT]) {
|
|
16639
|
+
if (existsSync7(cand + ext2))
|
|
16640
|
+
return cand + ext2;
|
|
16641
|
+
}
|
|
16642
|
+
for (const ext2 of TS_EXTS) {
|
|
16643
|
+
const idx = join7(cand, "index" + ext2);
|
|
16644
|
+
if (existsSync7(idx))
|
|
16645
|
+
return idx;
|
|
16646
|
+
}
|
|
16647
|
+
}
|
|
16648
|
+
return null;
|
|
16649
|
+
}
|
|
16650
|
+
function findLocalConst(ident, source) {
|
|
16651
|
+
const re = new RegExp(`(?:export\\s+)?const\\s+${ident}\\s*(?::[^=]+)?=\\s*`, "g");
|
|
16652
|
+
const m = re.exec(source);
|
|
16653
|
+
if (!m)
|
|
16654
|
+
return null;
|
|
16655
|
+
const start = m.index + m[0].length;
|
|
16656
|
+
return readExpression(source, start);
|
|
16657
|
+
}
|
|
16658
|
+
function readExpression(source, start) {
|
|
16659
|
+
let depth = 0;
|
|
16660
|
+
let inString = null;
|
|
16661
|
+
let i = start;
|
|
16662
|
+
for (;i < source.length; i++) {
|
|
16663
|
+
const c = source[i];
|
|
16664
|
+
if (inString) {
|
|
16665
|
+
if (c === "\\") {
|
|
16666
|
+
i++;
|
|
16667
|
+
continue;
|
|
16668
|
+
}
|
|
16669
|
+
if (c === inString)
|
|
16670
|
+
inString = null;
|
|
16671
|
+
continue;
|
|
16672
|
+
}
|
|
16673
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
16674
|
+
inString = c;
|
|
16675
|
+
continue;
|
|
16676
|
+
}
|
|
16677
|
+
if (c === "{" || c === "[" || c === "(")
|
|
16678
|
+
depth++;
|
|
16679
|
+
else if (c === "}" || c === "]" || c === ")")
|
|
16680
|
+
depth--;
|
|
16681
|
+
if (depth === 0 && (c === ";" || c === `
|
|
16682
|
+
`))
|
|
16683
|
+
break;
|
|
16684
|
+
}
|
|
16685
|
+
return source.slice(start, i).trim().replace(/[;]+$/, "").trim();
|
|
16686
|
+
}
|
|
16687
|
+
function findDefaultExportIdentifier(source) {
|
|
16688
|
+
const m = /export\s+default\s+([A-Za-z_$][\w$]*)\s*;?/.exec(source);
|
|
16689
|
+
return m ? m[1] : null;
|
|
16690
|
+
}
|
|
16691
|
+
function parseLiteralValue(expr) {
|
|
16692
|
+
expr = expr.trim();
|
|
16693
|
+
if (expr.startsWith("{")) {
|
|
16694
|
+
const keys = extractTopLevelKeys(expr);
|
|
16695
|
+
if (keys.length > 0)
|
|
16696
|
+
return { kind: "objectKeys", values: keys };
|
|
16697
|
+
return null;
|
|
16698
|
+
}
|
|
16699
|
+
if (expr.startsWith("[")) {
|
|
16700
|
+
const arrText = sliceTopLevelArray(expr);
|
|
16701
|
+
if (!arrText)
|
|
16702
|
+
return null;
|
|
16703
|
+
const stringRe = /["'`]([^"'`]*)["'`]/g;
|
|
16704
|
+
const inner = arrText.slice(1, -1);
|
|
16705
|
+
if (/^\s*\{/.test(inner)) {
|
|
16706
|
+
const objs = parseObjectArray(arrText);
|
|
16707
|
+
if (objs)
|
|
16708
|
+
return { kind: "objects", values: objs };
|
|
16709
|
+
return null;
|
|
16710
|
+
}
|
|
16711
|
+
const values = [];
|
|
16712
|
+
let m;
|
|
16713
|
+
while ((m = stringRe.exec(inner)) !== null)
|
|
16714
|
+
values.push(m[1]);
|
|
16715
|
+
if (values.length > 0)
|
|
16716
|
+
return { kind: "strings", values };
|
|
16717
|
+
}
|
|
16718
|
+
return null;
|
|
16719
|
+
}
|
|
16720
|
+
function extractTopLevelKeys(expr) {
|
|
16721
|
+
const inner = expr.slice(1, expr.length - 1);
|
|
16722
|
+
const keys = [];
|
|
16723
|
+
let depth = 0;
|
|
16724
|
+
let i = 0;
|
|
16725
|
+
let inString = null;
|
|
16726
|
+
let atKeyPos = true;
|
|
16727
|
+
while (i < inner.length) {
|
|
16728
|
+
const c = inner[i];
|
|
16729
|
+
if (inString) {
|
|
16730
|
+
if (c === "\\") {
|
|
16731
|
+
i += 2;
|
|
16732
|
+
continue;
|
|
16733
|
+
}
|
|
16734
|
+
if (c === inString)
|
|
16735
|
+
inString = null;
|
|
16736
|
+
i++;
|
|
16737
|
+
continue;
|
|
16738
|
+
}
|
|
16739
|
+
if (c === '"' || c === "'" || c === "`") {
|
|
16740
|
+
if (atKeyPos && depth === 0) {
|
|
16741
|
+
const quote = c;
|
|
16742
|
+
const end = inner.indexOf(quote, i + 1);
|
|
16743
|
+
if (end === -1)
|
|
16744
|
+
break;
|
|
16745
|
+
keys.push(inner.slice(i + 1, end));
|
|
16746
|
+
i = end + 1;
|
|
16747
|
+
while (i < inner.length && /\s/.test(inner[i]))
|
|
16748
|
+
i++;
|
|
16749
|
+
if (inner[i] === ":") {
|
|
16750
|
+
i++;
|
|
16751
|
+
atKeyPos = false;
|
|
16752
|
+
}
|
|
16753
|
+
continue;
|
|
16754
|
+
}
|
|
16755
|
+
inString = c;
|
|
16756
|
+
i++;
|
|
16757
|
+
continue;
|
|
16758
|
+
}
|
|
16759
|
+
if (c === "{" || c === "[" || c === "(") {
|
|
16760
|
+
depth++;
|
|
16761
|
+
i++;
|
|
16762
|
+
continue;
|
|
16763
|
+
}
|
|
16764
|
+
if (c === "}" || c === "]" || c === ")") {
|
|
16765
|
+
depth--;
|
|
16766
|
+
i++;
|
|
16767
|
+
continue;
|
|
16768
|
+
}
|
|
16769
|
+
if (depth === 0 && c === ",") {
|
|
16770
|
+
atKeyPos = true;
|
|
16771
|
+
i++;
|
|
16772
|
+
continue;
|
|
16773
|
+
}
|
|
16774
|
+
if (depth === 0 && c === ":") {
|
|
16775
|
+
atKeyPos = false;
|
|
16776
|
+
i++;
|
|
16777
|
+
continue;
|
|
16778
|
+
}
|
|
16779
|
+
if (atKeyPos && depth === 0 && /[A-Za-z_$]/.test(c)) {
|
|
16780
|
+
let j = i;
|
|
16781
|
+
while (j < inner.length && /[\w$]/.test(inner[j]))
|
|
16782
|
+
j++;
|
|
16783
|
+
const key = inner.slice(i, j);
|
|
16784
|
+
let k = j;
|
|
16785
|
+
while (k < inner.length && /\s/.test(inner[k]))
|
|
16786
|
+
k++;
|
|
16787
|
+
if (inner[k] === ":") {
|
|
16788
|
+
keys.push(key);
|
|
16789
|
+
i = k + 1;
|
|
16790
|
+
atKeyPos = false;
|
|
16791
|
+
continue;
|
|
16792
|
+
}
|
|
16793
|
+
i = j;
|
|
16794
|
+
continue;
|
|
16795
|
+
}
|
|
16796
|
+
i++;
|
|
16797
|
+
}
|
|
16798
|
+
return keys;
|
|
16799
|
+
}
|
|
16800
|
+
function literalToResolution(data) {
|
|
16801
|
+
if (Array.isArray(data)) {
|
|
16802
|
+
if (data.every((v) => typeof v === "string")) {
|
|
16803
|
+
return { kind: "strings", values: data };
|
|
16804
|
+
}
|
|
16805
|
+
if (data.every((v) => v && typeof v === "object")) {
|
|
16806
|
+
return { kind: "objects", values: data };
|
|
16807
|
+
}
|
|
16808
|
+
return null;
|
|
16809
|
+
}
|
|
16810
|
+
if (data && typeof data === "object") {
|
|
16811
|
+
return { kind: "objectKeys", values: Object.keys(data) };
|
|
16812
|
+
}
|
|
16813
|
+
return null;
|
|
16814
|
+
}
|
|
16132
16815
|
|
|
16133
16816
|
// src/scanner.ts
|
|
16134
16817
|
function extractRoutePath(filePath) {
|
|
@@ -16634,7 +17317,7 @@ async function scan(config) {
|
|
|
16634
17317
|
// src/config.ts
|
|
16635
17318
|
var import_fast_glob11 = __toESM(require_out4(), 1);
|
|
16636
17319
|
import { existsSync as existsSync9, readFileSync as readFileSync11 } from "node:fs";
|
|
16637
|
-
import { join as join9, resolve as
|
|
17320
|
+
import { join as join9, resolve as resolve4, relative as relative4, dirname as dirname6 } from "node:path";
|
|
16638
17321
|
var DEFAULT_CONFIG = {
|
|
16639
17322
|
dir: "./",
|
|
16640
17323
|
ignore: {
|
|
@@ -16672,7 +17355,7 @@ function loadConfig(options) {
|
|
|
16672
17355
|
absolute: true
|
|
16673
17356
|
});
|
|
16674
17357
|
if (options.config && existsSync9(options.config)) {
|
|
16675
|
-
const absConfig =
|
|
17358
|
+
const absConfig = resolve4(cwd, options.config);
|
|
16676
17359
|
if (!configFiles.includes(absConfig)) {
|
|
16677
17360
|
configFiles.push(absConfig);
|
|
16678
17361
|
}
|
|
@@ -16695,7 +17378,7 @@ function loadConfig(options) {
|
|
|
16695
17378
|
try {
|
|
16696
17379
|
const content = readFileSync11(configPath, "utf-8");
|
|
16697
17380
|
const config = JSON.parse(content);
|
|
16698
|
-
const configDir =
|
|
17381
|
+
const configDir = dirname6(configPath);
|
|
16699
17382
|
const relDir = relative4(cwd, configDir);
|
|
16700
17383
|
const prefixPattern = (p) => {
|
|
16701
17384
|
if (p.startsWith("**/") || p.startsWith("/") || !relDir)
|
|
@@ -16810,7 +17493,7 @@ program2.action(async (options) => {
|
|
|
16810
17493
|
let isMonorepo = existsSync11(appsDir) && lstatSync(appsDir).isDirectory();
|
|
16811
17494
|
if (!isMonorepo) {
|
|
16812
17495
|
let current = absoluteDir;
|
|
16813
|
-
while (current !==
|
|
17496
|
+
while (current !== dirname7(current)) {
|
|
16814
17497
|
const potentialApps = join11(current, "apps");
|
|
16815
17498
|
if (existsSync11(potentialApps) && lstatSync(potentialApps).isDirectory()) {
|
|
16816
17499
|
monorepoRoot = current;
|
|
@@ -16818,7 +17501,7 @@ program2.action(async (options) => {
|
|
|
16818
17501
|
isMonorepo = true;
|
|
16819
17502
|
break;
|
|
16820
17503
|
}
|
|
16821
|
-
current =
|
|
17504
|
+
current = dirname7(current);
|
|
16822
17505
|
}
|
|
16823
17506
|
}
|
|
16824
17507
|
if (isMonorepo && monorepoRoot !== absoluteDir) {
|
|
@@ -16919,6 +17602,10 @@ program2.action(async (options) => {
|
|
|
16919
17602
|
if (options.json) {
|
|
16920
17603
|
console.log(JSON.stringify(result, null, 2));
|
|
16921
17604
|
} else if (options.fix) {
|
|
17605
|
+
if (isScanAll && !hasUnusedItems(result)) {
|
|
17606
|
+
console.log(source_default.green(` ✅ Nothing to fix in ${appLabel}`));
|
|
17607
|
+
continue;
|
|
17608
|
+
}
|
|
16922
17609
|
const fixResult = await handleFixes(result, currentConfig, options, isMonorepo);
|
|
16923
17610
|
if (fixResult === "back") {
|
|
16924
17611
|
requestedBack = true;
|
|
@@ -17161,7 +17848,7 @@ Analyzing cascading impact...`));
|
|
|
17161
17848
|
const routeFiles = new Set;
|
|
17162
17849
|
const rootDir = config.appSpecificScan ? config.appSpecificScan.rootDir : config.dir;
|
|
17163
17850
|
for (const r of [...unusedRoutes, ...partiallyRoutes]) {
|
|
17164
|
-
routeFiles.add(
|
|
17851
|
+
routeFiles.add(resolve5(rootDir, r.filePath));
|
|
17165
17852
|
}
|
|
17166
17853
|
const title = `Unused API Routes (${totalRoutesIssues} items in ${routeFiles.size} files)`;
|
|
17167
17854
|
choices.push({ title, value: "routes" });
|
|
@@ -17458,7 +18145,7 @@ Analyzing cascading impact...`));
|
|
|
17458
18145
|
const fullPath = resolveFilePath(filePath, config);
|
|
17459
18146
|
const route = fileRoutes[0];
|
|
17460
18147
|
if (route.type === "nextjs" && (filePath.includes("app/api") || filePath.includes("pages/api"))) {
|
|
17461
|
-
filesToUnlink.add(
|
|
18148
|
+
filesToUnlink.add(dirname7(fullPath));
|
|
17462
18149
|
} else if (route.type === "nestjs" && (result.unusedFiles?.files.some((f) => f.path === filePath) || filePath.includes("api/"))) {
|
|
17463
18150
|
filesToUnlink.add(fullPath);
|
|
17464
18151
|
} else {
|
|
@@ -17512,7 +18199,7 @@ Analyzing cascading impact...`));
|
|
|
17512
18199
|
}
|
|
17513
18200
|
const actuallyFixed = new Set;
|
|
17514
18201
|
for (const [absPath, removals] of removalsByFile) {
|
|
17515
|
-
if (actuallyDeleted.has(absPath) || actuallyDeleted.has(
|
|
18202
|
+
if (actuallyDeleted.has(absPath) || actuallyDeleted.has(dirname7(absPath)))
|
|
17516
18203
|
continue;
|
|
17517
18204
|
if (!existsSync11(absPath))
|
|
17518
18205
|
continue;
|
|
@@ -17527,7 +18214,7 @@ Analyzing cascading impact...`));
|
|
|
17527
18214
|
}
|
|
17528
18215
|
result.routes = result.routes.filter((r) => {
|
|
17529
18216
|
const fullPath = resolveFilePath(r.filePath, config);
|
|
17530
|
-
if (actuallyDeleted.has(fullPath) || actuallyDeleted.has(
|
|
18217
|
+
if (actuallyDeleted.has(fullPath) || actuallyDeleted.has(dirname7(fullPath)))
|
|
17531
18218
|
return false;
|
|
17532
18219
|
if (actuallyFixed.has(fullPath)) {
|
|
17533
18220
|
r.unusedMethods = [];
|
|
@@ -17999,11 +18686,11 @@ function printTable(summary) {
|
|
|
17999
18686
|
}
|
|
18000
18687
|
function findGitRoot(startDir) {
|
|
18001
18688
|
let currentDir = startDir;
|
|
18002
|
-
while (currentDir !==
|
|
18689
|
+
while (currentDir !== dirname7(currentDir)) {
|
|
18003
18690
|
if (existsSync11(join11(currentDir, ".git"))) {
|
|
18004
18691
|
return true;
|
|
18005
18692
|
}
|
|
18006
|
-
currentDir =
|
|
18693
|
+
currentDir = dirname7(currentDir);
|
|
18007
18694
|
}
|
|
18008
18695
|
return false;
|
|
18009
18696
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pruny",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.1",
|
|
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",
|