oh-my-node-modules 1.2.9 → 1.3.2
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/README.md +5 -5
- package/dist/cli.js +53 -41
- package/dist/index.js +53 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ npm install -g oh-my-node-modules
|
|
|
17
17
|
### Interactive mode
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
onm
|
|
20
|
+
onm . # Start in current directory
|
|
21
21
|
onm ~/projects # Scan specific directory
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -37,16 +37,16 @@ Keyboard shortcuts:
|
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
39
|
# Quick scan report
|
|
40
|
-
onm --scan
|
|
40
|
+
onm --scan .
|
|
41
41
|
|
|
42
42
|
# JSON output
|
|
43
|
-
onm --scan --json
|
|
43
|
+
onm --scan --json .
|
|
44
44
|
|
|
45
45
|
# Auto-delete large node_modules
|
|
46
|
-
onm --auto --min-size 500mb --yes
|
|
46
|
+
onm --auto --min-size 500mb --yes .
|
|
47
47
|
|
|
48
48
|
# Preview what would be deleted
|
|
49
|
-
onm --auto --min-size 1gb --dry-run
|
|
49
|
+
onm --auto --min-size 1gb --dry-run .
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
## Features
|
package/dist/cli.js
CHANGED
|
@@ -3080,6 +3080,9 @@ function calculateStatistics(items) {
|
|
|
3080
3080
|
}
|
|
3081
3081
|
function shouldExcludePath(path, patterns) {
|
|
3082
3082
|
return patterns.some((pattern) => {
|
|
3083
|
+
if (pattern === "**/.*" || pattern === ".*") {
|
|
3084
|
+
return /(^|\/)\.[^\/]+($|\/)/.test(path);
|
|
3085
|
+
}
|
|
3083
3086
|
const regexPattern = pattern.replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
3084
3087
|
const regex = new RegExp(regexPattern, "i");
|
|
3085
3088
|
return regex.test(path);
|
|
@@ -3189,8 +3192,7 @@ async function getFastDirectorySize(dirPath) {
|
|
|
3189
3192
|
}
|
|
3190
3193
|
|
|
3191
3194
|
// src/scanner.ts
|
|
3192
|
-
var
|
|
3193
|
-
var SIZE_CALCULATION_CONCURRENCY = 3;
|
|
3195
|
+
var SIZE_CALCULATION_CONCURRENCY = 4;
|
|
3194
3196
|
async function findRepoRoot(startPath) {
|
|
3195
3197
|
let currentPath = startPath;
|
|
3196
3198
|
const root = process.platform === "win32" ? "C:\\" : "/";
|
|
@@ -3343,59 +3345,69 @@ async function scanForNodeModules(options, onProgress, lazy = false) {
|
|
|
3343
3345
|
const pathsToScan = [
|
|
3344
3346
|
{ path: options.rootPath, depth: 0 }
|
|
3345
3347
|
];
|
|
3346
|
-
const
|
|
3347
|
-
const sizeLimit = pLimit(SIZE_CALCULATION_CONCURRENCY);
|
|
3348
|
+
const foundNodeModules = [];
|
|
3348
3349
|
let processedCount = 0;
|
|
3349
3350
|
let totalEstimate = 1;
|
|
3350
|
-
let foundCount = 0;
|
|
3351
3351
|
while (pathsToScan.length > 0) {
|
|
3352
|
-
const
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3352
|
+
const { path: currentPath, depth } = pathsToScan.shift();
|
|
3353
|
+
if (visitedPaths.has(currentPath))
|
|
3354
|
+
continue;
|
|
3355
|
+
if (options.maxDepth !== undefined && depth > options.maxDepth)
|
|
3356
|
+
continue;
|
|
3357
|
+
if (shouldExcludePath(currentPath, options.excludePatterns))
|
|
3358
|
+
continue;
|
|
3359
|
+
visitedPaths.add(currentPath);
|
|
3360
|
+
result.directoriesScanned++;
|
|
3361
|
+
try {
|
|
3362
|
+
const entries = await fs2.readdir(currentPath, { withFileTypes: true });
|
|
3363
|
+
const hasNodeModules = entries.some((entry) => entry.isDirectory() && entry.name === "node_modules");
|
|
3364
|
+
if (hasNodeModules) {
|
|
3365
|
+
const nodeModulesPath = join2(currentPath, "node_modules");
|
|
3366
|
+
foundNodeModules.push({ nodeModulesPath, projectPath: currentPath });
|
|
3367
|
+
if (onProgress) {
|
|
3368
|
+
onProgress(Math.min(100, Math.round(processedCount / totalEstimate * 100)), foundNodeModules.length);
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
for (const entry of entries) {
|
|
3372
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
|
|
3373
|
+
const subPath = join2(currentPath, entry.name);
|
|
3374
|
+
if (!shouldExcludePath(subPath, options.excludePatterns)) {
|
|
3375
|
+
pathsToScan.push({ path: subPath, depth: depth + 1 });
|
|
3376
|
+
totalEstimate++;
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
} catch (error) {
|
|
3381
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3382
|
+
result.errors.push(`Error scanning ${currentPath}: ${errorMessage}`);
|
|
3383
|
+
}
|
|
3384
|
+
processedCount++;
|
|
3385
|
+
if (onProgress && foundNodeModules.length === 0) {
|
|
3386
|
+
onProgress(Math.min(100, Math.round(processedCount / totalEstimate * 100)), 0);
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
if (foundNodeModules.length > 0) {
|
|
3390
|
+
const sizeLimit = pLimit(SIZE_CALCULATION_CONCURRENCY);
|
|
3391
|
+
const analysisPromises = foundNodeModules.map(({ nodeModulesPath, projectPath }) => sizeLimit(async () => {
|
|
3362
3392
|
try {
|
|
3363
|
-
const
|
|
3364
|
-
|
|
3365
|
-
if (hasNodeModules) {
|
|
3366
|
-
const nodeModulesPath = join2(currentPath, "node_modules");
|
|
3367
|
-
const info = await sizeLimit(() => analyzeNodeModules(nodeModulesPath, currentPath, lazy));
|
|
3393
|
+
const info = await analyzeNodeModules(nodeModulesPath, projectPath, lazy);
|
|
3394
|
+
if (!lazy) {
|
|
3368
3395
|
const passesFilter = (!options.minSizeBytes || info.sizeBytes >= options.minSizeBytes) && (!options.olderThanDays || getAgeInDays2(info.lastModified) >= options.olderThanDays);
|
|
3369
|
-
if (passesFilter
|
|
3396
|
+
if (passesFilter) {
|
|
3370
3397
|
result.nodeModules.push(info);
|
|
3371
|
-
foundCount++;
|
|
3372
|
-
if (onProgress) {
|
|
3373
|
-
onProgress(Math.min(100, Math.round(processedCount / totalEstimate * 100)), foundCount);
|
|
3374
|
-
}
|
|
3375
|
-
}
|
|
3376
|
-
}
|
|
3377
|
-
for (const entry of entries) {
|
|
3378
|
-
if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
|
|
3379
|
-
const subPath = join2(currentPath, entry.name);
|
|
3380
|
-
if (!shouldExcludePath(subPath, options.excludePatterns)) {
|
|
3381
|
-
pathsToScan.push({ path: subPath, depth: depth + 1 });
|
|
3382
|
-
totalEstimate++;
|
|
3383
|
-
}
|
|
3384
3398
|
}
|
|
3399
|
+
} else {
|
|
3400
|
+
result.nodeModules.push(info);
|
|
3385
3401
|
}
|
|
3386
3402
|
} catch (error) {
|
|
3387
3403
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3388
|
-
result.errors.push(`Error
|
|
3389
|
-
}
|
|
3390
|
-
processedCount++;
|
|
3391
|
-
if (onProgress) {
|
|
3392
|
-
onProgress(Math.min(100, Math.round(processedCount / totalEstimate * 100)), foundCount);
|
|
3404
|
+
result.errors.push(`Error analyzing ${nodeModulesPath}: ${errorMessage}`);
|
|
3393
3405
|
}
|
|
3394
3406
|
}));
|
|
3395
|
-
await Promise.all(
|
|
3407
|
+
await Promise.all(analysisPromises);
|
|
3396
3408
|
}
|
|
3397
3409
|
if (onProgress) {
|
|
3398
|
-
onProgress(100,
|
|
3410
|
+
onProgress(100, result.nodeModules.length);
|
|
3399
3411
|
}
|
|
3400
3412
|
return result;
|
|
3401
3413
|
}
|
package/dist/index.js
CHANGED
|
@@ -306,6 +306,9 @@ function calculateStatistics(items) {
|
|
|
306
306
|
}
|
|
307
307
|
function shouldExcludePath(path, patterns) {
|
|
308
308
|
return patterns.some((pattern) => {
|
|
309
|
+
if (pattern === "**/.*" || pattern === ".*") {
|
|
310
|
+
return /(^|\/)\.[^\/]+($|\/)/.test(path);
|
|
311
|
+
}
|
|
309
312
|
const regexPattern = pattern.replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
310
313
|
const regex = new RegExp(regexPattern, "i");
|
|
311
314
|
return regex.test(path);
|
|
@@ -423,8 +426,7 @@ async function getFastDirectorySize(dirPath) {
|
|
|
423
426
|
}
|
|
424
427
|
|
|
425
428
|
// src/scanner.ts
|
|
426
|
-
var
|
|
427
|
-
var SIZE_CALCULATION_CONCURRENCY = 3;
|
|
429
|
+
var SIZE_CALCULATION_CONCURRENCY = 4;
|
|
428
430
|
async function findRepoRoot(startPath) {
|
|
429
431
|
let currentPath = startPath;
|
|
430
432
|
const root = process.platform === "win32" ? "C:\\" : "/";
|
|
@@ -577,59 +579,69 @@ async function scanForNodeModules(options, onProgress, lazy = false) {
|
|
|
577
579
|
const pathsToScan = [
|
|
578
580
|
{ path: options.rootPath, depth: 0 }
|
|
579
581
|
];
|
|
580
|
-
const
|
|
581
|
-
const sizeLimit = pLimit(SIZE_CALCULATION_CONCURRENCY);
|
|
582
|
+
const foundNodeModules = [];
|
|
582
583
|
let processedCount = 0;
|
|
583
584
|
let totalEstimate = 1;
|
|
584
|
-
let foundCount = 0;
|
|
585
585
|
while (pathsToScan.length > 0) {
|
|
586
|
-
const
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
586
|
+
const { path: currentPath, depth } = pathsToScan.shift();
|
|
587
|
+
if (visitedPaths.has(currentPath))
|
|
588
|
+
continue;
|
|
589
|
+
if (options.maxDepth !== undefined && depth > options.maxDepth)
|
|
590
|
+
continue;
|
|
591
|
+
if (shouldExcludePath(currentPath, options.excludePatterns))
|
|
592
|
+
continue;
|
|
593
|
+
visitedPaths.add(currentPath);
|
|
594
|
+
result.directoriesScanned++;
|
|
595
|
+
try {
|
|
596
|
+
const entries = await fs2.readdir(currentPath, { withFileTypes: true });
|
|
597
|
+
const hasNodeModules = entries.some((entry) => entry.isDirectory() && entry.name === "node_modules");
|
|
598
|
+
if (hasNodeModules) {
|
|
599
|
+
const nodeModulesPath = join2(currentPath, "node_modules");
|
|
600
|
+
foundNodeModules.push({ nodeModulesPath, projectPath: currentPath });
|
|
601
|
+
if (onProgress) {
|
|
602
|
+
onProgress(Math.min(100, Math.round(processedCount / totalEstimate * 100)), foundNodeModules.length);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
for (const entry of entries) {
|
|
606
|
+
if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
|
|
607
|
+
const subPath = join2(currentPath, entry.name);
|
|
608
|
+
if (!shouldExcludePath(subPath, options.excludePatterns)) {
|
|
609
|
+
pathsToScan.push({ path: subPath, depth: depth + 1 });
|
|
610
|
+
totalEstimate++;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
} catch (error) {
|
|
615
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
616
|
+
result.errors.push(`Error scanning ${currentPath}: ${errorMessage}`);
|
|
617
|
+
}
|
|
618
|
+
processedCount++;
|
|
619
|
+
if (onProgress && foundNodeModules.length === 0) {
|
|
620
|
+
onProgress(Math.min(100, Math.round(processedCount / totalEstimate * 100)), 0);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (foundNodeModules.length > 0) {
|
|
624
|
+
const sizeLimit = pLimit(SIZE_CALCULATION_CONCURRENCY);
|
|
625
|
+
const analysisPromises = foundNodeModules.map(({ nodeModulesPath, projectPath }) => sizeLimit(async () => {
|
|
596
626
|
try {
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
if (hasNodeModules) {
|
|
600
|
-
const nodeModulesPath = join2(currentPath, "node_modules");
|
|
601
|
-
const info = await sizeLimit(() => analyzeNodeModules(nodeModulesPath, currentPath, lazy));
|
|
627
|
+
const info = await analyzeNodeModules(nodeModulesPath, projectPath, lazy);
|
|
628
|
+
if (!lazy) {
|
|
602
629
|
const passesFilter = (!options.minSizeBytes || info.sizeBytes >= options.minSizeBytes) && (!options.olderThanDays || getAgeInDays2(info.lastModified) >= options.olderThanDays);
|
|
603
|
-
if (passesFilter
|
|
630
|
+
if (passesFilter) {
|
|
604
631
|
result.nodeModules.push(info);
|
|
605
|
-
foundCount++;
|
|
606
|
-
if (onProgress) {
|
|
607
|
-
onProgress(Math.min(100, Math.round(processedCount / totalEstimate * 100)), foundCount);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
for (const entry of entries) {
|
|
612
|
-
if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
|
|
613
|
-
const subPath = join2(currentPath, entry.name);
|
|
614
|
-
if (!shouldExcludePath(subPath, options.excludePatterns)) {
|
|
615
|
-
pathsToScan.push({ path: subPath, depth: depth + 1 });
|
|
616
|
-
totalEstimate++;
|
|
617
|
-
}
|
|
618
632
|
}
|
|
633
|
+
} else {
|
|
634
|
+
result.nodeModules.push(info);
|
|
619
635
|
}
|
|
620
636
|
} catch (error) {
|
|
621
637
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
622
|
-
result.errors.push(`Error
|
|
623
|
-
}
|
|
624
|
-
processedCount++;
|
|
625
|
-
if (onProgress) {
|
|
626
|
-
onProgress(Math.min(100, Math.round(processedCount / totalEstimate * 100)), foundCount);
|
|
638
|
+
result.errors.push(`Error analyzing ${nodeModulesPath}: ${errorMessage}`);
|
|
627
639
|
}
|
|
628
640
|
}));
|
|
629
|
-
await Promise.all(
|
|
641
|
+
await Promise.all(analysisPromises);
|
|
630
642
|
}
|
|
631
643
|
if (onProgress) {
|
|
632
|
-
onProgress(100,
|
|
644
|
+
onProgress(100, result.nodeModules.length);
|
|
633
645
|
}
|
|
634
646
|
return result;
|
|
635
647
|
}
|