mirror of
https://github.com/bower/bower.git
synced 2026-04-24 03:00:19 -04:00
Save and use resolutions to resolve conflicts.
This commit is contained in:
@@ -14,6 +14,9 @@ function Manager(config, logger) {
|
||||
this._config = config;
|
||||
this._logger = logger;
|
||||
this._repository = new PackageRepository(this._config);
|
||||
|
||||
this.setResolutions();
|
||||
this.configure();
|
||||
}
|
||||
|
||||
// -----------------
|
||||
@@ -23,12 +26,22 @@ Manager.prototype.setProduction = function (production) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Manager.prototype.getResolutions = function () {
|
||||
return this._resolutions;
|
||||
};
|
||||
|
||||
Manager.prototype.setResolutions = function (resolutions) {
|
||||
this._resolutions = resolutions || {};
|
||||
return this;
|
||||
};
|
||||
|
||||
Manager.prototype.configure = function (targets, resolved, installed) {
|
||||
// If working, error out
|
||||
if (this._working) {
|
||||
throw createError('Can\'t configure while working', 'EWORKING');
|
||||
}
|
||||
|
||||
targets = targets || [];
|
||||
this._targets = targets;
|
||||
this._resolved = {};
|
||||
this._installed = {};
|
||||
@@ -90,7 +103,6 @@ Manager.prototype.install = function () {
|
||||
}
|
||||
|
||||
destDir = path.join(this._config.cwd, this._config.directory);
|
||||
|
||||
return Q.nfcall(mkdirp, destDir)
|
||||
.then(function () {
|
||||
var promises = [];
|
||||
@@ -270,6 +282,9 @@ Manager.prototype._onFetchSuccess = function (decEndpoint, canonicalPkg, pkgMeta
|
||||
}
|
||||
}
|
||||
|
||||
// Progress!
|
||||
this._deferred.notify(decEndpoint);
|
||||
|
||||
// Parse dependencies
|
||||
this._parseDependencies(decEndpoint, pkgMeta, 'dependencies');
|
||||
// Do the same for the dev dependencies
|
||||
@@ -453,19 +468,27 @@ Manager.prototype._dissect = function () {
|
||||
};
|
||||
|
||||
Manager.prototype._electSuitable = function (name, semvers, nonSemvers) {
|
||||
var picks = [];
|
||||
var suitable;
|
||||
var resolution;
|
||||
var dataPicks;
|
||||
var choices;
|
||||
var suitable;
|
||||
var picks = [];
|
||||
|
||||
// If there are no semver targets, there's a conflict if several exist
|
||||
if (!semvers.length) {
|
||||
if (nonSemvers.length > 1) {
|
||||
picks.push.apply(picks, nonSemvers);
|
||||
} else {
|
||||
suitable = nonSemvers[0];
|
||||
// If there are both semver and non-semver, there's no way
|
||||
// to figure out the suitable one
|
||||
if (semvers.length && nonSemvers.length) {
|
||||
picks.push.apply(picks, semvers);
|
||||
picks.push.apply(picks, nonSemvers);
|
||||
// If there are only non-semver ones, the suitable is elected
|
||||
// only if there's one
|
||||
} else if (nonSemvers.length) {
|
||||
if (nonSemvers.length === 1) {
|
||||
return Q.resolve(nonSemvers[0]);
|
||||
}
|
||||
// Otherwise, find most suitable semver
|
||||
|
||||
picks.push.apply(picks, nonSemvers);
|
||||
// If there are only semver ones, figure out the which one
|
||||
// is compatible with every requirement
|
||||
} else {
|
||||
suitable = mout.array.find(semvers, function (subject) {
|
||||
return semvers.every(function (decEndpoint) {
|
||||
@@ -473,21 +496,32 @@ Manager.prototype._electSuitable = function (name, semvers, nonSemvers) {
|
||||
});
|
||||
});
|
||||
|
||||
// Note that the user needs to pick one if there's no
|
||||
// suitable version or if some one them were non-semver
|
||||
if (!suitable || nonSemvers.length) {
|
||||
picks.push.apply(picks, semvers);
|
||||
if (suitable) {
|
||||
return Q.resolve(suitable);
|
||||
}
|
||||
|
||||
picks.push.apply(picks, nonSemvers);
|
||||
picks.push.apply(picks, semvers);
|
||||
}
|
||||
|
||||
// If there are no picks, resolve to the suitable one
|
||||
if (!picks.length) {
|
||||
return Q.resolve(suitable);
|
||||
// Check if there's a resolution that resolves the conflict
|
||||
resolution = this._resolutions[name];
|
||||
if (resolution) {
|
||||
if (semver.valid(resolution) != null || semver.validRange(resolution) != null) {
|
||||
suitable = mout.array.find(picks, function (pick) {
|
||||
return semver.satisfies(pick.pkgMeta.version, resolution);
|
||||
});
|
||||
} else {
|
||||
suitable = mout.array.find(picks, function (pick) {
|
||||
return pick.pkgMeta._release === resolution;
|
||||
});
|
||||
}
|
||||
|
||||
if (suitable) {
|
||||
return Q.resolve(suitable);
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, the user needs to make a decision
|
||||
// Sort picks by version/release
|
||||
picks.sort(function (pick1, pick2) {
|
||||
var version1 = pick1.pkgMeta.version;
|
||||
@@ -511,9 +545,18 @@ Manager.prototype._electSuitable = function (name, semvers, nonSemvers) {
|
||||
}
|
||||
}
|
||||
|
||||
// Give priority to the one with most dependants
|
||||
if (pick1.dependants.length > pick2.dependants.length) {
|
||||
return -1;
|
||||
}
|
||||
if (pick1.dependants.length < pick2.dependants.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Prepare data to be sent bellow
|
||||
dataPicks = picks.map(function (pick) {
|
||||
return {
|
||||
endpoint: mout.object.pick(pick, ['name', 'source', 'target']),
|
||||
@@ -546,8 +589,17 @@ Manager.prototype._electSuitable = function (name, semvers, nonSemvers) {
|
||||
choices = picks.map(function (pick, index) { return index + 1; });
|
||||
return Q.nfcall(promptly.choose, 'Choice:', choices)
|
||||
.then(function (choice) {
|
||||
return picks[choice - 1];
|
||||
});
|
||||
var pick = picks[choice - 1];
|
||||
|
||||
// Store choice into resolutions
|
||||
if (pick.target === '*') {
|
||||
this._resolutions[name] = pick.pkgMeta._release || '*';
|
||||
} else {
|
||||
this._resolutions[name] = pick.target;
|
||||
}
|
||||
|
||||
return pick;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Manager.prototype._getCap = function (comparators, side) {
|
||||
|
||||
@@ -40,12 +40,13 @@ Project.prototype.install = function (endpoints, options) {
|
||||
// Analyse the project
|
||||
return this.analyse()
|
||||
.spread(function (json, tree, flattened) {
|
||||
// Walk down the tree adding missing and incompatible
|
||||
// as targets
|
||||
var promise;
|
||||
|
||||
// Walk down the tree adding missing as targets
|
||||
that._walkTree(tree, function (node, name) {
|
||||
if (node.missing || node.incompatible) {
|
||||
if (node.missing) {
|
||||
targets.push(node);
|
||||
} else {
|
||||
} else if (!node.incompatible) {
|
||||
resolved[name] = node;
|
||||
}
|
||||
}, true);
|
||||
@@ -53,37 +54,66 @@ Project.prototype.install = function (endpoints, options) {
|
||||
// Add endpoints as targets
|
||||
if (endpoints) {
|
||||
endpoints.forEach(function (endpoint) {
|
||||
targets.push(endpointParser.decompose(endpoint));
|
||||
var decEndpoint = endpointParser.decompose(endpoint);
|
||||
decEndpoint.specified = true;
|
||||
targets.push(decEndpoint);
|
||||
});
|
||||
}
|
||||
|
||||
// Bootstrap the process
|
||||
return that._bootstrap(targets, resolved, flattened);
|
||||
})
|
||||
// Handle save and saveDev options
|
||||
.then(function (installed) {
|
||||
if (!options.save && !options.saveDev) {
|
||||
return installed;
|
||||
// If there are targets configured, add incompatible
|
||||
if (targets.length) {
|
||||
that._walkTree(tree, function (node) {
|
||||
if (node.incompatible) {
|
||||
targets.push(node);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
// Cycle through the initial targets and not the installed
|
||||
// ones because some targets could already be installed
|
||||
mout.object.forOwn(targets, function (decEndpoint) {
|
||||
var source = decEndpoint.registry ? '' : decEndpoint.source;
|
||||
var target = decEndpoint.target;
|
||||
var endpoint = mout.string.ltrim(source + '#' + target, ['#']);
|
||||
// Bootstrap the process
|
||||
promise = that._bootstrap(targets, resolved, flattened);
|
||||
|
||||
if (options.save) {
|
||||
that._json.dependencies = that._json.dependencies || {};
|
||||
that._json.dependencies[decEndpoint.name] = endpoint;
|
||||
}
|
||||
// If there are targets configured, listen to when they
|
||||
// resolve in order to remove any associated resolution
|
||||
// This can only be done at this step because endpoint names
|
||||
// are not fully known before
|
||||
if (endpoints) {
|
||||
promise = promise.progress(function (decEndpoint) {
|
||||
var resolutions;
|
||||
|
||||
if (options.saveDev) {
|
||||
that._json.devDependencies = that._json.devDependencies || {};
|
||||
that._json.devDependencies[decEndpoint.name] = endpoint;
|
||||
}
|
||||
});
|
||||
if (decEndpoint.specified) {
|
||||
resolutions = that._manager.getResolutions();
|
||||
delete resolutions[decEndpoint.name];
|
||||
delete decEndpoint.specified;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return promise;
|
||||
})
|
||||
.then(function (installed) {
|
||||
// Handle save and saveDev options
|
||||
if (options.save || options.saveDev) {
|
||||
// Cycle through the initial targets and not the installed
|
||||
// ones because some targets could already be installed
|
||||
mout.object.forOwn(targets, function (decEndpoint) {
|
||||
var source = decEndpoint.registry ? '' : decEndpoint.source;
|
||||
var target = decEndpoint.target;
|
||||
var endpoint = mout.string.ltrim(source + '#' + target, ['#']);
|
||||
|
||||
if (options.save) {
|
||||
that._json.dependencies = that._json.dependencies || {};
|
||||
that._json.dependencies[decEndpoint.name] = endpoint;
|
||||
}
|
||||
|
||||
if (options.saveDev) {
|
||||
that._json.devDependencies = that._json.devDependencies || {};
|
||||
that._json.devDependencies[decEndpoint.name] = endpoint;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Save JSON, might contain changes to dependencies
|
||||
// and resolutions
|
||||
return that._saveJson()
|
||||
.then(function () {
|
||||
return installed;
|
||||
@@ -174,7 +204,9 @@ Project.prototype.update = function (names, options) {
|
||||
}
|
||||
|
||||
// Bootstrap the process
|
||||
return that._bootstrap(targets, resolved, flattened);
|
||||
return that._bootstrap(targets, resolved, flattened)
|
||||
// Save JSON, might contain changes to resolutions
|
||||
.then(that._saveJson.bind(that));
|
||||
})
|
||||
.fin(function () {
|
||||
that._working = false;
|
||||
@@ -329,10 +361,20 @@ Project.prototype._bootstrap = function (targets, resolved, flattened) {
|
||||
// Configure the manager and kick in the resolve process
|
||||
return this._manager
|
||||
.setProduction(this._production)
|
||||
.setResolutions(this._json.resolutions)
|
||||
.configure(targets, resolved, installed)
|
||||
.resolve()
|
||||
// Install resolved ones
|
||||
.then(function () {
|
||||
var resolutions = this._manager.getResolutions();
|
||||
|
||||
// Update resolutions
|
||||
if (mout.object.size(resolutions)) {
|
||||
this._json.resolutions = resolutions;
|
||||
} else {
|
||||
delete this._json.resolutions;
|
||||
}
|
||||
|
||||
// Install resolved ones
|
||||
return this._manager.install();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user