Lift isSatisfied out of resolver.js

If we axe the rest of resolver.js, we still need this piece.

It would be as simple as VersionConstraint#isSatisfiedBy(version)
except for the fact that we put the logic here about whether to allow
prereleases, which necessitates the “package” and “options” arguments
and makes this a constraint-solver-specific utility.  In the future,
we can put that logic somewhere else, but for now keeping it
preserves the current behavior.
This commit is contained in:
David Greenspan
2015-01-21 15:22:59 -08:00
parent 2491ffd9aa
commit 402eab7983
3 changed files with 88 additions and 70 deletions

View File

@@ -318,3 +318,79 @@ var getCostFunction = function (resolver, options) {
}
};
};
// - package: String package name
// - vConstraint: a PackageVersion.VersionConstraint, or an object
// with an `alternatives` property lifted from one.
// - version: version String
// - options: any object with an "anticipatedPrereleases" property.
CS.isConstraintSatisfied = function (package, vConstraint, version, options) {
var prereleaseNeedingLicense = false;
// We try not to allow "pre-release" versions (versions with a '-') unless
// they are explicitly mentioned. If the `anticipatedPrereleases` option is
// `true` set, all pre-release versions are allowed. Otherwise,
// anticipatedPrereleases lists pre-release versions that are always allow
// (this corresponds to pre-release versions mentioned explicitly in
// *top-level* constraints).
//
// Otherwise, if `candidateUV` is a pre-release, it needs to be "licensed" by
// being mentioned by name in *this* constraint or matched by an inexact
// constraint whose version also has a '-'.
//
// Note that a constraint "@2.0.0" can never match a version "2.0.1-rc.1"
// unless anticipatedPrereleases allows it, even if another constraint found
// in the graph (but not at the top level) explicitly mentions "2.0.1-rc.1".
// Why? The constraint solver assumes that adding a constraint to the resolver
// state can't make previously impossible choices now possible. If
// pre-releases mentioned anywhere worked, then applying the constraint
// "@2.0.0" followed by "@=2.0.1-rc.1" would result in "2.0.1-rc.1" ruled
// first impossible and then possible again. That will break this algorith, so
// we have to fix the meaning based on something known at the start of the
// search. (We could try to apply our prerelease-avoidance tactics solely in
// the cost functions, but then it becomes a much less strict rule.)
if (options.anticipatedPrereleases !== true
&& /-/.test(version)) {
var isAnticipatedPrerelease = (
_.has(options.anticipatedPrereleases, package) &&
_.has(options.anticipatedPrereleases[package], version));
if (! isAnticipatedPrerelease) {
prereleaseNeedingLicense = true;
}
}
return _.some(vConstraint.alternatives, function (simpleConstraint) {
var type = simpleConstraint.type;
if (type === "any-reasonable") {
return ! prereleaseNeedingLicense;
} else if (type === "exactly") {
var cVersion = simpleConstraint.versionString;
return (cVersion === version);
} else if (type === 'compatible-with') {
var cv = PV.parse(simpleConstraint.versionString);
var v = PV.parse(version);
if (prereleaseNeedingLicense && ! /-/.test(cv.raw)) {
return false;
}
// If the candidate version is less than the version named in the
// constraint, we are not satisfied.
if (PV.lessThan(v, cv)) {
return false;
}
// To be compatible, the two versions must have the same major version
// number.
if (v.major !== cv.major) {
return false;
}
return true;
} else {
throw Error("Unknown constraint type: " + type);
}
});
};

View File

@@ -321,73 +321,8 @@ ConstraintSolver.Constraint.prototype.isSatisfied = function (
candidateUV.name);
}
var prereleaseNeedingLicense = false;
// We try not to allow "pre-release" versions (versions with a '-') unless
// they are explicitly mentioned. If the `anticipatedPrereleases` option is
// `true` set, all pre-release versions are allowed. Otherwise,
// anticipatedPrereleases lists pre-release versions that are always allow
// (this corresponds to pre-release versions mentioned explicitly in
// *top-level* constraints).
//
// Otherwise, if `candidateUV` is a pre-release, it needs to be "licensed" by
// being mentioned by name in *this* constraint or matched by an inexact
// constraint whose version also has a '-'.
//
// Note that a constraint "@2.0.0" can never match a version "2.0.1-rc.1"
// unless anticipatedPrereleases allows it, even if another constraint found
// in the graph (but not at the top level) explicitly mentions "2.0.1-rc.1".
// Why? The constraint solver assumes that adding a constraint to the resolver
// state can't make previously impossible choices now possible. If
// pre-releases mentioned anywhere worked, then applying the constraint
// "@2.0.0" followed by "@=2.0.1-rc.1" would result in "2.0.1-rc.1" ruled
// first impossible and then possible again. That will break this algorith, so
// we have to fix the meaning based on something known at the start of the
// search. (We could try to apply our prerelease-avoidance tactics solely in
// the cost functions, but then it becomes a much less strict rule.)
if (resolveContext.anticipatedPrereleases !== true
&& /-/.test(candidateUV.version)) {
var isAnticipatedPrerelease = (
_.has(resolveContext.anticipatedPrereleases, self.name) &&
_.has(resolveContext.anticipatedPrereleases[self.name],
candidateUV.version));
if (! isAnticipatedPrerelease) {
prereleaseNeedingLicense = true;
}
}
return _.some(self.alternatives, function (simpleConstraint) {
var type = simpleConstraint.type;
if (type === "any-reasonable") {
return ! prereleaseNeedingLicense;
} else if (type === "exactly") {
var version = simpleConstraint.versionString;
return (version === candidateUV.version);
} else if (type === 'compatible-with') {
var version = simpleConstraint.versionString;
if (prereleaseNeedingLicense && ! /-/.test(version)) {
return false;
}
// If the candidate version is less than the version named in the
// constraint, we are not satisfied.
if (PackageVersion.lessThan(candidateUV.version, version)) {
return false;
}
// To be compatible, the two versions must have the same major version
// number.
if (candidateUV.majorVersion !== PackageVersion.majorVersion(version)) {
return false;
}
return true;
} else {
throw Error("Unknown constraint type: " + type);
}
});
return ConstraintSolver.isConstraintSatisfied(
self.name, self, candidateUV.version, resolveContext);
};
// An object that records the general context of a resolve call. It can be

View File

@@ -181,6 +181,7 @@ var prereleaseIdentifierToFraction = function (prerelease) {
};
// Takes in two meteor versions. Returns true if the first one is less than the second.
// Versions are strings or PackageVersion objects.
PV.lessThan = function (versionOne, versionTwo) {
return PV.compare(versionOne, versionTwo) < 0;
};
@@ -195,10 +196,16 @@ PV.majorVersion = function (versionString) {
};
// Takes in two meteor versions. Returns 0 if equal, 1 if v1 is greater, -1 if
// v2 is greater.
// v2 is greater. Versions are strings or PackageVersion objects.
PV.compare = function (versionOne, versionTwo) {
var v1 = PV.parse(versionOne);
var v2 = PV.parse(versionTwo);
var v1 = versionOne;
if (typeof v1 === 'string') {
v1 = PV.parse(v1);
}
var v2 = versionTwo;
if (typeof v2 === 'string') {
v2 = PV.parse(v2);
}
// If the semver parts are different, use the semver library to compare,
// ignoring wrap numbers. (The semver library will ignore the build ID