pruny 1.43.5 → 1.43.6
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 +60 -56
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12563,13 +12563,13 @@ var source_default = chalk;
|
|
|
12563
12563
|
|
|
12564
12564
|
// src/index.ts
|
|
12565
12565
|
var import_prompts = __toESM(require_prompts3(), 1);
|
|
12566
|
-
import { rmSync, existsSync as
|
|
12567
|
-
import { dirname as dirname6, join as
|
|
12566
|
+
import { rmSync, existsSync as existsSync11, readdirSync, lstatSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
12567
|
+
import { dirname as dirname6, join as join11, relative as relative5, resolve as resolve4 } from "node:path";
|
|
12568
12568
|
|
|
12569
12569
|
// src/scanner.ts
|
|
12570
12570
|
var import_fast_glob10 = __toESM(require_out4(), 1);
|
|
12571
|
-
import { existsSync as
|
|
12572
|
-
import { join as
|
|
12571
|
+
import { existsSync as existsSync8, readFileSync as readFileSync10 } from "node:fs";
|
|
12572
|
+
import { join as join8 } from "node:path";
|
|
12573
12573
|
|
|
12574
12574
|
// src/patterns.ts
|
|
12575
12575
|
var EXPORTED_METHOD_PATTERN = /export\s+(?:async\s+)?(?:function|const)\s+(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)/g;
|
|
@@ -15873,7 +15873,8 @@ async function scanUnusedServices(config) {
|
|
|
15873
15873
|
|
|
15874
15874
|
// src/scanners/broken-links.ts
|
|
15875
15875
|
var import_fast_glob9 = __toESM(require_out4(), 1);
|
|
15876
|
-
import { readFileSync as readFileSync9 } from "node:fs";
|
|
15876
|
+
import { readFileSync as readFileSync9, existsSync as existsSync7 } from "node:fs";
|
|
15877
|
+
import { join as join7 } from "node:path";
|
|
15877
15878
|
var LINK_PATTERNS = [
|
|
15878
15879
|
/<Link\s+[^>]*href\s*=\s*['"`](\/[^'"`\s{}$]+)['"`]/g,
|
|
15879
15880
|
/router\.(push|replace)\s*\(\s*['"`](\/[^'"`\s{}$]+)['"`]/g,
|
|
@@ -16010,13 +16011,13 @@ async function scanBrokenLinks(config) {
|
|
|
16010
16011
|
});
|
|
16011
16012
|
if (config.appSpecificScan) {
|
|
16012
16013
|
const { readdirSync, lstatSync } = await import("node:fs");
|
|
16013
|
-
const { join:
|
|
16014
|
-
const appsDir =
|
|
16014
|
+
const { join: join8 } = await import("node:path");
|
|
16015
|
+
const appsDir = join8(config.appSpecificScan.rootDir, "apps");
|
|
16015
16016
|
try {
|
|
16016
|
-
const apps = readdirSync(appsDir).filter((a) => lstatSync(
|
|
16017
|
+
const apps = readdirSync(appsDir).filter((a) => lstatSync(join8(appsDir, a)).isDirectory());
|
|
16017
16018
|
const expoAppDirs = [];
|
|
16018
16019
|
for (const app of apps) {
|
|
16019
|
-
const appPath =
|
|
16020
|
+
const appPath = join8(appsDir, app);
|
|
16020
16021
|
const frameworks = detectAppFramework(appPath);
|
|
16021
16022
|
if (frameworks.includes("expo") || frameworks.includes("react-native")) {
|
|
16022
16023
|
expoAppDirs.push(appPath);
|
|
@@ -16046,6 +16047,9 @@ async function scanBrokenLinks(config) {
|
|
|
16046
16047
|
continue;
|
|
16047
16048
|
allLinkPaths.add(cleaned);
|
|
16048
16049
|
if (!matchesRoute(cleaned, knownRoutes, routeSegmentsList)) {
|
|
16050
|
+
const publicPath = join7(appDir, "public", cleaned);
|
|
16051
|
+
if (existsSync7(publicPath))
|
|
16052
|
+
continue;
|
|
16049
16053
|
const ignorePatterns = [
|
|
16050
16054
|
...config.ignore.links || [],
|
|
16051
16055
|
...config.ignore.routes
|
|
@@ -16226,12 +16230,12 @@ function normalizeNestPath(path2) {
|
|
|
16226
16230
|
return path2.replace(/\/$/, "").replace(/\?.*$/, "").replace(/\$\{[^}]+\}/g, "*").replace(/:[^/]+/g, "*").toLowerCase();
|
|
16227
16231
|
}
|
|
16228
16232
|
async function detectGlobalPrefix(appDir) {
|
|
16229
|
-
const mainTsPath =
|
|
16230
|
-
const mainTsAltPath =
|
|
16233
|
+
const mainTsPath = join8(appDir, "src/main.ts");
|
|
16234
|
+
const mainTsAltPath = join8(appDir, "main.ts");
|
|
16231
16235
|
let content;
|
|
16232
|
-
if (
|
|
16236
|
+
if (existsSync8(mainTsPath)) {
|
|
16233
16237
|
content = readFileSync10(mainTsPath, "utf-8");
|
|
16234
|
-
} else if (
|
|
16238
|
+
} else if (existsSync8(mainTsAltPath)) {
|
|
16235
16239
|
content = readFileSync10(mainTsAltPath, "utf-8");
|
|
16236
16240
|
} else {
|
|
16237
16241
|
return "";
|
|
@@ -16290,8 +16294,8 @@ function checkRouteUsage(route, references, nestGlobalPrefix = "") {
|
|
|
16290
16294
|
return { used, usedMethods };
|
|
16291
16295
|
}
|
|
16292
16296
|
function getVercelExternalPaths(dir) {
|
|
16293
|
-
const vercelPath =
|
|
16294
|
-
if (!
|
|
16297
|
+
const vercelPath = join8(dir, "vercel.json");
|
|
16298
|
+
if (!existsSync8(vercelPath)) {
|
|
16295
16299
|
return [];
|
|
16296
16300
|
}
|
|
16297
16301
|
try {
|
|
@@ -16322,15 +16326,15 @@ function getVercelExternalPaths(dir) {
|
|
|
16322
16326
|
}
|
|
16323
16327
|
}
|
|
16324
16328
|
function getGitHubWorkflowPaths(dir) {
|
|
16325
|
-
const workflowDir =
|
|
16326
|
-
if (!
|
|
16329
|
+
const workflowDir = join8(dir, ".github", "workflows");
|
|
16330
|
+
if (!existsSync8(workflowDir))
|
|
16327
16331
|
return [];
|
|
16328
16332
|
const paths = [];
|
|
16329
16333
|
const apiPathPattern = /\/api\/[a-zA-Z0-9_\-/[\]]+/g;
|
|
16330
16334
|
try {
|
|
16331
16335
|
const files = import_fast_glob10.default.sync("*.{yml,yaml}", { cwd: workflowDir });
|
|
16332
16336
|
for (const file of files) {
|
|
16333
|
-
const content = readFileSync10(
|
|
16337
|
+
const content = readFileSync10(join8(workflowDir, file), "utf-8");
|
|
16334
16338
|
let match2;
|
|
16335
16339
|
apiPathPattern.lastIndex = 0;
|
|
16336
16340
|
while ((match2 = apiPathPattern.exec(content)) !== null) {
|
|
@@ -16349,10 +16353,10 @@ function getGitHubWorkflowPaths(dir) {
|
|
|
16349
16353
|
function getAutoDetectedExternalRoutes(dir) {
|
|
16350
16354
|
const routes = [];
|
|
16351
16355
|
const packagePaths = [
|
|
16352
|
-
|
|
16356
|
+
join8(dir, "package.json")
|
|
16353
16357
|
];
|
|
16354
16358
|
for (const pkgPath of packagePaths) {
|
|
16355
|
-
if (!
|
|
16359
|
+
if (!existsSync8(pkgPath))
|
|
16356
16360
|
continue;
|
|
16357
16361
|
try {
|
|
16358
16362
|
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
@@ -16398,7 +16402,7 @@ async function scan(config) {
|
|
|
16398
16402
|
ignore: config.ignore.folders
|
|
16399
16403
|
});
|
|
16400
16404
|
const nextRoutes = nextFiles.map((file) => {
|
|
16401
|
-
const fullPath =
|
|
16405
|
+
const fullPath = join8(scanCwd, file);
|
|
16402
16406
|
const content = readFileSync10(fullPath, "utf-8");
|
|
16403
16407
|
const { methods, methodLines } = extractExportedMethods(content);
|
|
16404
16408
|
return {
|
|
@@ -16418,7 +16422,7 @@ async function scan(config) {
|
|
|
16418
16422
|
ignore: config.ignore.folders
|
|
16419
16423
|
});
|
|
16420
16424
|
const nestRoutes = nestFiles.flatMap((file) => {
|
|
16421
|
-
const fullPath =
|
|
16425
|
+
const fullPath = join8(scanCwd, file);
|
|
16422
16426
|
const content = readFileSync10(fullPath, "utf-8");
|
|
16423
16427
|
const relativePathFromRoot = fullPath.replace(config.appSpecificScan ? config.appSpecificScan.rootDir + "/" : cwd + "/", "");
|
|
16424
16428
|
return extractNestRoutes(relativePathFromRoot, content, detectedGlobalPrefix);
|
|
@@ -16475,7 +16479,7 @@ async function scan(config) {
|
|
|
16475
16479
|
const allReferences = [];
|
|
16476
16480
|
const fileReferences = new Map;
|
|
16477
16481
|
for (const file of sourceFiles) {
|
|
16478
|
-
const filePath =
|
|
16482
|
+
const filePath = join8(referenceScanCwd, file);
|
|
16479
16483
|
try {
|
|
16480
16484
|
const content = readFileSync10(filePath, "utf-8");
|
|
16481
16485
|
const refs = extractApiReferences(content);
|
|
@@ -16585,8 +16589,8 @@ async function scan(config) {
|
|
|
16585
16589
|
|
|
16586
16590
|
// src/config.ts
|
|
16587
16591
|
var import_fast_glob11 = __toESM(require_out4(), 1);
|
|
16588
|
-
import { existsSync as
|
|
16589
|
-
import { join as
|
|
16592
|
+
import { existsSync as existsSync9, readFileSync as readFileSync11 } from "node:fs";
|
|
16593
|
+
import { join as join9, resolve as resolve3, relative as relative4, dirname as dirname5 } from "node:path";
|
|
16590
16594
|
var DEFAULT_CONFIG = {
|
|
16591
16595
|
dir: "./",
|
|
16592
16596
|
ignore: {
|
|
@@ -16623,7 +16627,7 @@ function loadConfig(options) {
|
|
|
16623
16627
|
ignore: DEFAULT_CONFIG.ignore.folders,
|
|
16624
16628
|
absolute: true
|
|
16625
16629
|
});
|
|
16626
|
-
if (options.config &&
|
|
16630
|
+
if (options.config && existsSync9(options.config)) {
|
|
16627
16631
|
const absConfig = resolve3(cwd, options.config);
|
|
16628
16632
|
if (!configFiles.includes(absConfig)) {
|
|
16629
16633
|
configFiles.push(absConfig);
|
|
@@ -16652,7 +16656,7 @@ function loadConfig(options) {
|
|
|
16652
16656
|
const prefixPattern = (p) => {
|
|
16653
16657
|
if (p.startsWith("**/") || p.startsWith("/") || !relDir)
|
|
16654
16658
|
return p;
|
|
16655
|
-
const prefixed =
|
|
16659
|
+
const prefixed = join9(relDir, p).replace(/\\/g, "/");
|
|
16656
16660
|
return prefixed.includes("/") ? `**/${prefixed}` : `**/${prefixed}`;
|
|
16657
16661
|
};
|
|
16658
16662
|
if (config.ignore?.routes)
|
|
@@ -16691,8 +16695,8 @@ function loadConfig(options) {
|
|
|
16691
16695
|
};
|
|
16692
16696
|
}
|
|
16693
16697
|
function parseGitIgnore(dir) {
|
|
16694
|
-
const gitIgnorePath =
|
|
16695
|
-
if (!
|
|
16698
|
+
const gitIgnorePath = join9(dir, ".gitignore");
|
|
16699
|
+
if (!existsSync9(gitIgnorePath))
|
|
16696
16700
|
return [];
|
|
16697
16701
|
try {
|
|
16698
16702
|
const content = readFileSync11(gitIgnorePath, "utf-8");
|
|
@@ -16709,8 +16713,8 @@ function parseGitIgnore(dir) {
|
|
|
16709
16713
|
function findConfigFile(dir) {
|
|
16710
16714
|
const candidates = ["pruny.config.json", ".prunyrc.json", ".prunyrc"];
|
|
16711
16715
|
for (const name of candidates) {
|
|
16712
|
-
const path2 =
|
|
16713
|
-
if (
|
|
16716
|
+
const path2 = join9(dir, name);
|
|
16717
|
+
if (existsSync9(path2)) {
|
|
16714
16718
|
return path2;
|
|
16715
16719
|
}
|
|
16716
16720
|
}
|
|
@@ -16718,11 +16722,11 @@ function findConfigFile(dir) {
|
|
|
16718
16722
|
}
|
|
16719
16723
|
|
|
16720
16724
|
// src/init.ts
|
|
16721
|
-
import { writeFileSync as writeFileSync2, existsSync as
|
|
16722
|
-
import { join as
|
|
16725
|
+
import { writeFileSync as writeFileSync2, existsSync as existsSync10 } from "node:fs";
|
|
16726
|
+
import { join as join10 } from "node:path";
|
|
16723
16727
|
function init(cwd = process.cwd()) {
|
|
16724
|
-
const configPath =
|
|
16725
|
-
if (
|
|
16728
|
+
const configPath = join10(cwd, "pruny.config.json");
|
|
16729
|
+
if (existsSync10(configPath)) {
|
|
16726
16730
|
console.log(source_default.yellow("⚠️ pruny.config.json already exists. Skipping."));
|
|
16727
16731
|
return;
|
|
16728
16732
|
}
|
|
@@ -16750,7 +16754,7 @@ program2.action(async (options) => {
|
|
|
16750
16754
|
config: options.config,
|
|
16751
16755
|
excludePublic: !options.public
|
|
16752
16756
|
});
|
|
16753
|
-
const absoluteDir = baseConfig.dir.startsWith("/") ? baseConfig.dir :
|
|
16757
|
+
const absoluteDir = baseConfig.dir.startsWith("/") ? baseConfig.dir : join11(process.cwd(), baseConfig.dir);
|
|
16754
16758
|
baseConfig.dir = absoluteDir;
|
|
16755
16759
|
if (options.verbose)
|
|
16756
16760
|
console.log("");
|
|
@@ -16758,13 +16762,13 @@ program2.action(async (options) => {
|
|
|
16758
16762
|
\uD83D\uDD0D Scanning for unused API routes...
|
|
16759
16763
|
`));
|
|
16760
16764
|
let monorepoRoot = absoluteDir;
|
|
16761
|
-
let appsDir =
|
|
16762
|
-
let isMonorepo =
|
|
16765
|
+
let appsDir = join11(monorepoRoot, "apps");
|
|
16766
|
+
let isMonorepo = existsSync11(appsDir) && lstatSync(appsDir).isDirectory();
|
|
16763
16767
|
if (!isMonorepo) {
|
|
16764
16768
|
let current = absoluteDir;
|
|
16765
16769
|
while (current !== dirname6(current)) {
|
|
16766
|
-
const potentialApps =
|
|
16767
|
-
if (
|
|
16770
|
+
const potentialApps = join11(current, "apps");
|
|
16771
|
+
if (existsSync11(potentialApps) && lstatSync(potentialApps).isDirectory()) {
|
|
16768
16772
|
monorepoRoot = current;
|
|
16769
16773
|
appsDir = potentialApps;
|
|
16770
16774
|
isMonorepo = true;
|
|
@@ -16782,13 +16786,13 @@ program2.action(async (options) => {
|
|
|
16782
16786
|
const ignoredApps = options.ignoreApps ? options.ignoreApps.split(",").map((a) => a.trim()) : [];
|
|
16783
16787
|
if (isMonorepo) {
|
|
16784
16788
|
if (monorepoRoot !== absoluteDir) {
|
|
16785
|
-
const appName = relative5(
|
|
16789
|
+
const appName = relative5(join11(monorepoRoot, "apps"), absoluteDir);
|
|
16786
16790
|
appsToScan.push(appName);
|
|
16787
16791
|
} else {
|
|
16788
16792
|
const apps = readdirSync(appsDir);
|
|
16789
16793
|
const availableApps = [];
|
|
16790
16794
|
for (const app of apps) {
|
|
16791
|
-
const appPath =
|
|
16795
|
+
const appPath = join11(appsDir, app);
|
|
16792
16796
|
if (lstatSync(appPath).isDirectory()) {
|
|
16793
16797
|
availableApps.push(app);
|
|
16794
16798
|
}
|
|
@@ -16850,7 +16854,7 @@ program2.action(async (options) => {
|
|
|
16850
16854
|
let appDir = monorepoRoot;
|
|
16851
16855
|
if (isMonorepo) {
|
|
16852
16856
|
appLabel = `App: ${appName}`;
|
|
16853
|
-
appDir =
|
|
16857
|
+
appDir = join11(monorepoRoot, "apps", appName);
|
|
16854
16858
|
currentConfig.appSpecificScan = {
|
|
16855
16859
|
appDir,
|
|
16856
16860
|
rootDir: monorepoRoot
|
|
@@ -17311,7 +17315,7 @@ Analyzing cascading impact...`));
|
|
|
17311
17315
|
}));
|
|
17312
17316
|
dryRunReport.uniqueFiles = brokenList.length;
|
|
17313
17317
|
}
|
|
17314
|
-
const reportPath =
|
|
17318
|
+
const reportPath = join11(process.cwd(), "pruny-dry-run.json");
|
|
17315
17319
|
writeFileSync3(reportPath, JSON.stringify(dryRunReport, null, 2));
|
|
17316
17320
|
console.log(source_default.green(`
|
|
17317
17321
|
✅ Dry run report saved to: ${source_default.bold(reportPath)}`));
|
|
@@ -17365,7 +17369,7 @@ Analyzing cascading impact...`));
|
|
|
17365
17369
|
if (!asset.used) {
|
|
17366
17370
|
try {
|
|
17367
17371
|
const fullPath = asset.path;
|
|
17368
|
-
if (
|
|
17372
|
+
if (existsSync11(fullPath)) {
|
|
17369
17373
|
rmSync(fullPath, { force: true });
|
|
17370
17374
|
console.log(source_default.red(` Deleted: ${asset.relativePath}`));
|
|
17371
17375
|
fixedSomething = true;
|
|
@@ -17453,7 +17457,7 @@ Analyzing cascading impact...`));
|
|
|
17453
17457
|
}
|
|
17454
17458
|
const actuallyDeleted = new Set;
|
|
17455
17459
|
for (const path2 of filesToUnlink) {
|
|
17456
|
-
if (
|
|
17460
|
+
if (existsSync11(path2)) {
|
|
17457
17461
|
rmSync(path2, { recursive: true, force: true });
|
|
17458
17462
|
actuallyDeleted.add(path2);
|
|
17459
17463
|
console.log(source_default.red(` Deleted: ${relative5(config.dir, path2)}`));
|
|
@@ -17466,7 +17470,7 @@ Analyzing cascading impact...`));
|
|
|
17466
17470
|
for (const [absPath, removals] of removalsByFile) {
|
|
17467
17471
|
if (actuallyDeleted.has(absPath) || actuallyDeleted.has(dirname6(absPath)))
|
|
17468
17472
|
continue;
|
|
17469
|
-
if (!
|
|
17473
|
+
if (!existsSync11(absPath))
|
|
17470
17474
|
continue;
|
|
17471
17475
|
const sortedRemovals = removals.sort((a, b) => b.line - a.line);
|
|
17472
17476
|
for (const rem of sortedRemovals) {
|
|
@@ -17496,8 +17500,8 @@ Analyzing cascading impact...`));
|
|
|
17496
17500
|
\uD83D\uDDD1️ Deleting unused source files...`));
|
|
17497
17501
|
for (const file of result.unusedFiles.files) {
|
|
17498
17502
|
try {
|
|
17499
|
-
const fullPath =
|
|
17500
|
-
if (!
|
|
17503
|
+
const fullPath = join11(config.dir, file.path);
|
|
17504
|
+
if (!existsSync11(fullPath))
|
|
17501
17505
|
continue;
|
|
17502
17506
|
rmSync(fullPath, { force: true });
|
|
17503
17507
|
console.log(source_default.red(` Deleted: ${file.path}`));
|
|
@@ -17534,8 +17538,8 @@ Analyzing cascading impact...`));
|
|
|
17534
17538
|
}
|
|
17535
17539
|
let fixedCount = 0;
|
|
17536
17540
|
for (const [file, methods] of methodsByFile.entries()) {
|
|
17537
|
-
const fullPath =
|
|
17538
|
-
if (!
|
|
17541
|
+
const fullPath = join11(config.dir, file);
|
|
17542
|
+
if (!existsSync11(fullPath))
|
|
17539
17543
|
continue;
|
|
17540
17544
|
const sortedMethods = methods.sort((a, b) => b.line - a.line);
|
|
17541
17545
|
for (const method of sortedMethods) {
|
|
@@ -17608,8 +17612,8 @@ Analyzing cascading impact...`));
|
|
|
17608
17612
|
}
|
|
17609
17613
|
let svcFixedCount = 0;
|
|
17610
17614
|
for (const [file, methods] of svcByFile.entries()) {
|
|
17611
|
-
const fullPath =
|
|
17612
|
-
if (!
|
|
17615
|
+
const fullPath = join11(config.dir, file);
|
|
17616
|
+
if (!existsSync11(fullPath))
|
|
17613
17617
|
continue;
|
|
17614
17618
|
const sortedMethods = methods.sort((a, b) => b.line - a.line);
|
|
17615
17619
|
for (const method of sortedMethods) {
|
|
@@ -17654,8 +17658,8 @@ async function fixUnusedExports(result, config) {
|
|
|
17654
17658
|
for (const [_file, exports] of exportsByFile.entries()) {
|
|
17655
17659
|
const sortedExports = exports.sort((a, b) => b.line - a.line);
|
|
17656
17660
|
for (const exp of sortedExports) {
|
|
17657
|
-
const fullPath =
|
|
17658
|
-
if (!
|
|
17661
|
+
const fullPath = join11(config.dir, exp.file);
|
|
17662
|
+
if (!existsSync11(fullPath))
|
|
17659
17663
|
continue;
|
|
17660
17664
|
if (removeExportFromLine(config.dir, exp)) {
|
|
17661
17665
|
console.log(source_default.green(` Fixed: ${exp.name} in ${exp.file}`));
|
|
@@ -17952,7 +17956,7 @@ function printTable(summary) {
|
|
|
17952
17956
|
function findGitRoot(startDir) {
|
|
17953
17957
|
let currentDir = startDir;
|
|
17954
17958
|
while (currentDir !== dirname6(currentDir)) {
|
|
17955
|
-
if (
|
|
17959
|
+
if (existsSync11(join11(currentDir, ".git"))) {
|
|
17956
17960
|
return true;
|
|
17957
17961
|
}
|
|
17958
17962
|
currentDir = dirname6(currentDir);
|