Skip to content

Commit 8ef5609

Browse files
authored
[Optimizer] Fix a stack overflow with watch_cache when it attempts to delete very large folders. (#54457)
1 parent e6e1373 commit 8ef5609

File tree

1 file changed

+31
-9
lines changed

1 file changed

+31
-9
lines changed

src/optimize/watch/watch_cache.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,18 @@
1818
*/
1919

2020
import { createHash } from 'crypto';
21-
import { readFile, writeFile } from 'fs';
21+
import { readFile, writeFile, readdir, unlink, rmdir } from 'fs';
2222
import { resolve } from 'path';
2323
import { promisify } from 'util';
24-
24+
import path from 'path';
2525
import del from 'del';
26-
import deleteEmpty from 'delete-empty';
27-
import globby from 'globby';
2826
import normalizePosixPath from 'normalize-path';
2927

3028
const readAsync = promisify(readFile);
3129
const writeAsync = promisify(writeFile);
30+
const readdirAsync = promisify(readdir);
31+
const unlinkAsync = promisify(unlink);
32+
const rmdirAsync = promisify(rmdir);
3233

3334
interface Params {
3435
logWithMetadata: (tags: string[], message: string, metadata?: { [key: string]: any }) => void;
@@ -95,11 +96,7 @@ export class WatchCache {
9596
await del(this.statePath, { force: true });
9697

9798
// delete everything in optimize/.cache directory
98-
await del(await globby([normalizePosixPath(this.cachePath)], { dot: true }));
99-
100-
// delete some empty folder that could be left
101-
// from the previous cache path reset action
102-
await deleteEmpty(this.cachePath);
99+
await recursiveDelete(normalizePosixPath(this.cachePath));
103100

104101
// delete dlls
105102
await del(this.dllsPath);
@@ -167,3 +164,28 @@ export class WatchCache {
167164
}
168165
}
169166
}
167+
168+
/**
169+
* Recursively deletes a folder. This is a workaround for a bug in `del` where
170+
* very large folders (with 84K+ files) cause a stack overflow.
171+
*/
172+
async function recursiveDelete(directory: string) {
173+
const entries = await readdirAsync(directory, { withFileTypes: true });
174+
await Promise.all(
175+
entries.map(entry => {
176+
const absolutePath = path.join(directory, entry.name);
177+
const result = entry.isDirectory()
178+
? recursiveDelete(absolutePath)
179+
: unlinkAsync(absolutePath);
180+
181+
// Ignore errors, if the file or directory doesn't exist.
182+
return result.catch(e => {
183+
if (e.code !== 'ENOENT') {
184+
throw e;
185+
}
186+
});
187+
})
188+
);
189+
190+
return rmdirAsync(directory);
191+
}

0 commit comments

Comments
 (0)