heroku-debug
Version:
debugging plugin for the CLI
122 lines (109 loc) • 3.08 kB
JavaScript
var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var path = require('path');
module.exports = findit;
function findit(basedir, opts) {
opts = opts || {};
var followSymlinks = !!opts.followSymlinks;
var myFs = opts.fs || fs;
var emitter = new EventEmitter();
var stopped = false;
var pending = 0;
var seen = {};
emitter.stop = stop;
walkPath(basedir);
return emitter;
function recursiveReadDir(basedir, linkPath) {
pendStart();
myFs.readdir(basedir, function(err, entries) {
if (stopped) return;
if (err) {
handleError(err, basedir);
pendEnd();
return;
}
entries.forEach(function(entry) {
var fullPath = path.join(basedir, entry);
var fullLinkPath = linkPath && path.join(linkPath, entry);
walkPath(fullPath, fullLinkPath);
});
pendEnd();
});
}
function walkPath(fullPath, linkPath) {
pendStart();
myFs.lstat(fullPath, function(err, stats) {
if (stopped) return;
if (err) {
handleError(err, fullPath);
pendEnd();
return;
}
emitter.emit('path', fullPath, stats, linkPath);
var dirStopped = false;
if (stats.isDirectory()) {
if (seen[fullPath]) {
err = new Error("file system loop detected");
err.code = 'ELOOP';
handleError(err, fullPath);
pendEnd();
return;
}
seen[fullPath] = true;
emitter.emit('directory', fullPath, stats, stopDir, linkPath);
if (!dirStopped) recursiveReadDir(fullPath, linkPath);
} else if (stats.isFile()) {
if (!seen[fullPath]) {
seen[fullPath] = true;
emitter.emit('file', fullPath, stats, linkPath);
}
} else if (stats.isSymbolicLink()) {
emitter.emit('link', fullPath, stats, linkPath);
if (followSymlinks) recursiveReadLink(fullPath);
}
pendEnd();
function stopDir() {
dirStopped = true;
}
});
}
function recursiveReadLink(linkPath) {
pendStart();
myFs.readlink(linkPath, function(err, linkString) {
if (stopped) return;
if (err) {
handleError(err, linkPath);
pendEnd();
return;
}
var fullPath = path.resolve(path.dirname(linkPath), linkString);
emitter.emit('readlink', linkPath, fullPath);
walkPath(fullPath, linkPath);
pendEnd();
});
}
function stop() {
if (stopped) return;
stopped = true;
emitter.emit('stop');
}
function handleError(err, errPath) {
if (!err || stopped) return;
err.path = errPath;
emitter.emit('error', err);
}
function pendStart() {
pending += 1;
}
function pendEnd() {
if (stopped) return;
pending -= 1;
if (pending === 0) {
emitter.emit('end');
} else if (pending < 0) {
// this should never happen; if this gets thrown we need to debug findit
// and this stack trace will help.
throw new Error("pendEnd called too many times");
}
}
}