Merge pull request #709 from bower/several-improv

Several improvements and fixes
This commit is contained in:
André Cruz
2013-08-03 08:10:28 -07:00
6 changed files with 182 additions and 77 deletions

View File

@@ -161,7 +161,14 @@ function setDependencies(project, json, answers) {
// TODO: The final expanded source is used instead of the original source
// While this the most correct it might be confusing to users
extraneous.forEach(function (extra) {
var jsonEndpoint = endpointParser.decomposed2json(extra.endpoint);
var jsonEndpoint;
// Skip linked packages
if (extra.linked) {
return;
}
jsonEndpoint = endpointParser.decomposed2json(extra.endpoint);
mout.object.mixIn(json.dependencies, jsonEndpoint);
});
}

View File

@@ -16,18 +16,18 @@ function list(options, config) {
project = new Project(config, logger);
project.getTree()
.spread(function (tree, flattened, extraneous) {
.spread(function (tree, flattened) {
if (options.paths) {
return logger.emit('end', paths(flattened));
}
if (config.offline) {
return logger.emit('end', normal(tree, extraneous));
return logger.emit('end', tree);
}
return checkVersions(project, tree, logger)
.then(function () {
logger.emit('end', normal(tree, extraneous));
logger.emit('end', tree);
});
})
.fail(function (error) {
@@ -110,17 +110,6 @@ function paths(flattened) {
return ret;
}
function normal(tree, extraneous) {
// Merge extraneous as root dependencies
// but signal it with a flag
extraneous.forEach(function (decEndpoint) {
decEndpoint.extraneous = true;
tree.dependencies[decEndpoint.endpoint.name] = decEndpoint;
});
return tree;
}
// -------------------
list.line = function (argv) {

View File

@@ -4,40 +4,50 @@ var Project = require('../core/Project');
var cli = require('../util/cli');
var defaultConfig = require('../config');
function prune(names, config) {
function prune(config) {
var project;
var logger = new Logger();
config = mout.object.deepFillIn(config || {}, defaultConfig);
project = new Project(config, logger);
// If names is an empty array, null them
if (names && !names.length) {
names = null;
}
// Figure out extraneous
project.getTree()
.spread(function (tree, flattened, extraneous) {
var names;
names = extraneous.map(function (extra) {
return extra.endpoint.name;
});
// Uninstall them
project.uninstall(names)
.then(function (removed) {
logger.emit('end', removed);
})
.fail(function (error) {
logger.emit('error', error);
});
clean(project)
.then(function (removed) {
logger.emit('end', removed);
})
.fail(function (error) {
logger.emit('error', error);
});
return logger;
}
function clean(project, removed) {
removed = removed || {};
// Continually call clean until there is no more extraneous
// dependencies to remove
return project.getTree()
.spread(function (tree, flattened, extraneous) {
var names = extraneous.map(function (extra) {
return extra.endpoint.name;
});
// Uninstall extraneous
project.uninstall(names)
.then(function (uninstalled) {
// Are we done?
if (!mout.object.size(uninstalled)) {
return removed;
}
// Not yet, recurse!
mout.object.mixIn(removed, uninstalled);
return clean(project, removed);
});
});
}
// -------------------
prune.line = function () {

View File

@@ -1,5 +1,6 @@
var mout = require('mout');
var Logger = require('bower-logger');
var Q = require('q');
var Project = require('../core/Project');
var cli = require('../util/cli');
var defaultConfig = require('../config');
@@ -12,14 +13,28 @@ function uninstall(names, options, config) {
config = mout.object.deepFillIn(config || {}, defaultConfig);
project = new Project(config, logger);
// If names is an empty array, null them
if (names && !names.length) {
names = null;
}
project.getTree()
.spread(function (tree, flattened) {
// Uninstall nodes
return project.uninstall(names, options)
// Clean out non-shared uninstalled dependencies
.then(function (uninstalled) {
var names = Object.keys(uninstalled);
var children = [];
project.uninstall(names, options)
.then(function (removed) {
logger.emit('end', removed);
// Grab the dependencies of packages that were uninstalled
mout.object.forOwn(flattened, function (node) {
if (names.indexOf(node.endpoint.name) !== -1) {
children.push.apply(children, mout.object.keys(node.dependencies));
}
});
// Clean them!
return clean(project, children, uninstalled);
});
})
.then(function (uninstalled) {
logger.emit('end', uninstalled);
})
.fail(function (error) {
logger.emit('error', error);
@@ -28,6 +43,55 @@ function uninstall(names, options, config) {
return logger;
}
function clean(project, names, removed) {
removed = removed || {};
return project.getTree()
.spread(function (tree, flattened) {
var nodes = [];
// Grab the nodes of each specified name
mout.object.forOwn(flattened, function (node) {
if (names.indexOf(node.endpoint.name) !== -1) {
nodes.push(node);
}
});
// Filter out those that have dependants
nodes = nodes.filter(function (node) {
return !node.nrDependants;
});
// Are we done?
if (!nodes.length) {
return Q.resolve(removed);
}
// Grab the nodes after filtering
names = nodes.map(function (node) {
return node.endpoint.name;
});
// Uninstall them
return project.uninstall(names)
// Clean out non-shared uninstalled dependencies
.then(function (uninstalled) {
var children;
mout.object.mixIn(removed, uninstalled);
// Grab the dependencies of packages that were uninstalled
children = [];
nodes.forEach(function (node) {
children.push.apply(children, mout.object.keys(node.dependencies));
});
// Recurse!
return clean(project, children, removed);
});
});
}
// -------------------
uninstall.line = function (argv) {

View File

@@ -146,8 +146,11 @@ Manager.prototype.install = function () {
var json = JSON.parse(contents.toString());
json._target = decEndpoint.target;
json = JSON.stringify(json, null, ' ');
if (decEndpoint.newly) {
json._direct = true;
}
json = JSON.stringify(json, null, ' ');
return Q.nfcall(fs.writeFile, metaFile, json);
});
});
@@ -195,6 +198,8 @@ Manager.prototype.toData = function (decEndpoint, extraKeys) {
}, this);
}
data.nrDependants = mout.object.size(decEndpoint.dependants);
return data;
};
@@ -371,9 +376,10 @@ Manager.prototype._parseDependencies = function (decEndpoint, pkgMeta, jsonKey)
return this._areCompatible(childDecEndpoint, resolved);
}, this);
// If we found one, add as resolved as resolved
// If we found one, add as resolved
// and copy resolved properties from the compatible one
if (compatible) {
decEndpoint.dependencies[key] = compatible;
childDecEndpoint.canonicalDir = compatible.canonicalDir;
childDecEndpoint.pkgMeta = compatible.pkgMeta;
childDecEndpoint.dependencies = compatible.dependencies;

View File

@@ -30,6 +30,7 @@ function Project(config, logger) {
// -----------------
Project.prototype.install = function (endpoints, options) {
var decEndpoints;
var that = this;
var targets = [];
var resolved = {};
@@ -46,7 +47,7 @@ Project.prototype.install = function (endpoints, options) {
// Analyse the project
return this._analyse()
.spread(function (json, tree) {
// Walk down the tree adding targets, resolved and incompatibles
// Recover tree
that.walkTree(tree, function (node, name) {
if (node.missing) {
targets.push(node);
@@ -61,19 +62,24 @@ Project.prototype.install = function (endpoints, options) {
if (node.linked) {
return false;
}
}, true);
});
// Add endpoints as targets
if (endpoints) {
endpoints.forEach(function (endpoint) {
decEndpoints = endpoints.map(function (endpoint) {
var decEndpoint = endpointParser.decompose(endpoint);
targets.push(decEndpoint);
// Mark as new so that a conflict for this target
// always require a choice
// Also allows for the target to be converted in case
// of being *
decEndpoint.newly = true;
targets.push(decEndpoint);
return decEndpoint;
});
} else {
decEndpoints = [];
}
// Bootstrap the process
@@ -82,8 +88,11 @@ Project.prototype.install = function (endpoints, options) {
.then(function (installed) {
// Handle save and saveDev options
if (that._options.save || that._options.saveDev) {
mout.object.forOwn(targets, function (decEndpoint) {
var jsonEndpoint = endpointParser.decomposed2json(decEndpoint);
// Cycle through the specified endpoints
decEndpoints.forEach(function (decEndpoint) {
var jsonEndpoint;
jsonEndpoint = endpointParser.decomposed2json(decEndpoint);
if (that._options.save) {
that._json.dependencies = mout.object.mixIn(that._json.dependencies || {}, jsonEndpoint);
@@ -128,16 +137,15 @@ Project.prototype.update = function (names, options) {
if (!names) {
// Mark each root dependency as targets
that.walkTree(tree, function (node) {
// Ignore linked extraneous because
// we don't know their real sources
if (node.extraneous && node.linked) {
return false;
}
targets.push(node);
return false;
}, true);
// Mark extraneous as targets
mout.object.forOwn(flattened, function (decEndpoint) {
if (decEndpoint.extraneous && !decEndpoint.linked) {
targets.push(decEndpoint);
}
});
// Otherwise, selectively update the specified ones
} else {
// Error out if some of the specified names
@@ -152,21 +160,19 @@ Project.prototype.update = function (names, options) {
// Add packages whose names are specified to be updated
that.walkTree(tree, function (node, name) {
// Ignore linked extraneous because
// we don't know their real source
if (node.extraneous && node.linked) {
return false;
}
if (names.indexOf(name) !== -1) {
targets.push(node);
return false;
}
}, true);
// Add extraneous whose names are specified to be updated
mout.object.forOwn(flattened, function (decEndpoint, name) {
if (decEndpoint.extraneous && !decEndpoint.linked && names.indexOf(name) !== -1) {
targets.push(decEndpoint);
}
});
// Walk down the tree adding missing, incompatible
// and resolved
// Recover tree
that.walkTree(tree, function (node, name) {
if (node.missing) {
targets.push(node);
@@ -314,8 +320,10 @@ Project.prototype.getTree = function () {
return this._analyse()
.spread(function (json, tree, flattened) {
var extraneous = [];
var additionalKeys = ['missing', 'extraneous', 'linked'];
tree = this._manager.toData(tree, ['missing', 'incompatible', 'linked']);
// Convert tree
tree = this._manager.toData(tree, additionalKeys);
// Mark incompatibles
this.walkTree(tree, function (node) {
@@ -336,13 +344,18 @@ Project.prototype.getTree = function () {
}
}, true);
// Find extraneous
// Convert extraneous
mout.object.forOwn(flattened, function (pkg) {
if (pkg.extraneous) {
extraneous.push(this._manager.toData(pkg, ['missing', 'incompatible', 'linked']));
extraneous.push(this._manager.toData(pkg, additionalKeys));
}
}, this);
// Convert flattened
flattened = mout.object.map(flattened, function (node) {
return this._manager.toData(node);
}, this);
return [tree, flattened, extraneous];
}.bind(this));
};
@@ -444,17 +457,31 @@ Project.prototype._analyse = function () {
])
.spread(function (json, installed, links) {
var root;
var jsonCopy = mout.lang.deepClone(json);
var flattened = mout.object.mixIn({}, installed, links);
root = {
name: json.name,
source: this._config.cwd,
target: json.version || '*',
pkgMeta: json,
pkgMeta: jsonCopy,
canonicalDir: this._config.cwd,
root: true
};
// Mix direct extraneous as dependencies
// (dependencies installed without --save/--save-dev)
jsonCopy.dependencies = jsonCopy.dependencies || {};
mout.object.forOwn(installed, function (decEndpoint, key) {
var pkgMeta = decEndpoint.pkgMeta;
// The _direct propery is saved by the manager when .newly is specified
if (!jsonCopy.dependencies[key] && pkgMeta._direct) {
decEndpoint.extraneous = true;
jsonCopy.dependencies[key] = pkgMeta._source + '#' + pkgMeta._target;
}
});
// Restore the original dependencies cross-references,
// that is, the parent-child relationships
this._restoreNode(root, flattened, 'dependencies');
@@ -463,13 +490,13 @@ Project.prototype._analyse = function () {
this._restoreNode(root, flattened, 'devDependencies');
}
// Parse extraneous
mout.object.forOwn(flattened, function (decEndpoint) {
// Restore the rest of the extraneousv (not installed directly)
mout.object.forOwn(flattened, function (decEndpoint, name) {
if (!decEndpoint.dependants) {
decEndpoint.extraneous = true;
// Restore it
this._restoreNode(decEndpoint, flattened, 'dependencies');
// Note that it has no dependants, just dependencies!
root.dependencies[name] = decEndpoint;
}
}, this);
@@ -618,6 +645,7 @@ Project.prototype._readLinks = function () {
that._logger.warn('deprecated', 'Package ' + name + ' is using the deprecated ' + deprecated);
}
json._direct = true; // Mark as a direct dep of root
decEndpoints[name] = {
name: name,
source: dir,
@@ -709,6 +737,7 @@ Project.prototype._restoreNode = function (node, flattened, jsonKey) {
node.dependencies = node.dependencies || {};
node.dependants = node.dependants || {};
// Only process deps that are yet processed
deps = mout.object.filter(node.pkgMeta[jsonKey], function (value, key) {
return !node.dependencies[key];
});