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 CHANGED
@@ -17,7 +17,7 @@ npm install -g oh-my-node-modules
17
17
  ### Interactive mode
18
18
 
19
19
  ```bash
20
- onm # Start in current directory
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 DEFAULT_CONCURRENCY = 5;
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 scanLimit = pLimit(DEFAULT_CONCURRENCY);
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 batch = pathsToScan.splice(0, Math.min(pathsToScan.length, DEFAULT_CONCURRENCY * 2));
3353
- const scanPromises = batch.map(({ path: currentPath, depth }) => scanLimit(async () => {
3354
- if (visitedPaths.has(currentPath))
3355
- return;
3356
- if (options.maxDepth !== undefined && depth > options.maxDepth)
3357
- return;
3358
- if (shouldExcludePath(currentPath, options.excludePatterns))
3359
- return;
3360
- visitedPaths.add(currentPath);
3361
- result.directoriesScanned++;
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 entries = await fs2.readdir(currentPath, { withFileTypes: true });
3364
- const hasNodeModules = entries.some((entry) => entry.isDirectory() && entry.name === "node_modules");
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 || lazy) {
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 scanning ${currentPath}: ${errorMessage}`);
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(scanPromises);
3407
+ await Promise.all(analysisPromises);
3396
3408
  }
3397
3409
  if (onProgress) {
3398
- onProgress(100, foundCount);
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 DEFAULT_CONCURRENCY = 5;
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 scanLimit = pLimit(DEFAULT_CONCURRENCY);
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 batch = pathsToScan.splice(0, Math.min(pathsToScan.length, DEFAULT_CONCURRENCY * 2));
587
- const scanPromises = batch.map(({ path: currentPath, depth }) => scanLimit(async () => {
588
- if (visitedPaths.has(currentPath))
589
- return;
590
- if (options.maxDepth !== undefined && depth > options.maxDepth)
591
- return;
592
- if (shouldExcludePath(currentPath, options.excludePatterns))
593
- return;
594
- visitedPaths.add(currentPath);
595
- result.directoriesScanned++;
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 entries = await fs2.readdir(currentPath, { withFileTypes: true });
598
- const hasNodeModules = entries.some((entry) => entry.isDirectory() && entry.name === "node_modules");
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 || lazy) {
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 scanning ${currentPath}: ${errorMessage}`);
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(scanPromises);
641
+ await Promise.all(analysisPromises);
630
642
  }
631
643
  if (onProgress) {
632
- onProgress(100, foundCount);
644
+ onProgress(100, result.nodeModules.length);
633
645
  }
634
646
  return result;
635
647
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-node-modules",
3
- "version": "1.2.9",
3
+ "version": "1.3.2",
4
4
  "description": "Visualize, analyze, and clean up node_modules directories to reclaim disk space",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",