pruny 1.18.0 → 1.20.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 +103 -75
- package/dist/workers/file-processor.js +17 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7649,13 +7649,20 @@ var API_METHOD_PATTERNS = [
|
|
|
7649
7649
|
{ regex: /axios\.put\s*\(\s*['"`](\/[^'"`\s)]+)['"`]/g, method: "PUT" },
|
|
7650
7650
|
{ regex: /axios\.delete\s*\(\s*['"`](\/[^'"`\s)]+)['"`]/g, method: "DELETE" },
|
|
7651
7651
|
{ regex: /axios\.patch\s*\(\s*['"`](\/[^'"`\s)]+)['"`]/g, method: "PATCH" },
|
|
7652
|
+
{ regex: /axios\.get\s*\(\s*`[^`]*?(\/[^`\s)]+)`/g, method: "GET" },
|
|
7653
|
+
{ regex: /axios\.post\s*\(\s*`[^`]*?(\/[^`\s)]+)`/g, method: "POST" },
|
|
7654
|
+
{ regex: /axios\.put\s*\(\s*`[^`]*?(\/[^`\s)]+)`/g, method: "PUT" },
|
|
7655
|
+
{ regex: /axios\.delete\s*\(\s*`[^`]*?(\/[^`\s)]+)`/g, method: "DELETE" },
|
|
7656
|
+
{ regex: /axios\.patch\s*\(\s*`[^`]*?(\/[^`\s)]+)`/g, method: "PATCH" },
|
|
7652
7657
|
{ regex: /useSWR\s*\(\s*['"`](\/[^'"`\s)]+)['"`]/g, method: "GET" },
|
|
7658
|
+
{ regex: /useSWR\s*\(\s*`[^`]*?(\/[^`\s)]+)`/g, method: "GET" },
|
|
7653
7659
|
{ regex: /fetch\s*\(\s*['"`](\/[^'"`\s)]+)['"`]/g, method: undefined },
|
|
7654
|
-
{ regex: /fetch\s*\(\s*`[^`]
|
|
7660
|
+
{ regex: /fetch\s*\(\s*`[^`]*?(\/[^`\s)]+)`/g, method: undefined },
|
|
7655
7661
|
{ regex: /['"`](\/api\/[^'"`\s]+)['"`]/g, method: undefined },
|
|
7656
|
-
{ regex:
|
|
7657
|
-
{ regex: /`[^`]
|
|
7658
|
-
{ regex: /['"`](\/[\w-]{2,}\/[
|
|
7662
|
+
{ regex: /`[^`]*?(\/api\/[^`\s]+)`/g, method: undefined },
|
|
7663
|
+
{ regex: /`[^`]*?(\/[\w-]{2,}\/[^`\s]*)`/g, method: undefined },
|
|
7664
|
+
{ regex: /['"`](\/[\w-]{2,}\/[^'"`\s]*)['"`]/g, method: undefined },
|
|
7665
|
+
{ regex: /['"`](\/api\/[^'"`\s]*)['"`]/g, method: undefined }
|
|
7659
7666
|
];
|
|
7660
7667
|
function extractApiReferences(content) {
|
|
7661
7668
|
const matches = [];
|
|
@@ -9362,6 +9369,8 @@ var IGNORED_EXPORT_NAMES = new Set([
|
|
|
9362
9369
|
"OPTIONS",
|
|
9363
9370
|
"default"
|
|
9364
9371
|
]);
|
|
9372
|
+
var NEST_LIFECYCLE_METHODS = new Set(["constructor", "onModuleInit", "onApplicationBootstrap", "onModuleDestroy", "beforeApplicationShutdown", "onApplicationShutdown"]);
|
|
9373
|
+
var classMethodRegex = /^\s*(?:async\s+)?([a-zA-Z0-9_$]+)\s*\([^)]*\)\s*(?::\s*[^\{]*)?\{/gm;
|
|
9365
9374
|
async function processFilesInParallel(files, cwd, workerCount) {
|
|
9366
9375
|
const __filename2 = fileURLToPath(import.meta.url);
|
|
9367
9376
|
const __dirname2 = dirname2(__filename2);
|
|
@@ -9468,6 +9477,7 @@ async function scanUnusedExports(config) {
|
|
|
9468
9477
|
}
|
|
9469
9478
|
const content = readFileSync3(join3(cwd, file), "utf-8");
|
|
9470
9479
|
totalContents.set(file, content);
|
|
9480
|
+
const isService = file.endsWith(".service.ts") || file.endsWith(".service.tsx");
|
|
9471
9481
|
const lines = content.split(`
|
|
9472
9482
|
`);
|
|
9473
9483
|
for (let i = 0;i < lines.length; i++) {
|
|
@@ -9491,6 +9501,20 @@ async function scanUnusedExports(config) {
|
|
|
9491
9501
|
}
|
|
9492
9502
|
}
|
|
9493
9503
|
}
|
|
9504
|
+
if (isService) {
|
|
9505
|
+
classMethodRegex.lastIndex = 0;
|
|
9506
|
+
while ((match2 = classMethodRegex.exec(line)) !== null) {
|
|
9507
|
+
const name = match2[1];
|
|
9508
|
+
if (name && !NEST_LIFECYCLE_METHODS.has(name) && !IGNORED_EXPORT_NAMES.has(name)) {
|
|
9509
|
+
const existing = exportMap.get(file)?.find((e) => e.name === name);
|
|
9510
|
+
if (!existing) {
|
|
9511
|
+
if (addExport(file, name, i + 1)) {
|
|
9512
|
+
allExportsCount++;
|
|
9513
|
+
}
|
|
9514
|
+
}
|
|
9515
|
+
}
|
|
9516
|
+
}
|
|
9517
|
+
}
|
|
9494
9518
|
}
|
|
9495
9519
|
} catch {}
|
|
9496
9520
|
}
|
|
@@ -9704,21 +9728,32 @@ function normalizeNextPath(path2) {
|
|
|
9704
9728
|
function normalizeNestPath(path2) {
|
|
9705
9729
|
return path2.replace(/\/$/, "").replace(/\?.*$/, "").replace(/:[^/]+/g, "*").toLowerCase();
|
|
9706
9730
|
}
|
|
9707
|
-
function checkRouteUsage(route, references, nestGlobalPrefix = "
|
|
9731
|
+
function checkRouteUsage(route, references, nestGlobalPrefix = "") {
|
|
9708
9732
|
const normalize = route.type === "nextjs" ? normalizeNextPath : normalizeNestPath;
|
|
9709
9733
|
const normalizedRoute = normalize(route.path);
|
|
9710
|
-
const
|
|
9711
|
-
|
|
9734
|
+
const variations = new Set([normalizedRoute]);
|
|
9735
|
+
if (route.type === "nestjs") {
|
|
9736
|
+
if (nestGlobalPrefix) {
|
|
9737
|
+
const prefixToRemove = `/${nestGlobalPrefix}`.replace(/\/+/g, "/");
|
|
9738
|
+
if (route.path.startsWith(prefixToRemove)) {
|
|
9739
|
+
variations.add(normalize(route.path.substring(prefixToRemove.length)));
|
|
9740
|
+
}
|
|
9741
|
+
}
|
|
9742
|
+
if (route.path.startsWith("/api/")) {
|
|
9743
|
+
variations.add(normalize(route.path.substring(4)));
|
|
9744
|
+
} else {
|
|
9745
|
+
variations.add(normalize("/api" + route.path));
|
|
9746
|
+
}
|
|
9747
|
+
}
|
|
9712
9748
|
const usedMethods = new Set;
|
|
9713
9749
|
let used = false;
|
|
9714
9750
|
for (const ref of references) {
|
|
9715
9751
|
const normalizedFound = ref.path.replace(/\/$/, "").replace(/\?.*$/, "").replace(/\$\{[^}]+\}/g, "*").toLowerCase();
|
|
9716
9752
|
let match2 = false;
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
} else if (normalizedRouteNoPrefix) {
|
|
9720
|
-
if (normalizedRouteNoPrefix === normalizedFound || normalizedFound.startsWith(normalizedRouteNoPrefix + "/") || minimatch(normalizedFound, normalizedRouteNoPrefix)) {
|
|
9753
|
+
for (const v of variations) {
|
|
9754
|
+
if (v === normalizedFound || normalizedFound.startsWith(v + "/") || minimatch(normalizedFound, v)) {
|
|
9721
9755
|
match2 = true;
|
|
9756
|
+
break;
|
|
9722
9757
|
}
|
|
9723
9758
|
}
|
|
9724
9759
|
if (match2) {
|
|
@@ -9895,7 +9930,7 @@ var DEFAULT_CONFIG = {
|
|
|
9895
9930
|
]
|
|
9896
9931
|
},
|
|
9897
9932
|
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
|
9898
|
-
nestGlobalPrefix: "
|
|
9933
|
+
nestGlobalPrefix: "",
|
|
9899
9934
|
extraRoutePatterns: []
|
|
9900
9935
|
};
|
|
9901
9936
|
function loadConfig(options) {
|
|
@@ -10185,13 +10220,11 @@ program2.action(async (options) => {
|
|
|
10185
10220
|
if (result.unusedFiles) {
|
|
10186
10221
|
result.unusedFiles.files = result.unusedFiles.files.filter((f) => matchesFilter(f.path));
|
|
10187
10222
|
result.unusedFiles.total = result.unusedFiles.files.length;
|
|
10188
|
-
result.unusedFiles.used = 0;
|
|
10189
10223
|
result.unusedFiles.unused = result.unusedFiles.files.length;
|
|
10190
10224
|
}
|
|
10191
10225
|
if (result.unusedExports) {
|
|
10192
10226
|
result.unusedExports.exports = result.unusedExports.exports.filter((e) => matchesFilter(e.file));
|
|
10193
10227
|
result.unusedExports.total = result.unusedExports.exports.length;
|
|
10194
|
-
result.unusedExports.used = 0;
|
|
10195
10228
|
result.unusedExports.unused = result.unusedExports.exports.length;
|
|
10196
10229
|
}
|
|
10197
10230
|
}
|
|
@@ -10243,7 +10276,7 @@ program2.action(async (options) => {
|
|
|
10243
10276
|
console.log("");
|
|
10244
10277
|
}
|
|
10245
10278
|
if (result.unusedExports && result.unusedExports.exports.length > 0) {
|
|
10246
|
-
console.log(source_default.red.bold(`\uD83D\uDD17 Unused Named Exports:
|
|
10279
|
+
console.log(source_default.red.bold(`\uD83D\uDD17 Unused Named Exports/Methods:
|
|
10247
10280
|
`));
|
|
10248
10281
|
for (const exp of result.unusedExports.exports) {
|
|
10249
10282
|
console.log(source_default.red(` ${exp.name}`));
|
|
@@ -10290,7 +10323,6 @@ program2.action(async (options) => {
|
|
|
10290
10323
|
fixedSomething = true;
|
|
10291
10324
|
} else {
|
|
10292
10325
|
console.log(source_default.yellow(` Skipped File Deletion (internally used): ${filePath}`));
|
|
10293
|
-
console.log(source_default.dim(` → This controller is imported in another file (e.g. app.module.ts).`));
|
|
10294
10326
|
const allMethodsToPrune = [];
|
|
10295
10327
|
for (const r of fileRoutes) {
|
|
10296
10328
|
for (const m of r.unusedMethods) {
|
|
@@ -10368,38 +10400,17 @@ program2.action(async (options) => {
|
|
|
10368
10400
|
console.log("");
|
|
10369
10401
|
}
|
|
10370
10402
|
if (result.unusedExports && result.unusedExports.exports.length > 0) {
|
|
10371
|
-
|
|
10372
|
-
|
|
10373
|
-
|
|
10374
|
-
|
|
10375
|
-
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
}
|
|
10380
|
-
let fixedCount = 0;
|
|
10381
|
-
for (const [file, exports] of exportsByFile.entries()) {
|
|
10382
|
-
const sortedExports = exports.sort((a, b) => b.line - a.line);
|
|
10383
|
-
for (const exp of sortedExports) {
|
|
10384
|
-
const fullPath = join8(config.dir, exp.file);
|
|
10385
|
-
if (!existsSync7(fullPath))
|
|
10386
|
-
continue;
|
|
10387
|
-
if (removeExportFromLine(config.dir, exp)) {
|
|
10388
|
-
console.log(source_default.green(` Fixed: ${exp.name} in ${exp.file}`));
|
|
10389
|
-
fixedCount++;
|
|
10390
|
-
fixedSomething = true;
|
|
10391
|
-
const expIdx = result.unusedExports.exports.indexOf(exp);
|
|
10392
|
-
if (expIdx !== -1) {
|
|
10393
|
-
result.unusedExports.exports.splice(expIdx, 1);
|
|
10394
|
-
result.unusedExports.unused--;
|
|
10395
|
-
}
|
|
10396
|
-
}
|
|
10397
|
-
}
|
|
10398
|
-
}
|
|
10399
|
-
if (fixedCount > 0) {
|
|
10400
|
-
console.log(source_default.green(`
|
|
10401
|
-
✅ Removed "export" from ${fixedCount} item(s).
|
|
10403
|
+
fixedSomething = await fixUnusedExports(result, config) || fixedSomething;
|
|
10404
|
+
}
|
|
10405
|
+
if (fixedSomething) {
|
|
10406
|
+
console.log(source_default.cyan.bold(`
|
|
10407
|
+
\uD83D\uDD04 Checking for cascading dead code (newly unused implementation)...`));
|
|
10408
|
+
const secondPass = await scanUnusedExports(config);
|
|
10409
|
+
if (secondPass.unused > 0) {
|
|
10410
|
+
console.log(source_default.yellow(` Found ${secondPass.unused} newly unused items/methods after pruning.
|
|
10402
10411
|
`));
|
|
10412
|
+
result.unusedExports = secondPass;
|
|
10413
|
+
await fixUnusedExports(result, config);
|
|
10403
10414
|
}
|
|
10404
10415
|
}
|
|
10405
10416
|
if (fixedSomething) {
|
|
@@ -10433,41 +10444,21 @@ program2.action(async (options) => {
|
|
|
10433
10444
|
for (const key of sortedKeys) {
|
|
10434
10445
|
const group = groupedRoutes.get(key);
|
|
10435
10446
|
const typeLabel = group.type === "nextjs" ? "Next.js" : "NestJS";
|
|
10436
|
-
const label = `${typeLabel} (${group.app})`;
|
|
10437
10447
|
summary.push({
|
|
10438
|
-
Category:
|
|
10448
|
+
Category: `${typeLabel} (${group.app})`,
|
|
10439
10449
|
Total: group.routes.length,
|
|
10440
10450
|
Used: group.routes.filter((r) => r.used).length,
|
|
10441
10451
|
Unused: group.routes.filter((r) => !r.used).length
|
|
10442
10452
|
});
|
|
10443
10453
|
}
|
|
10444
|
-
if (summary.length === 0)
|
|
10454
|
+
if (summary.length === 0)
|
|
10445
10455
|
summary.push({ Category: "API Routes", Total: result.total, Used: result.used, Unused: result.unused });
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
|
|
10451
|
-
|
|
10452
|
-
Unused: result.publicAssets.unused
|
|
10453
|
-
});
|
|
10454
|
-
}
|
|
10455
|
-
if (result.unusedFiles) {
|
|
10456
|
-
summary.push({
|
|
10457
|
-
Category: "Source Files",
|
|
10458
|
-
Total: result.unusedFiles.total,
|
|
10459
|
-
Used: result.unusedFiles.used,
|
|
10460
|
-
Unused: result.unusedFiles.unused
|
|
10461
|
-
});
|
|
10462
|
-
}
|
|
10463
|
-
if (result.unusedExports) {
|
|
10464
|
-
summary.push({
|
|
10465
|
-
Category: "Exported Items",
|
|
10466
|
-
Total: result.unusedExports.total,
|
|
10467
|
-
Used: result.unusedExports.used,
|
|
10468
|
-
Unused: result.unusedExports.unused
|
|
10469
|
-
});
|
|
10470
|
-
}
|
|
10456
|
+
if (result.publicAssets)
|
|
10457
|
+
summary.push({ Category: "Public Assets", Total: result.publicAssets.total, Used: result.publicAssets.used, Unused: result.publicAssets.unused });
|
|
10458
|
+
if (result.unusedFiles)
|
|
10459
|
+
summary.push({ Category: "Source Files", Total: result.unusedFiles.total, Used: result.unusedFiles.used, Unused: result.unusedFiles.unused });
|
|
10460
|
+
if (result.unusedExports)
|
|
10461
|
+
summary.push({ Category: "Exported Items", Total: result.unusedExports.total, Used: result.unusedExports.used, Unused: result.unusedExports.unused });
|
|
10471
10462
|
console.table(summary);
|
|
10472
10463
|
} catch (_err) {
|
|
10473
10464
|
console.error(source_default.red("Error scanning:"), _err);
|
|
@@ -10477,4 +10468,41 @@ program2.action(async (options) => {
|
|
|
10477
10468
|
console.log(source_default.dim(`
|
|
10478
10469
|
⏱️ Completed in ${elapsed}s`));
|
|
10479
10470
|
});
|
|
10471
|
+
async function fixUnusedExports(result, config) {
|
|
10472
|
+
if (!result.unusedExports || result.unusedExports.exports.length === 0)
|
|
10473
|
+
return false;
|
|
10474
|
+
console.log(source_default.yellow.bold(`\uD83D\uDD27 Fixing unused exports/methods...
|
|
10475
|
+
`));
|
|
10476
|
+
const exportsByFile = new Map;
|
|
10477
|
+
for (const exp of result.unusedExports.exports) {
|
|
10478
|
+
if (!exportsByFile.has(exp.file))
|
|
10479
|
+
exportsByFile.set(exp.file, []);
|
|
10480
|
+
exportsByFile.get(exp.file).push(exp);
|
|
10481
|
+
}
|
|
10482
|
+
let fixedCount = 0;
|
|
10483
|
+
let fixedSomething = false;
|
|
10484
|
+
for (const [file, exports] of exportsByFile.entries()) {
|
|
10485
|
+
const sortedExports = exports.sort((a, b) => b.line - a.line);
|
|
10486
|
+
for (const exp of sortedExports) {
|
|
10487
|
+
const fullPath = join8(config.dir, exp.file);
|
|
10488
|
+
if (!existsSync7(fullPath))
|
|
10489
|
+
continue;
|
|
10490
|
+
if (removeExportFromLine(config.dir, exp)) {
|
|
10491
|
+
console.log(source_default.green(` Fixed: ${exp.name} in ${exp.file}`));
|
|
10492
|
+
fixedCount++;
|
|
10493
|
+
fixedSomething = true;
|
|
10494
|
+
const expIdx = result.unusedExports.exports.indexOf(exp);
|
|
10495
|
+
if (expIdx !== -1) {
|
|
10496
|
+
result.unusedExports.exports.splice(expIdx, 1);
|
|
10497
|
+
result.unusedExports.unused--;
|
|
10498
|
+
}
|
|
10499
|
+
}
|
|
10500
|
+
}
|
|
10501
|
+
}
|
|
10502
|
+
if (fixedCount > 0)
|
|
10503
|
+
console.log(source_default.green(`
|
|
10504
|
+
✅ Cleaned up ${fixedCount} unused item(s).
|
|
10505
|
+
`));
|
|
10506
|
+
return fixedSomething;
|
|
10507
|
+
}
|
|
10480
10508
|
program2.parse();
|
|
@@ -22,11 +22,11 @@ var IGNORED_EXPORT_NAMES = new Set([
|
|
|
22
22
|
"POST",
|
|
23
23
|
"PUT",
|
|
24
24
|
"PATCH",
|
|
25
|
-
"DELETE",
|
|
26
|
-
"HEAD",
|
|
27
25
|
"OPTIONS",
|
|
28
26
|
"default"
|
|
29
27
|
]);
|
|
28
|
+
var NEST_LIFECYCLE_METHODS = new Set(["constructor", "onModuleInit", "onApplicationBootstrap", "onModuleDestroy", "beforeApplicationShutdown", "onApplicationShutdown"]);
|
|
29
|
+
var classMethodRegex = /^\s*(?:async\s+)?([a-zA-Z0-9_$]+)\s*\([^)]*\)\s*(?::\s*[^\{]*)?\{/gm;
|
|
30
30
|
var inlineExportRegex = /^export\s+(?:async\s+)?(?:const|let|var|function|type|interface|enum|class)\s+([a-zA-Z0-9_$]+)/gm;
|
|
31
31
|
var blockExportRegex = /^export\s*\{([^}]+)\}/gm;
|
|
32
32
|
if (parentPort && workerData) {
|
|
@@ -40,6 +40,7 @@ if (parentPort && workerData) {
|
|
|
40
40
|
contents.set(file, content);
|
|
41
41
|
const lines = content.split(`
|
|
42
42
|
`);
|
|
43
|
+
const isService = file.endsWith(".service.ts") || file.endsWith(".service.tsx");
|
|
43
44
|
for (let i = 0;i < lines.length; i++) {
|
|
44
45
|
const line = lines[i];
|
|
45
46
|
inlineExportRegex.lastIndex = 0;
|
|
@@ -66,6 +67,20 @@ if (parentPort && workerData) {
|
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
}
|
|
70
|
+
if (isService) {
|
|
71
|
+
classMethodRegex.lastIndex = 0;
|
|
72
|
+
while ((match = classMethodRegex.exec(line)) !== null) {
|
|
73
|
+
const name = match[1];
|
|
74
|
+
if (name && !NEST_LIFECYCLE_METHODS.has(name) && !IGNORED_EXPORT_NAMES.has(name)) {
|
|
75
|
+
const existing = exportMap.get(file)?.find((e) => e.name === name);
|
|
76
|
+
if (!existing) {
|
|
77
|
+
if (!exportMap.has(file))
|
|
78
|
+
exportMap.set(file, []);
|
|
79
|
+
exportMap.get(file).push({ name, line: i + 1, file });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
69
84
|
}
|
|
70
85
|
processedCount++;
|
|
71
86
|
if (processedCount % 10 === 0) {
|