pics, включая все подкаталоги.Время – O(N) по числу записей (файлы+каталоги), память – O(W), где W – максимальная ширина уровня.
1import { readdir, stat } from 'node:fs/promises';2import type { Dirent } from 'node:fs';3import { join, basename } from 'node:path';45type WalkOptions = {6 /** Следовать по симлинкам (по умолчанию false) */7 followSymlinks?: boolean;8};910/**11 * Обходит дерево каталогов в ширину (BFS) и возвращает список файлов.12 *13 * Требование: вывести имена всех файлов в каталоге `pics`,14 * включая все его подкаталоги.15 *16 * Пример:17 * (async () => {18 * const files = await walkDirBfs('pics'); // корень обхода19 * for (const f of files) console.log(basename(f)); // печать только имён20 * })().catch(console.error);21 */22export async function walkDirBfs(root: string, opts: WalkOptions = {}): Promise<string[]> {23 const files: string[] = [];24 const queue: string[] = [root];2526 while (queue.length) {27 const dir = queue.shift()!;28 let entries: Dirent[];29 try {30 entries = await readdir(dir, { withFileTypes: true });31 } catch {32 continue; // не каталог / нет доступа - пропускаем33 }3435 for (const entry of entries) {36 const full = join(dir, entry.name);3738 if (entry.isDirectory()) {39 queue.push(full);40 } else if (entry.isFile()) {41 files.push(full);42 } else if (opts.followSymlinks && entry.isSymbolicLink()) {43 try {44 const s = await stat(full);45 if (s.isDirectory()) queue.push(full);46 else if (s.isFile()) files.push(full);47 } catch (err) {48 const code = (err as NodeJS.ErrnoException)?.code;49 // игнорируем типичные «безопасные» кейсы симлинков50 if (code === 'ENOENT' || code === 'EACCES' || code === 'EPERM') {51 // no-op52 } else {53 throw err; // всё остальное - настоящая ошибка54 }55 }56 }57 }58 }5960 return files;61}