UNPKG

6.01 kBJavaScriptView Raw
1/*
2 * Copyright 2014-2016 Guy Bedford (http://guybedford.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17require('core-js/es6/string');
18
19var semverRegEx = /^(\d+)(?:\.(\d+)(?:\.(\d+)(?:-([\da-z-]+(?:\.[\da-z-]+)*)(?:\+([\da-z-]+(?:\.[\da-z-]+)*))?)?)?)?$/i;
20var numRegEx = /^\d+$/;
21
22function toInt(num) {
23 return parseInt(num, 10);
24}
25
26function parseSemver(v) {
27 var semver = v.match(semverRegEx);
28 if (!semver)
29 return {
30 tag: v
31 };
32 else
33 return {
34 major: toInt(semver[1]),
35 minor: toInt(semver[2]),
36 patch: toInt(semver[3]),
37 pre: semver[4] && semver[4].split('.')
38 };
39}
40
41var parts = ['major', 'minor', 'patch'];
42function semverCompareParsed(v1, v2, edge) {
43 // not semvers - tags have equal precedence
44 if (v1.tag && v2.tag)
45 return 0;
46
47 // semver beats non-semver
48 if (v1.tag)
49 return -1;
50 if (v2.tag)
51 return 1;
52
53 // compare version numbers
54 for (var i = 0; i < parts.length; i++) {
55 var part = parts[i];
56 var part1 = v1[part];
57 var part2 = v2[part];
58 if (part1 === part2)
59 continue;
60 if (isNaN(part1))
61 return -1;
62 if (isNaN(part2))
63 return 1;
64 return part1 > part2 ? 1 : -1;
65 }
66
67 if (!v1.pre && !v2.pre)
68 return 0;
69
70 if (!v1.pre)
71 return edge ? 0 : 1;
72 if (!v2.pre)
73 return -1;
74
75 // prerelease comparison
76 for (var j = 0, l = Math.min(v1.pre.length, v2.pre.length); j < l; j++) {
77 if (v1.pre[j] === v2.pre[j])
78 continue;
79
80 var isNum1 = v1.pre[j].match(numRegEx);
81 var isNum2 = v2.pre[j].match(numRegEx);
82
83 // numeric has lower precedence
84 if (isNum1 && !isNum2)
85 return -1;
86 if (isNum2 && !isNum1)
87 return 1;
88
89 // compare parts
90 if (isNum1 && isNum2)
91 return toInt(v1.pre[j]) > toInt(v2.pre[j]) ? 1 : -1;
92 else
93 return v1.pre[j] > v2.pre[j] ? 1 : -1;
94 }
95
96 if (v1.pre.length === v2.pre.length)
97 return 0;
98
99 // more pre-release fields win if equal
100 return v1.pre.length > v2.pre.length ? 1 : -1;
101}
102
103// match against a parsed range object
104// saves operation repetition
105// doesn't support tags
106// if not semver or fuzzy, assume exact
107function matchParsed(range, version, edge) {
108 var rangeVersion = range.version;
109
110 if (rangeVersion.tag)
111 return rangeVersion.tag === version.tag;
112
113 // if the version is less than the range, it's not a match
114 if (semverCompareParsed(rangeVersion, version, edge) === 1)
115 return false;
116
117 // now we just have to check that the version isn't too high for the range
118 if (isNaN(version.minor) || isNaN(version.patch))
119 return false;
120
121 // if the version has a prerelease, ensure the range version has a prerelease in it
122 // and that we match the range version up to the prerelease exactly
123 if (version.pre) {
124 if (!edge &&
125 !(rangeVersion.major === version.major &&
126 rangeVersion.minor === version.minor &&
127 rangeVersion.patch === version.patch))
128 return false;
129 return range.semver || range.fuzzy || rangeVersion.pre && rangeVersion.pre.join('.') === version.pre.join('.');
130 }
131
132 // check semver range
133 if (range.semver) {
134 // ^0
135 if (rangeVersion.major === 0 && isNaN(rangeVersion.minor))
136 return version.major < 1;
137 // ^1..
138 else if (rangeVersion.major >= 1)
139 return rangeVersion.major === version.major;
140 // ^0.1, ^0.2
141 else if (rangeVersion.minor >= 1)
142 return version.major === 0 && rangeVersion.minor === version.minor;
143 // ^0.0.x falls down to exact match below
144 }
145
146 // check fuzzy range (we can assume rangeVersion.minor exists, due to behaviour switch)
147 if (range.fuzzy)
148 return version.major === rangeVersion.major && version.minor <= rangeVersion.minor;
149
150 // exact match
151 // eg 001.002.003 matches 1.2.3
152 return !rangeVersion.pre && rangeVersion.major === version.major && rangeVersion.minor === version.minor && rangeVersion.patch === version.patch;
153}
154
155/*
156 * semver - is this a semver range
157 * fuzzy - is this a fuzzy range
158 * version - the parsed version object
159 */
160function parseRange(range) {
161 var rangeObj = {};
162
163 ((rangeObj.semver = range.startsWith('^')) ||
164 (rangeObj.fuzzy = range.startsWith('~'))
165 ) && (range = range.substr(1)); // jshint ignore:line
166
167 var rangeVersion = rangeObj.version = parseSemver(range);
168
169 if (rangeVersion.tag)
170 return rangeObj;
171
172 // 0, 0.1 behave like ~0, ~0.1
173 if (!rangeObj.fuzzy && !rangeObj.semver && (isNaN(rangeVersion.minor) || isNaN(rangeVersion.patch)))
174 rangeObj.fuzzy = true;
175
176 // ~1, ~0 behave like ^1, ^0
177 if (rangeObj.fuzzy && isNaN(rangeVersion.minor)) {
178 rangeObj.semver = true;
179 rangeObj.fuzzy = false;
180 }
181
182 // ^0.0 behaves like ~0.0
183 if (rangeObj.semver && rangeObj.major === 0 && !isNaN(rangeVersion.minor) && isNaN(rangeVersion.patch)) {
184 rangeObj.semver = false;
185 rangeObj.fuzzy = true;
186 }
187
188 return rangeObj;
189}
190
191exports.semverRegEx = semverRegEx;
192
193exports.compare = function(v1, v2) {
194 return semverCompareParsed(parseSemver(v1), parseSemver(v2));
195};
196
197exports.match = function match(range, version) {
198 // supported range types:
199 // 0.2, 1, ~1.2.3, ^1.2.3, ^0.4.3-alpha.1
200 if (range === '' || range === '*')
201 return true;
202 return matchParsed(parseRange(range), parseSemver(version));
203};
204
205exports.matchUnstable = function match(range, version) {
206 // supported range types:
207 // 0.2, 1, ~1.2.3, ^1.2.3, ^0.4.3-alpha.1
208 if (range === '' || range === '*')
209 return true;
210 return matchParsed(parseRange(range), parseSemver(version), true);
211};