pruny 1.24.0 → 1.26.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 +97 -19
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7635,8 +7635,8 @@ import { rmSync, existsSync as existsSync7, readdirSync, lstatSync } from "node:
7635
7635
  import { dirname as dirname4, join as join8 } from "node:path";
7636
7636
 
7637
7637
  // src/scanner.ts
7638
- var import_fast_glob4 = __toESM(require_out4(), 1);
7639
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
7638
+ var import_fast_glob5 = __toESM(require_out4(), 1);
7639
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "node:fs";
7640
7640
  import { join as join4 } from "node:path";
7641
7641
 
7642
7642
  // src/patterns.ts
@@ -9676,6 +9676,47 @@ async function scanUnusedExports(config) {
9676
9676
  };
9677
9677
  }
9678
9678
 
9679
+ // src/scanners/http-usage.ts
9680
+ var import_fast_glob4 = __toESM(require_out4(), 1);
9681
+ import { readFileSync as readFileSync4 } from "node:fs";
9682
+ async function scanHttpUsage(config) {
9683
+ const searchDir = config.appSpecificScan ? config.appSpecificScan.appDir : config.dir;
9684
+ const extensions = config.extensions;
9685
+ const extGlob = `**/*{${extensions.join(",")}}`;
9686
+ console.log(` \uD83D\uDD0D Tracking HTTP usage in: ${searchDir}`);
9687
+ const files = await import_fast_glob4.default(extGlob, {
9688
+ cwd: searchDir,
9689
+ ignore: [...config.ignore.folders],
9690
+ absolute: true
9691
+ });
9692
+ let axiosCount = 0;
9693
+ let fetchCount = 0;
9694
+ let gotCount = 0;
9695
+ let kyCount = 0;
9696
+ const axiosRegex = /\baxios(\.|[\s]*\()/g;
9697
+ const fetchRegex = /\bfetch[\s]*\(/g;
9698
+ const gotRegex = /\bgot(\.|[\s]*\()/g;
9699
+ const kyRegex = /\bky(\.|[\s]*\()/g;
9700
+ for (const file of files) {
9701
+ try {
9702
+ const content = readFileSync4(file, "utf-8");
9703
+ const axiosMatches = content.match(axiosRegex);
9704
+ if (axiosMatches)
9705
+ axiosCount += axiosMatches.length;
9706
+ const fetchMatches = content.match(fetchRegex);
9707
+ if (fetchMatches)
9708
+ fetchCount += fetchMatches.length;
9709
+ const gotMatches = content.match(gotRegex);
9710
+ if (gotMatches)
9711
+ gotCount += gotMatches.length;
9712
+ const kyMatches = content.match(kyRegex);
9713
+ if (kyMatches)
9714
+ kyCount += kyMatches.length;
9715
+ } catch (err) {}
9716
+ }
9717
+ return { axios: axiosCount, fetch: fetchCount, got: gotCount, ky: kyCount };
9718
+ }
9719
+
9679
9720
  // src/scanner.ts
9680
9721
  function extractRoutePath(filePath) {
9681
9722
  let path2 = filePath.replace(/^src\//, "").replace(/^apps\/[^/]+\//, "").replace(/^packages\/[^/]+\//, "");
@@ -9809,7 +9850,7 @@ function getVercelCronPaths(dir) {
9809
9850
  return [];
9810
9851
  }
9811
9852
  try {
9812
- const content = readFileSync4(vercelPath, "utf-8");
9853
+ const content = readFileSync5(vercelPath, "utf-8");
9813
9854
  const config = JSON.parse(content);
9814
9855
  if (!config.crons) {
9815
9856
  return [];
@@ -9839,13 +9880,13 @@ async function scan(config) {
9839
9880
  if (config.extraRoutePatterns) {
9840
9881
  activeNextPatterns.push(...config.extraRoutePatterns);
9841
9882
  }
9842
- const nextFiles = await import_fast_glob4.default(activeNextPatterns, {
9883
+ const nextFiles = await import_fast_glob5.default(activeNextPatterns, {
9843
9884
  cwd: scanCwd,
9844
9885
  ignore: config.ignore.folders
9845
9886
  });
9846
9887
  const nextRoutes = nextFiles.map((file) => {
9847
9888
  const fullPath = join4(scanCwd, file);
9848
- const content = readFileSync4(fullPath, "utf-8");
9889
+ const content = readFileSync5(fullPath, "utf-8");
9849
9890
  const { methods, methodLines } = extractExportedMethods(content);
9850
9891
  return {
9851
9892
  type: "nextjs",
@@ -9859,13 +9900,13 @@ async function scan(config) {
9859
9900
  };
9860
9901
  });
9861
9902
  const nestPatterns = ["**/*.controller.ts"];
9862
- const nestFiles = await import_fast_glob4.default(nestPatterns, {
9903
+ const nestFiles = await import_fast_glob5.default(nestPatterns, {
9863
9904
  cwd: scanCwd,
9864
9905
  ignore: config.ignore.folders
9865
9906
  });
9866
9907
  const nestRoutes = nestFiles.flatMap((file) => {
9867
9908
  const fullPath = join4(scanCwd, file);
9868
- const content = readFileSync4(fullPath, "utf-8");
9909
+ const content = readFileSync5(fullPath, "utf-8");
9869
9910
  const relativePathFromRoot = fullPath.replace(config.appSpecificScan ? config.appSpecificScan.rootDir + "/" : cwd + "/", "");
9870
9911
  return extractNestRoutes(relativePathFromRoot, content, config.nestGlobalPrefix);
9871
9912
  });
@@ -9881,7 +9922,7 @@ async function scan(config) {
9881
9922
  }
9882
9923
  const referenceScanCwd = config.appSpecificScan ? config.appSpecificScan.rootDir : cwd;
9883
9924
  const extGlob = `**/*{${config.extensions.join(",")}}`;
9884
- const sourceFiles = await import_fast_glob4.default(extGlob, {
9925
+ const sourceFiles = await import_fast_glob5.default(extGlob, {
9885
9926
  cwd: referenceScanCwd,
9886
9927
  ignore: [...config.ignore.folders, ...config.ignore.files]
9887
9928
  });
@@ -9890,7 +9931,7 @@ async function scan(config) {
9890
9931
  for (const file of sourceFiles) {
9891
9932
  const filePath = join4(referenceScanCwd, file);
9892
9933
  try {
9893
- const content = readFileSync4(filePath, "utf-8");
9934
+ const content = readFileSync5(filePath, "utf-8");
9894
9935
  const refs = extractApiReferences(content);
9895
9936
  if (refs.length > 0) {
9896
9937
  fileReferences.set(file, refs);
@@ -9932,13 +9973,14 @@ async function scan(config) {
9932
9973
  routes,
9933
9974
  publicAssets,
9934
9975
  unusedFiles,
9935
- unusedExports: await scanUnusedExports(config)
9976
+ unusedExports: await scanUnusedExports(config),
9977
+ httpUsage: await scanHttpUsage(config)
9936
9978
  };
9937
9979
  }
9938
9980
 
9939
9981
  // src/config.ts
9940
- var import_fast_glob5 = __toESM(require_out4(), 1);
9941
- import { existsSync as existsSync4, readFileSync as readFileSync5 } from "node:fs";
9982
+ var import_fast_glob6 = __toESM(require_out4(), 1);
9983
+ import { existsSync as existsSync4, readFileSync as readFileSync6 } from "node:fs";
9942
9984
  import { join as join5, resolve as resolve2, relative as relative3, dirname as dirname3 } from "node:path";
9943
9985
  var DEFAULT_CONFIG = {
9944
9986
  dir: "./",
@@ -9984,7 +10026,7 @@ var DEFAULT_CONFIG = {
9984
10026
  };
9985
10027
  function loadConfig(options) {
9986
10028
  const cwd = options.dir || "./";
9987
- const configFiles = import_fast_glob5.default.sync(["**/pruny.config.json", "**/.prunyrc.json", "**/.prunyrc"], {
10029
+ const configFiles = import_fast_glob6.default.sync(["**/pruny.config.json", "**/.prunyrc.json", "**/.prunyrc"], {
9988
10030
  cwd,
9989
10031
  ignore: DEFAULT_CONFIG.ignore.folders,
9990
10032
  absolute: true
@@ -10010,7 +10052,7 @@ function loadConfig(options) {
10010
10052
  let excludePublic = options.excludePublic ?? false;
10011
10053
  for (const configPath of configFiles) {
10012
10054
  try {
10013
- const content = readFileSync5(configPath, "utf-8");
10055
+ const content = readFileSync6(configPath, "utf-8");
10014
10056
  const config = JSON.parse(content);
10015
10057
  const configDir = dirname3(configPath);
10016
10058
  const relDir = relative3(cwd, configDir);
@@ -10057,7 +10099,7 @@ function parseGitIgnore(dir) {
10057
10099
  if (!existsSync4(gitIgnorePath))
10058
10100
  return [];
10059
10101
  try {
10060
- const content = readFileSync5(gitIgnorePath, "utf-8");
10102
+ const content = readFileSync6(gitIgnorePath, "utf-8");
10061
10103
  return content.split(`
10062
10104
  `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
10063
10105
  } catch {
@@ -10076,14 +10118,14 @@ function findConfigFile(dir) {
10076
10118
  }
10077
10119
 
10078
10120
  // src/fixer.ts
10079
- import { readFileSync as readFileSync6, writeFileSync, unlinkSync, existsSync as existsSync5 } from "node:fs";
10121
+ import { readFileSync as readFileSync7, writeFileSync, unlinkSync, existsSync as existsSync5 } from "node:fs";
10080
10122
  import { join as join6 } from "node:path";
10081
10123
  function removeExportFromLine(rootDir, exp) {
10082
10124
  const fullPath = join6(rootDir, exp.file);
10083
10125
  if (!existsSync5(fullPath))
10084
10126
  return false;
10085
10127
  try {
10086
- const content = readFileSync6(fullPath, "utf-8");
10128
+ const content = readFileSync7(fullPath, "utf-8");
10087
10129
  const lines = content.split(`
10088
10130
  `);
10089
10131
  const lineIndex = findDeclarationIndex(lines, exp.name, exp.line - 1);
@@ -10247,7 +10289,7 @@ function removeMethodFromRoute(rootDir, filePath, methodName, lineNum) {
10247
10289
  if (!existsSync5(fullPath))
10248
10290
  return false;
10249
10291
  try {
10250
- const content = readFileSync6(fullPath, "utf-8");
10292
+ const content = readFileSync7(fullPath, "utf-8");
10251
10293
  const lines = content.split(`
10252
10294
  `);
10253
10295
  const targetIndex = findDeclarationIndex(lines, methodName, lineNum - 1);
@@ -10291,7 +10333,7 @@ function init(cwd = process.cwd()) {
10291
10333
 
10292
10334
  // src/index.ts
10293
10335
  var program2 = new Command;
10294
- program2.name("pruny").description("Find and remove unused Next.js API routes").version("1.0.0").option("-d, --dir <path>", "Target directory to scan", "./").option("--fix", "Delete unused API routes").option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--no-public", "Disable public assets scanning").option("-v, --verbose", "Show detailed info").option("-f, --filter <pattern>", "Filter results by file path or app name");
10336
+ program2.name("pruny").description("Find and remove unused Next.js API routes").version("1.0.0").option("-d, --dir <path>", "Target directory to scan", "./").option("--fix", "Delete unused API routes").option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--no-public", "Disable public assets scanning").option("-v, --verbose", "Show detailed info").option("-f, --filter <pattern>", "Filter results by file path or app name").option("--ignore-apps <apps>", "Comma-separated list of apps to ignore");
10295
10337
  program2.command("init").description("Create a default pruny.config.json file").action(() => {
10296
10338
  init();
10297
10339
  });
@@ -10313,9 +10355,12 @@ program2.action(async (options) => {
10313
10355
  const appsDir = join8(absoluteDir, "apps");
10314
10356
  const isMonorepo = existsSync7(appsDir) && lstatSync(appsDir).isDirectory();
10315
10357
  const appsToScan = [];
10358
+ const ignoredApps = options.ignoreApps ? options.ignoreApps.split(",").map((a) => a.trim()) : [];
10316
10359
  if (isMonorepo) {
10317
10360
  const apps = readdirSync(appsDir);
10318
10361
  for (const app of apps) {
10362
+ if (ignoredApps.includes(app))
10363
+ continue;
10319
10364
  const appPath = join8(appsDir, app);
10320
10365
  if (lstatSync(appPath).isDirectory()) {
10321
10366
  appsToScan.push(app);
@@ -10324,6 +10369,10 @@ program2.action(async (options) => {
10324
10369
  console.log(source_default.bold(`
10325
10370
  \uD83C\uDFE2 Monorepo Detected. Found ${appsToScan.length} apps: ${appsToScan.join(", ")}
10326
10371
  `));
10372
+ if (ignoredApps.length > 0) {
10373
+ console.log(source_default.dim(` (Ignored: ${ignoredApps.join(", ")})
10374
+ `));
10375
+ }
10327
10376
  } else {
10328
10377
  appsToScan.push("root");
10329
10378
  }
@@ -10379,6 +10428,9 @@ function logScanStats(result, context) {
10379
10428
  if (result.unusedExports) {
10380
10429
  console.log(source_default.blue(` • Exported Items: ${result.unusedExports.total}`));
10381
10430
  }
10431
+ if (result.httpUsage) {
10432
+ console.log(source_default.cyan(` • HTTP Clients: Axios: ${result.httpUsage.axios}, Fetch: ${result.httpUsage.fetch}, Got: ${result.httpUsage.got}, Ky: ${result.httpUsage.ky}`));
10433
+ }
10382
10434
  console.log("");
10383
10435
  }
10384
10436
  function filterResults(result, filterPattern) {
@@ -10670,5 +10722,31 @@ function printSummaryTable(result, context) {
10670
10722
  summary.push({ Category: "Source Files", Total: result.unusedFiles.total, Used: result.unusedFiles.used, Unused: result.unusedFiles.unused });
10671
10723
  if (result.unusedExports)
10672
10724
  summary.push({ Category: "Exported Items", Total: result.unusedExports.total, Used: result.unusedExports.used, Unused: result.unusedExports.unused });
10725
+ if (result.httpUsage) {
10726
+ summary.push({
10727
+ Category: "Axios Calls",
10728
+ Total: result.httpUsage.axios,
10729
+ Used: result.httpUsage.axios,
10730
+ Unused: 0
10731
+ });
10732
+ summary.push({
10733
+ Category: "Fetch Calls",
10734
+ Total: result.httpUsage.fetch,
10735
+ Used: result.httpUsage.fetch,
10736
+ Unused: 0
10737
+ });
10738
+ summary.push({
10739
+ Category: "Got Calls",
10740
+ Total: result.httpUsage.got,
10741
+ Used: result.httpUsage.got,
10742
+ Unused: 0
10743
+ });
10744
+ summary.push({
10745
+ Category: "Ky Calls",
10746
+ Total: result.httpUsage.ky,
10747
+ Used: result.httpUsage.ky,
10748
+ Unused: 0
10749
+ });
10750
+ }
10673
10751
  console.table(summary);
10674
10752
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pruny",
3
- "version": "1.24.0",
3
+ "version": "1.26.0",
4
4
  "description": "Find and remove unused Next.js API routes & Nest.js Controllers",
5
5
  "type": "module",
6
6
  "files": [