fs-walk.js/lib/walk3.js

157 lines
4.0 KiB
JavaScript
Raw Normal View History

2010-12-12 06:46:51 +00:00
(function (undefined) {
var fs = require('fs'),
upath = require('path'),
util = require('util'),
Futures = require('futures'),
events = require('events');
function create(path, options) {
// TODO add types by listener dynamically
var emitter = new events.EventEmitter(),
oneNodeEvents = [
"file",
"directory",
"blockDevice",
"characterDevice",
"symbolicLink",
"FIFO",
"socket"
],
oneNodeTypes = [
"isFile",
"isDirectory",
"isBlockDevice",
"isCharacterDevice",
"isSymbolicLink",
"isFIFO",
"isSocket"
],
multiNodeEvents = [
// multiple
"files",
"directories",
"blockDevices",
"characterDevices",
"symbolicLinks",
"FIFOs",
"sockets"
],
fstat = (options||{}).followLinks ? fs.stat : fs.lstat;
function normalize(path) {
return path;
var index;
path = upath.normalize(path);
index = path.length - 1;
if (index > 0 && '/' === path[index]) {
path = path.substr(0, index);
}
return path;
}
function sortNodesByType(path, stats, nodes, next) {
// This could easily be unrolled
// but in this case concise was more readable for me
oneNodeTypes.forEach(function (isType, i) {
if (stats[isType]()) {
if (stats.type) { throw new Error("is_" + type + " and " + isType); }
stats.type = oneNodeEvents[i];
nodes[multiNodeEvents[i]].push(stats);
// TODO throw to break;
}
});
if (!stats.type) { throw new Error(upath.join(path, stats.name) + ' isAnUndefinedType'); }
function emitSingleEvents(stats, next) {
var num;
// get the current number of listeners (may change)
num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
function nextWhenReady() {
num -= 1;
if (0 === num) { next(); }
}
nextWhenReady();
// Total number of listeners
emitter.emit(stats.type, path, stats, function () {
nextWhenReady();
});
emitter.emit("node", path, stats, function () {
nextWhenReady();
});
} // emitSingleEvents
emitSingleEvents(stats, next);
}
function getStats(path, files, walkDirs) {
var nodes = [], o = {};
// TODO unroll for better readability
multiNodeEvents.forEach(function (eventType) {
o[eventType] = [];
});
function nextFile() {
var file = files.pop(), dirs = [], fnames = [];
if (undefined === file) {
o.directories.forEach(function (dir) {
dirs.push(dir.name);
});
return walkDirs(dirs);
}
fstat(upath.join(path, file), function (err, stats) {
//console.log("`stat`ed file " + file);
stats = stats || {};
stats.name = file;
nodes.push(stats);
if (err) {
stats.error = err
// TODO single error emittor
o.errors.push(stats);
util.debug("[Error 1] " + util.inspect(err));
return nextFile();
}
sortNodesByType(path, stats, o, nextFile);
});
}
nextFile();
}
function walk(path, next) {
fs.readdir(path, function (err, nodes) {
if (err) { return next(); /*TODO*/ throw err; }
getStats(path, nodes, function (dirs) {
walkDirs(path, dirs, function () {
next();
});
});
});
}
function walkDirs(path, dirs, cb) {
function nextDir() {
var dir = dirs.pop();
if (undefined === dir) {
delete dirs;
return cb();
}
walk(upath.join(path, dir), nextDir);
}
nextDir();
}
walk(normalize(path), function () {
//throw Error("hey");
//console.log("Utterly Done!");
});
return emitter;
}
module.exports = create;
}());