mirror of
https://github.com/bower/bower.git
synced 2026-02-11 22:44:58 -05:00
Implement the uninstall command.
Made also some tweaks to the render stuff.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
'help': require('./help'),
|
||||
'install': require('./install'),
|
||||
'update': require('./update')
|
||||
'update': require('./update'),
|
||||
'uninstall': require('./uninstall')
|
||||
};
|
||||
|
||||
58
lib/commands/uninstall.js
Normal file
58
lib/commands/uninstall.js
Normal file
@@ -0,0 +1,58 @@
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var mout = require('mout');
|
||||
var Project = require('../core/Project');
|
||||
var cli = require('../util/cli');
|
||||
var help = require('./help');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function uninstall(endpoints, options, config) {
|
||||
var project;
|
||||
var emitter = new Emitter();
|
||||
|
||||
options = options || {};
|
||||
config = mout.object.deepMixIn(config, defaultConfig);
|
||||
|
||||
// If endpoints are an empty array, null them
|
||||
if (endpoints && !endpoints.length) {
|
||||
endpoints = null;
|
||||
}
|
||||
|
||||
project = new Project(config);
|
||||
project.uninstall(endpoints, options)
|
||||
.then(function (uninstalled) {
|
||||
emitter.emit('end', uninstalled);
|
||||
}, function (error) {
|
||||
emitter.emit('error', error);
|
||||
}, function (notification) {
|
||||
emitter.emit('notification', notification);
|
||||
});
|
||||
|
||||
return emitter;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
uninstall.line = function (argv) {
|
||||
var options = uninstall.options(argv);
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
if (options.help || !names.length) {
|
||||
return help('uninstall');
|
||||
}
|
||||
|
||||
return uninstall(names, options);
|
||||
};
|
||||
|
||||
uninstall.options = function (argv) {
|
||||
return cli.readOptions({
|
||||
'help': { type: Boolean, shorthand: 'h' },
|
||||
'save': { type: Boolean, shorthand: 'S' },
|
||||
'save-dev': { type: Boolean, shorthand: 'D' }
|
||||
}, argv);
|
||||
};
|
||||
|
||||
uninstall.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = uninstall;
|
||||
@@ -48,7 +48,8 @@ try {
|
||||
'strict-ssl': true,
|
||||
'user-agent': 'node/' + process.version + ' ' + process.platform + ' ' + process.arch,
|
||||
'git': 'git',
|
||||
'color': true
|
||||
'color': true,
|
||||
'interactive': false
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error('Unable to parse runtime configuration: ' + e.message);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
var mout = require('mout');
|
||||
|
||||
function StandardRenderer(colorful) {
|
||||
var wideCommands = ['install', 'update'];
|
||||
|
||||
function StandardRenderer(command, colorful) {
|
||||
this._sizes = {
|
||||
id: 10, // Id max chars
|
||||
label: 23, // Label max chars
|
||||
@@ -9,14 +11,20 @@ function StandardRenderer(colorful) {
|
||||
this._colors = {
|
||||
warn: 'yellow',
|
||||
error: 'red',
|
||||
conflict: 'magenta',
|
||||
'default': 'cyan'
|
||||
};
|
||||
|
||||
this._command = command;
|
||||
this._colorful = colorful == null ? true : colorful;
|
||||
this._compact = process.stdout.columns < 120;
|
||||
this._compact = wideCommands.indexOf(command) === -1 ? true : process.stdout.columns < 120;
|
||||
}
|
||||
|
||||
StandardRenderer.prototype.end = function () {};
|
||||
StandardRenderer.prototype.end = function (data) {
|
||||
if (this[this._command]) {
|
||||
this[this._command](data);
|
||||
}
|
||||
};
|
||||
|
||||
StandardRenderer.prototype.error = function (err) {
|
||||
var str;
|
||||
@@ -31,8 +39,10 @@ StandardRenderer.prototype.error = function (err) {
|
||||
str += mout.string.trim(err.details) + '\n';
|
||||
}
|
||||
|
||||
// Print stack
|
||||
str += '\n' + err.stack + '\n';
|
||||
// Print stack if the error is not skippable
|
||||
if (!err.skippable) {
|
||||
str += '\n' + err.stack + '\n';
|
||||
}
|
||||
|
||||
this._write(process.stderr, 'bower ' + str);
|
||||
};
|
||||
@@ -78,6 +88,11 @@ StandardRenderer.prototype._genericNotification = function (notification) {
|
||||
this._write(stream, 'bower ' + str);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._mutualNotification = function (notification) {
|
||||
notification.id = 'conflict';
|
||||
this._genericNotification(notification);
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._checkoutNotification = function (notification) {
|
||||
if (this._compact) {
|
||||
notification.message = notification.from + '#' + notification.message;
|
||||
@@ -102,7 +117,7 @@ StandardRenderer.prototype._prefixNotification = function (notification) {
|
||||
|
||||
// Construct the label
|
||||
if (notification.from && notification.data.endpoint) {
|
||||
label = notification.from ? notification.from + '#' + notification.data.endpoint.target : '';
|
||||
label = notification.from + '#' + notification.data.endpoint.target;
|
||||
// Make it empty if there's not enough information
|
||||
} else {
|
||||
label = '';
|
||||
|
||||
@@ -34,12 +34,12 @@ function readOptions(options, argv) {
|
||||
return parsedOptions;
|
||||
}
|
||||
|
||||
function getRenderer(config) {
|
||||
function getRenderer(command, config) {
|
||||
if (config.json) {
|
||||
return new renderers.Json();
|
||||
return new renderers.Json(command);
|
||||
}
|
||||
|
||||
return new renderers.Standard(config.color);
|
||||
return new renderers.Standard(command, config.color);
|
||||
}
|
||||
|
||||
module.exports.readOptions = readOptions;
|
||||
|
||||
Reference in New Issue
Block a user