mirror of
https://github.com/bower/bower.git
synced 2026-02-12 06:55:04 -05:00
Implement the uninstall command.
Made also some tweaks to the render stuff.
This commit is contained in:
@@ -3,6 +3,8 @@ var path = require('path');
|
||||
var fs = require('fs');
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var rimraf = require('rimraf');
|
||||
var promptly = require('promptly');
|
||||
var bowerJson = require('bower-json');
|
||||
var Manager = require('./Manager');
|
||||
var defaultConfig = require('../config');
|
||||
@@ -40,7 +42,7 @@ Project.prototype.install = function (endpoints, options) {
|
||||
// Start by repairing the project, installing only missing packages
|
||||
return this._repair()
|
||||
// Analyse the project
|
||||
.then(that._analyse.bind(this))
|
||||
.then(that.analyse.bind(this))
|
||||
.spread(function (json, tree, flattened) {
|
||||
var targets = {};
|
||||
var resolved = {};
|
||||
@@ -71,19 +73,19 @@ Project.prototype.install = function (endpoints, options) {
|
||||
return that._bootstrap(targets, resolved, installed)
|
||||
// Handle save and saveDev options
|
||||
.then(function () {
|
||||
var key;
|
||||
var jsonKey;
|
||||
|
||||
if (!options.save && !options.saveDev) {
|
||||
return;
|
||||
}
|
||||
|
||||
key = options.save ? 'dependencies' : 'devDependencies';
|
||||
that._json[key] = that._json[key] || {};
|
||||
jsonKey = options.save ? 'dependencies' : 'devDependencies';
|
||||
that._json[jsonKey] = that._json[jsonKey] || {};
|
||||
|
||||
mout.object.forOwn(targets, function (decEndpoint) {
|
||||
var source = decEndpoint.registry ? '' : decEndpoint.source;
|
||||
var target = decEndpoint.pkgMeta.version ? '~' + decEndpoint.pkgMeta.version : decEndpoint.target;
|
||||
that._json[key][decEndpoint.name] = mout.string.ltrim(source + '#' + target, ['#']);
|
||||
that._json[jsonKey][decEndpoint.name] = mout.string.ltrim(source + '#' + target, ['#']);
|
||||
});
|
||||
|
||||
return that._saveJson()
|
||||
@@ -116,7 +118,7 @@ Project.prototype.update = function (names, options) {
|
||||
// If no names were specified, we update every package
|
||||
if (!names) {
|
||||
// Analyse the project
|
||||
promise = this._analyse()
|
||||
promise = this.analyse()
|
||||
.spread(function (json, tree, flattened) {
|
||||
// Mark each json entry as targets
|
||||
targets = mout.object.map(json.dependencies, function (value, key) {
|
||||
@@ -136,7 +138,7 @@ Project.prototype.update = function (names, options) {
|
||||
// Analyse the project
|
||||
.then(function (result) {
|
||||
repaired = result;
|
||||
return that._analyse();
|
||||
return that.analyse();
|
||||
})
|
||||
.spread(function (json, tree, flattened) {
|
||||
targets = {};
|
||||
@@ -192,16 +194,98 @@ Project.prototype.update = function (names, options) {
|
||||
};
|
||||
|
||||
Project.prototype.uninstall = function (names, options) {
|
||||
var that = this;
|
||||
var deferred = Q.defer();
|
||||
|
||||
this.analyse()
|
||||
.spread(function (json, tree, flattened) {
|
||||
var promise = Q.resolve();
|
||||
var packages = [];
|
||||
|
||||
names.forEach(function (name) {
|
||||
var decEndpoint = flattened[name];
|
||||
|
||||
// Check if it is not installed
|
||||
if (!decEndpoint || decEndpoint.missing) {
|
||||
packages[name] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Decide if the package will be uninstalled or skipped
|
||||
// This is done with a promise that resolves to a boolean
|
||||
promise = promise
|
||||
.then(function () {
|
||||
var dependants;
|
||||
var message;
|
||||
var data;
|
||||
|
||||
// Check if it has dependants
|
||||
// Note that the root is filtered from the dependants
|
||||
// as well as other dependants marked to be uninstalled
|
||||
dependants = [];
|
||||
mout.object.forOwn(decEndpoint.dependants, function (decEndpoint) {
|
||||
if (!decEndpoint.root && names.indexOf(decEndpoint.name) === -1) {
|
||||
dependants.push(decEndpoint);
|
||||
}
|
||||
});
|
||||
|
||||
// If the package has no dependants or the force config is enabled,
|
||||
// mark it to be removed
|
||||
if (!dependants.length || that._config.force) {
|
||||
packages[name] = decEndpoint.dir;
|
||||
// Otherwise we need to figure it out if the user really wants to remove it,
|
||||
// even with dependants
|
||||
} else {
|
||||
message = dependants.map(function (dep) { return dep.name; }).join(', ') + ' depend on ' + decEndpoint.name;
|
||||
data = {
|
||||
package: decEndpoint.name,
|
||||
dependants: dependants.map(function (decEndpoint) {
|
||||
return decEndpoint.name;
|
||||
})
|
||||
};
|
||||
|
||||
// If interactive is disabled, error out
|
||||
if (!that._config.interactive) {
|
||||
throw createError(message, 'ECONFLICT', {
|
||||
skippable: true,
|
||||
data: data
|
||||
});
|
||||
// Question the user
|
||||
} else {
|
||||
deferred.notify({
|
||||
level: 'conflict',
|
||||
id: 'mutual',
|
||||
message: message,
|
||||
data: data
|
||||
});
|
||||
|
||||
return Q.nfcall(promptly.confirm, 'Continue anyway? (y/n)')
|
||||
.then(function (confirmed) {
|
||||
// If the user decided to skip it, remove from the array so that it won't
|
||||
// influence subsequent dependants
|
||||
if (!confirmed) {
|
||||
mout.array.remove(names, name);
|
||||
} else {
|
||||
packages[name] = decEndpoint.dir;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return promise
|
||||
.then(function () {
|
||||
return that._removePackages(packages, options)
|
||||
.progress(deferred.notify);
|
||||
});
|
||||
})
|
||||
.then(deferred.resolve, deferred.reject);
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
Project.prototype.getTree = function () {
|
||||
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
Project.prototype._analyse = function () {
|
||||
Project.prototype.analyse = function () {
|
||||
return F.all([
|
||||
this._readJson(),
|
||||
this._readInstalled()
|
||||
@@ -215,10 +299,10 @@ Project.prototype._analyse = function () {
|
||||
source: this._config.cwd,
|
||||
target: json.version,
|
||||
dir: this._config.cwd,
|
||||
pkgMeta: json
|
||||
pkgMeta: json,
|
||||
root: true
|
||||
};
|
||||
|
||||
|
||||
// Restore the original dependencies cross-references,
|
||||
// that is, the parent-child relationships
|
||||
this._restoreNode(root, flattened);
|
||||
@@ -238,6 +322,8 @@ Project.prototype._analyse = function () {
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
Project.prototype._bootstrap = function (targets, resolved, installed) {
|
||||
// Configure the manager and kick in the resolve process
|
||||
return this._manager
|
||||
@@ -252,7 +338,7 @@ Project.prototype._bootstrap = function (targets, resolved, installed) {
|
||||
Project.prototype._repair = function (incompatible) {
|
||||
var that = this;
|
||||
|
||||
return this._analyse()
|
||||
return this.analyse()
|
||||
.spread(function (json, tree, flattened) {
|
||||
var targets = [];
|
||||
var resolved = {};
|
||||
@@ -332,7 +418,7 @@ Project.prototype._readJson = function () {
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
Project.prototype._saveJson = function (json) {
|
||||
Project.prototype._saveJson = function () {
|
||||
var deferred = Q.defer();
|
||||
|
||||
if (!this._jsonFile) {
|
||||
@@ -345,9 +431,7 @@ Project.prototype._saveJson = function (json) {
|
||||
deferred.resolve();
|
||||
});
|
||||
} else {
|
||||
json = json || this._json;
|
||||
|
||||
Q.nfcall(fs.writeFile, this._jsonFile, JSON.stringify(json, null, ' '))
|
||||
Q.nfcall(fs.writeFile, this._jsonFile, JSON.stringify(this._json, null, ' '))
|
||||
.then(deferred.resolve, deferred.reject, deferred.notify);
|
||||
}
|
||||
|
||||
@@ -399,6 +483,65 @@ Project.prototype._readInstalled = function () {
|
||||
});
|
||||
};
|
||||
|
||||
Project.prototype._removePackages = function (packages, options) {
|
||||
var promises = [];
|
||||
var jsonKey = options.save ? 'dependencies' : (options.saveDev ? 'devDependencies' : null);
|
||||
var deferred = Q.defer();
|
||||
|
||||
mout.object.forOwn(packages, function (dir, name) {
|
||||
var promise;
|
||||
|
||||
// Delete directory
|
||||
if (!dir) {
|
||||
process.nextTick(function () {
|
||||
deferred.notify({
|
||||
level: 'info',
|
||||
id: 'missing',
|
||||
message: name,
|
||||
data: {
|
||||
package: name
|
||||
}
|
||||
});
|
||||
});
|
||||
promise = Q.resolve();
|
||||
} else {
|
||||
process.nextTick(function () {
|
||||
deferred.notify({
|
||||
level: 'action',
|
||||
id: 'uninstall',
|
||||
message: name,
|
||||
data: {
|
||||
package: name,
|
||||
dir: dir
|
||||
}
|
||||
});
|
||||
});
|
||||
promise = Q.nfcall(rimraf, dir);
|
||||
}
|
||||
|
||||
// Remove from json only if successfully deleted
|
||||
if (jsonKey && this._json[jsonKey]) {
|
||||
promise = promise
|
||||
.then(function () {
|
||||
delete this._json[jsonKey][name];
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
promises.push(promise);
|
||||
}, this);
|
||||
|
||||
Q.all(promises)
|
||||
// Save json
|
||||
.then(this._saveJson.bind(this))
|
||||
// Resolve with removed packages
|
||||
.then(function () {
|
||||
return packages;
|
||||
})
|
||||
.then(deferred.resolve, deferred.reject, deferred.notify);
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
Project.prototype._walkTree = function (node, fn) {
|
||||
var queue = [node];
|
||||
var result;
|
||||
@@ -434,7 +577,7 @@ Project.prototype._restoreNode = function (node, flattened, jsonKey) {
|
||||
flattened[key] = local = json;
|
||||
local.missing = true;
|
||||
// Even if it is installed, check if it's compatible
|
||||
} else if (!local.incompatible && !this._manager.areCompatible(local.pkgMeta.version || '*', json.target)) {
|
||||
} else if (!local.incompatible && !local.missing && !this._manager.areCompatible(local.pkgMeta.version || '*', json.target)) {
|
||||
json.pkgMeta = local.pkgMeta;
|
||||
flattened[key] = local = json;
|
||||
local = json;
|
||||
|
||||
Reference in New Issue
Block a user