@jkcfg/std
Version:
jk standard library
90 lines (89 loc) • 3.14 kB
JavaScript
/**
* @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 */