fileutils
Version:
Some utilities that diminish the suck of working with the file system in node
194 lines (177 loc) • 5.81 kB
JavaScript
var fs = require("fs");
/**
* Call fileHandler with the file name and file Stat for each file found inside
* of the provided directory.
*
* Call the optionally provided completeHandler with an array of files (mingled
* with directories) and an array of Stat objects (one for each of the found
* files.
*
* Following is an example of a simple usage:
*
* eachFileOrDirectory('test/', function(err, file, stat) {
* if (err) throw err;
* if (!stat.isDirectory()) {
* console.log(">> Found file: " + file);
* }
* });
*
* Following is an example that waits for all files and directories to be
* scanned and then uses the entire result to do somthing:
*
* eachFileOrDirectory('test/', null, function(files, stats) {
* if (err) throw err;
* var len = files.length;
* for (var i = 0; i < len; i++) {
* if (!stats[i].isDirectory()) {
* console.log(">> Found file: " + files[i]);
* }
* }
* });
*/
var eachFileOrDirectory = function(directory, fileHandler, opt_completeHandler) {
var filesToCheck = 0;
var checkedFiles = [];
var checkedStats = [];
var checkCompletTimeoutId = null;
directory = (directory) ? directory : './';
var fullFilePath = function(dir, file) {
return dir.replace(/\/$/, '') + '/' + file;
};
var checkComplete = function() {
if (filesToCheck == 0 && opt_completeHandler) {
// There is a way for this callback to trigger prematurely,
// Need to wait for one frame to ensure new files have not
// been added.
clearTimeout(checkCompletTimeoutId);
checkCompletTimeoutId = setTimeout(() => {
if (filesToCheck === 0 && opt_completeHandler) {
opt_completeHandler(null, checkedFiles, checkedStats);
}
}, 1);
}
};
var onFileOrDirectory = function(fileOrDirectory) {
filesToCheck++;
fs.stat(fileOrDirectory, function(err, stat) {
filesToCheck--;
if (err) return fileHandler(err);
checkedFiles.push(fileOrDirectory);
checkedStats.push(stat);
fileHandler(null, fileOrDirectory, stat);
if (stat.isDirectory()) {
onDirectory(fileOrDirectory);
}
checkComplete();
});
};
var onDirectory = function(dir) {
filesToCheck++;
fs.readdir(dir, function(err, files) {
filesToCheck--;
if (err) return fileHandler(err);
files.forEach(function(file, index) {
file = fullFilePath(dir, file);
onFileOrDirectory(file);
});
checkComplete();
});
}
onFileOrDirectory(directory);
};
/**
* Recursivly, asynchronously traverse the file system calling the provided
* callback for each file (non-directory) found.
*
* Traversal will begin on the provided path.
*/
var eachFile = function(path, callback, opt_completeHandler) {
var files = [];
var stats = [];
eachFileOrDirectory(path, function(err, file, stat) {
if (err && callback) return callback(err);
if (!stat.isDirectory()) {
if (opt_completeHandler) {
files.push(file);
if (opt_completeHandler.length >= 3) {
stats.push(stat);
}
}
if (!err && callback) callback(null, file, stat);
}
}, function(err) {
if (err && opt_completeHandler) return opt_completeHandler(err);
if (!err && opt_completeHandler) opt_completeHandler(null, files, stats);
});
};
/**
* Works just like eachFile, but it only includes files that match a provided
* regular expression.
*
* eachFileMatching(/_test.js/, 'test', function(err, file, stat) {
* if (err) throw err;
* console.log(">> Found file: " + file);
* });
*
*/
var eachFileMatching = function(expression, path, callback, opt_completeHandler) {
var files = [];
var stats = [];
eachFile(path, function(err, file, stat) {
if (err) return callback(err);
if (expression.test(file)) {
if (opt_completeHandler) {
files.push(file);
if (opt_completeHandler.length >= 3) {
stats.push(stat);
}
}
if (callback) callback(null, file, stat);
}
}, function(err) {
if (err && opt_completeHandler) return opt_completeHandler(err);
if (!err && opt_completeHandler) opt_completeHandler(null, files, stats);
});
};
/**
* Read each file with a file name that matches the provided expression
* and was found in the provided path.
*
* Calls the optionally provided callback for each file found.
*
* Calls the optionally provided completeHandler when the search is
* complete.
*
* readEachFileMatching(/_test.js/, 'test', function(err, file, stat, content) {
* if (err) throw err;
* console.log(">> Found file: " + file + " with: " + content.length + " chars");
* });
*/
var readEachFileMatching = function(expression, path, callback, opt_completeHandler) {
var files = [];
var contents = [];
var stats = [];
eachFileMatching(expression, path, function(err, file, stat) {
fs.readFile(file, function(err, content) {
if (err && callback) return callback(err);
// Only aggregate results if a client decided that's okay.
if (opt_completeHandler) {
files.push(file);
if (opt_completeHandler.length >= 3) {
stats.push(stat);
}
if (opt_completeHandler.length >= 4) {
contents.push(content);
}
}
if (!err && callback) callback(null, file, stat, content);
});
}, function(err) {
if (err && opt_completeHandler) return opt_completeHandler(err);
if (!err && opt_completeHandler) opt_completeHandler(err, files, stats, contents);
});
};
exports.eachFile = eachFile;
exports.eachFileMatching = eachFileMatching;
exports.eachFileOrDirectory = eachFileOrDirectory;
exports.readEachFileMatching = readEachFileMatching;