diff options
Diffstat (limited to 'node_modules/walk/lib/walk.js')
-rw-r--r-- | node_modules/walk/lib/walk.js | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/node_modules/walk/lib/walk.js b/node_modules/walk/lib/walk.js new file mode 100644 index 0000000..ee8ade5 --- /dev/null +++ b/node_modules/walk/lib/walk.js @@ -0,0 +1,302 @@ +// Adapted from work by jorge@jorgechamorro.com on 2010-11-25 +(function () { + "use strict"; + + function noop() {} + + var fs = require('fs') + , forEachAsync = require('foreachasync').forEachAsync + , EventEmitter = require('events').EventEmitter + , TypeEmitter = require('./node-type-emitter') + , util = require('util') + , path = require('path') + ; + + function appendToDirs(stat) { + /*jshint validthis:true*/ + if(stat.flag && stat.flag === NO_DESCEND) { return; } + 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 + ; + + options = options || {}; + me._wStat = options.followLinks && 'stat' || 'lstat'; + me._wStatSync = me._wStat + 'Sync'; + me._wsync = sync; + me._wq = []; + me._wqueue = [me._wq]; + me._wcurpath = undefined; + me._wfilters = options.filters || []; + me._wfirstrun = true; + me._wcurpath = pathname; + + if (me._wsync) { + //console.log('_walkSync'); + me._wWalk = me._wWalkSync; + } else { + //console.log('_walkASync'); + me._wWalk = me._wWalkAsync; + } + + options.listeners = options.listeners || {}; + Object.keys(options.listeners).forEach(function (event) { + var callbacks = options.listeners[event] + ; + + if ('function' === typeof callbacks) { + callbacks = [callbacks]; + } + + callbacks.forEach(function (callback) { + me.on(event, callback); + }); + }); + + 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); + // TODO v3.0 (don't noop the next if there are listeners) + 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 + path.sep + file; + + if (!me._wsync) { + // TODO how to remove this anony? + fs[me._wStat](statPath, function (err, stat) { + me._wLstatHandler(err, stat); + }); + return; + } + + try { + me._wLstatHandler(null, fs[me._wStatSync](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) { + // TODO v3.0 (don't noop the next) + // .errors is an array of stats with { name: name, error: error } + 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 + , parent + , child + ; + + me._wfnodegroups = fnodeGroups; + me._wcurfiles = files; + + // no error, great + if (!err) { + me._wReadFiles(); + return; + } + + // TODO path.sep + me._wcurpath = me._wcurpath.replace(/\/$/, ''); + + // error? not first run? => directory error + if (!me._wfirstrun) { + // TODO v3.0 (don't noop the next if there are listeners) + me.emit('directoryError', me._wcurpath, { error: err }, noop); + // TODO v3.0 + //me.emit('directoryError', me._wcurpath.replace(/^(.*)\/.*$/, '$1'), { name: me._wcurpath.replace(/^.*\/(.*)/, '$1'), error: err }, noop); + me._wReadFiles(); + return; + } + + // error? first run? => maybe a file, maybe a true error + me._wfirstrun = false; + + // readdir failed (might be a file), try a stat on the parent + parent = me._wcurpath.replace(/^(.*)\/.*$/, '$1'); + fs[me._wStat](parent, function (e, stat) { + + if (stat) { + // success + // now try stat on this as a child of the parent directory + child = me._wcurpath.replace(/^.*\/(.*)$/, '$1'); + me._wcurfiles = [child]; + me._wcurpath = parent; + } else { + // TODO v3.0 + //me.emit('directoryError', me._wcurpath.replace(/^(.*)\/.*$/, '$1'), { name: me._wcurpath.replace(/^.*\/(.*)/, '$1'), error: err }, noop); + // TODO v3.0 (don't noop the next) + // the original readdir error, not the parent stat error + me.emit('nodeError', me._wcurpath, { error: err }, noop); + } + + me._wReadFiles(); + }); + }; + Walker.prototype._wFilter = function () { + var me = this + , exclude + ; + + // Stop directories that contain filter keywords + // from continuing through the walk process + exclude = me._wfilters.some(function (filter) { + if (me._wcurpath.match(filter)) { + return true; + } + }); + + return exclude; + }; + Walker.prototype._wWalkSync = function () { + //console.log('walkSync'); + var err + , files + , me = this + ; + + try { + files = fs.readdirSync(me._wcurpath); + } catch(e) { + err = e; + } + + me._wReaddirHandler(err, files); + }; + Walker.prototype._wWalkAsync = function () { + //console.log('walkAsync'); + 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(); + while (me._wq.length && me._wFilter()) { + me._wcurpath = me._wq.pop(); + } + if (me._wcurpath && !me._wFilter()) { + me._wWalk(); + } else { + me._wNext(); + } + return; + } + me._wqueue.length -= 1; + if (me._wqueue.length) { + me._wq = me._wqueue[me._wqueue.length - 1]; + return me._wNext(); + } + + // To not break compatibility + //process.nextTick(function () { + me.emit('end'); + //}); + }; + Walker.prototype._wJoinPath = function (v, i, o) { + var me = this + ; + + o[i] = [me._wcurpath, path.sep, v].join(''); + }; + Walker.prototype.pause = function () { + this._paused = true; + }; + Walker.prototype.resume = function () { + this._paused = false; + this._wNext(); + }; + + exports.walk = function (path, opts) { + return new Walker(path, opts, false); + }; + + exports.walkSync = function (path, opts) { + return new Walker(path, opts, true); + }; +}()); |