fs.readdir() with filter, recursion, absolute paths, promises, streams, and more!
Installations
npm install @mrmlnc/readdir-enhanced
Releases
Unable to fetch releases
Developer
Developer Guide
Module System
CommonJS
Min. Node Version
>=4
Typescript Support
No
Node Version
9.4.0
NPM Version
5.6.0
Statistics
86 Stars
295 Commits
4 Forks
4 Watching
4 Branches
6 Contributors
Updated on 14 May 2024
Languages
JavaScript (68.26%)
TypeScript (31.74%)
Total Downloads
Cumulative downloads
Total Downloads
1,586,511,596
Last day
-6.1%
644,103
Compared to previous day
Last week
2.2%
3,520,037
Compared to previous week
Last month
21.5%
14,330,311
Compared to previous month
Last year
-40.4%
169,950,483
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Enhanced fs.readdir()
Features
-
Fully backward-compatible drop-in replacement for
fs.readdir()
andfs.readdirSync()
-
Can crawl sub-directories - you can even control which ones
-
Supports filtering results using globs, regular expressions, or custom logic
-
Can return absolute paths
-
Can return
fs.Stats
objects rather than just paths -
Exposes additional APIs: Promise, Stream, EventEmitter, and Async Iterator.
Example
1import readdir from "@jsdevtools/readdir-enhanced"; 2import through2 from "through2"; 3 4// Synchronous API 5let files = readdir.sync("my/directory"); 6 7// Callback API 8readdir.async("my/directory", (err, files) => { ... }); 9 10// Promises API 11readdir.async("my/directory") 12 .then((files) => { ... }) 13 .catch((err) => { ... }); 14 15// Async/Await API 16let files = await readdir.async("my/directory"); 17 18// Async Iterator API 19for await (let item of readdir.iterator("my/directory")) { 20 ... 21} 22 23// EventEmitter API 24readdir.stream("my/directory") 25 .on("data", (path) => { ... }) 26 .on("file", (path) => { ... }) 27 .on("directory", (path) => { ... }) 28 .on("symlink", (path) => { ... }) 29 .on("error", (err) => { ... }); 30 31// Streaming API 32let stream = readdir.stream("my/directory") 33 .pipe(through2.obj(function(data, enc, next) { 34 console.log(data); 35 this.push(data); 36 next(); 37 });
Installation
Install using npm:
1npm install @jsdevtools/readdir-enhanced
Pick Your API
Readdir Enhanced has multiple APIs, so you can pick whichever one you prefer. Here are some things to consider about each API:
Function | Returns | Syntax | Blocks the thread? | Buffers results? |
---|---|---|---|---|
readdirSync() readdir.sync() | Array | Synchronous | yes | yes |
readdir() readdir.async() readdirAsync() | Promise | async/await Promise.then() callback | no | yes |
readdir.iterator() readdirIterator() | Iterator | for await...of | no | no |
readdir.stream() readdirStream() | Readable Stream | stream.on("data") stream.read() stream.pipe() | no | no |
Blocking the Thread
The synchronous API blocks the thread until all results have been read. Only use this if you know the directory does not contain many items, or if your program needs the results before it can do anything else.
Buffered Results
Some APIs buffer the results, which means you get all the results at once (as an array). This can be more convenient to work with, but it can also consume a significant amount of memory, depending on how many results there are. The non-buffered APIs return each result to you one-by-one, which means you can start processing the results even while the directory is still being read.
Alias Exports
The example above imported the readdir
default export and used its properties, such as readdir.sync
or readdir.async
to call specific APIs. For convenience, each of the different APIs is exported as a named function that you can import directly.
readdir.sync()
is also exported asreaddirSync()
readdir.async()
is also exported asreaddirAsync()
readdir.iterator()
is also exported asreaddirIterator()
readdir.stream()
is also exported asreaddirStream()
Here's how to import named exports rather than the default export:
1import { readdirSync, readdirAsync, readdirIterator, readdirStream } from "@jsdevtools/readdir-enhanced";
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.
Crawl Subdirectories
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.
1import readdir from "@jsdevtools/readdir-enhanced"; 2 3readdir("my/directory", {deep: true}, (err, files) => { 4 console.log(files); 5 // => subdir1 6 // => subdir1/file.txt 7 // => subdir1/subdir2 8 // => subdir1/subdir2/file.txt 9 // => subdir1/subdir2/subdir3 10 // => subdir1/subdir2/subdir3/file.txt 11});
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
.
1import readdir from "@jsdevtools/readdir-enhanced"; 2 3readdir("my/directory", {deep: 2}, (err, files) => { 4 console.log(files); 5 // => subdir1 6 // => subdir1/file.txt 7 // => subdir1/subdir2 8 // => subdir1/subdir2/file.txt 9 // => subdir1/subdir2/subdir3 10});
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
[\\/]
.
1import readdir from "@jsdevtools/readdir-enhanced"; 2 3// Only crawl the "lib" and "bin" subdirectories 4// (notice that the "node_modules" subdirectory does NOT get crawled) 5readdir("my/directory", {deep: /lib|bin/}, (err, files) => { 6 console.log(files); 7 // => bin 8 // => bin/cli.js 9 // => lib 10 // => lib/index.js 11 // => node_modules 12 // => package.json 13});
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 additionalpath
anddepth
properties. Thepath
is relative to the starting directory by default, but you can customize this viaoptions.basePath
. Thedepth
is the number of subdirectories beneath the base path (seeoptions.deep
).
1import readdir from "@jsdevtools/readdir-enhanced"; 2 3// Crawl all subdirectories, except "node_modules" 4function ignoreNodeModules (stats) { 5 return stats.path.indexOf("node_modules") === -1; 6} 7 8readdir("my/directory", {deep: ignoreNodeModules}, (err, files) => { 9 console.log(files); 10 // => bin 11 // => bin/cli.js 12 // => lib 13 // => lib/index.js 14 // => node_modules 15 // => package.json 16});
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
[\\/]
.
1import readdir from "@jsdevtools/readdir-enhanced"; 2 3// Find all .txt files 4readdir("my/directory", {filter: "*.txt"}); 5 6// Find all package.json files 7readdir("my/directory", {filter: "**/package.json", deep: true}); 8 9// Find everything with at least one number in the name 10readdir("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 additionalpath
anddepth
properties. Thepath
is relative to the starting directory by default, but you can customize this viaoptions.basePath
. Thedepth
is the number of subdirectories beneath the base path (seeoptions.deep
).
1import readdir from "@jsdevtools/readdir-enhanced"; 2 3// Only return file names containing an underscore 4function myFilter(stats) { 5 return stats.isFile() && stats.path.indexOf("_") >= 0; 6} 7 8readdir("my/directory", {filter: myFilter}, (err, files) => { 9 console.log(files); 10 // => __myFile.txt 11 // => my_other_file.txt 12 // => img_1.jpg 13 // => node_modules 14});
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. Setting the stats
option returns an array of fs.Stats
objects instead of path 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 additionalpath
anddepth
properties. Thepath
is relative to the starting directory by default, but you can customize this viaoptions.basePath
. Thedepth
is the number of subdirectories beneath the base path (seeoptions.deep
).
1import readdir from "@jsdevtools/readdir-enhanced"; 2 3readdir("my/directory", { stats: true }, (err, stats) => { 4 for (let stat of stats) { 5 console.log(`${stat.path} was created at ${stat.birthtime}`); 6 } 7});
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.
1import readdir from "@jsdevtools/readdir-enhanced"; 2import { resolve } from "path"; 3 4// Get absolute paths 5let absPath = resolve("my/dir"); 6readdir("my/directory", {basePath: absPath}, (err, files) => { 7 console.log(files); 8 // => /absolute/path/to/my/directory/file1.txt 9 // => /absolute/path/to/my/directory/file2.txt 10 // => /absolute/path/to/my/directory/subdir 11}); 12 13// Get paths relative to the working directory 14readdir("my/directory", {basePath: "my/directory"}, (err, files) => { 15 console.log(files); 16 // => my/directory/file1.txt 17 // => my/directory/file2.txt 18 // => my/directory/subdir 19});
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.
1import readdir from "@jsdevtools/readdir-enhanced"; 2 3// Always use Windows path separators 4readdir("my/directory", {sep: "\\", deep: true}, (err, files) => { 5 console.log(files); 6 // => subdir1 7 // => subdir1\file.txt 8 // => subdir1\subdir2 9 // => subdir1\subdir2\file.txt 10 // => subdir1\subdir2\subdir3 11 // => subdir1\subdir2\subdir3\file.txt 12});
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
.
1import readdir from "@jsdevtools/readdir-enhanced"; 2 3function myCustomReaddirMethod(dir, callback) { 4 callback(null, ["__myFile.txt"]); 5} 6 7let options = { 8 fs: { 9 readdir: myCustomReaddirMethod 10 } 11}; 12 13readdir("my/directory", options, (err, files) => { 14 console.log(files); 15 // => __myFile.txt 16});
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.
1import { readdir, readdirSync } from "@jsdevtools/readdir-enhanced"; 2 3// Use it just like Node's built-in fs.readdir function 4readdir("my/directory", (er, files) => { ... }); 5 6// Use it just like Node's built-in fs.readdirSync function 7let files = readdirSync("my/directory");
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.
1import readdir from "@jsdevtools/readdir-enhanced"; 2 3// Build the stream using the Streaming API 4let stream = readdir.stream("my/directory") 5 .on("data", (path) => { ... }); 6 7// Listen to the end event to detect the end of the stream 8stream.on("end", () => { 9 console.log("Stream finished!"); 10});
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()
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.
Contributing
Contributions, enhancements, and bug-fixes are welcome! Open 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.
This package is Treeware. If you use it in production, then we ask that you buy the world a tree to thank us for our work. By contributing to the Treeware forest you’ll be creating employment for local families and restoring wildlife habitats.
Big Thanks To
Thanks to these awesome companies for their support of Open Source developers ❤
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
dependency not pinned by hash detected -- score normalized to 3
Details
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/CI-CD.yaml:85: update your workflow using https://app.stepsecurity.io/secureworkflow/JS-DevTools/readdir-enhanced/CI-CD.yaml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/CI-CD.yaml:88: update your workflow using https://app.stepsecurity.io/secureworkflow/JS-DevTools/readdir-enhanced/CI-CD.yaml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/CI-CD.yaml:97: update your workflow using https://app.stepsecurity.io/secureworkflow/JS-DevTools/readdir-enhanced/CI-CD.yaml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/CI-CD.yaml:108: update your workflow using https://app.stepsecurity.io/secureworkflow/JS-DevTools/readdir-enhanced/CI-CD.yaml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/CI-CD.yaml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/JS-DevTools/readdir-enhanced/CI-CD.yaml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/CI-CD.yaml:39: update your workflow using https://app.stepsecurity.io/secureworkflow/JS-DevTools/readdir-enhanced/CI-CD.yaml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/CI-CD.yaml:59: update your workflow using https://app.stepsecurity.io/secureworkflow/JS-DevTools/readdir-enhanced/CI-CD.yaml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/CI-CD.yaml:71: update your workflow using https://app.stepsecurity.io/secureworkflow/JS-DevTools/readdir-enhanced/CI-CD.yaml/master?enable=pin
- Info: 0 out of 4 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 4 third-party GitHubAction dependencies pinned
- Info: 2 out of 2 npmCommand dependencies pinned
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Warn: no topLevel permission defined: .github/workflows/CI-CD.yaml:1
- Info: no jobLevel write permissions found
Reason
Found 1/29 approved changesets -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'master'
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 2 are checked with a SAST tool
Reason
28 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92
- Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c
- Warn: Project is vulnerable to: GHSA-ff7x-qrg7-qggm
- Warn: Project is vulnerable to: GHSA-2j2x-2gpw-g8fm
- Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5
- Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6
- Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97
- Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj
- Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37
- Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h
- Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9
- Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3
- Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h
- Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9
- Warn: Project is vulnerable to: GHSA-g6ww-v8xp-vmwg
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
- Warn: Project is vulnerable to: GHSA-hxcc-f52p-wc94
- Warn: Project is vulnerable to: GHSA-4rq4-32rv-6wp6
- Warn: Project is vulnerable to: GHSA-64g7-mvw6-v9qj
- Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v
- Warn: Project is vulnerable to: GHSA-g3ch-rx76-35fx
- Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7
- Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh
Score
2.7
/10
Last Scanned on 2024-11-25
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreOther packages similar to @mrmlnc/readdir-enhanced
readdir-enhanced
fs.readdir with sync, async, streaming, and async iterator APIs + filtering, recursion, absolute paths, etc.
readdir-glob
Recursive fs.readdir with streaming API and glob filtering.
fs-readdir-recursive
Recursively read a directory
recursive-readdir
Get an array of all files in a directory and subdirectories.