diff options
| author | Andy Hayden <andyhayden1@gmail.com> | 2019-02-15 08:20:59 -0800 |
|---|---|---|
| committer | Ryan Dahl <ry@tinyclouds.org> | 2019-02-15 11:20:59 -0500 |
| commit | 954fe83f62770257ea516396feb31ba961bc0967 (patch) | |
| tree | e7168ce6cd34fcbbfc053d35d26f211bb55273ba /fs/walk.ts | |
| parent | 0eed9b30298e1ba83d8b21bad24ee77dff59942c (diff) | |
Add fs.walk (denoland/deno_std#192)
Original: https://github.com/denoland/deno_std/commit/3be908facd092e91b4ec1433effd710f5c9532b5
Diffstat (limited to 'fs/walk.ts')
| -rw-r--r-- | fs/walk.ts | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/fs/walk.ts b/fs/walk.ts new file mode 100644 index 000000000..92e4ba593 --- /dev/null +++ b/fs/walk.ts @@ -0,0 +1,134 @@ +import { + FileInfo, + cwd, + readDir, + readDirSync, + readlink, + readlinkSync, + stat, + statSync +} from "deno"; +import { relative } from "path.ts"; + +export interface WalkOptions { + maxDepth?: number; + exts?: string[]; + match?: RegExp[]; + skip?: RegExp[]; + // FIXME don't use `any` here? + onError?: (err: any) => void; + followSymlinks?: Boolean; +} + +/** Generate all files in a directory recursively. + * + * for await (const fileInfo of walk()) { + * console.log(fileInfo.path); + * assert(fileInfo.isFile()); + * }; + */ +export async function* walk( + dir: string = ".", + options: WalkOptions = {} +): AsyncIterableIterator<FileInfo> { + options.maxDepth -= 1; + let ls: FileInfo[] = []; + try { + ls = await readDir(dir); + } catch (err) { + if (options.onError) { + options.onError(err); + } + } + for (let f of ls) { + if (f.isSymlink()) { + if (options.followSymlinks) { + f = await resolve(f); + } else { + continue; + } + } + if (f.isFile()) { + if (include(f, options)) { + yield f; + } + } else { + if (!(options.maxDepth < 0)) { + yield* walk(f.path, options); + } + } + } +} + +/** Generate all files in a directory recursively. + * + * for (const fileInfo of walkSync()) { + * console.log(fileInfo.path); + * assert(fileInfo.isFile()); + * }; + */ +export function* walkSync( + dir: string = ".", + options: WalkOptions = {} +): IterableIterator<FileInfo> { + options.maxDepth -= 1; + let ls: FileInfo[] = []; + try { + ls = readDirSync(dir); + } catch (err) { + if (options.onError) { + options.onError(err); + } + } + for (let f of ls) { + if (f.isSymlink()) { + if (options.followSymlinks) { + f = resolveSync(f); + } else { + continue; + } + } + if (f.isFile()) { + if (include(f, options)) { + yield f; + } + } else { + if (!(options.maxDepth < 0)) { + yield* walkSync(f.path, options); + } + } + } +} + +function include(f: FileInfo, options: WalkOptions): Boolean { + if (options.exts && !options.exts.some(ext => f.path.endsWith(ext))) { + return false; + } + if (options.match && !options.match.some(pattern => pattern.test(f.path))) { + return false; + } + if (options.skip && options.skip.some(pattern => pattern.test(f.path))) { + return false; + } + return true; +} + +async function resolve(f: FileInfo): Promise<FileInfo> { + // This is the full path, unfortunately if we were to make it relative + // it could resolve to a symlink and cause an infinite loop. + const fpath = await readlink(f.path); + f = await stat(fpath); + // workaround path not being returned by stat + f.path = fpath; + return f; +} + +function resolveSync(f: FileInfo): FileInfo { + // This is the full path, unfortunately if we were to make it relative + // it could resolve to a symlink and cause an infinite loop. + const fpath = readlinkSync(f.path); + f = statSync(fpath); + // workaround path not being returned by stat + f.path = fpath; + return f; +} |
