Skip to content

Commit 626e270

Browse files
Merge pull request #24 from push-based/feature/fix-scan-perf
fix(angular-toolkit-mcp-server): fix scanning performance issue on win
2 parents 7e04a54 + b7ba7f4 commit 626e270

1 file changed

Lines changed: 54 additions & 10 deletions

File tree

packages/shared/utils/src/lib/file/find-in-file.ts

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,47 @@ import { Dirent } from 'node:fs';
22
import * as fs from 'node:fs/promises';
33
import * as path from 'node:path';
44

5+
/**
6+
* Default concurrency limit for file operations.
7+
* Set conservatively to avoid EMFILE errors on Windows.
8+
*/
9+
const DEFAULT_CONCURRENCY = 50;
10+
11+
/**
12+
* Process items with limited concurrency to avoid EMFILE errors.
13+
* Uses a semaphore pattern to limit parallel file operations.
14+
*/
15+
async function processWithConcurrency<T, R>(
16+
items: T[],
17+
processor: (item: T) => Promise<R>,
18+
concurrency: number = DEFAULT_CONCURRENCY,
19+
): Promise<R[]> {
20+
const results: R[] = [];
21+
let index = 0;
22+
23+
async function worker(): Promise<void> {
24+
while (index < items.length) {
25+
const currentIndex = index++;
26+
const item = items[currentIndex];
27+
try {
28+
const result = await processor(item);
29+
results[currentIndex] = result;
30+
} catch {
31+
// Errors are handled by the processor or ignored
32+
results[currentIndex] = undefined as R;
33+
}
34+
}
35+
}
36+
37+
// Create worker pool
38+
const workers = Array(Math.min(concurrency, items.length))
39+
.fill(null)
40+
.map(() => worker());
41+
42+
await Promise.all(workers);
43+
return results;
44+
}
45+
546
/**
647
* Searches for `.ts` files containing the search pattern.
748
* @param {string} baseDir - The directory to search. Should be absolute or resolved by the caller.
@@ -21,18 +62,21 @@ export async function findFilesWithPattern(
2162
tsFiles.push(file);
2263
}
2364

24-
const results: SourceLocation[] = [];
25-
for (const file of tsFiles) {
26-
try {
27-
const hits = await findInFile(file, searchPattern);
28-
if (hits.length > 0) {
29-
results.push(...hits);
65+
// Process files with limited concurrency to avoid EMFILE errors
66+
const searchResults = await processWithConcurrency(
67+
tsFiles,
68+
async (file) => {
69+
try {
70+
return await findInFile(file, searchPattern);
71+
} catch (ctx) {
72+
console.error(`Error searching file ${file}:`, ctx);
73+
return [];
3074
}
31-
} catch (ctx) {
32-
console.error(`Error searching file ${file}:`, ctx);
33-
}
34-
}
75+
},
76+
DEFAULT_CONCURRENCY,
77+
);
3578

79+
const results: SourceLocation[] = searchResults.flat();
3680
return results.map((r: SourceLocation) => r.file);
3781
}
3882

0 commit comments

Comments
 (0)