Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

updated to prototype

  • Loading branch information...
commit b7454f32c41234bc2bb7b4177c602adc072134a3 1 parent 63051eb
@coolaj86 authored
Showing with 212 additions and 132 deletions.
  1. +5 −4 lib/node-type-emitter.js
  2. +192 −128 lib/walk.js
  3. +15 −0 test-walk.js
View
9 lib/node-type-emitter.js
@@ -1,3 +1,4 @@
+/*jshint strict:true node:true es5:true onevar:true laxcomma:true laxbreak:true*/
(function () {
"use strict";
@@ -43,12 +44,12 @@
// 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) {
+ function emitSingleEvents(emitter, path, stats, next, self) {
var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
function nextWhenReady() {
num -= 1;
- if (0 === num) { next(); }
+ if (0 === num) { next.call(self); }
}
emitter.emit(stats.type, path, stats, nextWhenReady);
@@ -60,12 +61,12 @@
// 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) {
+ function emitPluralEvents(emitter, path, nodes, next, self) {
var num = 1;
function nextWhenReady() {
num -= 1;
- if (0 === num) { next(); }
+ if (0 === num) { next.call(self); }
}
fnodeTypesPlural.concat(["nodes", "errors"]).forEach(function (fnodeType) {
View
320 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
(function () {
- "use strict"
+ "use strict";
// Array.prototype.forEachAsync(next, item, i, collection)
//require('Array.prototype.forEachAsync');
@@ -11,161 +12,224 @@
, forEachAsync = require('forEachAsync')
, EventEmitter = require('events').EventEmitter
, TypeEmitter = require('./node-type-emitter')
+ , util = require('util')
;
- function create(pathname, options, sync) {
- var emitter = new EventEmitter()
- , q = []
- , queue = [q]
- , curpath
- , firstRun = true
+ function appendToDirs(stat) {
+ /*jshint validthis:true*/
+ this.push(stat.name);
+ }
+
+ function wFilesHandlerWrapper(items) {
+ /*jshint validthis:true*/
+ this._wFilesHandler(noop, items);
+ }
+
+ function Walker(pathname, options, sync) {
+ EventEmitter.call(this);
+
+ var me = this
;
- 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);
- }
- }
+ me._wsync = true;
+ me._wq = [];
+ me._wqueue = [me._wq];
+ me._wcurpath = undefined;
+ me._wfistrun = true;
+ me._wcurpath = pathname;
- 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();
- });
- }
+ if (me._wsync) {
+ me._wWalk = me._wWalkSync;
+ } else {
+ me._wWalk = me._wWalkAsync;
+ }
- 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);
- }
- }
+ // TODO just one little anony won't hurt...
+ process.nextTick(function () {
+ me._wWalk();
+ });
+ }
- if (!err) {
- readFiles();
- return;
- }
+ // Inherits must come before prototype additions
+ util.inherits(Walker, EventEmitter);
- if (!firstRun) {
- emitter.emit('directoryError', curpath, { error: err }, noop);
- readFiles();
- return;
- }
+ 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
+ ;
- firstRun = false;
- fs.lstat(curpath, function (e, stat) {
- if (stat) {
- files = [curpath.replace(/.*\//, '')];
- curpath = curpath.replace(files[0], '');
- }
+ me._wcurfile = file;
+ me._wCurFileCallback = cont;
+ me.emit('name', me._wcurpath, file, noop);
- readFiles();
+ statPath = me._wcurpath + '/' + file;
+
+ if (!me._wsync) {
+ // TODO how to remove this anony?
+ fs.lstat(statPath, function (err, stat) {
+ me._wLstatHandler(err, stat);
});
+ return;
}
- function walkSync() {
- var err, files;
+ try {
+ me._wLstatHandler(null, fs.lstatSync(statPath));
+ } catch(e) {
+ me._wLstatHandler(e);
+ }
+ };
+ Walker.prototype._wOnEmitDone = function () {
+ var me = this
+ , dirs = []
+ ;
- try {
- files = fs.readdirSync(curpath);
- } catch(e) {
- err = e;
- }
+ 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
+ ;
- readdirHandler(err, files);
+ 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
+ ;
- function walk() {
- fs.readdir(curpath, readdirHandler);
+ if (!me._wcurfiles || 0 === me._wcurfiles.length) {
+ return me._wNext();
}
-
- function next() {
- if (q.length) {
- curpath = q.pop();
- return walk();
- }
- if (queue.length -= 1) {
- q = queue[queue.length - 1];
- return next();
- }
- emitter.emit('end');
+
+ // 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);
}
-
- function fullPath(v, i, o) {
- o[i] = [curpath, '/', v].join('');
+ };
+ Walker.prototype._wReaddirHandler = function (err, files) {
+ var fnodeGroups = TypeEmitter.createNodeGroups()
+ , me = this
+ ;
+
+ me._wfnodegroups = fnodeGroups;
+ me._wcurfiles = files;
+
+
+ if (!err) {
+ me._wReadFiles();
+ return;
}
-
- curpath = pathname;
- if (sync) {
- walk = walkSync;
- process.nextTick(walk);
- } else {
- walk();
+ if (!me._wfistrun) {
+ me.emit('directoryError', me._wcurpath, { error: err }, noop);
+ me._wReadFiles();
+ return;
}
- return emitter;
- }
+ 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) {
- return create(path, opts, false);
+ return new Walker(path, opts, false);
};
exports.walkSync = function (path, opts) {
- return create(path, opts, true);
+ return new Walker(path, opts, true);
};
}());
View
15 test-walk.js
@@ -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();
+ });
+
+}());
Please sign in to comment.
Something went wrong with that request. Please try again.