Last active
September 9, 2023 13:43
-
-
Save qwtel/fd82ab097cbe1db50ded9505f183ccb8 to your computer and use it in GitHub Desktop.
[node.js 8+] Recursively get all files in a directory
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Node 8+ | |
// -------------------------------------------------------------- | |
// No external dependencies | |
const { promisify } = require('util'); | |
const { resolve } = require('path'); | |
const fs = require('fs'); | |
const readdir = promisify(fs.readdir); | |
const stat = promisify(fs.stat); | |
async function getFiles(dir) { | |
const subdirs = await readdir(dir); | |
const files = await Promise.all(subdirs.map(async (subdir) => { | |
const res = resolve(dir, subdir); | |
return (await stat(res)).isDirectory() ? getFiles(res) : res; | |
})); | |
return files.reduce((a, f) => a.concat(f), []); | |
} | |
// Usage | |
getFiles(__dirname) | |
.then(files => console.log(files)) | |
.catch(e => console.error(e)); | |
// Node 10.10+ | |
// -------------------------------------------------------------- | |
// Updated for node 10+ with even more whizbang: | |
const { resolve } = require('path'); | |
const { readdir } = require('fs').promises; | |
async function getFiles(dir) { | |
const dirents = await readdir(dir, { withFileTypes: true }); | |
const files = await Promise.all(dirents.map((dirent) => { | |
const res = resolve(dir, dirent.name); | |
return dirent.isDirectory() ? getFiles(res) : res; | |
})); | |
return Array.prototype.concat(...files); | |
} | |
// Note that starting with node 11.15.0 you can use | |
// `files.flat()` instead of `Array.prototype.concat(...files)` | |
// to flatten the array. | |
// Node 11+ | |
// -------------------------------------------------------------- | |
// The following version uses async iterators. | |
const { resolve } = require('path'); | |
const { readdir } = require('fs').promises; | |
async function* getFiles(dir) { | |
const dirents = await readdir(dir, { withFileTypes: true }); | |
for (const dirent of dirents) { | |
const res = resolve(dir, dirent.name); | |
if (dirent.isDirectory()) { | |
yield* getFiles(res); | |
} else { | |
yield res; | |
} | |
} | |
} | |
// Usage has changed because the return type is now an | |
// async iterator instead of a promise: | |
(async () => { | |
for await (const f of getFiles('.')) { | |
console.log(f); | |
} | |
})() | |
// I've written more about async iterators here: | |
// https://qwtel.com/posts/software/async-generators-in-the-wild/ |
@cleidigh resolve
here is actually path.resolve
which takes multiple paths and returns a single value.
Note the const { resolve } = require('path');
in the snippet.
@mrchief
Thanks! I got completely hung up on the resolve and missed the reference to the path module.
I'm trying to adapt this using a Mozilla Thunderbird DirectoryInterator as opposed to readDir.
I have been trying every possible combination of promise handling , but I am obviously flummoxed.
Removing the unnecessary path combination in my case still fails.
I'm going to keep experimenting but I may post my best if I can't get it. This approach is certainly what I need to use just with some tweaks that Confound me.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@qwtel
Great article/examples. Despite having years of embedded multitasking OS development background, Promises just have so many gotchas and ambiguities from my perspective. Still anyway question:
I'm thoroughly confused about the following statements:
Everything I have read is very clear that Promise.resolve() can only return one value. Using any object/array can obviously get around that.
What paradigm allows the above resolve with two parameters? More importantly resolve is undefined (executing under Thunderbird)
with a directory iterator so I had to do some adaptation, but I don't think the resolve question changes.
Thanks
@cleidigh