UNPKG

@jkcfg/std

Version:

jk standard library

90 lines (89 loc) 3.14 kB
/** * @module std/fs */ import { RPCSync } from './internal/rpc'; import { valueFromUTF8Bytes } from './internal/data'; export class FileInfo { constructor(n, p, d) { this.name = n; this.path = p; this.isdir = d; } } export class Directory { constructor(n, p, files) { this.name = n; this.path = p; this.files = files; } } export function info(path, options = {}) { const { module = '' } = options; const response = RPCSync('std.fileinfo', path, module); const { name: n, path: p, isdir: d } = valueFromUTF8Bytes(response); return new FileInfo(n, p, d); } export function dir(path, options = {}) { const { module = '' } = options; const response = RPCSync('std.dir', path, module); const { name: n, path: p, files: fs } = valueFromUTF8Bytes(response); const infos = []; for (const f of fs) { const { name: infoname, path: infopath, isdir: infoisdir } = f; infos.push(new FileInfo(infoname, infopath, infoisdir)); } return new Directory(n, p, infos); } export function join(base, name) { return `${base}/${name}`; } const always = () => true; const noop = () => { }; /* eslint-disable no-labels */ /** walk is a generator function that yields the files and directories * under a given path, in a preorder traversal. "Preorder" means that * the traversal is depth-first, and a directory is yielded * immediately before the traversal examines its contents. * * @param path the starting point for the traversal, which should be a * directory. * * @param opts pre- and post-hooks for the walk. The pre-hook is * called for each directory after it is yielded; if it returns a * falsey value, the directory is not traversed. The post-hook is * called after a directory's contents have all been traversed. The * starting point does not get treated as part of the traversal, i.e., * it starts with the contents of the directory at `path`. */ export function* walk(path, opts = {}) { const { pre = always, post = noop } = opts; const top = dir(path); // the stack is going to keep lists of files to examine const stack = []; let next = top.files; // eslint-disable-next-line no-restricted-syntax runNext: while (next !== undefined) { let i = 0; for (; i < next.length; i += 1) { const f = next[i]; yield f; if (f.isdir && pre(f)) { const d = dir(f.path); // If we need to recurse into the subdirectory, push the work // yet to do here, then process the subdirectory's files. if (d.files.length > 0) { stack.push(next.slice(i + 1)); next = d.files; continue runNext; } // If not, we can just continue through the current directory. post(); } } // If we've exhausted the slice, we're popping a directory if (i === next.length && stack.length > 0) post(); next = stack.pop(); } } /* eslint-enable */