Any ideas on an async directory search using fs.readdir? I realise that we could introduce recursion and call the read directory function with the next directory to read, bu
Check out the final-fs library. It provides a readdirRecursive
function:
ffs.readdirRecursive(dirPath, true, 'my/initial/path')
.then(function (files) {
// in the `files` variable you've got all the files
})
.otherwise(function (err) {
// something went wrong
});
check out loaddir https://npmjs.org/package/loaddir
npm install loaddir
loaddir = require('loaddir')
allJavascripts = []
loaddir({
path: __dirname + '/public/javascripts',
callback: function(){ allJavascripts.push(this.relativePath + this.baseName); }
})
You can use fileName
instead of baseName
if you need the extension as well.
An added bonus is that it will watch the files as well and call the callback again. There are tons of configuration options to make it extremely flexible.
I just remade the guard
gem from ruby using loaddir in a short while
Async
const fs = require('fs')
const path = require('path')
const readdir = (p, done, a = [], i = 0) => fs.readdir(p, (e, d = []) =>
d.map(f => readdir(a[a.push(path.join(p, f)) - 1], () =>
++i == d.length && done(a), a)).length || done(a))
readdir(__dirname, console.log)
Sync
const fs = require('fs')
const path = require('path')
const readdirSync = (p, a = []) => {
if (fs.statSync(p).isDirectory())
fs.readdirSync(p).map(f => readdirSync(a[a.push(path.join(p, f)) - 1], a))
return a
}
console.log(readdirSync(__dirname))
Async readable
function readdir (currentPath, done, allFiles = [], i = 0) {
fs.readdir(currentPath, function (e, directoryFiles = []) {
if (!directoryFiles.length)
return done(allFiles)
directoryFiles.map(function (file) {
var joinedPath = path.join(currentPath, file)
allFiles.push(joinedPath)
readdir(joinedPath, function () {
i = i + 1
if (i == directoryFiles.length)
done(allFiles)}
, allFiles)
})
})
}
readdir(__dirname, console.log)
Note: both versions will follow symlinks (same as the original fs.readdir
)
If you want to use an npm package, wrench is pretty good.
var wrench = require("wrench");
var files = wrench.readdirSyncRecursive("directory");
wrench.readdirRecursive("directory", function (error, files) {
// live your dreams
});
EDIT (2018):
Anyone reading through in recent time: The author deprecated this package in 2015:
wrench.js is deprecated, and hasn't been updated in quite some time. I heavily recommend using fs-extra to do any extra filesystem operations.
Using async/await, this should work:
const FS = require('fs');
const readDir = promisify(FS.readdir);
const fileStat = promisify(FS.stat);
async function getFiles(dir) {
let files = await readDir(dir);
let result = files.map(file => {
let path = Path.join(dir,file);
return fileStat(path).then(stat => stat.isDirectory() ? getFiles(path) : path);
});
return flatten(await Promise.all(result));
}
function flatten(arr) {
return Array.prototype.concat(...arr);
}
You can use bluebird.Promisify or this:
/**
* Returns a function that will wrap the given `nodeFunction`. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument.
*
* @param {Function} nodeFunction
* @returns {Function}
*/
module.exports = function promisify(nodeFunction) {
return function(...args) {
return new Promise((resolve, reject) => {
nodeFunction.call(this, ...args, (err, data) => {
if(err) {
reject(err);
} else {
resolve(data);
}
})
});
};
};
Node 8+ has Promisify built-in
See my other answer for a generator approach that can give results even faster.
Here's a recursive method of getting all files including subdirectories.
const FileSystem = require("fs");
const Path = require("path");
//...
function getFiles(directory) {
directory = Path.normalize(directory);
let files = FileSystem.readdirSync(directory).map((file) => directory + Path.sep + file);
files.forEach((file, index) => {
if (FileSystem.statSync(file).isDirectory()) {
Array.prototype.splice.apply(files, [index, 1].concat(getFiles(file)));
}
});
return files;
}