tested several variations
This commit is contained in:
parent
fa29c9140c
commit
4df55b7ced
|
@ -0,0 +1,88 @@
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// "FIFO" isn't easy to convert to camelCase and back reliably
|
||||||
|
var isFnodeTypes = [
|
||||||
|
"isFile", "isDirectory", "isSymbolicLink", "isBlockDevice", "isCharacterDevice", "isFIFO", "isSocket"
|
||||||
|
],
|
||||||
|
fnodeTypes = [
|
||||||
|
"file", "directory", "symbolicLink", "blockDevice", "characterDevice", "FIFO", "socket"
|
||||||
|
],
|
||||||
|
fnodeTypesPlural = [
|
||||||
|
"files", "directories", "symbolicLinks", "blockDevices", "characterDevices", "FIFOs", "sockets"
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
function createNodeGroups() {
|
||||||
|
var nodeGroups = {};
|
||||||
|
fnodeTypesPlural.concat("nodes", "errors").forEach(function (fnodeTypePlural) {
|
||||||
|
nodeGroups[fnodeTypePlural] = [];
|
||||||
|
});
|
||||||
|
return nodeGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Determine each file node's type
|
||||||
|
//
|
||||||
|
function sortFnodesByType(stat, fnodes) {
|
||||||
|
var i, isType;
|
||||||
|
|
||||||
|
for (i = 0; i < isFnodeTypes.length; i += 1) {
|
||||||
|
isType = isFnodeTypes[i];
|
||||||
|
if (stat[isType]()) {
|
||||||
|
stat.type = fnodeTypes[i];
|
||||||
|
fnodes[fnodeTypesPlural[i]].push(stat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
emitNodeType: emitSingleEvents,
|
||||||
|
emitNodeTypeGroups: emitPluralEvents,
|
||||||
|
isFnodeTypes: isFnodeTypes,
|
||||||
|
fnodeTypes: fnodeTypes,
|
||||||
|
fnodeTypesPlural: fnodeTypesPlural,
|
||||||
|
sortFnodesByType: sortFnodesByType,
|
||||||
|
createNodeGroups: createNodeGroups
|
||||||
|
};
|
||||||
|
}());
|
|
@ -0,0 +1,79 @@
|
||||||
|
(function () {
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
// Array.prototype.forEachAsync(next, item, i, collection)
|
||||||
|
require('futures/forEachAsync');
|
||||||
|
|
||||||
|
var fs = require('fs'),
|
||||||
|
EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
|
// 2010-11-25 jorge@jorgechamorro.com
|
||||||
|
function create(pathname, cb) {
|
||||||
|
var emitter = new EventEmitter(),
|
||||||
|
q = [],
|
||||||
|
queue = [q],
|
||||||
|
curpath;
|
||||||
|
|
||||||
|
function walk() {
|
||||||
|
fs.readdir(curpath, function(err, files) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
// XXX bug was here. next() was omitted
|
||||||
|
if (!files || 0 == files.length) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
var stats = [];
|
||||||
|
emitter.emit('names', curpath, files, stats);
|
||||||
|
files.forEachAsync(function (cont, file) {
|
||||||
|
emitter.emit('name', curpath, file);
|
||||||
|
fs.lstat(curpath + '/' + file, function (err, stat) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
if (stat) {
|
||||||
|
stat.name = file;
|
||||||
|
stats.push(stat);
|
||||||
|
emitter.emit('stat', curpath, file, stat);
|
||||||
|
}
|
||||||
|
cont();
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
var dirs = []
|
||||||
|
emitter.emit('stats', curpath, files, stats);
|
||||||
|
stats.forEach(function (stat) {
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
dirs.push(stat.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dirs.forEach(fullPath);
|
||||||
|
queue.push(q = dirs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if (q.length) {
|
||||||
|
curpath = q.pop();
|
||||||
|
return walk();
|
||||||
|
}
|
||||||
|
if (queue.length -= 1) {
|
||||||
|
q = queue[queue.length-1];
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
emitter.emit('end');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullPath(v,i,o) {
|
||||||
|
o[i]= [curpath, '/', v].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
curpath = pathname;
|
||||||
|
walk();
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = create;
|
||||||
|
}());
|
|
@ -0,0 +1,86 @@
|
||||||
|
(function () {
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
// Array.prototype.forEachAsync(next, item, i, collection)
|
||||||
|
require('futures/forEachAsync');
|
||||||
|
|
||||||
|
var fs = require('fs'),
|
||||||
|
EventEmitter = require('events').EventEmitter,
|
||||||
|
TypeEmitter = require('./node-type-emitter');
|
||||||
|
|
||||||
|
// 2010-11-25 jorge@jorgechamorro.com
|
||||||
|
function create(pathname, cb) {
|
||||||
|
var emitter = new EventEmitter(),
|
||||||
|
q = [],
|
||||||
|
queue = [q],
|
||||||
|
curpath;
|
||||||
|
|
||||||
|
function walk() {
|
||||||
|
fs.readdir(curpath, function(err, files) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
// XXX bug was here. next() was omitted
|
||||||
|
if (!files || 0 == files.length) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
var stats = [],
|
||||||
|
fnodeGroups = TypeEmitter.createNodeGroups();
|
||||||
|
|
||||||
|
// TODO could allow user to selectively stat
|
||||||
|
// and don't stat if there are no stat listeners
|
||||||
|
emitter.emit('names', curpath, files);
|
||||||
|
files.forEachAsync(function (cont, file) {
|
||||||
|
emitter.emit('name', curpath, file);
|
||||||
|
fs.lstat(curpath + '/' + file, function (err, stat) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
if (!stat) {
|
||||||
|
cont();
|
||||||
|
}
|
||||||
|
stat.name = file;
|
||||||
|
stats.push(stat);
|
||||||
|
//emitter.emit('stat', curpath, file, stat);
|
||||||
|
TypeEmitter.sortFnodesByType(stat, fnodeGroups);
|
||||||
|
TypeEmitter.emitNodeType(emitter, curpath, stat, cont);
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
var dirs = []
|
||||||
|
//emitter.emit('stats', curpath, files, stats);
|
||||||
|
TypeEmitter.emitNodeTypeGroups(emitter, curpath, fnodeGroups, function () {
|
||||||
|
fnodeGroups.directories.forEach(function (stat) {
|
||||||
|
dirs.push(stat.name);
|
||||||
|
});
|
||||||
|
dirs.forEach(fullPath);
|
||||||
|
queue.push(q = dirs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if (q.length) {
|
||||||
|
curpath = q.pop();
|
||||||
|
return walk();
|
||||||
|
}
|
||||||
|
if (queue.length -= 1) {
|
||||||
|
q = queue[queue.length-1];
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
emitter.emit('end');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullPath(v,i,o) {
|
||||||
|
o[i]= [curpath, '/', v].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
curpath = pathname;
|
||||||
|
walk();
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = create;
|
||||||
|
}());
|
|
@ -0,0 +1,165 @@
|
||||||
|
(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;
|
||||||
|
}());
|
|
@ -0,0 +1,165 @@
|
||||||
|
// TODO
|
||||||
|
// * add types by listener dynamically
|
||||||
|
// * unroll loops for better readability?
|
||||||
|
// * should emitted errors wait for `next()`?
|
||||||
|
(function (undefined) {
|
||||||
|
var fs = require('fs'),
|
||||||
|
upath = require('path'),
|
||||||
|
util = require('util'),
|
||||||
|
Futures = require('futures'),
|
||||||
|
events = require('events'),
|
||||||
|
noop = function () {},
|
||||||
|
// "FIFO" isn't easy to convert to camelCame 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"
|
||||||
|
];
|
||||||
|
|
||||||
|
function newVersion() {
|
||||||
|
throw new Error("New Version. Please see API on github.com/coolaj86/node-walk");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new walk instance
|
||||||
|
function create(path, options, cb) {
|
||||||
|
if (cb) {
|
||||||
|
newVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
var emitter = new events.EventEmitter(),
|
||||||
|
fstat = (options||{}).followLinks ? fs.stat : fs.lstat;
|
||||||
|
|
||||||
|
|
||||||
|
// 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(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(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(path, stats, fnodes, nextFile) {
|
||||||
|
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);
|
||||||
|
// TODO throw to break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!stats.type) { throw new Error(upath.join(path, stats.name) + ' isAnUndefinedType'); }
|
||||||
|
emitSingleEvents(path, stats, nextFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Asynchronously get the stats
|
||||||
|
//
|
||||||
|
function getStats(path, files, walkDirs) {
|
||||||
|
var nodeGroups = {};
|
||||||
|
|
||||||
|
fnodeTypesPlural.concat("nodes", "errors").forEach(function (fnodeTypePlural) {
|
||||||
|
nodeGroups[fnodeTypePlural] = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
function nextFile() {
|
||||||
|
var file = files.pop(), dirs = [], fnames = [];
|
||||||
|
|
||||||
|
if (undefined === file) {
|
||||||
|
emitPluralEvents(path, nodeGroups, function () {
|
||||||
|
nodeGroups.directories.forEach(function (dir) {
|
||||||
|
dirs.push(dir.name);
|
||||||
|
});
|
||||||
|
walkDirs(dirs);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fstat(upath.join(path, file), function (err, stats) {
|
||||||
|
stats = stats || {};
|
||||||
|
stats.name = file;
|
||||||
|
nodeGroups.nodes.push(stats);
|
||||||
|
if (err) {
|
||||||
|
stats.error = err;
|
||||||
|
stats.type = 'error';
|
||||||
|
nodeGroups.errors.push(stats);
|
||||||
|
//emitter.emit('fileError', path, stats, noop);
|
||||||
|
return nextFile();
|
||||||
|
}
|
||||||
|
sortFnodesByType(path, stats, nodeGroups, nextFile);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
nextFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
function walk(path, next) {
|
||||||
|
fs.readdir(path, function (err, nodes) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('directoryError', path, { error: err, name: path }, noop);
|
||||||
|
return next(); /*TODO*/ throw err;
|
||||||
|
}
|
||||||
|
getStats(path, nodes, function (dirs) {
|
||||||
|
walkDirs(path, dirs, 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(upath.normalize(path), function () {
|
||||||
|
emitter.emit('end');
|
||||||
|
});
|
||||||
|
emitter.walk = newVersion;
|
||||||
|
emitter.whenever = newVersion;
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
module.exports = create;
|
||||||
|
module.exports.isFnodeTypes = isFnodeTypes;
|
||||||
|
module.exports.fnodeTypes = fnodeTypes;
|
||||||
|
module.exports.fnodeTypesPlural = fnodeTypesPlural;
|
||||||
|
}());
|
227
lib/walk.js
227
lib/walk.js
|
@ -1,165 +1,86 @@
|
||||||
// TODO
|
(function () {
|
||||||
// * add types by listener dynamically
|
"use strict"
|
||||||
// * unroll loops for better readability?
|
|
||||||
// * should emitted errors wait for `next()`?
|
// Array.prototype.forEachAsync(next, item, i, collection)
|
||||||
(function (undefined) {
|
require('futures/forEachAsync');
|
||||||
|
|
||||||
var fs = require('fs'),
|
var fs = require('fs'),
|
||||||
upath = require('path'),
|
EventEmitter = require('events').EventEmitter,
|
||||||
util = require('util'),
|
TypeEmitter = require('./node-type-emitter');
|
||||||
Futures = require('futures'),
|
|
||||||
events = require('events'),
|
|
||||||
noop = function () {},
|
|
||||||
// "FIFO" isn't easy to convert to camelCame 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"
|
|
||||||
];
|
|
||||||
|
|
||||||
function newVersion() {
|
// 2010-11-25 jorge@jorgechamorro.com
|
||||||
throw new Error("New Version. Please see API on github.com/coolaj86/node-walk");
|
function create(pathname, cb) {
|
||||||
}
|
var emitter = new EventEmitter(),
|
||||||
|
q = [],
|
||||||
|
queue = [q],
|
||||||
|
curpath;
|
||||||
|
|
||||||
// Create a new walk instance
|
function walk() {
|
||||||
function create(path, options, cb) {
|
fs.readdir(curpath, function(err, files) {
|
||||||
if (cb) {
|
if (err) {
|
||||||
newVersion();
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
// XXX bug was here. next() was omitted
|
||||||
|
if (!files || 0 == files.length) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
var stats = [],
|
||||||
|
fnodeGroups = TypeEmitter.createNodeGroups();
|
||||||
|
|
||||||
|
// TODO could allow user to selectively stat
|
||||||
|
// and don't stat if there are no stat listeners
|
||||||
|
emitter.emit('names', curpath, files);
|
||||||
|
files.forEachAsync(function (cont, file) {
|
||||||
|
emitter.emit('name', curpath, file);
|
||||||
|
fs.lstat(curpath + '/' + file, function (err, stat) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
if (!stat) {
|
||||||
|
cont();
|
||||||
|
}
|
||||||
|
stat.name = file;
|
||||||
|
stats.push(stat);
|
||||||
|
//emitter.emit('stat', curpath, file, stat);
|
||||||
|
TypeEmitter.sortFnodesByType(stat, fnodeGroups);
|
||||||
|
TypeEmitter.emitNodeType(emitter, curpath, stat, cont);
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
var dirs = []
|
||||||
|
//emitter.emit('stats', curpath, files, stats);
|
||||||
|
TypeEmitter.emitNodeTypeGroups(emitter, curpath, fnodeGroups, function () {
|
||||||
|
fnodeGroups.directories.forEach(function (stat) {
|
||||||
|
dirs.push(stat.name);
|
||||||
|
});
|
||||||
|
dirs.forEach(fullPath);
|
||||||
|
queue.push(q = dirs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var emitter = new events.EventEmitter(),
|
function next() {
|
||||||
fstat = (options||{}).followLinks ? fs.stat : fs.lstat;
|
if (q.length) {
|
||||||
|
curpath = q.pop();
|
||||||
|
return walk();
|
||||||
// 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(path, stats, next) {
|
|
||||||
var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
|
|
||||||
|
|
||||||
function nextWhenReady() {
|
|
||||||
num -= 1;
|
|
||||||
if (0 === num) { next(); }
|
|
||||||
}
|
}
|
||||||
|
if (queue.length -= 1) {
|
||||||
emitter.emit(stats.type, path, stats, nextWhenReady);
|
q = queue[queue.length-1];
|
||||||
emitter.emit("node", path, stats, nextWhenReady);
|
return next();
|
||||||
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(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(path, stats, fnodes, nextFile) {
|
|
||||||
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);
|
|
||||||
// TODO throw to break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!stats.type) { throw new Error(upath.join(path, stats.name) + ' isAnUndefinedType'); }
|
|
||||||
emitSingleEvents(path, stats, nextFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Asynchronously get the stats
|
|
||||||
//
|
|
||||||
function getStats(path, files, walkDirs) {
|
|
||||||
var nodeGroups = {};
|
|
||||||
|
|
||||||
fnodeTypesPlural.concat("nodes", "errors").forEach(function (fnodeTypePlural) {
|
|
||||||
nodeGroups[fnodeTypePlural] = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
function nextFile() {
|
|
||||||
var file = files.pop(), dirs = [], fnames = [];
|
|
||||||
|
|
||||||
if (undefined === file) {
|
|
||||||
emitPluralEvents(path, nodeGroups, function () {
|
|
||||||
nodeGroups.directories.forEach(function (dir) {
|
|
||||||
dirs.push(dir.name);
|
|
||||||
});
|
|
||||||
walkDirs(dirs);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fstat(upath.join(path, file), function (err, stats) {
|
|
||||||
stats = stats || {};
|
|
||||||
stats.name = file;
|
|
||||||
nodeGroups.nodes.push(stats);
|
|
||||||
if (err) {
|
|
||||||
stats.error = err;
|
|
||||||
stats.type = 'error';
|
|
||||||
nodeGroups.errors.push(stats);
|
|
||||||
//emitter.emit('fileError', path, stats, noop);
|
|
||||||
return nextFile();
|
|
||||||
}
|
|
||||||
sortFnodesByType(path, stats, nodeGroups, nextFile);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
nextFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
function walk(path, next) {
|
|
||||||
fs.readdir(path, function (err, nodes) {
|
|
||||||
if (err) {
|
|
||||||
emitter.emit('directoryError', path, { error: err, name: path }, noop);
|
|
||||||
return next(); /*TODO*/ throw err;
|
|
||||||
}
|
|
||||||
getStats(path, nodes, function (dirs) {
|
|
||||||
walkDirs(path, dirs, 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(upath.normalize(path), function () {
|
|
||||||
emitter.emit('end');
|
emitter.emit('end');
|
||||||
});
|
}
|
||||||
emitter.walk = newVersion;
|
|
||||||
emitter.whenever = newVersion;
|
function fullPath(v,i,o) {
|
||||||
|
o[i]= [curpath, '/', v].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
curpath = pathname;
|
||||||
|
walk();
|
||||||
|
|
||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = create;
|
module.exports = create;
|
||||||
module.exports.isFnodeTypes = isFnodeTypes;
|
|
||||||
module.exports.fnodeTypes = fnodeTypes;
|
|
||||||
module.exports.fnodeTypesPlural = fnodeTypesPlural;
|
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var walk = require('../lib/walk-jqueue-2'),
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
function sort(a,b) {
|
||||||
|
a= a.toLowerCase();
|
||||||
|
b= b.toLowerCase();
|
||||||
|
if (a > b) return -1;
|
||||||
|
if (a < b) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.argv.forEach(function(val, index, array) {
|
||||||
|
if (index > 1) {
|
||||||
|
emitter = walk(val);
|
||||||
|
emitter.on('name', function (path, file, stat) {
|
||||||
|
count += 1;
|
||||||
|
console.log( ["[", count, "] ", path, '/', file].join('') )
|
||||||
|
});
|
||||||
|
emitter.on('names', function (path, files, stats) {
|
||||||
|
files.sort(sort);
|
||||||
|
//console.log('sort: ' + files.join(' ; '));
|
||||||
|
});
|
||||||
|
emitter.on('error', function () {
|
||||||
|
// ignore
|
||||||
|
});
|
||||||
|
emitter.on('stat', function (path, file, stat) {
|
||||||
|
//console.log('stat: ' + file);
|
||||||
|
});
|
||||||
|
emitter.on('stats', function (path, files, stats) {
|
||||||
|
//console.log('stats: ' + files.join(' ; '));
|
||||||
|
});
|
||||||
|
emitter.on('end', function () {
|
||||||
|
console.log("The eagle has landed.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
|
@ -0,0 +1,123 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var walk = require('../lib/walk-jqueue-3'),
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
function sort(a,b) {
|
||||||
|
a= a.toLowerCase();
|
||||||
|
b= b.toLowerCase();
|
||||||
|
if (a > b) return -1;
|
||||||
|
if (a < b) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.argv.forEach(function(startpath, index) {
|
||||||
|
if (index > 1) {
|
||||||
|
emitter = walk(startpath);
|
||||||
|
|
||||||
|
// Non-`stat`ed Nodes
|
||||||
|
/*
|
||||||
|
emitter.on('name', function (path, file, stat) {
|
||||||
|
count += 1;
|
||||||
|
//console.log( ["[", count, "] ", path, '/', file].join('') )
|
||||||
|
console.log( [path, '/', file].join('') )
|
||||||
|
});
|
||||||
|
emitter.on('names', function (path, files, stats) {
|
||||||
|
files.sort(sort);
|
||||||
|
//console.log('sort: ' + files.join(' ; '));
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Single `stat`ed Nodes
|
||||||
|
emitter.on('error', function (path, err, next) {
|
||||||
|
// ignore
|
||||||
|
});
|
||||||
|
emitter.on('directoryError', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('node', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
//console.log( ["[", count, "] ", path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
emitter.on('file', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
//console.log( ["[", count, "] ", path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('directory', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('symbolicLink', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
emitter.on('blockDevice', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('characterDevice', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('FIFO', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('socket', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Grouped `stat`ed Nodes
|
||||||
|
emitter.on('errors', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('nodes', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
emitter.on('files', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('directories', function (path, stats, next) {
|
||||||
|
//delete stats[1];
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('symbolicLinks', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('blockDevices', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('characterDevices', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('FIFOs', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('sockets', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The end of all things
|
||||||
|
emitter.on('end', function () {
|
||||||
|
console.log("The eagle has landed.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
|
@ -0,0 +1,33 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
(function () {
|
||||||
|
var walk = require('../lib/walk-queue'),
|
||||||
|
emitter = walk(process.argv[2] || '.'),
|
||||||
|
_ = require('underscore'),
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
emitter.on('error', function (err, path, files) {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
emitter.on('nodes', function (path, files, next) {
|
||||||
|
//next();
|
||||||
|
var filenames = _.map(files, function (file) {
|
||||||
|
return path + '/' + file;
|
||||||
|
})
|
||||||
|
filenames.forEach(function (name) {
|
||||||
|
count += 1;
|
||||||
|
console.log('[' + count + '] ' + name)
|
||||||
|
});
|
||||||
|
//filenames.forEach(console.log);
|
||||||
|
//console.log(_.pluck(files, 'name'));
|
||||||
|
});
|
||||||
|
emitter.on('directories', function (path, files, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('directory', function (path, stat, next) {
|
||||||
|
//console.log(stat.name);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('end', function () {
|
||||||
|
console.log("The eagle has landed");
|
||||||
|
});
|
||||||
|
}());
|
Loading…
Reference in New Issue