pruny 1.17.0 → 1.18.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.
Files changed (2) hide show
  1. package/dist/index.js +64 -24
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9680,14 +9680,21 @@ function extractNestRoutes(filePath, content, globalPrefix = "api") {
9680
9680
  return routes;
9681
9681
  }
9682
9682
  function shouldIgnore(path2, ignorePatterns) {
9683
- const normalizedPath = path2.replace(/\\/g, "/").replace(/^\.\//, "");
9683
+ const cleanPath = path2.replace(/\\/g, "/").replace(/^\//, "").replace(/^\.\//, "");
9684
9684
  return ignorePatterns.some((pattern) => {
9685
- const normalizedPattern = pattern.replace(/\\/g, "/").replace(/^\.\//, "");
9686
- if (minimatch(normalizedPath, normalizedPattern))
9685
+ let cleanPattern = pattern.replace(/\\/g, "/").replace(/^\.\//, "");
9686
+ const isAbsolute = cleanPattern.startsWith("/");
9687
+ if (isAbsolute)
9688
+ cleanPattern = cleanPattern.substring(1);
9689
+ if (minimatch(cleanPath, cleanPattern))
9687
9690
  return true;
9688
- const folderPattern = normalizedPattern.endsWith("/") ? normalizedPattern : normalizedPattern + "/";
9689
- if (normalizedPath.startsWith(folderPattern))
9691
+ const folderPattern = cleanPattern.endsWith("/") ? cleanPattern : cleanPattern + "/";
9692
+ if (cleanPath.startsWith(folderPattern))
9690
9693
  return true;
9694
+ if (!isAbsolute && !cleanPattern.includes("/") && !cleanPattern.includes("*")) {
9695
+ if (cleanPath.endsWith("/" + cleanPattern) || cleanPath === cleanPattern)
9696
+ return true;
9697
+ }
9691
9698
  return false;
9692
9699
  });
9693
9700
  }
@@ -9808,7 +9815,7 @@ async function scan(config) {
9808
9815
  } catch {}
9809
9816
  }
9810
9817
  for (const route of routes) {
9811
- if (shouldIgnore(route.path, config.ignore.routes)) {
9818
+ if (shouldIgnore(route.path, config.ignore.routes) || shouldIgnore(route.filePath, config.ignore.routes)) {
9812
9819
  route.used = true;
9813
9820
  route.references.push("(ignored by config)");
9814
9821
  route.unusedMethods = [];
@@ -9926,10 +9933,11 @@ function loadConfig(options) {
9926
9933
  const prefixPattern = (p) => {
9927
9934
  if (p.startsWith("**/") || p.startsWith("/") || !relDir)
9928
9935
  return p;
9929
- return join5(relDir, p).replace(/\\/g, "/");
9936
+ const prefixed = join5(relDir, p).replace(/\\/g, "/");
9937
+ return prefixed.includes("/") ? `**/${prefixed}` : `**/${prefixed}`;
9930
9938
  };
9931
9939
  if (config.ignore?.routes)
9932
- mergedIgnore.routes.push(...config.ignore.routes.map(prefixPattern));
9940
+ mergedIgnore.routes.push(...config.ignore.routes);
9933
9941
  if (config.ignore?.folders)
9934
9942
  mergedIgnore.folders.push(...config.ignore.folders.map(prefixPattern));
9935
9943
  if (config.ignore?.files)
@@ -10252,38 +10260,70 @@ program2.action(async (options) => {
10252
10260
  if (unusedRoutes.length > 0) {
10253
10261
  console.log(source_default.yellow.bold(`\uD83D\uDDD1️ Deleting unused routes...
10254
10262
  `));
10255
- for (const route of unusedRoutes) {
10256
- const fullPath = join8(config.dir, route.filePath);
10263
+ const routesByFile = new Map;
10264
+ for (const r of unusedRoutes) {
10265
+ const list = routesByFile.get(r.filePath) || [];
10266
+ list.push(r);
10267
+ routesByFile.set(r.filePath, list);
10268
+ }
10269
+ for (const [filePath, fileRoutes] of routesByFile) {
10270
+ const fullPath = join8(config.dir, filePath);
10271
+ if (!existsSync7(fullPath))
10272
+ continue;
10273
+ const route = fileRoutes[0];
10257
10274
  const routeDir = dirname4(fullPath);
10258
10275
  try {
10259
- if (!existsSync7(fullPath))
10260
- continue;
10261
10276
  if (route.type === "nextjs") {
10262
- if (route.filePath.includes("app/api") || route.filePath.includes("pages/api")) {
10277
+ if (filePath.includes("app/api") || filePath.includes("pages/api")) {
10263
10278
  rmSync(routeDir, { recursive: true, force: true });
10264
10279
  console.log(source_default.red(` Deleted Folder: ${routeDir}`));
10265
10280
  } else {
10266
10281
  rmSync(fullPath, { force: true });
10267
- console.log(source_default.red(` Deleted File: ${route.filePath}`));
10282
+ console.log(source_default.red(` Deleted File: ${filePath}`));
10268
10283
  }
10284
+ fixedSomething = true;
10269
10285
  } else if (route.type === "nestjs") {
10270
- rmSync(fullPath, { force: true });
10271
- console.log(source_default.red(` Deleted File: ${route.filePath}`));
10286
+ const isInternallyUnused = result.unusedFiles?.files.some((f) => f.path === filePath);
10287
+ if (isInternallyUnused || filePath.includes("api/")) {
10288
+ rmSync(fullPath, { force: true });
10289
+ console.log(source_default.red(` Deleted File: ${filePath}`));
10290
+ fixedSomething = true;
10291
+ } else {
10292
+ 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
+ const allMethodsToPrune = [];
10295
+ for (const r of fileRoutes) {
10296
+ for (const m of r.unusedMethods) {
10297
+ if (r.methodLines[m] !== undefined) {
10298
+ allMethodsToPrune.push({ method: m, line: r.methodLines[m] });
10299
+ }
10300
+ }
10301
+ }
10302
+ allMethodsToPrune.sort((a, b) => b.line - a.line);
10303
+ for (const { method, line } of allMethodsToPrune) {
10304
+ if (removeMethodFromRoute(config.dir, filePath, method, line)) {
10305
+ console.log(source_default.green(` Fixed: Removed ${method} from ${filePath}`));
10306
+ fixedSomething = true;
10307
+ }
10308
+ }
10309
+ }
10272
10310
  } else {
10273
10311
  rmSync(fullPath, { force: true });
10274
- console.log(source_default.red(` Deleted File: ${route.filePath}`));
10312
+ console.log(source_default.red(` Deleted File: ${filePath}`));
10313
+ fixedSomething = true;
10275
10314
  }
10276
- fixedSomething = true;
10277
- const idx = result.routes.indexOf(route);
10278
- if (idx !== -1)
10279
- result.routes.splice(idx, 1);
10280
- } catch (_err) {
10281
- console.log(source_default.yellow(` Failed to delete: ${route.filePath}`));
10315
+ for (const r of fileRoutes) {
10316
+ const idx = result.routes.indexOf(r);
10317
+ if (idx !== -1)
10318
+ result.routes.splice(idx, 1);
10319
+ }
10320
+ } catch (err) {
10321
+ console.log(source_default.yellow(` Failed to fix: ${filePath}`));
10282
10322
  }
10283
10323
  }
10284
10324
  console.log("");
10285
10325
  }
10286
- const partiallyRoutes = result.routes.filter((r) => r.used && r.unusedMethods.length > 0);
10326
+ const partiallyRoutes = result.routes.filter((r) => r.used && r.unusedMethods && r.unusedMethods.length > 0);
10287
10327
  if (partiallyRoutes.length > 0) {
10288
10328
  console.log(source_default.yellow.bold(`\uD83D\uDD27 Fixing partially unused routes...
10289
10329
  `));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pruny",
3
- "version": "1.17.0",
3
+ "version": "1.18.0",
4
4
  "description": "Find and remove unused Next.js API routes & Nest.js Controllers",
5
5
  "type": "module",
6
6
  "files": [