diff --git a/src/find-all.ts b/src/find-all.ts index d752dbc..d8881f6 100644 --- a/src/find-all.ts +++ b/src/find-all.ts @@ -22,13 +22,21 @@ async function* findTSConfig( visited: Set = new Set() ): AsyncGenerator { if (!visited.has(dir)) { - const dirents = await fs.readdir(dir, { withFileTypes: true }); - for (const dirent of dirents) { - if (dirent.isDirectory() && (!options?.skip || !options.skip(dirent.name))) { - yield* findTSConfig(path.resolve(dir, dirent.name), options, visited); - } else if (dirent.isFile() && dirent.name === 'tsconfig.json') { - yield path.resolve(dir, dirent.name); + visited.add(dir); + try { + const dirents = await fs.readdir(dir, { withFileTypes: true }); + for (const dirent of dirents) { + if (dirent.isDirectory() && (!options?.skip || !options.skip(dirent.name))) { + yield* findTSConfig(path.resolve(dir, dirent.name), options, visited); + } else if (dirent.isFile() && dirent.name === 'tsconfig.json') { + yield path.resolve(dir, dirent.name); + } } + } catch (e) { + if (e.code === 'EACCES' || e.code === 'ENOENT') { + return; // directory inaccessible or deleted + } + throw e; } } } diff --git a/tests/find-all.ts b/tests/find-all.ts index 10eba00..aba8f31 100644 --- a/tests/find-all.ts +++ b/tests/find-all.ts @@ -2,6 +2,7 @@ import { suite } from 'uvu'; import * as assert from 'uvu/assert'; import path from 'path'; import { findAll } from '../src/find-all.js'; +import * as fs from 'fs'; import glob from 'tiny-glob'; const test = suite('findAll'); @@ -52,13 +53,14 @@ test('should find tsconfig in child directory', async () => { }); test('should find multiple tsconfig in child directories', async () => { - const expected = (await glob('tests/fixtures/**/tsconfig.json')).map((file) => + const expected = (await glob('tests/fixtures/find-all/multiple/**/tsconfig.json')).map((file) => path.resolve(file) ); expected.sort(); - const found = await findAll(path.join('tests', 'fixtures')); + + const found = await findAll(path.join('tests', 'fixtures', 'find-all', 'multiple')); found.sort(); - assert.equal(found, expected, 'found all tsconfig in test/fixtures'); + assert.equal(found, expected, 'found all tsconfig in test/fixtures/find-all/multiple'); }); test('should handle directories with recursive symlinks', async () => { @@ -71,7 +73,6 @@ test('should handle directories with recursive symlinks', async () => { found.sort(); assert.equal(found, expected, 'found all tsconfig in test/fixtures/find-all/recursive-symlink'); }); -test.run(); test('should exclude skipped directories', async () => { const expected = [ @@ -88,4 +89,29 @@ test('should exclude skipped directories', async () => { 'found filtered tsconfig in test/fixtures/find-all/recursive-symlink' ); }); + +test('should handle directories with inaccessible children', async () => { + const inaccessible = path.resolve( + 'tests', + 'fixtures', + 'find-all', + 'inaccessible-dir', + '_inaccessible' + ); + try { + if (fs.existsSync(inaccessible)) { + fs.chmodSync(inaccessible, 0o000); + } + } catch (e) { + assert.unreachable(`failed to set inaccessible-child permissions: ${e}`); + } + const expected = [ + path.resolve('tests', 'fixtures', 'find-all', 'inaccessible-dir', 'tsconfig.json') + ]; + expected.sort(); + const found = await findAll(path.join('tests', 'fixtures', 'find-all', 'inaccessible-dir')); + found.sort(); + assert.equal(found, expected, 'found all tsconfig in test/fixtures/find-all/inaccessible-dir'); +}); + test.run(); diff --git a/tests/fixtures/find-all/inaccessible-dir/_inaccessible/.gitkeep b/tests/fixtures/find-all/inaccessible-dir/_inaccessible/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/find-all/inaccessible-dir/tsconfig.json b/tests/fixtures/find-all/inaccessible-dir/tsconfig.json new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/find-all/multiple/a/tsconfig.json b/tests/fixtures/find-all/multiple/a/tsconfig.json new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/find-all/multiple/b/tsconfig.json b/tests/fixtures/find-all/multiple/b/tsconfig.json new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/find-all/multiple/c/d/tsconfig.json b/tests/fixtures/find-all/multiple/c/d/tsconfig.json new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/find-all/multiple/tsconfig.json b/tests/fixtures/find-all/multiple/tsconfig.json new file mode 100644 index 0000000..e69de29