fs-walk.js/lib/walk-queue.js

166 lines
4.6 KiB
JavaScript

(function () {
"use strict";
var fs = require('fs'),
fstat = fs.lstat,
Futures = require('futures'),
EventEmitter = require('events').EventEmitter,
upath = require('path'),
// "FIFO" isn't easy to convert to camelCase and back reliably
isFnodeTypes = [
"isFile", "isDirectory", "isBlockDevice", "isCharacterDevice", "isSymbolicLink", "isFIFO", "isSocket"
],
fnodeTypes = [
"file", "directory", "blockDevice", "characterDevice", "symbolicLink", "FIFO", "socket"
],
fnodeTypesPlural = [
"files", "directories", "blockDevices", "characterDevices", "symbolicLinks", "FIFOs", "sockets"
];
// Get the current number of listeners (which may change)
// Emit events to each listener
// Wait for all listeners to `next()` before continueing
// (in theory this may avoid disk thrashing)
function emitSingleEvents(emitter, path, stats, next) {
var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
function nextWhenReady() {
num -= 1;
if (0 === num) { next(); }
}
emitter.emit(stats.type, path, stats, nextWhenReady);
emitter.emit("node", path, stats, nextWhenReady);
nextWhenReady();
}
// Since the risk for disk thrashing among anything
// other than files is relatively low, all types are
// emitted at once, but all must complete before advancing
function emitPluralEvents(emitter, path, nodes, next) {
var num = 1;
function nextWhenReady() {
num -= 1;
if (0 === num) { next(); }
}
fnodeTypesPlural.concat(["nodes", "errors"]).forEach(function (fnodeType) {
if (0 === nodes[fnodeType].length) { return; }
num += emitter.listeners(fnodeType).length;
emitter.emit(fnodeType, path, nodes[fnodeType], nextWhenReady);
});
nextWhenReady();
}
// Determine each file node's type
//
function sortFnodesByType(stats, fnodes) {
isFnodeTypes.forEach(function (isType, i) {
if (stats[isType]()) {
if (stats.type) { throw new Error("is_" + type + " and " + isType); }
stats.type = fnodeTypes[i];
fnodes[fnodeTypesPlural[i]].push(stats);
//console.log(isType, fnodeTypesPlural[i], stats.name);
// TODO throw to break;
}
});
/*
// Won't really ever happen
if (!stats.type) {
stats.error = new Error(upath.join(path, stats.name) + ' isAnUndefinedType');
}
*/
}
function create(path) {
var emitter = new EventEmitter(),
paths = [],
path;
function next() {
// path could be local if dirHandler were anonymous
//console.log('LEN: '+ paths.length);
if (0 == paths.length) {
emitter.emit('end');
return;
}
path = paths.pop();
//console.log("POP: " + path);
fs.readdir(path, dirHandler);
}
function nodesHandler(nodes, args) {
//console.log('USE: ' + path);
var statses = [];
var nodeGroups = {};
fnodeTypesPlural.concat("nodes", "errors").forEach(function (fnodeTypePlural) {
nodeGroups[fnodeTypePlural] = [];
});
args.forEach(function (arg, i) {
var file = nodes[i],
err = arg[0],
stats = arg[1];
if (err) {
stats = { error: err, name: file };
emitter.emit('error', err, path, stats);
}
if (stats) {
stats.name = file;
sortFnodesByType(stats, nodeGroups);
emitter.emit('stat', path, stats);
}
});
emitter.emit('stats', path, statses);
nodeGroups['directories'].forEach(function (stat) {
paths.push(path + '/' + stat.name);
//console.log('PUSH: ' + path + '/' + stat.name);
});
/*
//console.log('USE: ' + path);
next();
*/
emitPluralEvents(emitter, path, nodeGroups, next);
}
function dirHandler(err, nodes) {
//console.log("HANDLE: " + path);
var join = Futures.join(),
i;
if (err) {
emitter.emit('error', err, path);
}
if (!nodes || 0 == nodes.length) {
//console.log('EMPTY: ' + path);
return next();
}
// TODO don't duplicate efforts
emitter.emit('nodes', path, nodes);
for (i = 0; i < nodes.length; i += 1) {
fstat(path + '/' + nodes[i], join.deliverer());
}
join.when(function () {
var args = Array.prototype.slice.call(arguments);
nodesHandler(nodes, args);
});
}
//paths.push([path]);
paths.push(path);
next();
return emitter;
}
module.exports = create;
}());