Created
January 31, 2022 20:47
-
-
Save jhunterkohler/182ba73929152bdf4d89ceda467d51b1 to your computer and use it in GitHub Desktop.
Alright file system walker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2021 Hunter Kohler <[email protected]> | |
*/ | |
import fs from "fs"; | |
import path from "path"; | |
interface WalkdirOptions { | |
withFileTypes?: boolean; | |
followlinks?: boolean; | |
files?: boolean; | |
encoding?: BufferEncoding; | |
blockdev?: boolean; | |
chardev?: boolean; | |
dirs?: boolean; | |
fifos?: boolean; | |
sockets?: boolean; | |
symlinks?: boolean; | |
maxdepth?: number; | |
} | |
/** | |
* Walk a directory tree. | |
* | |
* @param root Start of walk. | |
* @param options Walk options. | |
*/ | |
export async function* walkdirs( | |
root: fs.PathLike, | |
options?: WalkdirOptions | |
): AsyncIterator<string> { | |
const { | |
followlinks = false, | |
files = true, | |
encoding = "utf8", | |
blockdev = true, | |
chardev = true, | |
dirs = true, | |
fifos = true, | |
sockets = true, | |
symlinks = true, | |
maxdepth = Infinity, | |
} = options ?? {}; | |
const dirstack = [{ dirpath: String(root), depth: 0 }]; | |
const diropts = { withFileTypes: true, encoding } as const; | |
const linkopts = { encoding }; | |
const seen = new Set<string>(); | |
while (dirstack.length) { | |
const { dirpath, depth } = dirstack.pop()!; | |
if (depth >= maxdepth) { | |
continue; | |
} | |
const dir: (string | fs.Dirent)[] = await fs.promises.readdir( | |
dirpath, | |
diropts | |
); | |
for (const dirent of dir) { | |
let filepath: string; | |
let stats: fs.Dirent | fs.Stats; | |
if (typeof dirent == "string") { | |
filepath = dirent; | |
stats = await fs.promises.stat(filepath); | |
} else { | |
filepath = path.join(dirpath, dirent.name); | |
stats = dirent; | |
} | |
/* | |
* Stop cycles if symlinks are allowed. (Though I suppose UNIX | |
* systems could implement a circular file system haha). | |
*/ | |
if (seen.has(filepath)) { | |
continue; | |
} else { | |
seen.add(filepath); | |
} | |
if (stats.isFile()) { | |
if (files) { | |
yield filepath; | |
} | |
} else if (stats.isDirectory()) { | |
if (dirs) { | |
yield filepath; | |
} | |
dirstack.push({ dirpath: filepath, depth: depth + 1 }); | |
} else if (stats.isSymbolicLink()) { | |
if (symlinks) { | |
yield filepath; | |
} | |
if (followlinks) { | |
dir.push(await fs.promises.readlink(filepath, linkopts)); | |
} | |
} else if (stats.isBlockDevice()) { | |
if (blockdev) { | |
yield filepath; | |
} | |
} else if (stats.isCharacterDevice()) { | |
if (chardev) { | |
yield filepath; | |
} | |
} else if (stats.isFIFO()) { | |
if (fifos) { | |
yield filepath; | |
} | |
} else if (stats.isSocket()) { | |
if (sockets) { | |
yield filepath; | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment