@mrmlnc/readdir-enhanced review
@mrmlnc/readdir-enhanced 2.2.1 is a 2018 fork of a recursive Node directory reader. Beyond `fs.readdir`, it supplies blocking, Promise, callback, and object-stream forms; depth controls; glob, RegExp, and function filters; Stats-returning variants; output path rewriting; and injectable filesystem methods. The package on npm is frozen under the @mrmlnc scope, while its repository now documents the later @jsdevtools successor. Read the 2.2.1 tarball's API when maintaining this fork.
Our @mrmlnc/readdir-enhanced 2.2.1 install occupied 1 MB and passed npm audit, but its npm scope has been frozen since 2018 and its live repository documents a successor. Keep it only for existing version 2 callers, then migrate to Node core or a maintained walker.
We installed it
| Install | ✓ · 1.7s | 3 packages on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package |
| Browser | n/a | could not be bundled for the browser (Node-only code, most likely) |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does @mrmlnc/readdir-enhanced install cleanly?
Yes. In a fresh container with an empty cache, npm install @mrmlnc/readdir-enhanced finished in 2 seconds, leaving 3 packages and 1 MB on disk. npm audit reported no known vulnerabilities.
Can @mrmlnc/readdir-enhanced run in a browser?
Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.
Does @mrmlnc/readdir-enhanced work with both ESM and CommonJS?
Yes. Both import '@mrmlnc/readdir-enhanced' and require('@mrmlnc/readdir-enhanced') worked in Node 22 in our run. The package is published as CommonJS.
Does @mrmlnc/readdir-enhanced include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
@mrmlnc/readdir-enhanced or readdirp: which should you use?
readdirp: Use it for a current recursive directory stream with bounded-memory consumption. Our @mrmlnc/readdir-enhanced 2.2.1 install occupied 1 MB and passed npm audit, but its npm scope has been frozen since 2018 and its live repository documents a successor.
When should you not use @mrmlnc/readdir-enhanced?
You are choosing a new walker: this scope has not released since 2018 and the linked project moved to @jsdevtools
Use it if
- Existing code imports the @mrmlnc scope and calls its version 2 sync, async, stream, or `.stat` aliases
- A Node 4-compatible dependency needs recursive walking and filters behind an `fs.readdir`-like call
- Your virtual filesystem can provide `readdir`, `lstat`, and `stat` to the package's injected `fs` option
- You need object-stream results from this exact legacy API while preparing a controlled migration
- You are choosing a new walker: this scope has not released since 2018 and the linked project moved to @jsdevtools
- You plan to copy the live repository examples: its async iterator and `stats: true` API belong to the successor, not 2.2.1
- The directory tree may contain symlink cycles: deep traversal follows directory links without a visited-inode guard
- Current Node's `fs.promises.readdir` with directory entries or recursion already covers your needs without 2 old dependencies
- You need browser code: our esbuild browser build failed because this package operates on Node filesystem APIs
Setup reality
We installed @mrmlnc/readdir-enhanced 2.2.1 in our sandbox in 1.7 seconds. It produced 3 packages and 1 MB on disk, and npm audit reported 0 known vulnerabilities. The release is 112 KB unpacked, declares 2 direct dependencies, 0 peers, the MIT license, and Node 4 or newer. No native compilation, credential, or config step was involved.
The package is CommonJS with no exports map. Both require() and ESM import worked in Node 22, and our inspection found bundled TypeScript declarations. The browser build failed, which is expected for a directory walker using Node filesystem calls. Import the exact @mrmlnc name; the README packed in 2.2.1 shows the unscoped package in several examples and can silently lead users to another artifact.
Async and sync calls buffer all results. The stream form emits entries as traversal proceeds and is the safer choice for a large tree. deep defaults off and accepts a boolean, depth, glob, RegExp, or predicate. Function filters and recursion predicates receive Stats objects extended with path and depth, so those modes add metadata work. Glob matching uses forward slashes even on Windows.
Version 2 returns Stats through methods such as async.stat() and stream.stat(); do not copy the successor's stats: true option. basePath changes returned labels without changing the directory read. An injected fs object is merged with Node defaults, so a remote adapter should provide readdir, lstat, and stat together. Deep traversal follows directory symlinks and does not track visited inodes, making cycle prevention the caller's job.
Patterns
Read one directory with a Promise read-async
const readdir = require('@mrmlnc/readdir-enhanced');
const entries = await readdir('./src');The default async call buffers every result before its Promise resolves.
Use the callback alias read-callback
readdir.async('./src', (error, entries) => {
if (error) throw error;
console.log(entries);
});The callback changes result delivery only; traversal still buffers the full array.
Read a small startup directory synchronously read-sync
const entries = readdir.sync('./templates');`sync()` blocks the Node thread until all selected entries have been read and buffered.
Traverse all descendant directories walk-recursively
const entries = await readdir('./src', { deep: true });Directories and files are both returned. Directory symlinks are followed without cycle detection.
Stop after two directory levels limit-depth
const entries = await readdir('./src', { deep: 2 });Depth is counted below the starting path. Negative and fractional depth values are invalid.
Find package manifests filter-glob
const files = await readdir('.', {
deep: true,
filter: '**/package.json'
});Glob strings always use forward slashes, including on Windows. Filtering does not stop traversal into unmatched directories.
Avoid entering node_modules prune-directory
const files = await readdir('.', {
deep: stat => !stat.path.split(/[\\/]/).includes('node_modules')
});Use the `deep` predicate to prevent traversal. A result filter can hide a directory after the walker already entered it.
Request Stats objects with the version 2 API return-stats
const stats = await readdir.async.stat('./uploads', { deep: 1 });
for (const stat of stats) console.log(stat.path, stat.depth, stat.size);In 2.2.1, `.stat()` variants return Stats. The successor's `stats: true` form is a different API.
Consume a large walk as a stream stream-tree
const walk = readdir.stream('./archive', { deep: true });
walk.on('data', entry => processEntry(entry));
walk.on('error', error => console.error(error));A data listener starts flowing consumption. Always attach an error listener before traversal reaches inaccessible paths.
Label results with an absolute base absolute-results
const path = require('node:path');
const root = path.resolve('./assets');
const entries = await readdir(root, { basePath: root });`basePath` prefixes output strings. It does not change which root the package opens.
Walk a virtual filesystem inject-filesystem
const entries = await readdir('/virtual', {
deep: true,
fs: {
readdir: vfs.readdir.bind(vfs),
lstat: vfs.lstat.bind(vfs),
stat: vfs.stat.bind(vfs)
}
});Overrides merge with Node's local fs methods. Supply all three calls to avoid mixing virtual paths with local metadata.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| readdirp | npm | Use it for a current recursive directory stream with bounded-memory consumption. |
| fast-glob | npm | Use it when matching glob patterns is the main job rather than emulating readdir. |
| globby | npm | Use it for higher-level glob searches with ignore patterns and modern Promise APIs. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

