updated to prototype
This commit is contained in:
parent
63051eb24f
commit
b7454f32c4
|
@ -1,3 +1,4 @@
|
||||||
|
/*jshint strict:true node:true es5:true onevar:true laxcomma:true laxbreak:true*/
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
@ -43,12 +44,12 @@
|
||||||
// Emit events to each listener
|
// Emit events to each listener
|
||||||
// Wait for all listeners to `next()` before continueing
|
// Wait for all listeners to `next()` before continueing
|
||||||
// (in theory this may avoid disk thrashing)
|
// (in theory this may avoid disk thrashing)
|
||||||
function emitSingleEvents(emitter, path, stats, next) {
|
function emitSingleEvents(emitter, path, stats, next, self) {
|
||||||
var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
|
var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
|
||||||
|
|
||||||
function nextWhenReady() {
|
function nextWhenReady() {
|
||||||
num -= 1;
|
num -= 1;
|
||||||
if (0 === num) { next(); }
|
if (0 === num) { next.call(self); }
|
||||||
}
|
}
|
||||||
|
|
||||||
emitter.emit(stats.type, path, stats, nextWhenReady);
|
emitter.emit(stats.type, path, stats, nextWhenReady);
|
||||||
|
@ -60,12 +61,12 @@
|
||||||
// Since the risk for disk thrashing among anything
|
// Since the risk for disk thrashing among anything
|
||||||
// other than files is relatively low, all types are
|
// other than files is relatively low, all types are
|
||||||
// emitted at once, but all must complete before advancing
|
// emitted at once, but all must complete before advancing
|
||||||
function emitPluralEvents(emitter, path, nodes, next) {
|
function emitPluralEvents(emitter, path, nodes, next, self) {
|
||||||
var num = 1;
|
var num = 1;
|
||||||
|
|
||||||
function nextWhenReady() {
|
function nextWhenReady() {
|
||||||
num -= 1;
|
num -= 1;
|
||||||
if (0 === num) { next(); }
|
if (0 === num) { next.call(self); }
|
||||||
}
|
}
|
||||||
|
|
||||||
fnodeTypesPlural.concat(["nodes", "errors"]).forEach(function (fnodeType) {
|
fnodeTypesPlural.concat(["nodes", "errors"]).forEach(function (fnodeType) {
|
||||||
|
|
362
lib/walk.js
362
lib/walk.js
|
@ -1,6 +1,7 @@
|
||||||
|
/*jshint strict:true node:true es5:true onevar:true laxcomma:true laxbreak:true*/
|
||||||
// Adapted from work by jorge@jorgechamorro.com on 2010-11-25
|
// Adapted from work by jorge@jorgechamorro.com on 2010-11-25
|
||||||
(function () {
|
(function () {
|
||||||
"use strict"
|
"use strict";
|
||||||
|
|
||||||
// Array.prototype.forEachAsync(next, item, i, collection)
|
// Array.prototype.forEachAsync(next, item, i, collection)
|
||||||
//require('Array.prototype.forEachAsync');
|
//require('Array.prototype.forEachAsync');
|
||||||
|
@ -11,161 +12,224 @@
|
||||||
, forEachAsync = require('forEachAsync')
|
, forEachAsync = require('forEachAsync')
|
||||||
, EventEmitter = require('events').EventEmitter
|
, EventEmitter = require('events').EventEmitter
|
||||||
, TypeEmitter = require('./node-type-emitter')
|
, TypeEmitter = require('./node-type-emitter')
|
||||||
|
, util = require('util')
|
||||||
;
|
;
|
||||||
|
|
||||||
function create(pathname, options, sync) {
|
function appendToDirs(stat) {
|
||||||
var emitter = new EventEmitter()
|
/*jshint validthis:true*/
|
||||||
, q = []
|
this.push(stat.name);
|
||||||
, queue = [q]
|
|
||||||
, curpath
|
|
||||||
, firstRun = true
|
|
||||||
;
|
|
||||||
|
|
||||||
function readdirHandler(err, files) {
|
|
||||||
var fnodeGroups = TypeEmitter.createNodeGroups()
|
|
||||||
;
|
|
||||||
|
|
||||||
function filesHandler(cont, file) {
|
|
||||||
var statPath
|
|
||||||
;
|
|
||||||
|
|
||||||
function lstatHandler(err, stat) {
|
|
||||||
stat = stat || {};
|
|
||||||
stat.name = file;
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
stat.error = err;
|
|
||||||
//emitter.emit('error', curpath, stat);
|
|
||||||
emitter.emit('nodeError', curpath, stat, noop);
|
|
||||||
fnodeGroups.errors.push(stat);
|
|
||||||
cont();
|
|
||||||
} else {
|
|
||||||
TypeEmitter.sortFnodesByType(stat, fnodeGroups);
|
|
||||||
TypeEmitter.emitNodeType(emitter, curpath, stat, cont);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emitter.emit('name', curpath, file, noop);
|
|
||||||
|
|
||||||
statPath = curpath + '/' + file;
|
|
||||||
|
|
||||||
if (sync) {
|
|
||||||
try {
|
|
||||||
lstatHandler(null, fs.lstatSync(statPath));
|
|
||||||
} catch(e) {
|
|
||||||
lstatHandler(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fs.lstat(statPath, lstatHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function postFilesHandler() {
|
|
||||||
if (fnodeGroups.errors.length) {
|
|
||||||
emitter.emit('errors', curpath, fnodeGroups.errors, noop);
|
|
||||||
}
|
|
||||||
TypeEmitter.emitNodeTypeGroups(emitter, curpath, fnodeGroups, function () {
|
|
||||||
var dirs = [];
|
|
||||||
fnodeGroups.directories.forEach(function (stat) {
|
|
||||||
dirs.push(stat.name);
|
|
||||||
});
|
|
||||||
dirs.forEach(fullPath);
|
|
||||||
queue.push(q = dirs);
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function readFiles() {
|
|
||||||
if (!files || 0 == files.length) {
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO could allow user to selectively stat
|
|
||||||
// and don't stat if there are no stat listeners
|
|
||||||
emitter.emit('names', curpath, files, noop);
|
|
||||||
|
|
||||||
if (sync) {
|
|
||||||
files.forEach(function (items) {
|
|
||||||
filesHandler(noop, items);
|
|
||||||
});
|
|
||||||
postFilesHandler();
|
|
||||||
} else {
|
|
||||||
forEachAsync(files, filesHandler).then(postFilesHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!err) {
|
|
||||||
readFiles();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!firstRun) {
|
|
||||||
emitter.emit('directoryError', curpath, { error: err }, noop);
|
|
||||||
readFiles();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstRun = false;
|
|
||||||
fs.lstat(curpath, function (e, stat) {
|
|
||||||
|
|
||||||
if (stat) {
|
|
||||||
files = [curpath.replace(/.*\//, '')];
|
|
||||||
curpath = curpath.replace(files[0], '');
|
|
||||||
}
|
|
||||||
|
|
||||||
readFiles();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function walkSync() {
|
|
||||||
var err, files;
|
|
||||||
|
|
||||||
try {
|
|
||||||
files = fs.readdirSync(curpath);
|
|
||||||
} catch(e) {
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
readdirHandler(err, files);
|
|
||||||
}
|
|
||||||
|
|
||||||
function walk() {
|
|
||||||
fs.readdir(curpath, readdirHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (sync) {
|
|
||||||
walk = walkSync;
|
|
||||||
process.nextTick(walk);
|
|
||||||
} else {
|
|
||||||
walk();
|
|
||||||
}
|
|
||||||
|
|
||||||
return emitter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wFilesHandlerWrapper(items) {
|
||||||
|
/*jshint validthis:true*/
|
||||||
|
this._wFilesHandler(noop, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Walker(pathname, options, sync) {
|
||||||
|
EventEmitter.call(this);
|
||||||
|
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
me._wsync = true;
|
||||||
|
me._wq = [];
|
||||||
|
me._wqueue = [me._wq];
|
||||||
|
me._wcurpath = undefined;
|
||||||
|
me._wfistrun = true;
|
||||||
|
me._wcurpath = pathname;
|
||||||
|
|
||||||
|
if (me._wsync) {
|
||||||
|
me._wWalk = me._wWalkSync;
|
||||||
|
} else {
|
||||||
|
me._wWalk = me._wWalkAsync;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO just one little anony won't hurt...
|
||||||
|
process.nextTick(function () {
|
||||||
|
me._wWalk();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inherits must come before prototype additions
|
||||||
|
util.inherits(Walker, EventEmitter);
|
||||||
|
|
||||||
|
Walker.prototype._wLstatHandler = function (err, stat) {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
stat = stat || {};
|
||||||
|
stat.name = me._wcurfile;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
stat.error = err;
|
||||||
|
//me.emit('error', curpath, stat);
|
||||||
|
me.emit('nodeError', me._wcurpath, stat, noop);
|
||||||
|
me._wfnodegroups.errors.push(stat);
|
||||||
|
me._wCurFileCallback();
|
||||||
|
} else {
|
||||||
|
TypeEmitter.sortFnodesByType(stat, me._wfnodegroups);
|
||||||
|
// NOTE: wCurFileCallback doesn't need thisness, so this is okay
|
||||||
|
TypeEmitter.emitNodeType(me, me._wcurpath, stat, me._wCurFileCallback, me);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Walker.prototype._wFilesHandler = function (cont, file) {
|
||||||
|
var statPath
|
||||||
|
, me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
me._wcurfile = file;
|
||||||
|
me._wCurFileCallback = cont;
|
||||||
|
me.emit('name', me._wcurpath, file, noop);
|
||||||
|
|
||||||
|
statPath = me._wcurpath + '/' + file;
|
||||||
|
|
||||||
|
if (!me._wsync) {
|
||||||
|
// TODO how to remove this anony?
|
||||||
|
fs.lstat(statPath, function (err, stat) {
|
||||||
|
me._wLstatHandler(err, stat);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
me._wLstatHandler(null, fs.lstatSync(statPath));
|
||||||
|
} catch(e) {
|
||||||
|
me._wLstatHandler(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Walker.prototype._wOnEmitDone = function () {
|
||||||
|
var me = this
|
||||||
|
, dirs = []
|
||||||
|
;
|
||||||
|
|
||||||
|
me._wfnodegroups.directories.forEach(appendToDirs, dirs);
|
||||||
|
dirs.forEach(me._wJoinPath, me);
|
||||||
|
me._wqueue.push(me._wq = dirs);
|
||||||
|
me._wNext();
|
||||||
|
};
|
||||||
|
Walker.prototype._wPostFilesHandler = function () {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
if (me._wfnodegroups.errors.length) {
|
||||||
|
me.emit('errors', me._wcurpath, me._wfnodegroups.errors, noop);
|
||||||
|
}
|
||||||
|
// XXX emitNodeTypes still needs refactor
|
||||||
|
TypeEmitter.emitNodeTypeGroups(me, me._wcurpath, me._wfnodegroups, me._wOnEmitDone, me);
|
||||||
|
};
|
||||||
|
Walker.prototype._wReadFiles = function () {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
if (!me._wcurfiles || 0 === me._wcurfiles.length) {
|
||||||
|
return me._wNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO could allow user to selectively stat
|
||||||
|
// and don't stat if there are no stat listeners
|
||||||
|
me.emit('names', me._wcurpath, me._wcurfiles, noop);
|
||||||
|
|
||||||
|
if (me._wsync) {
|
||||||
|
me._wcurfiles.forEach(wFilesHandlerWrapper, me);
|
||||||
|
me._wPostFilesHandler();
|
||||||
|
} else {
|
||||||
|
forEachAsync(me._wcurfiles, me._wFilesHandler, me).then(me._wPostFilesHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Walker.prototype._wReaddirHandler = function (err, files) {
|
||||||
|
var fnodeGroups = TypeEmitter.createNodeGroups()
|
||||||
|
, me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
me._wfnodegroups = fnodeGroups;
|
||||||
|
me._wcurfiles = files;
|
||||||
|
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
me._wReadFiles();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!me._wfistrun) {
|
||||||
|
me.emit('directoryError', me._wcurpath, { error: err }, noop);
|
||||||
|
me._wReadFiles();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
me._wfistrun = false;
|
||||||
|
// TODO how to remove this anony?
|
||||||
|
fs.lstat(me._wcurpath, function (e, stat) {
|
||||||
|
|
||||||
|
if (stat) {
|
||||||
|
files = [me._wcurpath.replace(/.*\//, '')];
|
||||||
|
me._wcurpath = me._wcurpath.replace(files[0], '');
|
||||||
|
}
|
||||||
|
|
||||||
|
me._wReadFiles();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Walker.prototype._wWalkSync = function () {
|
||||||
|
var err
|
||||||
|
, files
|
||||||
|
, me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
try {
|
||||||
|
files = fs.readdirSync(me._wcurpath);
|
||||||
|
} catch(e) {
|
||||||
|
err = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
me._wReaddirHandler(err, files);
|
||||||
|
};
|
||||||
|
Walker.prototype._wWalkAsync = function () {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
// TODO how to remove this anony?
|
||||||
|
fs.readdir(me._wcurpath, function (err, files) {
|
||||||
|
me._wReaddirHandler(err, files);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Walker.prototype._wNext = function () {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
if (me._paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (me._wq.length) {
|
||||||
|
me._wcurpath = me._wq.pop();
|
||||||
|
me._wWalk();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
me._wqueue.length -= 1;
|
||||||
|
if (me._wqueue.length) {
|
||||||
|
me._wq = me._wqueue[me._wqueue.length - 1];
|
||||||
|
return this._wNext();
|
||||||
|
}
|
||||||
|
me.emit('end');
|
||||||
|
};
|
||||||
|
Walker.prototype._wJoinPath = function (v, i, o) {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
o[i] = [me._wcurpath, '/', v].join('');
|
||||||
|
};
|
||||||
|
Walker.prototype.pause = function () {
|
||||||
|
this._paused = true;
|
||||||
|
};
|
||||||
|
Walker.prototype.resume = function () {
|
||||||
|
this._paused = false;
|
||||||
|
this._wNext();
|
||||||
|
};
|
||||||
|
|
||||||
exports.walk = function (path, opts) {
|
exports.walk = function (path, opts) {
|
||||||
return create(path, opts, false);
|
return new Walker(path, opts, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.walkSync = function (path, opts) {
|
exports.walkSync = function (path, opts) {
|
||||||
return create(path, opts, true);
|
return new Walker(path, opts, true);
|
||||||
};
|
};
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*jshint strict:true node:true es5:true onevar:true laxcomma:true laxbreak:true*/
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var walk = require('./lib/walk').walk
|
||||||
|
, walker
|
||||||
|
;
|
||||||
|
|
||||||
|
walker = walk('./');
|
||||||
|
walker.on('file', function (root, stat, next) {
|
||||||
|
console.log(root, stat.name);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
Loading…
Reference in New Issue