UNPKG

@aleclarson/watchlist

Version:

Recursively watch a list of directories & run a command on any file system changes

82 lines (69 loc) 1.94 kB
const { promisify } = require('util'); const { resolve, join } = require('path'); const { existsSync, readdir, stat, watch:fsw } = require('fs'); const toStats = promisify(stat); const toRead = promisify(readdir); // modified: lukeed/totalist async function walk(dir, callback, pre='') { await toRead(dir).then(arr => { return Promise.all( arr.map(str => { let abs = join(dir, str); return toStats(abs).then(stats => { if (!stats.isDirectory()) return; callback(join(pre, str), abs, stats); return walk(abs, callback, join(pre, str)); }); }) ); }); } async function setup(dir, onChange) { let output = {}; try { output[dir] = fsw(dir, { recursive: true }, onChange.bind(0, dir)); } catch (err) { if (err.code !== 'ERR_FEATURE_UNAVAILABLE_ON_PLATFORM') throw err; output[dir] = fsw(dir, onChange.bind(0, dir)); await walk(dir, (rel, abs) => { output[abs] = fsw(abs, onChange.bind(0, abs)); }); } return output; } async function watch(list, callback, opts={}) { const cwd = resolve('.', opts.cwd || '.'); const dirs = new Set(list.map(str => resolve(cwd, str)).filter(existsSync)); const ignores = ['node_modules'].concat(opts.ignore || []).map(x => new RegExp(x, 'i')); let wip = 0; let delay = 0; const Watchers = new Map; async function handle() { wip = 1; try { await callback(); } catch (error) { console.error(error); } if (--wip) handle(); } // TODO: Catch `EPERM` on Windows for removed dir async function onChange(dir, type, filename) { if (wip > 1 || ignores.some(x => x.test(filename))) return; clearTimeout(delay); delay = setTimeout(() => { if (wip++) return; if (opts.clear) console.clear(); handle(); }, 100); } let dir, output, key; for (dir of dirs) { output = await setup(dir, onChange); for (key in output) Watchers.set(key, output[key]); } if (opts.eager) { await handle(); } } exports.watch = watch;