pics, включая подкаталоги.Время – O(N) по числу записей; память – O(H), где H – максимальная глубина.
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 * Обходит дерево каталогов в глубину (DFS, стек) и возвращает список файлов.12 *13 * Требование: вывести имена всех файлов в каталоге `pics`, включая все его подкаталоги.14 *15 * Пример:16 * (async () => {17 * const files = await walkDirDfs('pics'); // корень обхода18 * files.forEach(f => console.log(basename(f))); // печать только имён19 * })().catch(console.error);20 */21export async function walkDirDfs(root: string, opts: WalkOptions = {}): Promise<string[]> {22 const files: string[] = [];23 const stack: string[] = [root];2425 while (stack.length) {26 const dir = stack.pop()!;27 let entries: Dirent[];28 try {29 entries = await readdir(dir, { withFileTypes: true });30 } catch {31 continue; // не каталог / нет доступа – пропускаем32 }3334 for (const entry of entries) {35 const full = join(dir, entry.name);3637 if (entry.isDirectory()) {38 // Кладём подкаталог в стек; при желании можно сортировать для детерминированного порядка39 stack.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()) stack.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}