Uninstall now removes also nested deps if not shared.

The uninstall now warns and halts uninstalling shared packages.
Though it can be forced with the --force flag.
Also fix CS, and fix small bug.
This commit is contained in:
André Cruz
2012-11-18 16:39:15 +00:00
parent 2c7435a33f
commit aa30fe885a
7 changed files with 103 additions and 45 deletions

View File

@@ -27,7 +27,7 @@ module.exports = function (paths, options) {
var emitter = new Emitter;
var manager = new Manager(paths, { force: options && options.force });
if (options && options.save) save(emitter, manager, paths);
if (options && options.save) save(manager, paths);
manager
.on('data' , emitter.emit.bind(emitter, 'data'))

View File

@@ -47,11 +47,11 @@ var getTree = function (packages, subPackages, result) {
};
var generatePath = function (name, main) {
if (typeof main == 'string') {
if (typeof main === 'string') {
return path.join(config.directory, name, main);
} else if (_.isArray(main)) {
main = main.map(function (main) { return generatePath(name, main); });
return main.length == 1 ? main[0] : main;
return main.length === 1 ? main[0] : main;
}
};
@@ -149,7 +149,7 @@ module.exports = function (options) {
async.forEach(_.values(packages), function (pkg, next) {
pkg.once('loadJSON', function () {
if (this.json.repository && (this.json.repository.type == 'git' || this.json.repository.type == 'local-repo')) {
if (this.json.repository && (this.json.repository.type === 'git' || this.json.repository.type === 'local-repo')) {
pkg.once('versions', function (versions) {
pkg.tags = versions.map(function (ver) {
return semver.valid(ver) ? semver.clean(ver) : ver;

View File

@@ -9,67 +9,131 @@
var Emitter = require('events').EventEmitter;
var async = require('async');
var nopt = require('nopt');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var template = require('../util/template');
var Manager = require('../core/manager');
var save = require('../util/save');
var config = require('../core/config');
var help = require('./help');
var shorthand = { 'h': ['--help'], 'S': ['--save'] };
var optionTypes = { help: Boolean };
var optionTypes = { help: Boolean, force: Boolean };
var shorthand = { 'h': ['--help'], 'S': ['--save'], 'f': ['--force'] };
module.exports = function (names, options) {
var packages, uninstallables;
var packages, uninstallables, packagesCount = {};
var emitter = new Emitter;
var manager = new Manager;
// TODO: only deletect on conflict if force is true
// TODO: delete nested deps
if (options.save) save.discard(emitter, manager, names);
var force = !!options.force;
manager.on('data', emitter.emit.bind(emitter, 'data'));
manager.on('error', emitter.emit.bind(emitter, 'error'));
var resolveLocal = function () {
packages = _.flatten(_.values(manager.dependencies));
uninstallables = packages.filter(function (pkg) {
return _.include(names, pkg.name);
});
async.forEach(packages, function (pkg, next) {
pkg.once('loadJSON', next).loadJSON();
}, function () {
showWarnings();
if (showWarnings(force) && !force) return;
includeShared();
uninstall();
});
};
var showWarnings = function () {
var showWarnings = function (force) {
var foundConflicts = false;
packages.forEach(function (pkg) {
if (!pkg.json.dependencies) return;
if (uninstallables.indexOf(pkg) !== -1) return;
var conflicts = _.intersection(
Object.keys(pkg.json.dependencies),
_.pluck(uninstallables, 'name')
);
conflicts.forEach(function (conflictName) {
template('warning-uninstall', {packageName: pkg.name, conflictName: conflictName})
.on('data', emitter.emit.bind(emitter, 'data'));
});
if (conflicts.length) {
foundConflicts = true;
if (!force) {
conflicts.forEach(function (conflictName) {
emitter.emit('data', template('warning-uninstall', { packageName: pkg.name, conflictName: conflictName }, true));
});
}
}
});
if (foundConflicts && !force) {
emitter.emit('data', template('warn', { message: 'To proceed, run uninstall with the --force flag'}, true));
}
return foundConflicts;
};
var includeShared = function () {
count(packages, packagesCount);
uninstallables.forEach(function (pkg) {
parseUninstallableDeps(pkg);
});
for (var name in packagesCount) {
var pkg = manager.dependencies[name][0];
if ((!packagesCount[name] || (packagesCount[name] === 1 && !manager.json.dependencies[name]))
&& uninstallables.indexOf(pkg) === -1) {
if (packagesCount[name] > 0) packagesCount[name] -= 1;
uninstallables.push(pkg);
}
}
};
var count = function (packages, counts) {
packages.forEach(function (pkg) {
counts[pkg.name] = (counts[pkg.name] || 0) + 1;
if (pkg.json.dependencies) {
for (var key in pkg.json.dependencies) {
count(manager.dependencies[key], counts);
}
}
});
};
var parseUninstallableDeps = function (pkg) {
if (packagesCount[pkg.name] > 0) packagesCount[pkg.name] -= 1;
if (pkg.json.dependencies) {
for (var key in pkg.json.dependencies) {
parseUninstallableDeps(manager.dependencies[key][0]);
}
}
};
var uninstall = function () {
async.forEach(uninstallables, function (pkg, next) {
pkg.on('uninstall', next).uninstall();
}, emitter.emit.bind(emitter, 'end'));
}, function () {
// Finally save
if (options.save) save();
emitter.emit.bind(emitter, 'end');
});
};
manager.on('resolveLocal', resolveLocal).resolveLocal();
var save = function () {
names.forEach(function (name) {
delete manager.json.dependencies[name];
});
fs.writeFileSync(path.join(manager.cwd, config.json), JSON.stringify(manager.json, null, 2));
};
manager.on('loadJSON', function () {
manager.on('resolveLocal', resolveLocal).resolveLocal();
}).loadJSON();
return emitter;
};
@@ -78,7 +142,7 @@ module.exports.line = function (argv) {
var options = nopt(optionTypes, shorthand, argv);
if (options.help) return help('uninstall');
var names = options.argv.remain.slice(1);
var names = options.argv.remain.slice(1);
return module.exports(names, options);
};

View File

@@ -84,7 +84,6 @@ var Package = function (name, endpoint, manager) {
this.tag = split[1];
} else {
split = endpoint.split('#', 2);
this.name = split[0];
this.tag = split[1];
}
@@ -93,6 +92,7 @@ var Package = function (name, endpoint, manager) {
if (this.gitUrl) this.name = path.basename(endpoint).replace(/(\.git)?(#.*)?$/, '');
else if (this.path) this.name = path.basename(this.path, this.assetType);
else if (this.assetUrl) this.name = this.name = path.basename(this.assetUrl, this.assetType);
else if (split) this.name = split[0];
}
// Store a reference to the original tag & original path
@@ -171,7 +171,7 @@ Package.prototype.install = function () {
.on('data', this.emit.bind(this, 'data'));
}
if (path.resolve(this.path) == this.localPath) {
if (path.resolve(this.path) === this.localPath) {
this.emit('install');
return this;
}
@@ -232,7 +232,7 @@ Package.prototype.uninstall = function () {
.on('data', this.emit.bind(this, 'data'));
rimraf(this.path, function (err) {
if (err) return this.emit('error', err);
this.emit.bind(this, 'uninstall');
this.emit('uninstall');
}.bind(this));
};
@@ -325,7 +325,7 @@ Package.prototype.download = function () {
this.once('loadJSON', this.saveUnit).loadJSON();
}.bind(this);
if (this.assetType === '.zip' || this.assetType == '.tar') this.once('extract', next).extract();
if (this.assetType === '.zip' || this.assetType === '.tar') this.once('extract', next).extract();
else next();
}.bind(this));
@@ -624,26 +624,26 @@ Package.prototype.fetchEndpoint = function () {
Package.prototype.readEndpoint = function (replace) {
if (!this.json.repository) return;
if (this.json.repository.type == 'git') {
if (this.json.repository.type === 'git') {
if (replace || !this.gitUrl) {
this.gitUrl = this.json.repository.url;
this.generateResourceId();
}
return { type: 'git', endpoint: this.gitUrl };
}
if (this.json.repository.type == 'local-repo') {
if (this.json.repository.type === 'local-repo') {
if (replace || !this.gitPath) {
this.gitPath = path.resolve(this.json.repository.path);
}
return { type: 'local', endpoint: this.path };
}
if (this.json.repository.type == 'local') {
if (this.json.repository.type === 'local') {
if (replace || !this.path) {
this.path = path.resolve(this.json.repository.path);
}
return { type: 'local', endpoint: this.path };
}
if (this.json.repository.type == 'asset') {
if (this.json.repository.type === 'asset') {
if (replace || !this.assetUrl) {
this.assetUrl = this.json.repository.url;
this.assetType = path.extname(this.assetUrl);

View File

@@ -13,7 +13,7 @@ var _ = require('lodash');
var config = require('../core/config');
function save(eventType, modifier, emitter, manager, paths) {
function save(eventType, modifier, manager, paths) {
// Tipically wait for the resolve event and also the load json event
manager.on(eventType, manager.on('loadJSON', function () {
// Find the original required packages in the command
@@ -22,9 +22,9 @@ function save(eventType, modifier, emitter, manager, paths) {
return _.find(Object.keys(this.dependencies), function (key) {
var dep = this.dependencies[key][0];
return dep.name == curr
|| (dep.url && dep.url == curr)
|| (dep.originalPath && dep.originalPath == curr);
return dep.name === curr
|| (dep.url && dep.url === curr)
|| (dep.originalPath && dep.originalPath === curr);
}.bind(this));
}.bind(this));
@@ -33,8 +33,7 @@ function save(eventType, modifier, emitter, manager, paths) {
return this.dependencies[name][0];
}.bind(this));
// The modifier can be addDependency or removeDependency
pkgs.forEach(modifier.bind(this));
pkgs.forEach(addDependency.bind(this));
// Finally save the modified json
fs.writeFileSync(path.join(this.cwd, config.json), JSON.stringify(this.json, null, 2));
@@ -61,9 +60,4 @@ function addDependency(pkg) {
return this.json.dependencies[pkg.name] = path ? path + (tag ? '#' + tag : '') : tag || 'latest';
}
function removeDependency(pkg) {
delete this.json.dependencies[pkg.name];
}
module.exports = save.bind(this, 'resolve', addDependency);
module.exports.discard = save.bind(this, 'resolveLocal', removeDependency);
module.exports = save.bind(this, 'resolve', addDependency);

View File

@@ -1 +1 @@
{{#yellow}}warn{{/yellow}} {{name}} {{#grey}}{{shizzle}}{{/grey}}
bower {{#yellow}}warn{{/yellow}} {{#grey}}{{message}}{{/grey}}

View File

@@ -1 +1 @@
{{#yellow}}warning{{/yellow}} {{#cyan}}{{packageName}}{{/cyan}} depends on {{#grey}}{{conflictName}}{{/grey}}
{{#red}}conflict{{/red}} {{#cyan}}{{packageName}}{{/cyan}} depends on {{#grey}}{{conflictName}}{{/grey}}