pruny 1.15.0 → 1.16.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 +121 -38
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7631,8 +7631,8 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
7631
7631
|
var source_default = chalk;
|
|
7632
7632
|
|
|
7633
7633
|
// src/index.ts
|
|
7634
|
-
import { rmSync } from "node:fs";
|
|
7635
|
-
import { dirname as
|
|
7634
|
+
import { rmSync, existsSync as existsSync7 } from "node:fs";
|
|
7635
|
+
import { dirname as dirname4, join as join8 } from "node:path";
|
|
7636
7636
|
|
|
7637
7637
|
// src/scanner.ts
|
|
7638
7638
|
var import_fast_glob4 = __toESM(require_out4(), 1);
|
|
@@ -9231,7 +9231,11 @@ async function scanUnusedFiles(config) {
|
|
|
9231
9231
|
"scripts/**/*.{ts,js}",
|
|
9232
9232
|
"cypress/**/*.{ts,js,tsx}",
|
|
9233
9233
|
"**/public/sw.js",
|
|
9234
|
-
"**/sw.{js,ts}"
|
|
9234
|
+
"**/sw.{js,ts}",
|
|
9235
|
+
"**/main.{ts,js}",
|
|
9236
|
+
"api/index.ts",
|
|
9237
|
+
"**/app.module.ts",
|
|
9238
|
+
"**/api/index.ts"
|
|
9235
9239
|
];
|
|
9236
9240
|
for (const file of allFiles) {
|
|
9237
9241
|
const isEntry = entryPatterns.some((pattern) => {
|
|
@@ -9687,22 +9691,29 @@ function shouldIgnore(path2, ignorePatterns) {
|
|
|
9687
9691
|
return false;
|
|
9688
9692
|
});
|
|
9689
9693
|
}
|
|
9690
|
-
function
|
|
9694
|
+
function normalizeNextPath(path2) {
|
|
9691
9695
|
return path2.replace(/\/$/, "").replace(/\?.*$/, "").replace(/\$\{[^}]+\}/g, "*").replace(/\[[^\]]+\]/g, "*").toLowerCase();
|
|
9692
9696
|
}
|
|
9693
|
-
function
|
|
9694
|
-
|
|
9697
|
+
function normalizeNestPath(path2) {
|
|
9698
|
+
return path2.replace(/\/$/, "").replace(/\?.*$/, "").replace(/:[^/]+/g, "*").toLowerCase();
|
|
9699
|
+
}
|
|
9700
|
+
function checkRouteUsage(route, references, nestGlobalPrefix = "api") {
|
|
9701
|
+
const normalize = route.type === "nextjs" ? normalizeNextPath : normalizeNestPath;
|
|
9702
|
+
const normalizedRoute = normalize(route.path);
|
|
9703
|
+
const prefixToRemove = `/${nestGlobalPrefix}`;
|
|
9704
|
+
const normalizedRouteNoPrefix = route.type === "nestjs" && route.path.startsWith(prefixToRemove) ? normalize(route.path.substring(prefixToRemove.length)) : null;
|
|
9695
9705
|
const usedMethods = new Set;
|
|
9696
9706
|
let used = false;
|
|
9697
9707
|
for (const ref of references) {
|
|
9698
|
-
const normalizedFound =
|
|
9708
|
+
const normalizedFound = ref.path.replace(/\/$/, "").replace(/\?.*$/, "").replace(/\$\{[^}]+\}/g, "*").toLowerCase();
|
|
9699
9709
|
let match2 = false;
|
|
9700
|
-
if (normalizedRoute === normalizedFound)
|
|
9701
|
-
match2 = true;
|
|
9702
|
-
else if (normalizedFound.startsWith(normalizedRoute))
|
|
9703
|
-
match2 = true;
|
|
9704
|
-
else if (minimatch(normalizedFound, normalizedRoute))
|
|
9710
|
+
if (normalizedRoute === normalizedFound || normalizedFound.startsWith(normalizedRoute + "/") || minimatch(normalizedFound, normalizedRoute)) {
|
|
9705
9711
|
match2 = true;
|
|
9712
|
+
} else if (normalizedRouteNoPrefix) {
|
|
9713
|
+
if (normalizedRouteNoPrefix === normalizedFound || normalizedFound.startsWith(normalizedRouteNoPrefix + "/") || minimatch(normalizedFound, normalizedRouteNoPrefix)) {
|
|
9714
|
+
match2 = true;
|
|
9715
|
+
}
|
|
9716
|
+
}
|
|
9706
9717
|
if (match2) {
|
|
9707
9718
|
used = true;
|
|
9708
9719
|
if (ref.method) {
|
|
@@ -9803,7 +9814,7 @@ async function scan(config) {
|
|
|
9803
9814
|
route.unusedMethods = [];
|
|
9804
9815
|
continue;
|
|
9805
9816
|
}
|
|
9806
|
-
const { used, usedMethods } = checkRouteUsage(route
|
|
9817
|
+
const { used, usedMethods } = checkRouteUsage(route, allReferences, config.nestGlobalPrefix);
|
|
9807
9818
|
if (used) {
|
|
9808
9819
|
route.used = true;
|
|
9809
9820
|
if (usedMethods.has("ALL")) {
|
|
@@ -9812,7 +9823,7 @@ async function scan(config) {
|
|
|
9812
9823
|
route.unusedMethods = route.methods.filter((m) => !usedMethods.has(m));
|
|
9813
9824
|
}
|
|
9814
9825
|
for (const [file, refs] of fileReferences) {
|
|
9815
|
-
if (checkRouteUsage(route
|
|
9826
|
+
if (checkRouteUsage(route, refs, config.nestGlobalPrefix).used) {
|
|
9816
9827
|
route.references.push(file);
|
|
9817
9828
|
}
|
|
9818
9829
|
}
|
|
@@ -9837,7 +9848,7 @@ async function scan(config) {
|
|
|
9837
9848
|
// src/config.ts
|
|
9838
9849
|
var import_fast_glob5 = __toESM(require_out4(), 1);
|
|
9839
9850
|
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
9840
|
-
import { join as join5, resolve as resolve2 } from "node:path";
|
|
9851
|
+
import { join as join5, resolve as resolve2, relative as relative2, dirname as dirname3 } from "node:path";
|
|
9841
9852
|
var DEFAULT_CONFIG = {
|
|
9842
9853
|
dir: "./",
|
|
9843
9854
|
ignore: {
|
|
@@ -9859,19 +9870,19 @@ var DEFAULT_CONFIG = {
|
|
|
9859
9870
|
"**/vendor/**"
|
|
9860
9871
|
],
|
|
9861
9872
|
files: [
|
|
9862
|
-
"
|
|
9863
|
-
"
|
|
9864
|
-
"
|
|
9865
|
-
"
|
|
9866
|
-
"public/robots.txt",
|
|
9867
|
-
"public/sitemap*.xml",
|
|
9868
|
-
"public/favicon.ico",
|
|
9869
|
-
"public/sw.js",
|
|
9870
|
-
"public/manifest.json",
|
|
9871
|
-
"public/twitter-image.*",
|
|
9872
|
-
"public/opengraph-image.*",
|
|
9873
|
-
"public/apple-icon.*",
|
|
9874
|
-
"public/icon.*",
|
|
9873
|
+
"**/*.test.ts",
|
|
9874
|
+
"**/*.spec.ts",
|
|
9875
|
+
"**/*.test.tsx",
|
|
9876
|
+
"**/*.spec.tsx",
|
|
9877
|
+
"**/public/robots.txt",
|
|
9878
|
+
"**/public/sitemap*.xml",
|
|
9879
|
+
"**/public/favicon.ico",
|
|
9880
|
+
"**/public/sw.js",
|
|
9881
|
+
"**/public/manifest.json",
|
|
9882
|
+
"**/public/twitter-image.*",
|
|
9883
|
+
"**/public/opengraph-image.*",
|
|
9884
|
+
"**/public/apple-icon.*",
|
|
9885
|
+
"**/public/icon.*",
|
|
9875
9886
|
"**/proxy.*",
|
|
9876
9887
|
"**/middleware.*"
|
|
9877
9888
|
]
|
|
@@ -9910,12 +9921,19 @@ function loadConfig(options) {
|
|
|
9910
9921
|
try {
|
|
9911
9922
|
const content = readFileSync5(configPath, "utf-8");
|
|
9912
9923
|
const config = JSON.parse(content);
|
|
9924
|
+
const configDir = dirname3(configPath);
|
|
9925
|
+
const relDir = relative2(cwd, configDir);
|
|
9926
|
+
const prefixPattern = (p) => {
|
|
9927
|
+
if (p.startsWith("**/") || p.startsWith("/") || !relDir)
|
|
9928
|
+
return p;
|
|
9929
|
+
return join5(relDir, p).replace(/\\/g, "/");
|
|
9930
|
+
};
|
|
9913
9931
|
if (config.ignore?.routes)
|
|
9914
|
-
mergedIgnore.routes.push(...config.ignore.routes);
|
|
9932
|
+
mergedIgnore.routes.push(...config.ignore.routes.map(prefixPattern));
|
|
9915
9933
|
if (config.ignore?.folders)
|
|
9916
|
-
mergedIgnore.folders.push(...config.ignore.folders);
|
|
9934
|
+
mergedIgnore.folders.push(...config.ignore.folders.map(prefixPattern));
|
|
9917
9935
|
if (config.ignore?.files)
|
|
9918
|
-
mergedIgnore.files.push(...config.ignore.files);
|
|
9936
|
+
mergedIgnore.files.push(...config.ignore.files.map(prefixPattern));
|
|
9919
9937
|
if (config.extensions)
|
|
9920
9938
|
mergedExtensions = [...new Set([...mergedExtensions, ...config.extensions])];
|
|
9921
9939
|
if (config.nestGlobalPrefix)
|
|
@@ -9924,8 +9942,6 @@ function loadConfig(options) {
|
|
|
9924
9942
|
extraRoutePatterns.push(...config.extraRoutePatterns);
|
|
9925
9943
|
if (config.excludePublic !== undefined)
|
|
9926
9944
|
excludePublic = config.excludePublic;
|
|
9927
|
-
if (config.excludePublic !== undefined)
|
|
9928
|
-
excludePublic = config.excludePublic;
|
|
9929
9945
|
} catch {}
|
|
9930
9946
|
}
|
|
9931
9947
|
const gitIgnorePatterns = parseGitIgnore(cwd);
|
|
@@ -9972,6 +9988,8 @@ import { readFileSync as readFileSync6, writeFileSync, unlinkSync, existsSync as
|
|
|
9972
9988
|
import { join as join6 } from "node:path";
|
|
9973
9989
|
function removeExportFromLine(rootDir, exp) {
|
|
9974
9990
|
const fullPath = join6(rootDir, exp.file);
|
|
9991
|
+
if (!existsSync5(fullPath))
|
|
9992
|
+
return false;
|
|
9975
9993
|
try {
|
|
9976
9994
|
const content = readFileSync6(fullPath, "utf-8");
|
|
9977
9995
|
const lines = content.split(`
|
|
@@ -10230,18 +10248,40 @@ program2.action(async (options) => {
|
|
|
10230
10248
|
`));
|
|
10231
10249
|
}
|
|
10232
10250
|
if (options.fix) {
|
|
10251
|
+
let fixedSomething = false;
|
|
10233
10252
|
if (unusedRoutes.length > 0) {
|
|
10234
10253
|
console.log(source_default.yellow.bold(`\uD83D\uDDD1️ Deleting unused routes...
|
|
10235
10254
|
`));
|
|
10236
10255
|
for (const route of unusedRoutes) {
|
|
10237
|
-
const
|
|
10256
|
+
const fullPath = join8(config.dir, route.filePath);
|
|
10257
|
+
const routeDir = dirname4(fullPath);
|
|
10238
10258
|
try {
|
|
10239
|
-
|
|
10240
|
-
|
|
10259
|
+
if (!existsSync7(fullPath))
|
|
10260
|
+
continue;
|
|
10261
|
+
if (route.type === "nextjs") {
|
|
10262
|
+
if (route.filePath.includes("app/api") || route.filePath.includes("pages/api")) {
|
|
10263
|
+
rmSync(routeDir, { recursive: true, force: true });
|
|
10264
|
+
console.log(source_default.red(` Deleted Folder: ${routeDir}`));
|
|
10265
|
+
} else {
|
|
10266
|
+
rmSync(fullPath, { force: true });
|
|
10267
|
+
console.log(source_default.red(` Deleted File: ${route.filePath}`));
|
|
10268
|
+
}
|
|
10269
|
+
} else if (route.type === "nestjs") {
|
|
10270
|
+
rmSync(fullPath, { force: true });
|
|
10271
|
+
console.log(source_default.red(` Deleted File: ${route.filePath}`));
|
|
10272
|
+
} else {
|
|
10273
|
+
rmSync(fullPath, { force: true });
|
|
10274
|
+
console.log(source_default.red(` Deleted File: ${route.filePath}`));
|
|
10275
|
+
}
|
|
10276
|
+
fixedSomething = true;
|
|
10277
|
+
const idx = result.routes.indexOf(route);
|
|
10278
|
+
if (idx !== -1)
|
|
10279
|
+
result.routes.splice(idx, 1);
|
|
10241
10280
|
} catch (_err) {
|
|
10242
10281
|
console.log(source_default.yellow(` Failed to delete: ${route.filePath}`));
|
|
10243
10282
|
}
|
|
10244
10283
|
}
|
|
10284
|
+
console.log("");
|
|
10245
10285
|
}
|
|
10246
10286
|
const partiallyRoutes = result.routes.filter((r) => r.used && r.unusedMethods.length > 0);
|
|
10247
10287
|
if (partiallyRoutes.length > 0) {
|
|
@@ -10249,15 +10289,44 @@ program2.action(async (options) => {
|
|
|
10249
10289
|
`));
|
|
10250
10290
|
for (const route of partiallyRoutes) {
|
|
10251
10291
|
const sortedMethods = [...route.unusedMethods].filter((m) => route.methodLines[m] !== undefined).sort((a, b) => route.methodLines[b] - route.methodLines[a]);
|
|
10292
|
+
let fixedCount = 0;
|
|
10252
10293
|
for (const method of sortedMethods) {
|
|
10253
10294
|
const lineNum = route.methodLines[method];
|
|
10254
10295
|
if (removeMethodFromRoute(config.dir, route.filePath, method, lineNum)) {
|
|
10255
10296
|
console.log(source_default.green(` Fixed: Removed ${method} from ${route.path}`));
|
|
10297
|
+
fixedCount++;
|
|
10298
|
+
fixedSomething = true;
|
|
10256
10299
|
}
|
|
10257
10300
|
}
|
|
10301
|
+
if (fixedCount === route.methods.length) {
|
|
10302
|
+
const idx = result.routes.indexOf(route);
|
|
10303
|
+
if (idx !== -1)
|
|
10304
|
+
result.routes.splice(idx, 1);
|
|
10305
|
+
} else {
|
|
10306
|
+
route.unusedMethods = route.unusedMethods.filter((m) => !sortedMethods.includes(m));
|
|
10307
|
+
}
|
|
10258
10308
|
}
|
|
10259
10309
|
console.log("");
|
|
10260
10310
|
}
|
|
10311
|
+
if (result.unusedFiles && result.unusedFiles.files.length > 0) {
|
|
10312
|
+
console.log(source_default.yellow.bold(`\uD83D\uDDD1️ Deleting unused source files...
|
|
10313
|
+
`));
|
|
10314
|
+
for (const file of result.unusedFiles.files) {
|
|
10315
|
+
try {
|
|
10316
|
+
const fullPath = join8(config.dir, file.path);
|
|
10317
|
+
if (!existsSync7(fullPath))
|
|
10318
|
+
continue;
|
|
10319
|
+
rmSync(fullPath, { force: true });
|
|
10320
|
+
console.log(source_default.red(` Deleted: ${file.path}`));
|
|
10321
|
+
fixedSomething = true;
|
|
10322
|
+
} catch (_err) {
|
|
10323
|
+
console.log(source_default.yellow(` Failed to delete: ${file.path}`));
|
|
10324
|
+
}
|
|
10325
|
+
}
|
|
10326
|
+
result.unusedFiles.files = [];
|
|
10327
|
+
result.unusedFiles.unused = 0;
|
|
10328
|
+
console.log("");
|
|
10329
|
+
}
|
|
10261
10330
|
if (result.unusedExports && result.unusedExports.exports.length > 0) {
|
|
10262
10331
|
console.log(source_default.yellow.bold(`\uD83D\uDD27 Fixing unused exports (removing "export" keyword)...
|
|
10263
10332
|
`));
|
|
@@ -10272,9 +10341,18 @@ program2.action(async (options) => {
|
|
|
10272
10341
|
for (const [file, exports] of exportsByFile.entries()) {
|
|
10273
10342
|
const sortedExports = exports.sort((a, b) => b.line - a.line);
|
|
10274
10343
|
for (const exp of sortedExports) {
|
|
10344
|
+
const fullPath = join8(config.dir, exp.file);
|
|
10345
|
+
if (!existsSync7(fullPath))
|
|
10346
|
+
continue;
|
|
10275
10347
|
if (removeExportFromLine(config.dir, exp)) {
|
|
10276
10348
|
console.log(source_default.green(` Fixed: ${exp.name} in ${exp.file}`));
|
|
10277
10349
|
fixedCount++;
|
|
10350
|
+
fixedSomething = true;
|
|
10351
|
+
const expIdx = result.unusedExports.exports.indexOf(exp);
|
|
10352
|
+
if (expIdx !== -1) {
|
|
10353
|
+
result.unusedExports.exports.splice(expIdx, 1);
|
|
10354
|
+
result.unusedExports.unused--;
|
|
10355
|
+
}
|
|
10278
10356
|
}
|
|
10279
10357
|
}
|
|
10280
10358
|
}
|
|
@@ -10284,8 +10362,13 @@ program2.action(async (options) => {
|
|
|
10284
10362
|
`));
|
|
10285
10363
|
}
|
|
10286
10364
|
}
|
|
10287
|
-
|
|
10288
|
-
|
|
10365
|
+
if (fixedSomething) {
|
|
10366
|
+
result.unused = result.routes.filter((r) => !r.used).length;
|
|
10367
|
+
result.used = result.routes.filter((r) => r.used).length;
|
|
10368
|
+
result.total = result.routes.length;
|
|
10369
|
+
}
|
|
10370
|
+
} else if (unusedRoutes.length > 0 || result.unusedExports && result.unusedExports.exports.length > 0 || result.unusedFiles && result.unusedFiles.files.length > 0) {
|
|
10371
|
+
console.log(source_default.dim(`\uD83D\uDCA1 Run with --fix to automatically clean up unused routes, files, and exports.
|
|
10289
10372
|
`));
|
|
10290
10373
|
}
|
|
10291
10374
|
console.log(source_default.bold(`\uD83D\uDCCA Summary Report
|