pruny 1.13.0 → 1.15.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 +117 -88
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9621,13 +9621,22 @@ function extractRoutePath(filePath) {
|
|
|
9621
9621
|
}
|
|
9622
9622
|
function extractExportedMethods(content) {
|
|
9623
9623
|
const methods = [];
|
|
9624
|
+
const methodLines = {};
|
|
9625
|
+
const lines = content.split(`
|
|
9626
|
+
`);
|
|
9624
9627
|
let match2;
|
|
9628
|
+
EXPORTED_METHOD_PATTERN.lastIndex = 0;
|
|
9625
9629
|
while ((match2 = EXPORTED_METHOD_PATTERN.exec(content)) !== null) {
|
|
9626
9630
|
if (match2[1]) {
|
|
9627
|
-
|
|
9631
|
+
const methodName = match2[1];
|
|
9632
|
+
methods.push(methodName);
|
|
9633
|
+
const pos = match2.index;
|
|
9634
|
+
const lineNum = content.substring(0, pos).split(`
|
|
9635
|
+
`).length;
|
|
9636
|
+
methodLines[methodName] = lineNum;
|
|
9628
9637
|
}
|
|
9629
9638
|
}
|
|
9630
|
-
return methods;
|
|
9639
|
+
return { methods, methodLines };
|
|
9631
9640
|
}
|
|
9632
9641
|
function extractNestRoutes(filePath, content, globalPrefix = "api") {
|
|
9633
9642
|
const controllerMatch = content.match(NEST_CONTROLLER_PATTERN);
|
|
@@ -9640,12 +9649,16 @@ function extractNestRoutes(filePath, content, globalPrefix = "api") {
|
|
|
9640
9649
|
while ((methodMatch = NEST_METHOD_PATTERN.exec(content)) !== null) {
|
|
9641
9650
|
const methodType = methodMatch[1].toUpperCase();
|
|
9642
9651
|
const methodPath = methodMatch[2] || "";
|
|
9652
|
+
const pos = methodMatch.index;
|
|
9653
|
+
const lineNum = content.substring(0, pos).split(`
|
|
9654
|
+
`).length;
|
|
9643
9655
|
const fullPath = `/${globalPrefix}/${controllerPath}/${methodPath}`.replace(/\/+/g, "/").replace(/\/$/, "");
|
|
9644
9656
|
const existing = routes.find((r) => r.path === fullPath);
|
|
9645
9657
|
if (existing) {
|
|
9646
9658
|
if (!existing.methods.includes(methodType)) {
|
|
9647
9659
|
existing.methods.push(methodType);
|
|
9648
9660
|
existing.unusedMethods.push(methodType);
|
|
9661
|
+
existing.methodLines[methodType] = lineNum;
|
|
9649
9662
|
}
|
|
9650
9663
|
} else {
|
|
9651
9664
|
routes.push({
|
|
@@ -9655,7 +9668,8 @@ function extractNestRoutes(filePath, content, globalPrefix = "api") {
|
|
|
9655
9668
|
used: false,
|
|
9656
9669
|
references: [],
|
|
9657
9670
|
methods: [methodType],
|
|
9658
|
-
unusedMethods: [methodType]
|
|
9671
|
+
unusedMethods: [methodType],
|
|
9672
|
+
methodLines: { [methodType]: lineNum }
|
|
9659
9673
|
});
|
|
9660
9674
|
}
|
|
9661
9675
|
}
|
|
@@ -9733,7 +9747,7 @@ async function scan(config) {
|
|
|
9733
9747
|
});
|
|
9734
9748
|
const nextRoutes = nextFiles.map((file) => {
|
|
9735
9749
|
const content = readFileSync4(join4(cwd, file), "utf-8");
|
|
9736
|
-
const methods = extractExportedMethods(content);
|
|
9750
|
+
const { methods, methodLines } = extractExportedMethods(content);
|
|
9737
9751
|
return {
|
|
9738
9752
|
type: "nextjs",
|
|
9739
9753
|
path: extractRoutePath(file),
|
|
@@ -9741,7 +9755,8 @@ async function scan(config) {
|
|
|
9741
9755
|
used: false,
|
|
9742
9756
|
references: [],
|
|
9743
9757
|
methods,
|
|
9744
|
-
unusedMethods: [...methods]
|
|
9758
|
+
unusedMethods: [...methods],
|
|
9759
|
+
methodLines
|
|
9745
9760
|
};
|
|
9746
9761
|
});
|
|
9747
9762
|
const nestPatterns = ["**/*.controller.ts"];
|
|
@@ -9953,7 +9968,7 @@ function findConfigFile(dir) {
|
|
|
9953
9968
|
}
|
|
9954
9969
|
|
|
9955
9970
|
// src/fixer.ts
|
|
9956
|
-
import { readFileSync as readFileSync6, writeFileSync, unlinkSync } from "node:fs";
|
|
9971
|
+
import { readFileSync as readFileSync6, writeFileSync, unlinkSync, existsSync as existsSync5 } from "node:fs";
|
|
9957
9972
|
import { join as join6 } from "node:path";
|
|
9958
9973
|
function removeExportFromLine(rootDir, exp) {
|
|
9959
9974
|
const fullPath = join6(rootDir, exp.file);
|
|
@@ -10039,6 +10054,32 @@ function isFileEmpty(content) {
|
|
|
10039
10054
|
}
|
|
10040
10055
|
return true;
|
|
10041
10056
|
}
|
|
10057
|
+
function removeMethodFromRoute(rootDir, filePath, methodName, lineNum) {
|
|
10058
|
+
const fullPath = join6(rootDir, filePath);
|
|
10059
|
+
if (!existsSync5(fullPath))
|
|
10060
|
+
return false;
|
|
10061
|
+
try {
|
|
10062
|
+
const content = readFileSync6(fullPath, "utf-8");
|
|
10063
|
+
const lines = content.split(`
|
|
10064
|
+
`);
|
|
10065
|
+
const lineIndex = lineNum - 1;
|
|
10066
|
+
const deletedLines = deleteDeclaration(lines, lineIndex);
|
|
10067
|
+
if (deletedLines > 0) {
|
|
10068
|
+
const newContent = lines.join(`
|
|
10069
|
+
`);
|
|
10070
|
+
if (isFileEmpty(newContent)) {
|
|
10071
|
+
unlinkSync(fullPath);
|
|
10072
|
+
} else {
|
|
10073
|
+
writeFileSync(fullPath, newContent, "utf-8");
|
|
10074
|
+
}
|
|
10075
|
+
return true;
|
|
10076
|
+
}
|
|
10077
|
+
return false;
|
|
10078
|
+
} catch (err) {
|
|
10079
|
+
console.error(`Error removing method ${methodName} in ${filePath}:`, err);
|
|
10080
|
+
return false;
|
|
10081
|
+
}
|
|
10082
|
+
}
|
|
10042
10083
|
|
|
10043
10084
|
// src/init.ts
|
|
10044
10085
|
import { writeFileSync as writeFileSync2, existsSync as existsSync6 } from "node:fs";
|
|
@@ -10081,21 +10122,21 @@ program2.action(async (options) => {
|
|
|
10081
10122
|
`));
|
|
10082
10123
|
try {
|
|
10083
10124
|
let result = await scan(config);
|
|
10125
|
+
const getAppName = (filePath) => {
|
|
10126
|
+
if (filePath.startsWith("apps/"))
|
|
10127
|
+
return filePath.split("/").slice(0, 2).join("/");
|
|
10128
|
+
if (filePath.startsWith("packages/"))
|
|
10129
|
+
return filePath.split("/").slice(0, 2).join("/");
|
|
10130
|
+
return "Root";
|
|
10131
|
+
};
|
|
10084
10132
|
if (options.filter) {
|
|
10085
10133
|
const filter2 = options.filter.toLowerCase();
|
|
10086
10134
|
console.log(source_default.blue(`
|
|
10087
10135
|
\uD83D\uDD0D Filtering results by "${filter2}"...
|
|
10088
10136
|
`));
|
|
10089
|
-
const getAppName2 = (filePath) => {
|
|
10090
|
-
if (filePath.startsWith("apps/"))
|
|
10091
|
-
return filePath.split("/").slice(0, 2).join("/");
|
|
10092
|
-
if (filePath.startsWith("packages/"))
|
|
10093
|
-
return filePath.split("/").slice(0, 2).join("/");
|
|
10094
|
-
return "Root";
|
|
10095
|
-
};
|
|
10096
10137
|
const matchesFilter = (path2) => {
|
|
10097
10138
|
const lowerPath = path2.toLowerCase();
|
|
10098
|
-
const appName =
|
|
10139
|
+
const appName = getAppName(path2).toLowerCase();
|
|
10099
10140
|
if (appName.includes(filter2))
|
|
10100
10141
|
return true;
|
|
10101
10142
|
const segments = lowerPath.split("/");
|
|
@@ -10186,24 +10227,76 @@ program2.action(async (options) => {
|
|
|
10186
10227
|
}
|
|
10187
10228
|
if (unusedRoutes.length === 0 && partiallyUnusedRoutes.length === 0 && (!result.publicAssets || result.publicAssets.unused === 0)) {
|
|
10188
10229
|
console.log(source_default.green(`✅ Everything is used! Clean as a whistle.
|
|
10230
|
+
`));
|
|
10231
|
+
}
|
|
10232
|
+
if (options.fix) {
|
|
10233
|
+
if (unusedRoutes.length > 0) {
|
|
10234
|
+
console.log(source_default.yellow.bold(`\uD83D\uDDD1️ Deleting unused routes...
|
|
10235
|
+
`));
|
|
10236
|
+
for (const route of unusedRoutes) {
|
|
10237
|
+
const routeDir = dirname3(join8(config.dir, route.filePath));
|
|
10238
|
+
try {
|
|
10239
|
+
rmSync(routeDir, { recursive: true, force: true });
|
|
10240
|
+
console.log(source_default.red(` Deleted: ${route.filePath}`));
|
|
10241
|
+
} catch (_err) {
|
|
10242
|
+
console.log(source_default.yellow(` Failed to delete: ${route.filePath}`));
|
|
10243
|
+
}
|
|
10244
|
+
}
|
|
10245
|
+
}
|
|
10246
|
+
const partiallyRoutes = result.routes.filter((r) => r.used && r.unusedMethods.length > 0);
|
|
10247
|
+
if (partiallyRoutes.length > 0) {
|
|
10248
|
+
console.log(source_default.yellow.bold(`\uD83D\uDD27 Fixing partially unused routes...
|
|
10249
|
+
`));
|
|
10250
|
+
for (const route of partiallyRoutes) {
|
|
10251
|
+
const sortedMethods = [...route.unusedMethods].filter((m) => route.methodLines[m] !== undefined).sort((a, b) => route.methodLines[b] - route.methodLines[a]);
|
|
10252
|
+
for (const method of sortedMethods) {
|
|
10253
|
+
const lineNum = route.methodLines[method];
|
|
10254
|
+
if (removeMethodFromRoute(config.dir, route.filePath, method, lineNum)) {
|
|
10255
|
+
console.log(source_default.green(` Fixed: Removed ${method} from ${route.path}`));
|
|
10256
|
+
}
|
|
10257
|
+
}
|
|
10258
|
+
}
|
|
10259
|
+
console.log("");
|
|
10260
|
+
}
|
|
10261
|
+
if (result.unusedExports && result.unusedExports.exports.length > 0) {
|
|
10262
|
+
console.log(source_default.yellow.bold(`\uD83D\uDD27 Fixing unused exports (removing "export" keyword)...
|
|
10263
|
+
`));
|
|
10264
|
+
const exportsByFile = new Map;
|
|
10265
|
+
for (const exp of result.unusedExports.exports) {
|
|
10266
|
+
if (!exportsByFile.has(exp.file)) {
|
|
10267
|
+
exportsByFile.set(exp.file, []);
|
|
10268
|
+
}
|
|
10269
|
+
exportsByFile.get(exp.file).push(exp);
|
|
10270
|
+
}
|
|
10271
|
+
let fixedCount = 0;
|
|
10272
|
+
for (const [file, exports] of exportsByFile.entries()) {
|
|
10273
|
+
const sortedExports = exports.sort((a, b) => b.line - a.line);
|
|
10274
|
+
for (const exp of sortedExports) {
|
|
10275
|
+
if (removeExportFromLine(config.dir, exp)) {
|
|
10276
|
+
console.log(source_default.green(` Fixed: ${exp.name} in ${exp.file}`));
|
|
10277
|
+
fixedCount++;
|
|
10278
|
+
}
|
|
10279
|
+
}
|
|
10280
|
+
}
|
|
10281
|
+
if (fixedCount > 0) {
|
|
10282
|
+
console.log(source_default.green(`
|
|
10283
|
+
✅ Removed "export" from ${fixedCount} item(s).
|
|
10284
|
+
`));
|
|
10285
|
+
}
|
|
10286
|
+
}
|
|
10287
|
+
} else if (unusedRoutes.length > 0 || result.unusedExports && result.unusedExports.exports.length > 0) {
|
|
10288
|
+
console.log(source_default.dim(`\uD83D\uDCA1 Run with --fix to automatically clean up unused routes and exports.
|
|
10189
10289
|
`));
|
|
10190
10290
|
}
|
|
10191
10291
|
console.log(source_default.bold(`\uD83D\uDCCA Summary Report
|
|
10192
10292
|
`));
|
|
10193
10293
|
const summary = [];
|
|
10194
|
-
const getAppName = (filePath) => {
|
|
10195
|
-
if (filePath.startsWith("apps/"))
|
|
10196
|
-
return filePath.split("/").slice(0, 2).join("/");
|
|
10197
|
-
if (filePath.startsWith("packages/"))
|
|
10198
|
-
return filePath.split("/").slice(0, 2).join("/");
|
|
10199
|
-
return "Root";
|
|
10200
|
-
};
|
|
10201
10294
|
const groupedRoutes = new Map;
|
|
10202
10295
|
for (const route of result.routes) {
|
|
10203
|
-
const
|
|
10204
|
-
const key = `${
|
|
10296
|
+
const keyAppName = getAppName(route.filePath);
|
|
10297
|
+
const key = `${keyAppName}::${route.type}`;
|
|
10205
10298
|
if (!groupedRoutes.has(key)) {
|
|
10206
|
-
groupedRoutes.set(key, { type: route.type, app:
|
|
10299
|
+
groupedRoutes.set(key, { type: route.type, app: keyAppName, routes: [] });
|
|
10207
10300
|
}
|
|
10208
10301
|
groupedRoutes.get(key).routes.push(route);
|
|
10209
10302
|
}
|
|
@@ -10253,70 +10346,6 @@ program2.action(async (options) => {
|
|
|
10253
10346
|
});
|
|
10254
10347
|
}
|
|
10255
10348
|
console.table(summary);
|
|
10256
|
-
console.log("");
|
|
10257
|
-
if (options.verbose) {
|
|
10258
|
-
const used = result.routes.filter((r) => r.used);
|
|
10259
|
-
if (used.length > 0) {
|
|
10260
|
-
console.log(source_default.green.bold(`✅ Used routes (References):
|
|
10261
|
-
`));
|
|
10262
|
-
for (const route of used) {
|
|
10263
|
-
console.log(source_default.green(` ${route.path}`));
|
|
10264
|
-
if (route.references.length > 0) {
|
|
10265
|
-
for (const ref of route.references.slice(0, 3)) {
|
|
10266
|
-
console.log(source_default.dim(` ← ${ref}`));
|
|
10267
|
-
}
|
|
10268
|
-
if (route.references.length > 3) {
|
|
10269
|
-
console.log(source_default.dim(` ... and ${route.references.length - 3} more`));
|
|
10270
|
-
}
|
|
10271
|
-
}
|
|
10272
|
-
}
|
|
10273
|
-
console.log("");
|
|
10274
|
-
}
|
|
10275
|
-
}
|
|
10276
|
-
if (options.fix) {
|
|
10277
|
-
if (unusedRoutes.length > 0) {
|
|
10278
|
-
console.log(source_default.yellow.bold(`\uD83D\uDDD1️ Deleting unused routes...
|
|
10279
|
-
`));
|
|
10280
|
-
for (const route of unusedRoutes) {
|
|
10281
|
-
const routeDir = dirname3(join8(config.dir, route.filePath));
|
|
10282
|
-
try {
|
|
10283
|
-
rmSync(routeDir, { recursive: true, force: true });
|
|
10284
|
-
console.log(source_default.red(` Deleted: ${route.filePath}`));
|
|
10285
|
-
} catch (_err) {
|
|
10286
|
-
console.log(source_default.yellow(` Failed to delete: ${route.filePath}`));
|
|
10287
|
-
}
|
|
10288
|
-
}
|
|
10289
|
-
}
|
|
10290
|
-
if (result.unusedExports && result.unusedExports.exports.length > 0) {
|
|
10291
|
-
console.log(source_default.yellow.bold(`\uD83D\uDD27 Fixing unused exports (removing "export" keyword)...
|
|
10292
|
-
`));
|
|
10293
|
-
const exportsByFile = new Map;
|
|
10294
|
-
for (const exp of result.unusedExports.exports) {
|
|
10295
|
-
if (!exportsByFile.has(exp.file)) {
|
|
10296
|
-
exportsByFile.set(exp.file, []);
|
|
10297
|
-
}
|
|
10298
|
-
exportsByFile.get(exp.file).push(exp);
|
|
10299
|
-
}
|
|
10300
|
-
let fixedCount = 0;
|
|
10301
|
-
for (const [file, exports] of exportsByFile.entries()) {
|
|
10302
|
-
const sortedExports = exports.sort((a, b) => b.line - a.line);
|
|
10303
|
-
for (const exp of sortedExports) {
|
|
10304
|
-
if (removeExportFromLine(config.dir, exp)) {
|
|
10305
|
-
console.log(source_default.green(` Fixed: ${exp.name} in ${exp.file}`));
|
|
10306
|
-
fixedCount++;
|
|
10307
|
-
}
|
|
10308
|
-
}
|
|
10309
|
-
}
|
|
10310
|
-
if (fixedCount > 0) {
|
|
10311
|
-
console.log(source_default.green(`
|
|
10312
|
-
✅ Removed "export" from ${fixedCount} item(s).
|
|
10313
|
-
`));
|
|
10314
|
-
}
|
|
10315
|
-
}
|
|
10316
|
-
} else if (unusedRoutes.length > 0 || result.unusedExports && result.unusedExports.exports.length > 0) {
|
|
10317
|
-
console.log(source_default.dim(`\uD83D\uDCA1 Run with --fix to automatically clean up unused routes and exports.
|
|
10318
|
-
`));
|
|
10319
|
-
}
|
|
10320
10349
|
} catch (_err) {
|
|
10321
10350
|
console.error(source_default.red("Error scanning:"), _err);
|
|
10322
10351
|
process.exit(1);
|