Skip to content

Instantly share code, notes, and snippets.

@qwtel
Last active September 9, 2023 13:43
Show Gist options
  • Save qwtel/fd82ab097cbe1db50ded9505f183ccb8 to your computer and use it in GitHub Desktop.
Save qwtel/fd82ab097cbe1db50ded9505f183ccb8 to your computer and use it in GitHub Desktop.
[node.js 8+] Recursively get all files in a directory
// 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/
@mrchief
Copy link

mrchief commented Jan 16, 2020

@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.

@cleidigh
Copy link

@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