Source: apc-abstract/lib/file/readdir_recursive.js

/**
 * Read a directory recursive.
 * @function lib.file.readdirRecursive
 * @param {string} dirname - Directory name to read.
 * @param {function} callback - Callback with read filenames.
 * @author Taka Okunishi
 *
 */
var fs = require('fs'),
    path = require('path');

exports = module.exports = function (dirname, callback) {
    if (!callback) callback = new Function;
    var async = require('async');
    fs.readdir(dirname, function (err, filenames) {
        if (err) {
            callback(err);
            return;
        }
        async.parallel(
            filenames
                .map(function (filename) {
                    return path.resolve(dirname, filename);
                })
                .map(function (filename) {
                    return function (callback) {
                        fs.lstat(filename, function (err, stats) {
                            if (err) {
                                callback(err);
                            } else {
                                if (stats.isDirectory()) {
                                    var subDirname = filename;
                                    exports(subDirname, function (err, filenames) {
                                        callback(err, filenames
                                            .map(function (filename) {
                                                return path.resolve(subDirname, filename);
                                            }));
                                    });
                                } else {
                                    callback(null, filename);
                                }
                            }
                        });
                    };
                }),
            function (err, filenames) {
                callback(err, filenames
                    .reduce(function (a, b) {
                        return [].concat(a).concat(b);
                    }, [])
                    .filter(function (filename) {
                        return !!filename;
                    })
                    .map(function (filename) {
                        return path.relative(dirname, filename);
                    }));
            }
        );
    });
};