Micro library to walk a directory tree with middlewares.
dir-files does not decide how to walk. It gives you a queue of entries and runs
a chain of plugins over each one; the plugins decide what gets read, what gets
queued next, and what gets skipped. Reading a directory, descending into it and
filtering by pattern are all just plugins, and so is anything you write yourself.
importpathfrom'node:path';importdirFilesfrom'dir-files';constdfp=dirFiles.plugins;dirFiles({path: process.cwd(),plugins: [dfp.skip(functionskipSpecial(file){constc=file.name.charAt(0);returnc==='.'||c==='$'||file.name==='node_modules';}),dfp.stat(),dfp.queueDir(),dfp.readDir(),dfp.queueDirFiles(),dfp.skip(functionskipEmptyNameOrDir(file){return!file.name||file.stat.isDirectory();}),functionprintFile(file){console.log('~ '+path.join(file.dir.sub,file.name));},],callback(err){if(err)throwerr;},});~ README.md
~ examples/cli.js
~ examples/dynamic.js
~ examples/glob.js
~ examples/recursive.js
~ package.json
~ src/index.ts
~ src/plugins/glob.ts
...
npm install dir-filesRequires Node 20 or newer. The package is ESM only and ships TypeScript declarations; there is no CommonJS build.
The traversal is a queue of entries. Each entry is either a named entry —
a child of some directory that has not been entered yet — or an entered
directory, whose name is ''.
For every entry taken off the queue, the plugin chain runs in order:
- Each plugin may declare a
filter. If it returns falsy, that plugin is skipped for this entry and the chain moves on. - The plugin body runs. Synchronous plugins return; asynchronous ones call a callback.
- Returning (or calling back with)
this.SKIPends the chain for this entry. Returning anything else truthy is treated as an error.
Plugins move work along by mutating this.queue. queueDir turns a named
directory into an entered one; readDir lists an entered directory into
dir.files; queueDirFiles turns that listing back into named entries. New
work goes on the front of the queue, which makes the walk depth-first.
Everything is mutable on purpose: a plugin can push onto this.queue, swap out
this.plugins mid-traversal, or hang extra data on the entry. See
examples/dynamic.js for a chain that rebuilds itself
per entry.
- API reference — options, the context, entries, errors.
- Plugins — every bundled plugin, and how to write one.
- Migrating from 1.0 — what changed in 1.1.
- Development — running the checks, and Windows notes.
Run them against a build (npm run build first):
node examples/recursive.js # print every file in the repo
node examples/glob.js # filter with include/exclude patterns
node examples/dynamic.js # build the plugin chain per entry
node examples/cli.js <path># walk a path and report per-plugin timingsnpm install
npm run check # lint + typecheck + test + build
npm test# vitest
npm run coverage # vitest with a coverage report
npm run build # vite library build into dist/See docs/development.md for more, including a known issue with resolving local binaries on Windows under Volta.
MIT