updated to prototype

This commit is contained in:
AJ ONeal 2012-06-06 13:19:44 -06:00
parent 63051eb24f
commit b7454f32c4
3 changed files with 233 additions and 153 deletions

View File

@ -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) {

View File

@ -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);
}; };
}()); }());

15
test-walk.js Normal file
View File

@ -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();
});
}());