fs-walk.js/lib/walk.js

303 lines
7.7 KiB
JavaScript
Raw Permalink Normal View History

2011-05-03 03:11:03 +00:00
// Adapted from work by jorge@jorgechamorro.com on 2010-11-25
2011-02-04 06:31:25 +00:00
(function () {
2012-06-06 19:19:44 +00:00
"use strict";
2011-02-04 06:57:11 +00:00
function noop() {}
var fs = require('fs')
2014-05-20 23:12:07 +00:00
, forEachAsync = require('foreachasync').forEachAsync
, EventEmitter = require('events').EventEmitter
, TypeEmitter = require('./node-type-emitter')
2012-06-06 19:19:44 +00:00
, util = require('util')
, path = require('path')
;
2011-02-04 06:31:25 +00:00
2012-06-06 19:19:44 +00:00
function appendToDirs(stat) {
/*jshint validthis:true*/
if(stat.flag && stat.flag === NO_DESCEND) { return; }
2012-06-06 19:19:44 +00:00
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
;
2011-02-04 06:31:25 +00:00
2013-06-23 05:56:13 +00:00
options = options || {};
me._wStat = options.followLinks && 'stat' || 'lstat';
me._wStatSync = me._wStat + 'Sync';
me._wsync = sync;
2012-06-06 19:19:44 +00:00
me._wq = [];
me._wqueue = [me._wq];
me._wcurpath = undefined;
me._wfilters = options.filters || [];
me._wfirstrun = true;
2012-06-06 19:19:44 +00:00
me._wcurpath = pathname;
2011-05-03 03:11:03 +00:00
2012-06-06 19:19:44 +00:00
if (me._wsync) {
2014-01-26 23:02:19 +00:00
//console.log('_walkSync');
2012-06-06 19:19:44 +00:00
me._wWalk = me._wWalkSync;
} else {
2014-01-26 23:02:19 +00:00
//console.log('_walkASync');
2012-06-06 19:19:44 +00:00
me._wWalk = me._wWalkAsync;
}
2011-05-03 03:11:03 +00:00
2013-06-23 05:56:13 +00:00
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);
});
2012-06-06 19:19:44 +00:00
});
2013-06-23 05:56:13 +00:00
me._wWalk();
2012-06-06 19:19:44 +00:00
}
2011-05-03 03:11:03 +00:00
2012-06-06 19:19:44 +00:00
// Inherits must come before prototype additions
util.inherits(Walker, EventEmitter);
2011-05-03 03:11:03 +00:00
2012-06-06 19:19:44 +00:00
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);
2015-01-06 02:06:32 +00:00
// TODO v3.0 (don't noop the next if there are listeners)
2012-06-06 19:19:44 +00:00
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
;
2012-01-16 03:47:08 +00:00
2012-06-06 19:19:44 +00:00
me._wcurfile = file;
me._wCurFileCallback = cont;
me.emit('name', me._wcurpath, file, noop);
2012-01-16 03:47:08 +00:00
statPath = me._wcurpath + path.sep + file;
2012-06-06 19:19:44 +00:00
if (!me._wsync) {
// TODO how to remove this anony?
2013-06-23 05:56:13 +00:00
fs[me._wStat](statPath, function (err, stat) {
2012-06-06 19:19:44 +00:00
me._wLstatHandler(err, stat);
2012-01-16 03:47:08 +00:00
});
2012-06-06 19:19:44 +00:00
return;
2011-05-03 03:11:03 +00:00
}
2012-06-06 19:19:44 +00:00
try {
2013-06-23 05:56:13 +00:00
me._wLstatHandler(null, fs[me._wStatSync](statPath));
2012-06-06 19:19:44 +00:00
} catch(e) {
me._wLstatHandler(e);
}
};
Walker.prototype._wOnEmitDone = function () {
var me = this
, dirs = []
;
2011-05-03 03:11:03 +00:00
2012-06-06 19:19:44 +00:00
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
;
2011-05-03 03:11:03 +00:00
2012-06-06 19:19:44 +00:00
if (me._wfnodegroups.errors.length) {
2015-01-06 02:06:32 +00:00
// TODO v3.0 (don't noop the next)
// .errors is an array of stats with { name: name, error: error }
2012-06-06 19:19:44 +00:00
me.emit('errors', me._wcurpath, me._wfnodegroups.errors, noop);
2011-05-03 03:11:03 +00:00
}
2012-06-06 19:19:44 +00:00
// XXX emitNodeTypes still needs refactor
TypeEmitter.emitNodeTypeGroups(me, me._wcurpath, me._wfnodegroups, me._wOnEmitDone, me);
};
Walker.prototype._wReadFiles = function () {
var me = this
;
2011-05-03 03:11:03 +00:00
2012-06-06 19:19:44 +00:00
if (!me._wcurfiles || 0 === me._wcurfiles.length) {
return me._wNext();
2010-11-21 05:02:53 +00:00
}
2012-06-06 19:19:44 +00:00
// 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);
2011-02-04 06:31:25 +00:00
}
2012-06-06 19:19:44 +00:00
};
Walker.prototype._wReaddirHandler = function (err, files) {
var fnodeGroups = TypeEmitter.createNodeGroups()
, me = this
2015-01-06 02:06:32 +00:00
, parent
, child
2012-06-06 19:19:44 +00:00
;
me._wfnodegroups = fnodeGroups;
me._wcurfiles = files;
// no error, great
2012-06-06 19:19:44 +00:00
if (!err) {
me._wReadFiles();
return;
2011-02-04 06:31:25 +00:00
}
2011-05-03 03:11:03 +00:00
2015-01-06 02:06:32 +00:00
// TODO path.sep
me._wcurpath = me._wcurpath.replace(/\/$/, '');
// error? not first run? => directory error
if (!me._wfirstrun) {
2015-01-06 02:06:32 +00:00
// TODO v3.0 (don't noop the next if there are listeners)
2012-06-06 19:19:44 +00:00
me.emit('directoryError', me._wcurpath, { error: err }, noop);
2015-01-06 02:06:32 +00:00
// TODO v3.0
//me.emit('directoryError', me._wcurpath.replace(/^(.*)\/.*$/, '$1'), { name: me._wcurpath.replace(/^.*\/(.*)/, '$1'), error: err }, noop);
2012-06-06 19:19:44 +00:00
me._wReadFiles();
return;
2011-05-03 03:11:03 +00:00
}
// error? first run? => maybe a file, maybe a true error
me._wfirstrun = false;
2015-01-06 02:12:42 +00:00
// readdir failed (might be a file), try a stat on the parent
2015-01-06 02:06:32 +00:00
parent = me._wcurpath.replace(/^(.*)\/.*$/, '$1');
fs[me._wStat](parent, function (e, stat) {
2012-06-06 19:19:44 +00:00
if (stat) {
2015-01-06 02:06:32 +00:00
// success
// now try stat on this as a child of the parent directory
child = me._wcurpath.replace(/^.*\/(.*)$/, '$1');
2015-01-06 02:12:42 +00:00
me._wcurfiles = [child];
2015-01-06 02:06:32 +00:00
me._wcurpath = parent;
} else {
2015-01-06 02:06:32 +00:00
// 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);
2012-06-06 19:19:44 +00:00
}
me._wReadFiles();
});
};
Walker.prototype._wFilter = function () {
var me = this
, exclude
2012-06-06 19:19:44 +00:00
;
// 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;
}
});
2012-06-06 19:19:44 +00:00
return exclude;
};
Walker.prototype._wWalkSync = function () {
//console.log('walkSync');
var err
, files
, me = this
;
try {
files = fs.readdirSync(me._wcurpath);
} catch(e) {
err = e;
2012-06-06 19:19:44 +00:00
}
me._wReaddirHandler(err, files);
};
Walker.prototype._wWalkAsync = function () {
2013-06-23 06:11:51 +00:00
//console.log('walkAsync');
2012-06-06 19:19:44 +00:00
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();
}
2014-04-11 15:13:18 +00:00
if (me._wcurpath && !me._wFilter()) {
me._wWalk();
} else {
me._wNext();
}
2012-06-06 19:19:44 +00:00
return;
}
me._wqueue.length -= 1;
if (me._wqueue.length) {
me._wq = me._wqueue[me._wqueue.length - 1];
return me._wNext();
2012-06-06 19:19:44 +00:00
}
2013-06-23 05:56:13 +00:00
// To not break compatibility
//process.nextTick(function () {
me.emit('end');
//});
2012-06-06 19:19:44 +00:00
};
Walker.prototype._wJoinPath = function (v, i, o) {
var me = this
;
o[i] = [me._wcurpath, path.sep, v].join('');
2012-06-06 19:19:44 +00:00
};
Walker.prototype.pause = function () {
this._paused = true;
};
Walker.prototype.resume = function () {
this._paused = false;
this._wNext();
};
2011-02-04 06:31:25 +00:00
2011-05-03 03:11:03 +00:00
exports.walk = function (path, opts) {
2012-06-06 19:19:44 +00:00
return new Walker(path, opts, false);
2011-05-03 03:11:03 +00:00
};
exports.walkSync = function (path, opts) {
2012-06-06 19:19:44 +00:00
return new Walker(path, opts, true);
2011-05-03 03:11:03 +00:00
};
2010-11-21 05:02:53 +00:00
}());