Skip to content

Instantly share code, notes, and snippets.

@cadorn
Created November 14, 2012 16:53
Show Gist options
  • Save cadorn/4073284 to your computer and use it in GitHub Desktop.
Save cadorn/4073284 to your computer and use it in GitHub Desktop.
Slow with Q promises, fast with callbacks (NodeJS)

The promises based implementation is much slower than the callback based implementation.

I am wondering why exactly that is the case.

Usage

var Q = require("q");
var SCANNER = require("./promises");
//var SCANNER = require("./callbacks");

// NOTE: To run this test, update path below to a package with many dependencies.
//       e.g. https://github.com/ajaxorg/cloud9
Q.when(SCANNER.for("/pinf/workspaces/github.com/ajaxorg/cloud9").fsTree(), function(tree) {
    console.log(tree.toString());
}).end();
const PATH = require("path");
const FS = require("graceful-fs");
const Q = require("sourcemint-util-js/lib/q");
const UTIL = require("sourcemint-util-js/lib/util");
const WAITFOR = require("sourcemint-util-js/lib/wait-for");
var instances = {};
exports.for = function(packageRootPath) {
if (!instances[packageRootPath]) {
instances[packageRootPath] = new Scanner(packageRootPath);
}
return instances[packageRootPath];
}
var Scanner = function(packageRootPath) {
var self = this;
self.fsTree = function(options) {
options = options || {};
try {
var tree = new FsNode();
return tree.initForPath(packageRootPath, options).then(function() {
return tree;
}).fail(function(err) {
throw err;
});
} catch(err) {
return Q.reject(err);
}
}
return self;
}
var FsNode = function(parent, dir, name, level) {
var self = this;
self.top = (parent && parent.top) || self;
self.name = name || false;
self.parent = parent || null;
self.dir = dir || false;
self.level = level || 0;
self.relpath = "";
self.reset = function(path) {
self.path = path || null;
self.exists = false;
self.children = {};
self.childrenIgnored = false;
self.descriptors = {};
self.symlinked = false;
self.circular = false;
}
self.reset();
}
//Node.prototype = new BaseNode();
FsNode.prototype.initForPath = function(path, options, _refreshedPackages) {
var self = this;
if (options.debug) console.log("[sm] Trigger initForPath for node: " + path);
options.ignorePackages = options.ignorePackages || ["sm", "npm"];
self.reset(path);
_refreshedPackages = _refreshedPackages || {};
if (_refreshedPackages[self.path] && _refreshedPackages[self.path].symlinked !== "inside") {
self.circular = _refreshedPackages[self.path];
return Q.ref();
}
_refreshedPackages[self.path] = self;
function populateLocator() {
function findDependency(dependencies) {
if (Array.isArray(dependencies)) {
for (var i=0 ; i<dependencies.length ; i++) {
if (dependencies[i] === self.name) {
// Found but no version specified.
return "*";
}
}
} else {
for (var key in dependencies) {
if (key === self.name) {
if (dependencies[key] === "" || dependencies[key] === "latest") {
return "*";
} else {
return dependencies[key];
}
}
}
}
return false;
}
function normalizeMapping(locator) {
if (typeof locator.viaPm !== "undefined" && locator.viaPm === "sm") {
if (UTIL.isArrayLike(locator.pointer)) {
locator.pm = locator.pointer[0];
locator.descriptorOverlay = locator.pointer[2] || false;
locator.pointer = locator.pointer[1];
} else {
locator.pm = "sm";
}
}
}
var locator = {
// `sm` or `npm` depending on which attribute used.
viaPm: false,
// The name of the attribute used.
viaAttribute: false,
// The name of the declared package manager to use (or default based on `viaPm`).
pm: false,
// The 'selector' (in case of default registry; i.e. npm) or 'location' uri.
pointer: false,
// Overrides for the package descriptor.
descriptorOverlay: false,
// Flag to indicate whether dependency is or should be bundled.
bundled: false
};
if (self.parent) {
if (self.descriptors.package.mappings && (locator.pointer = findDependency(self.descriptors.package.mappings))) {
locator.viaPm = "sm";
locator.viaAttribute = "mappings";
normalizeMapping(locator);
} else
if (self.descriptors.package.devMappings && (locator.pointer = findDependency(self.descriptors.package.devMappings))) {
locator.viaPm = "sm";
locator.viaAttribute = "devMappings";
normalizeMapping(locator);
} else
if (self.descriptors.package.dependencies && (locator.pointer = findDependency(self.descriptors.package.dependencies))) {
locator.viaPm = "npm";
locator.pm = "npm";
locator.viaAttribute = "dependencies";
} else
if (self.descriptors.package.devDependencies && (locator.pointer = findDependency(self.descriptors.package.devDependencies))) {
locator.viaPm = "npm";
locator.pm = "npm";
locator.viaAttribute = "devDependencies";
}
if (self.descriptors.package.bundleDependencies && findDependency(self.descriptors.package.bundleDependencies)) {
locator.viaPm = "npm";
locator.pm = "npm";
locator.bundled = true;
}
} else
if(self.level === 0 && options.topPointer) {
locator.pointer = options.topPointer;
locator.viaPm = "sm";
locator.viaAttribute = "mappings";
normalizeMapping(locator);
}
if (locator.pointer !== false && /^\.{1,2}\//.test(locator.pointer) && self.parent) {
var oldPointer = locator.pointer;
locator.pointer = PATH.join(self.parent.path, locator.pointer);
// Path may not traverse higher than declaring package.
if (locator.pointer.substring(0, self.parent.path.length) !== self.parent.path) {
throw new Error("Dependency location '" + oldPointer + "' may not point higher than declaring package ");
}
}
// Fix `0.x.`.
if (/.\.$/.test(locator.pointer)) {
locator.pointer = locator.pointer.substring(0, locator.pointer.length -1);
}
self.descriptors.locator = (locator.viaPm)?locator:false;
}
var descriptors = [
["package", "package.json"],
["smSource", ".sourcemint/source.json"],
["program", "program.json"],
["programRT", "program.rt.json"],
["npmShrinkwrap", "npm-shrinkwrap.json"],
["smCatalog", "sm-catalog.json"],
["smCatalogLocked", "sm-catalog.locked.json"]
];
var exists = {};
var mtimes = {};
var cachedDescriptors = {};
function checkExists(relpath, callback) {
if (typeof exists[relpath] !== "undefined") return callback(null, exists[relpath]);
PATH.exists(PATH.join(self.path, relpath), function(oo) {
return callback(null, (exists[relpath] = oo));
});
}
function loadDescriptor(relpath, callback) {
checkExists(relpath, function(err, exists) {
if (err) return callback(err);
if (!exists) return callback(null, false);
var path = PATH.join(self.path, relpath);
FS.readFile(path, function(err, data) {
if (data.length === 0) {
console.log("[sm] WARNING: File '" + path + "' is empty although it should not be!");
return callback(null, false);
}
try {
return callback(null, JSON.parse(data));
} catch(err) {
err.message += "(path: " + path + ")";
return callback(err);
}
});
});
}
function loadMtimes(callback) {
var waitFor = WAITFOR.parallel(callback);
descriptors.map(function(pair) {
waitFor(function(done) {
checkExists(pair[1], function(err, exists) {
if (err) return done(err);
if (!exists) return done();
FS.stat(PATH.join(self.path, pair[1]), function(err, stat) {
if (err) return done(err);
mtimes[pair[1]] = stat.mtime.getTime()/1000;
return done();
});
});
});
});
waitFor();
}
function loadOriginalDescriptorsAndWriteCache(callback) {
var waitFor = WAITFOR.parallel(function(err) {
if (err) return callback(err);
// Write new cache file if something has changed.
if (!writeCache) return callback(null);
function save() {
var cache = {};
descriptors.forEach(function(pair) {
if (self.descriptors[pair[0]]) {
cache[pair[1]] = {
mtime: mtimes[pair[1]],
descriptor: self.descriptors[pair[0]]
}
}
});
// TODO: Write to tmp file and rename.
return FS.writeFile(PATH.join(self.path, ".sourcemint/.descriptors.cache.json"), JSON.stringify(cache, null, 4), callback);
}
checkExists(".sourcemint", function(err, exists) {
if (err) return callback(err);
if (exists) return save();
FS.mkdir(PATH.join(self.path, ".sourcemint"), function(err) {
if (err) return callback(err);
return save();
});
});
});
var writeCache = false;
descriptors.map(function(pair) {
waitFor(function(done) {
if (cachedDescriptors[pair[1]]) {
self.descriptors[pair[0]] = cachedDescriptors[pair[1]];
return done();
}
loadDescriptor(pair[1], function(err, descriptor) {
if (err) return done(err);
if ((self.descriptors[pair[0]] = descriptor)) {
writeCache = true;
}
return done();
});
});
});
waitFor();
}
function updateDynamic(callback) {
populateLocator();
if (!self.descriptors.package) return callback(null);
self.exists = true;
// Set name of top package.
if (self.level === 0 && self.name === false) {
self.name = self.descriptors.package.name;
}
FS.realpath(self.path, function(err, path) {
if (err) return callback(err);
self.path = path;
return callback(null);
});
}
function initChildren(callback) {
if (typeof options.levels === "number") {
if (self.level >= options.levels) return callback(null);
}
if (options.ignorePackages.indexOf(self.name) !== -1 && self.top.name !== self.name) {
self.childrenIgnored = true;
return callback(null);
}
if (self.symlinked === "inside") return callback(null);
var packages = {};
function addPackagesForAttribute(attribute) {
var dependencies = self.descriptors.package[attribute];
if (!dependencies) return;
var dir;
if (Array.isArray(dependencies)) {
dir = "mapped_packages";
if (/[dD]ependencies/i.test(attribute)) dir = "node_modules";
for (var i=0 ; i<dependencies.length ; i++) {
packages[dependencies[i]] = [attribute, dir];
}
} else {
for (var key in dependencies) {
dir = "mapped_packages";
if (/[dD]ependencies/i.test(attribute)) dir = "node_modules";
if (Array.isArray(dependencies[key]) && dependencies[key][0] === "npm") {
dir = "node_modules";
}
packages[key] = [attribute, dir];
}
}
}
addPackagesForAttribute("mappings");
addPackagesForAttribute("devMappings");
addPackagesForAttribute("optionalMappings");
addPackagesForAttribute("dependencies");
addPackagesForAttribute("devDependencies");
addPackagesForAttribute("optionalDependencies");
//addPackagesForAttribute("bundleDependencies");
function includeDevDependency(name) {
if (!packages[name]) return true;
if (!/^dev/.test(packages[name][0])) return true;
if (options.nodev === true) return false;
if (self.level >= 1 && options.dev !== true) return false;
return true;
}
var waitForDirs = WAITFOR.parallel(function(err) {
var waitForPackages = WAITFOR.parallel(callback);
Object.keys(packages).forEach(function(name) {
waitForPackages(function(done) {
if (self.children[name]) return done();
if (!includeDevDependency(name)) return done();
var node = self.children[name] = new FsNode(self, packages[name][1], name, self.level + 1);
return node.initForPath(PATH.join(self.path, packages[name][1], name), options, _refreshedPackages).when(done, done);
});
});
waitForPackages();
});
[
"node_modules",
"mapped_packages"
].forEach(function(dir) {
waitForDirs(function(done) {
checkExists(dir, function(err, exists) {
if (err) return done(err);
if (!exists) return done();
FS.readdir(PATH.join(self.path, dir), function(err, basenames) {
if (err) return done(err);
var waitForPackages = WAITFOR.parallel(done);
basenames.forEach(function(basename) {
waitForPackages(function(done) {
if (/~backup-/.test(basename)) return done();
if (/^\./.test(basename)) return done();
if (!includeDevDependency(basename)) return done();
if (self.children[basename]) {
return done(new Error("Package '" + basename + "' was found in **more than one** dependencies directory!"));
}
var path = PATH.join(self.path, dir, basename);
FS.lstat(path, function(err, stat) {
if (err) return done(err)
if (!stat.isDirectory() && !stat.isSymbolicLink()) return done();
delete packages[basename];
var node = self.children[basename] = new FsNode(self, dir, basename, self.level + 1);
function initChild() {
return node.initForPath(path, options, _refreshedPackages).when(done, done);
}
if (stat.isSymbolicLink()) {
node.symlinked = "outside";
FS.readlink(path, function(err, linkStr) {
if (err) return done(err);
if (!/^\//.test(linkStr)) {
if (PATH.join(self.path, dir, linkStr).substring(0, self.path.length) === self.path) {
node.symlinked = "inside";
}
}
return initChild();
});
} else {
return initChild();
}
});
});
});
waitForPackages();
});
});
});
});
waitForDirs();
}
var deferred = Q.defer();
// Get mtime of all descriptors.
loadMtimes(function(err) {
if (err) return deferred.reject(err);
// Load and validate descriptor cache.
return loadDescriptor(".sourcemint/.descriptors.cache.json", function(err, cache) {
if (err) return deferred.reject(err);
if (cache) {
descriptors.forEach(function(pair) {
if (cache[pair[1]] && cache[pair[1]].mtime === mtimes[pair[1]]) {
cachedDescriptors[pair[1]] = cache[pair[1]].descriptor;
}
});
}
// Load original descriptors if not cached.
loadOriginalDescriptorsAndWriteCache(function(err) {
if (err) return deferred.reject(err);
// Update dynamic (non-cachable).
updateDynamic(function(err) {
if (err) return deferred.reject(err);
initChildren(function(err) {
if (err) return deferred.reject(err);
//console.log("done");
return deferred.resolve();
});
});
});
});
});
return deferred.promise.fail(function(err) {
err.message += "(path: " + self.path + ")";
throw err;
});
}
FsNode.prototype.toString = function() {
var str = this.level + " : " + this.name + " (" + UTIL.len(this.children) + ")";
if (!this.exists) {
str += " missing";
}
str += "\n";
UTIL.forEach(this.children, function(child) {
var parts = child[1].toString().split("\n").map(function(line) {
return " " + line;
});
str += " " + parts.splice(0, parts.length-1).join("\n") + "\n";
});
return str;
}
const PATH = require("path");
const FS = require("graceful-fs");
const Q = require("sourcemint-util-js/lib/q");
const UTIL = require("sourcemint-util-js/lib/util");
var instances = {};
exports.for = function(packageRootPath) {
if (!instances[packageRootPath]) {
instances[packageRootPath] = new Scanner(packageRootPath);
}
return instances[packageRootPath];
}
var Scanner = function(packageRootPath) {
var self = this;
self.fsTree = function(options) {
options = options || {};
try {
var tree = new FsNode();
return tree.initForPath(packageRootPath, options).then(function() {
return tree;
}).fail(function(err) {
throw err;
});
} catch(err) {
return Q.reject(err);
}
}
return self;
}
var FsNode = function(parent, dir, name, level) {
var self = this;
self.top = (parent && parent.top) || self;
self.name = name || false;
self.parent = parent || null;
self.dir = dir || false;
self.level = level || 0;
self.relpath = "";
self.reset = function(path) {
self.path = path || null;
self.exists = false;
self.children = {};
self.childrenIgnored = false;
self.descriptors = {};
self.symlinked = false;
self.circular = false;
}
self.reset();
}
//Node.prototype = new BaseNode();
FsNode.prototype.initForPath = function(path, options, _refreshedPackages) {
var self = this;
if (options.debug) console.log("[sm] Trigger initForPath for node: " + path);
options.ignorePackages = options.ignorePackages || ["sm", "npm"];
self.reset(path);
_refreshedPackages = _refreshedPackages || {};
if (_refreshedPackages[self.path] && _refreshedPackages[self.path].symlinked !== "inside") {
self.circular = _refreshedPackages[self.path];
return Q.ref();
}
_refreshedPackages[self.path] = self;
function populateLocator() {
function findDependency(dependencies) {
if (Array.isArray(dependencies)) {
for (var i=0 ; i<dependencies.length ; i++) {
if (dependencies[i] === self.name) {
// Found but no version specified.
return "*";
}
}
} else {
for (var key in dependencies) {
if (key === self.name) {
if (dependencies[key] === "" || dependencies[key] === "latest") {
return "*";
} else {
return dependencies[key];
}
}
}
}
return false;
}
function normalizeMapping(locator) {
if (typeof locator.viaPm !== "undefined" && locator.viaPm === "sm") {
if (UTIL.isArrayLike(locator.pointer)) {
locator.pm = locator.pointer[0];
locator.descriptorOverlay = locator.pointer[2] || false;
locator.pointer = locator.pointer[1];
} else {
locator.pm = "sm";
}
}
}
var locator = {
// `sm` or `npm` depending on which attribute used.
viaPm: false,
// The name of the attribute used.
viaAttribute: false,
// The name of the declared package manager to use (or default based on `viaPm`).
pm: false,
// The 'selector' (in case of default registry; i.e. npm) or 'location' uri.
pointer: false,
// Overrides for the package descriptor.
descriptorOverlay: false,
// Flag to indicate whether dependency is or should be bundled.
bundled: false
};
if (self.parent) {
if (self.descriptors.package.mappings && (locator.pointer = findDependency(self.descriptors.package.mappings))) {
locator.viaPm = "sm";
locator.viaAttribute = "mappings";
normalizeMapping(locator);
} else
if (self.descriptors.package.devMappings && (locator.pointer = findDependency(self.descriptors.package.devMappings))) {
locator.viaPm = "sm";
locator.viaAttribute = "devMappings";
normalizeMapping(locator);
} else
if (self.descriptors.package.dependencies && (locator.pointer = findDependency(self.descriptors.package.dependencies))) {
locator.viaPm = "npm";
locator.pm = "npm";
locator.viaAttribute = "dependencies";
} else
if (self.descriptors.package.devDependencies && (locator.pointer = findDependency(self.descriptors.package.devDependencies))) {
locator.viaPm = "npm";
locator.pm = "npm";
locator.viaAttribute = "devDependencies";
}
if (self.descriptors.package.bundleDependencies && findDependency(self.descriptors.package.bundleDependencies)) {
locator.viaPm = "npm";
locator.pm = "npm";
locator.bundled = true;
}
} else
if(self.level === 0 && options.topPointer) {
locator.pointer = options.topPointer;
locator.viaPm = "sm";
locator.viaAttribute = "mappings";
normalizeMapping(locator);
}
if (locator.pointer !== false && /^\.{1,2}\//.test(locator.pointer) && self.parent) {
var oldPointer = locator.pointer;
locator.pointer = PATH.join(self.parent.path, locator.pointer);
// Path may not traverse higher than declaring package.
if (locator.pointer.substring(0, self.parent.path.length) !== self.parent.path) {
throw new Error("Dependency location '" + oldPointer + "' may not point higher than declaring package ");
}
}
// Fix `0.x.`.
if (/.\.$/.test(locator.pointer)) {
locator.pointer = locator.pointer.substring(0, locator.pointer.length -1);
}
self.descriptors.locator = (locator.viaPm)?locator:false;
}
var descriptors = [
["package", "package.json"],
["smSource", ".sourcemint/source.json"],
["program", "program.json"],
["programRT", "program.rt.json"],
["npmShrinkwrap", "npm-shrinkwrap.json"],
["smCatalog", "sm-catalog.json"],
["smCatalogLocked", "sm-catalog.locked.json"]
];
var exists = {};
var mtimes = {};
var cachedDescriptors = {};
function checkExists(relpath) {
if (typeof exists[relpath] !== "undefined") return Q.resolve(exists[relpath]);
var deferred = Q.defer();
PATH.exists(PATH.join(self.path, relpath), function(oo) {
return deferred.resolve((exists[relpath] = oo));
});
return deferred.promise;
}
function loadDescriptor(relpath) {
return checkExists(relpath).then(function(exists) {
if (!exists) return false;
var path = PATH.join(self.path, relpath);
return Q.ninvoke(FS, "readFile", path).then(function(data) {
if (data.length === 0) {
console.log("[sm] WARNING: File '" + path + "' is empty although it should not be!");
return false;
}
try {
return JSON.parse(data);
} catch(err) {
err.message += "(path: " + path + ")";
throw err;
}
});
});
}
return Q.all(descriptors.map(function(pair) {
// Get mtime of all descriptors.
return checkExists(pair[1]).then(function(exists) {
if (!exists) return;
return Q.ninvoke(FS, "stat", PATH.join(self.path, pair[1])).then(function(stats) {
mtimes[pair[1]] = stats.mtime.getTime()/1000;
});
});
})).then(function() {
// Load and validate descriptor cache.
return loadDescriptor(".sourcemint/.descriptors.cache.json").then(function(cache) {
if (!cache) return;
descriptors.forEach(function(pair) {
if (cache[pair[1]] && cache[pair[1]].mtime === mtimes[pair[1]]) {
cachedDescriptors[pair[1]] = cache[pair[1]].descriptor;
}
});
});
}).then(function() {
// Load original descriptors if not cached.
var writeCache = false;
return Q.all(descriptors.map(function(pair) {
if (cachedDescriptors[pair[1]]) {
self.descriptors[pair[0]] = cachedDescriptors[pair[1]];
return;
}
return loadDescriptor(pair[1]).then(function(descriptor) {
if ((self.descriptors[pair[0]] = descriptor)) {
writeCache = true;
}
});
})).then(function() {
// Write new cache file if something has changed.
if (!writeCache) return;
function save() {
var cache = {};
descriptors.forEach(function(pair) {
if (self.descriptors[pair[0]]) {
cache[pair[1]] = {
mtime: mtimes[pair[1]],
descriptor: self.descriptors[pair[0]]
}
}
});
// TODO: Write to tmp file and rename.
return Q.ninvoke(FS, "writeFile", PATH.join(self.path, ".sourcemint/.descriptors.cache.json"), JSON.stringify(cache, null, 4));
}
return checkExists(".sourcemint").then(function(exists) {
if (exists) return save();
return Q.ninvoke(FS, "mkdir", PATH.join(self.path, ".sourcemint")).then(save);
});
});
}).then(function() {
// Update dynamic (non-cachable).
populateLocator();
if (!self.descriptors.package) return;
self.exists = true;
// Set name of top package.
if (self.level === 0 && self.name === false) {
self.name = self.descriptors.package.name;
}
return Q.ninvoke(FS, "realpath", self.path).then(function(path) {
self.path = path;
});
}).then(function() {
if (typeof options.levels === "number") {
if (self.level >= options.levels) return;
}
if (options.ignorePackages.indexOf(self.name) !== -1 && self.top.name !== self.name) {
self.childrenIgnored = true;
return;
}
if (self.symlinked === "inside") return;
var packages = {};
function addPackagesForAttribute(attribute) {
var dependencies = self.descriptors.package[attribute];
if (!dependencies) return;
var dir;
if (Array.isArray(dependencies)) {
dir = "mapped_packages";
if (/[dD]ependencies/i.test(attribute)) dir = "node_modules";
for (var i=0 ; i<dependencies.length ; i++) {
packages[dependencies[i]] = [attribute, dir];
}
} else {
for (var key in dependencies) {
dir = "mapped_packages";
if (/[dD]ependencies/i.test(attribute)) dir = "node_modules";
if (Array.isArray(dependencies[key]) && dependencies[key][0] === "npm") {
dir = "node_modules";
}
packages[key] = [attribute, dir];
}
}
}
addPackagesForAttribute("mappings");
addPackagesForAttribute("devMappings");
addPackagesForAttribute("optionalMappings");
addPackagesForAttribute("dependencies");
addPackagesForAttribute("devDependencies");
addPackagesForAttribute("optionalDependencies");
//addPackagesForAttribute("bundleDependencies");
function includeDevDependency(name) {
if (!packages[name]) return true;
if (!/^dev/.test(packages[name][0])) return true;
if (options.nodev === true) return false;
if (self.level >= 1 && options.dev !== true) return false;
return true;
}
return Q.all([
"node_modules",
"mapped_packages"
].map(function(dir) {
return checkExists(dir).then(function(exists) {
if (!exists) return;
return Q.ninvoke(FS, "readdir", PATH.join(self.path, dir)).then(function(basenames) {
return Q.all(basenames.map(function(basename) {
if (/~backup-/.test(basename)) return;
if (/^\./.test(basename)) return;
if (!includeDevDependency(basename)) return;
if (self.children[basename]) {
throw new Error("Package '" + basename + "' was found in **more than one** dependencies directory!");
}
var path = PATH.join(self.path, dir, basename);
return Q.ninvoke(FS, "lstat", path).then(function(stat) {
if (!stat.isDirectory() && !stat.isSymbolicLink()) return;
delete packages[basename];
var node = self.children[basename] = new FsNode(self, dir, basename, self.level + 1);
return Q.call(function() {
if (stat.isSymbolicLink()) {
node.symlinked = "outside";
return Q.ninvoke(FS, "readlink", path).then(function(linkStr) {
if (!/^\//.test(linkStr)) {
if (PATH.join(self.path, dir, linkStr).substring(0, self.path.length) === self.path) {
node.symlinked = "inside";
}
}
});
}
}).then(function() {
return node.initForPath(path, options, _refreshedPackages);
});
});
}));
});
});
})).then(function() {
return Q.all(Object.keys(packages).map(function(name) {
if (self.children[name]) return;
if (!includeDevDependency(name)) return;
var node = self.children[name] = new FsNode(self, packages[name][1], name, self.level + 1);
return node.initForPath(PATH.join(self.path, packages[name][1], name), options, _refreshedPackages);
}));
});
}).fail(function(err) {
err.message += "(path: " + self.path + ")";
throw err;
});
}
FsNode.prototype.toString = function() {
var str = this.level + " : " + this.name + " (" + UTIL.len(this.children) + ")";
if (!this.exists) {
str += " missing";
}
str += "\n";
UTIL.forEach(this.children, function(child) {
var parts = child[1].toString().split("\n").map(function(line) {
return " " + line;
});
str += " " + parts.splice(0, parts.length-1).join("\n") + "\n";
});
return str;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment