Enhanced fs.readdir()
readdir-enhanced
is a backward-compatible drop-in replacement for fs.readdir()
and fs.readdirSync()
with tons of extra features (filtering, recursion, absolute paths, stats, and more) as well as additional APIs for Promises, Streams, and EventEmitters.
Pick Your API
readdir-enhanced
has multiple APIs, so you can pick whichever one you prefer. There are three main APIs:
-
Synchronous API
aliases: readdir.sync
, readdir.readdirSync
Blocks the thread until all directory contents are read, and then returns all the results.
-
Async API
aliases: readdir
, readdir.async
, readdir.readdirAsync
Reads the starting directory contents asynchronously and buffers all the results until all contents have been read. Supports callback or Promise syntax (see example below).
-
Streaming API
aliases: readdir.stream
, readdir.readdirStream
The streaming API reads the starting directory asynchronously and returns the results in real-time as they are read. The results can be piped to other Node.js streams, or you can listen for specific events via the EventEmitter interface. (see example below)
const readdir = require('readdir-enhanced');
const through2 = require('through2');
let files = readdir.sync('my/directory');
readdir.async('my/directory', function(err, files) { ... });
readdir.async('my/directory')
.then(function(files) { ... })
.catch(function(err) { ... });
let files = await readdir.async('my/directory');
readdir.stream('my/directory')
.on('data', function(path) { ... })
.on('file', function(path) { ... })
.on('directory', function(path) { ... })
.on('symlink', function(path) { ... })
.on('error', function(err) { ... });
let stream = readdir.stream('my/directory')
.pipe(through2.obj(function(data, enc, next) {
console.log(data);
this.push(data);
next();
});
A Note on Streams
The readdir-enhanced
streaming API follows the Node.js streaming API. A lot of questions around the streaming API can be answered by reading the Node.js documentation.. However, we've tried to answer the most common questions here.
Stream Events
All events in the Node.js streaming API are supported by readdir-enhanced
. These events include "end", "close", "drain", "error", plus more. An exhaustive list of events is available in the Node.js documentation.
Detect when the Stream has finished
Using these events, we can detect when the stream has finished reading files.
const readdir = require('readdir-enhanced');
let stream = readdir.stream('my/directory')
.on('data', function(path) { ... });
stream.on('end', function() {
console.log('Stream finished!');
});
Paused Streams vs. Flowing Streams
As with all Node.js streams, a readdir-enhanced
stream starts in "paused mode". For the stream to start emitting files, you'll need to switch it to "flowing mode".
There are many ways to trigger flowing mode, such as adding a stream.data(function() { ... })
handler, using stream.pipe()
or calling stream.resume()
.
Unless you trigger flowing mode, your stream will stay paused and you won't receive any file events.
More information on paused vs. flowing mode can be found in the Node.js documentation.
Enhanced Features
readdir-enhanced
adds several features to the built-in fs.readdir()
function. All of the enhanced features are opt-in, which makes readdir-enhanced
fully backward compatible by default. You can enable any of the features by passing-in an options
argument as the second parameter.
Recursion
By default, readdir-enhanced
will only return the top-level contents of the starting directory. But you can set the deep
option to recursively traverse the subdirectories and return their contents as well.
Crawl ALL subdirectories
The deep
option can be set to true
to traverse the entire directory structure.
const readdir = require('readdir-enhanced');
readdir('my/directory', {deep: true}, function(err, files) {
console.log(files);
});
Crawl to a specific depth
The deep
option can be set to a number to only traverse that many levels deep. For example, calling readdir('my/directory', {deep: 2})
will return subdir1/file.txt
and subdir1/subdir2/file.txt
, but it won't return subdir1/subdir2/subdir3/file.txt
.
const readdir = require('readdir-enhanced');
readdir('my/directory', {deep: 2}, function(err, files) {
console.log(files);
});
Crawl subdirectories by name
For simple use-cases, you can use a regular expression or a glob pattern to crawl only the directories whose path matches the pattern. The path is relative to the starting directory by default, but you can customize this via options.basePath
.
NOTE: Glob patterns always use forward-slashes, even on Windows. This does not apply to regular expressions though. Regular expressions should use the appropraite path separator for the environment. Or, you can match both types of separators using [\\/]
.
const readdir = require('readdir-enhanced');
readdir('my/directory', {deep: /lib|bin/}, function(err, files) {
console.log(files);
});
Custom recursion logic
For more advanced recursion, you can set the deep
option to a function that accepts an fs.Stats
object and returns a truthy value if the starting directory should be crawled.
NOTE: The fs.Stats
object that's passed to the function has additional path
and depth
properties. The path
is relative to the starting directory by default, but you can customize this via options.basePath
. The depth
is the number of subdirectories beneath the base path (see options.deep
).
const readdir = require('readdir-enhanced');
function ignoreNodeModules (stats) {
return stats.path.indexOf('node_modules') === -1;
}
readdir('my/directory', {deep: ignoreNodeModules}, function(err, files) {
console.log(files);
});
Filtering
The filter
option lets you limit the results based on any criteria you want.
Filter by name
For simple use-cases, you can use a regular expression or a glob pattern to filter items by their path. The path is relative to the starting directory by default, but you can customize this via options.basePath
.
NOTE: Glob patterns always use forward-slashes, even on Windows. This does not apply to regular expressions though. Regular expressions should use the appropraite path separator for the environment. Or, you can match both types of separators using [\\/]
.
const readdir = require('readdir-enhanced');
readdir('my/directory', {filter: '*.txt'});
readdir('my/directory', {filter: '**/package.json', deep: true});
readdir('my/directory', {filter: /\d+/});
Custom filtering logic
For more advanced filtering, you can specify a filter function that accepts an fs.Stats
object and returns a truthy value if the item should be included in the results.
NOTE: The fs.Stats
object that's passed to the filter function has additional path
and depth
properties. The path
is relative to the starting directory by default, but you can customize this via options.basePath
. The depth
is the number of subdirectories beneath the base path (see options.deep
).
const readdir = require('readdir-enhanced');
function myFilter(stats) {
return stats.isFile() && stats.path.indexOf('_') >= 0;
}
readdir('my/directory', {filter: myFilter}, function(err, files) {
console.log(files);
});
Base Path
By default all readdir-enhanced
functions return paths that are relative to the starting directory. But you can use the basePath
option to customize this. The basePath
will be prepended to all of the returned paths. One common use-case for this is to set basePath
to the absolute path of the starting directory, so that all of the returned paths will be absolute.
const readdir = require('readdir-enhanced');
const path = require('path');
let absPath = path.resolve('my/dir');
readdir('my/directory', {basePath: absPath}, function(err, files) {
console.log(files);
});
readdir('my/directory', {basePath: 'my/directory'}, function(err, files) {
console.log(files);
});
Path Separator
By default, readdir-enhanced
uses the correct path separator for your OS (\
on Windows, /
on Linux & MacOS). But you can set the sep
option to any separator character(s) that you want to use instead. This is usually used to ensure consistent path separators across different OSes.
const readdir = require('readdir-enhanced');
readdir('my/directory', {sep: '\\', deep: true}, function(err, files) {
console.log(files);
});
Custom FS methods
By default, readdir-enhanced
uses the default Node.js FileSystem module for methods like fs.stat
, fs.readdir
and fs.lstat
. But in some situations, you can want to use your own FS methods (FTP, SSH, remote drive and etc). So you can provide your own implementation of FS methods by setting options.fs
or specific methods, such as options.fs.stat
.
const readdir = require('readdir-enhanced');
function myCustomReaddirMethod(dir, callback) {
callback(null, ['__myFile.txt']);
}
let options = {
fs: {
readdir: myCustomReaddirMethod
}
};
readdir('my/directory', options, function(err, files) {
console.log(files);
});
Get fs.Stats
objects instead of strings
All of the readdir-enhanced
functions listed above return an array of strings (paths). But in some situations, the path isn't enough information. So, readdir-enhanced
provides alternative versions of each function, which return an array of fs.Stats
objects instead of strings. The fs.Stats
object contains all sorts of useful information, such as the size, the creation date/time, and helper methods such as isFile()
, isDirectory()
, isSymbolicLink()
, etc.
NOTE: The fs.Stats
objects that are returned also have additional path
and depth
properties. The path
is relative to the starting directory by default, but you can customize this via options.basePath
. The depth
is the number of subdirectories beneath the base path (see options.deep
).
To get fs.Stats
objects instead of strings, just add the word "Stat" to the function name. As with the normal functions, each one is aliased (e.g. readdir.async.stat
is the same as readdir.readdirAsyncStat
), so you can use whichever naming style you prefer.
const readdir = require('readdir-enhanced');
let stats = readdir.sync.stat('my/directory');
let stats = readdir.readdirSyncStat('my/directory');
readdir.async.stat('my/directory', function(err, stats) { ... });
readdir.readdirAsyncStat('my/directory', function(err, stats) { ... });
readdir.stream.stat('my/directory')
.on('data', function(stat) { ... })
.on('file', function(stat) { ... })
.on('directory', function(stat) { ... })
.on('symlink', function(stat) { ... });
readdir.readdirStreamStat('my/directory')
.on('data', function(stat) { ... })
.on('file', function(stat) { ... })
.on('directory', function(stat) { ... })
.on('symlink', function(stat) { ... });
Backward Compatible
readdir-enhanced
is fully backward-compatible with Node.js' built-in fs.readdir()
and fs.readdirSync()
functions, so you can use it as a drop-in replacement in existing projects without affecting existing functionality, while still being able to use the enhanced features as needed.
const readdir = require('readdir-enhanced');
let readdirSync = readdir.sync;
readdir('my/directory', function(err, files) { ... });
let files = readdirSync('my/directory');
Contributing
Contributions, enhancements, and bug-fixes are welcome! File an issue on GitHub and submit a pull request.
Building
To build the project locally on your computer:
-
Clone this repo
git clone https://github.com/JS-DevTools/readdir-enhanced.git
-
Install dependencies
npm install
-
Run the tests
npm test
License
readdir-enhanced
is 100% free and open-source, under the MIT license. Use it however you want.
Big Thanks To
Thanks to these awesome companies for their support of Open Source developers ❤