@mrmlnc/readdir-enhanced
@mrmlnc/readdir-enhanced 2.2.1 is a CommonJS fork of the old readdir-enhanced directory walker. It extends Node's readdir with synchronous, callback, Promise, and object-mode stream APIs; optional recursive traversal; glob, regular-expression, or function filters; custom output roots and separators; Stats results; and replaceable filesystem methods. The fork was published twice in one day in 2018 and then stopped. Its GitHub URL now redirects to the later JS-DevTools project, whose current README and package name describe a different successor release line.
Do not start new work on the @mrmlnc fork; its documentation and repository identity have drifted to a successor package. Existing users should pin it while migrating to @jsdevtools/readdir-enhanced, readdirp, fast-glob, or Node core based on the actual traversal need.
Use it if
- You are maintaining a dependency that already imports the @mrmlnc scope and depends on its version 2 method aliases
- You need one old-Node-compatible API combining recursive traversal, filtering, Promise results, and object-mode streams
- You need to inject readdir, stat, or lstat implementations for a virtual filesystem while preserving this package's traversal contract
- You are choosing a package for new code: the repository now documents @jsdevtools/readdir-enhanced 6.x, while this @mrmlnc fork has had no npm release since February 2018
- You might copy examples from the current repository README: it uses the successor scope and newer APIs such as iterator() and stats: true that version 2.2.1 does not provide
- You recursively scan untrusted directory trees: the source follows directory symlinks and keeps no visited-inode set, so a symlink cycle can cause unbounded traversal when deep is true
- You only need modern Node basics: fs.promises.readdir supports withFileTypes and recursive options in current Node releases without two legacy dependencies
- You need predictable support ownership: npm identifies @mrmlnc/readdir-enhanced, its packaged README calls itself a fork but shows unscoped require examples, and the linked repository has moved to another organization and package scope
Setup reality
Install with npm install @mrmlnc/readdir-enhanced and import that exact scope. The README packed in 2.2.1 incorrectly shows require('readdir-enhanced'), which selects a different npm package. This release is CommonJS-only, supports Node 4 and later, includes types.d.ts, and installs call-me-maybe plus glob-to-regexp; there are no native builds, peer dependencies, credentials, or config files. The default async function buffers every result before its Promise or callback completes. sync() also buffers and blocks the event loop. Use stream() for a large tree, attach an error listener, and consume or resume it so traversal actually starts. Recursion is off by default; deep: true follows directory symlinks because the implementation resolves link targets and retains an isSymbolicLink marker, with no cycle detection. Glob strings always match forward-slash paths, even on Windows, while RegExp matching uses the configured platform separator. Function filters and recursion predicates receive a Stats object extended with path and depth, which adds stat calls. Version 2 uses async.stat(), sync.stat(), and stream.stat() for Stats results; the successor's stats: true option is not in this package's declarations. basePath only changes returned paths, not the directory being read. A custom fs object is merged method by method with local fs defaults, so remote or virtual filesystems should provide readdir, lstat, and stat together to avoid accidentally mixing backends.
Patterns
Read a directory with a Promiseread-directory-async
const readdir = require('@mrmlnc/readdir-enhanced');
const entries = await readdir('./src');
console.log(entries);The default export is the async path-returning function. It buffers the whole result array before resolving.
Use the callback APIread-directory-callback
const readdir = require('@mrmlnc/readdir-enhanced');
readdir.async('./src', (error, entries) => {
if (error) throw error;
console.log(entries);
});async is an alias of the default function. Supplying a callback changes delivery, not traversal or buffering.
Read synchronously for startup workread-directory-sync
const readdir = require('@mrmlnc/readdir-enhanced');
const entries = readdir.sync('./templates');
console.log(entries);sync blocks until every selected entry is read and buffered. Avoid it on request paths or large trees.
Walk every subdirectorywalk-recursively
const readdir = require('@mrmlnc/readdir-enhanced');
const entries = await readdir('./src', { deep: true });deep traversal includes directory entries as well as files and follows directory symlinks without detecting cycles.
Limit recursion depthlimit-recursion-depth
const readdir = require('@mrmlnc/readdir-enhanced');
const entries = await readdir('./src', { deep: 2 });deep must be a non-negative integer, boolean, function, regular expression, or non-empty glob string. Fractions and negative values throw.
Find package files recursivelyfilter-by-glob
const readdir = require('@mrmlnc/readdir-enhanced');
const manifests = await readdir('.', {
deep: true,
filter: '**/package.json',
});Glob matching always uses forward slashes, including on Windows. Filtering results does not by itself stop traversal.
Filter by entry type and sizefilter-with-stats
const readdir = require('@mrmlnc/readdir-enhanced');
const files = await readdir('.', {
deep: true,
filter: (entry) => entry.isFile() && entry.size > 1024,
});A function filter receives a Stats object with added path and depth properties, so the walker must stat each candidate.
Prevent traversal into node_modulesskip-node-modules
const readdir = require('@mrmlnc/readdir-enhanced');
const entries = await readdir('.', {
deep: (entry) => !entry.path.split(/[\\/]/).includes('node_modules'),
});The deep predicate controls whether a directory is entered. A filter alone can hide node_modules results while still spending time walking them.
Return absolute-looking pathsreturn-absolute-paths
const path = require('node:path');
const readdir = require('@mrmlnc/readdir-enhanced');
const root = path.resolve('./assets');
const entries = await readdir(root, { basePath: root });basePath is prepended to results and does not change the directory being read. Pass a base that corresponds to root or labels become misleading.
Return Stats objects in version 2return-stats
const readdir = require('@mrmlnc/readdir-enhanced');
const entries = await readdir.async.stat('./uploads', { deep: 1 });
for (const entry of entries) {
console.log(entry.path, entry.size, entry.depth);
}Use the .stat method variants in 2.2.1. The current successor README's stats: true option is not declared for this release.
Stream a large traversalstream-large-tree
const readdir = require('@mrmlnc/readdir-enhanced');
const stream = readdir.stream('./archive', { deep: true });
stream.on('data', (entry) => console.log(entry));
stream.on('error', (error) => console.error(error));
stream.on('end', () => console.log('done'));Adding a data handler switches the Readable into flowing mode. Always handle error; streaming predicate errors can be emitted while processing continues.
Inject filesystem methodsinject-filesystem
const readdir = require('@mrmlnc/readdir-enhanced');
const entries = await readdir('/virtual', {
deep: true,
fs: {
readdir: virtualFs.readdir.bind(virtualFs),
lstat: virtualFs.lstat.bind(virtualFs),
stat: virtualFs.stat.bind(virtualFs),
},
});Overrides are merged with local fs defaults. Provide all three methods for a remote backend so recursion never mixes virtual directory names with local stats.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @jsdevtools/readdir-enhanced | npm | You want the maintained successor API, including an async iterator and newer Node support |
| readdirp | npm | You want a focused modern recursive directory stream with low memory use |
| fast-glob | npm | Your real task is matching many glob patterns quickly rather than emulating fs.readdir |
| glob | npm | You need full glob semantics, ignore rules, and a widely maintained traversal implementation |