jspm
Version:
Registry and format agnostic JavaScript package manager
212 lines (176 loc) • 6.01 kB
JavaScript
/*
* Copyright 2014-2015 Guy Bedford (http://guybedford.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
require('core-js/es6/string');
var semverRegEx = /^(\d+)(?:\.(\d+)(?:\.(\d+)(?:-([\da-z-]+(?:\.[\da-z-]+)*)(?:\+([\da-z-]+(?:\.[\da-z-]+)*))?)?)?)?$/i;
var numRegEx = /^\d+$/;
function toInt(num) {
return parseInt(num, 10);
}
function parseSemver(v) {
var semver = v.match(semverRegEx);
if (!semver)
return {
tag: v
};
else
return {
major: toInt(semver[1]),
minor: toInt(semver[2]),
patch: toInt(semver[3]),
pre: semver[4] && semver[4].split('.')
};
}
var parts = ['major', 'minor', 'patch'];
function semverCompareParsed(v1, v2, edge) {
// not semvers - tags have equal precedence
if (v1.tag && v2.tag)
return 0;
// semver beats non-semver
if (v1.tag)
return -1;
if (v2.tag)
return 1;
// compare version numbers
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
var part1 = v1[part];
var part2 = v2[part];
if (part1 === part2)
continue;
if (isNaN(part1))
return -1;
if (isNaN(part2))
return 1;
return part1 > part2 ? 1 : -1;
}
if (!v1.pre && !v2.pre)
return 0;
if (!v1.pre)
return edge ? 0 : 1;
if (!v2.pre)
return -1;
// prerelease comparison
for (var j = 0, l = Math.min(v1.pre.length, v2.pre.length); j < l; j++) {
if (v1.pre[j] === v2.pre[j])
continue;
var isNum1 = v1.pre[j].match(numRegEx);
var isNum2 = v2.pre[j].match(numRegEx);
// numeric has lower precedence
if (isNum1 && !isNum2)
return -1;
if (isNum2 && !isNum1)
return 1;
// compare parts
if (isNum1 && isNum2)
return toInt(v1.pre[j]) > toInt(v2.pre[j]) ? 1 : -1;
else
return v1.pre[j] > v2.pre[j] ? 1 : -1;
}
if (v1.pre.length === v2.pre.length)
return 0;
// more pre-release fields win if equal
return v1.pre.length > v2.pre.length ? 1 : -1;
}
// match against a parsed range object
// saves operation repetition
// doesn't support tags
// if not semver or fuzzy, assume exact
function matchParsed(range, version, edge) {
var rangeVersion = range.version;
if (rangeVersion.tag)
return rangeVersion.tag === version.tag;
// if the version is less than the range, it's not a match
if (semverCompareParsed(rangeVersion, version, edge) === 1)
return false;
// now we just have to check that the version isn't too high for the range
if (isNaN(version.minor) || isNaN(version.patch))
return false;
// if the version has a prerelease, ensure the range version has a prerelease in it
// and that we match the range version up to the prerelease exactly
if (version.pre) {
if (!edge &&
!(rangeVersion.major === version.major &&
rangeVersion.minor === version.minor &&
rangeVersion.patch === version.patch))
return false;
return range.semver || range.fuzzy || rangeVersion.pre && rangeVersion.pre.join('.') === version.pre.join('.');
}
// check semver range
if (range.semver) {
// ^0
if (rangeVersion.major === 0 && isNaN(rangeVersion.minor))
return version.major < 1;
// ^1..
else if (rangeVersion.major >= 1)
return rangeVersion.major === version.major;
// ^0.1, ^0.2
else if (rangeVersion.minor >= 1)
return version.major === 0 && rangeVersion.minor === version.minor;
// ^0.0.x falls down to exact match below
}
// check fuzzy range (we can assume rangeVersion.minor exists, due to behaviour switch)
if (range.fuzzy)
return version.major === rangeVersion.major && version.minor <= rangeVersion.minor;
// exact match
// eg 001.002.003 matches 1.2.3
return !rangeVersion.pre && rangeVersion.major === version.major && rangeVersion.minor === version.minor && rangeVersion.patch === version.patch;
}
/*
* semver - is this a semver range
* fuzzy - is this a fuzzy range
* version - the parsed version object
*/
function parseRange(range) {
var rangeObj = {};
((rangeObj.semver = range.startsWith('^')) ||
(rangeObj.fuzzy = range.startsWith('~'))
) && (range = range.substr(1)); // jshint ignore:line
var rangeVersion = rangeObj.version = parseSemver(range);
if (rangeVersion.tag)
return rangeObj;
// 0, 0.1 behave like ~0, ~0.1
if (!rangeObj.fuzzy && !rangeObj.semver && (isNaN(rangeVersion.minor) || isNaN(rangeVersion.patch)))
rangeObj.fuzzy = true;
// ~1, ~0 behave like ^1, ^0
if (rangeObj.fuzzy && isNaN(rangeVersion.minor)) {
rangeObj.semver = true;
rangeObj.fuzzy = false;
}
// ^0.0 behaves like ~0.0
if (rangeObj.semver && rangeObj.major === 0 && !isNaN(rangeVersion.minor) && isNaN(rangeVersion.patch)) {
rangeObj.semver = false;
rangeObj.fuzzy = true;
}
return rangeObj;
}
exports.semverRegEx = semverRegEx;
exports.compare = function(v1, v2) {
return semverCompareParsed(parseSemver(v1), parseSemver(v2));
};
exports.match = function match(range, version) {
// supported range types:
// 0.2, 1, ~1.2.3, ^1.2.3, ^0.4.3-alpha.1
if (range === '' || range === '*')
return true;
return matchParsed(parseRange(range), parseSemver(version));
};
exports.matchUnstable = function match(range, version) {
// supported range types:
// 0.2, 1, ~1.2.3, ^1.2.3, ^0.4.3-alpha.1
if (range === '' || range === '*')
return true;
return matchParsed(parseRange(range), parseSemver(version), true);
};