summaryrefslogtreecommitdiff
path: root/fs/walk.ts
diff options
context:
space:
mode:
authorAndy Hayden <andyhayden1@gmail.com>2019-02-15 08:20:59 -0800
committerRyan Dahl <ry@tinyclouds.org>2019-02-15 11:20:59 -0500
commit954fe83f62770257ea516396feb31ba961bc0967 (patch)
treee7168ce6cd34fcbbfc053d35d26f211bb55273ba /fs/walk.ts
parent0eed9b30298e1ba83d8b21bad24ee77dff59942c (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.ts134
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;
+}