Skip to content

Commit 059a5ad

Browse files
eemeliisaacs
authored andcommitted
Always exclude prereleases from range maximums
In semver v4, the ranges were simplified in 2331a9e "to remove -0 everywhere". This had the unintended side effect of including the endpoint version's prerelease versions in the resulting range, as lt('1.0.0-0', '1.0.0') === true. Tests are updated, adding -0 to the maximal endpoints. One test is moved from range-include to range-exclude, comparing 2.0.0-rc1 against ^1.0.0 Fixes #223 Fixes #254 EDIT(isaacs): Updated to resolve conflicts on master branch. PR-URL: #320 Credit: @eemeli Close: #320 Reviewed-by: @isaacs
1 parent f27dcf5 commit 059a5ad

File tree

6 files changed

+116
-115
lines changed

6 files changed

+116
-115
lines changed

README.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -256,37 +256,37 @@ inclusive range, then all versions that start with the supplied parts
256256
of the tuple are accepted, but nothing that would be greater than the
257257
provided tuple parts.
258258

259-
* `1.2.3 - 2.3` := `>=1.2.3 <2.4.0`
260-
* `1.2.3 - 2` := `>=1.2.3 <3.0.0`
259+
* `1.2.3 - 2.3` := `>=1.2.3 <2.4.0-0`
260+
* `1.2.3 - 2` := `>=1.2.3 <3.0.0-0`
261261

262262
#### X-Ranges `1.2.x` `1.X` `1.2.*` `*`
263263

264264
Any of `X`, `x`, or `*` may be used to "stand in" for one of the
265265
numeric values in the `[major, minor, patch]` tuple.
266266

267267
* `*` := `>=0.0.0` (Any version satisfies)
268-
* `1.x` := `>=1.0.0 <2.0.0` (Matching major version)
269-
* `1.2.x` := `>=1.2.0 <1.3.0` (Matching major and minor versions)
268+
* `1.x` := `>=1.0.0 <2.0.0-0` (Matching major version)
269+
* `1.2.x` := `>=1.2.0 <1.3.0-0` (Matching major and minor versions)
270270

271271
A partial version range is treated as an X-Range, so the special
272272
character is in fact optional.
273273

274274
* `""` (empty string) := `*` := `>=0.0.0`
275-
* `1` := `1.x.x` := `>=1.0.0 <2.0.0`
276-
* `1.2` := `1.2.x` := `>=1.2.0 <1.3.0`
275+
* `1` := `1.x.x` := `>=1.0.0 <2.0.0-0`
276+
* `1.2` := `1.2.x` := `>=1.2.0 <1.3.0-0`
277277

278278
#### Tilde Ranges `~1.2.3` `~1.2` `~1`
279279

280280
Allows patch-level changes if a minor version is specified on the
281281
comparator. Allows minor-level changes if not.
282282

283-
* `~1.2.3` := `>=1.2.3 <1.(2+1).0` := `>=1.2.3 <1.3.0`
284-
* `~1.2` := `>=1.2.0 <1.(2+1).0` := `>=1.2.0 <1.3.0` (Same as `1.2.x`)
285-
* `~1` := `>=1.0.0 <(1+1).0.0` := `>=1.0.0 <2.0.0` (Same as `1.x`)
286-
* `~0.2.3` := `>=0.2.3 <0.(2+1).0` := `>=0.2.3 <0.3.0`
287-
* `~0.2` := `>=0.2.0 <0.(2+1).0` := `>=0.2.0 <0.3.0` (Same as `0.2.x`)
288-
* `~0` := `>=0.0.0 <(0+1).0.0` := `>=0.0.0 <1.0.0` (Same as `0.x`)
289-
* `~1.2.3-beta.2` := `>=1.2.3-beta.2 <1.3.0` Note that prereleases in
283+
* `~1.2.3` := `>=1.2.3 <1.(2+1).0` := `>=1.2.3 <1.3.0-0`
284+
* `~1.2` := `>=1.2.0 <1.(2+1).0` := `>=1.2.0 <1.3.0-0` (Same as `1.2.x`)
285+
* `~1` := `>=1.0.0 <(1+1).0.0` := `>=1.0.0 <2.0.0-0` (Same as `1.x`)
286+
* `~0.2.3` := `>=0.2.3 <0.(2+1).0` := `>=0.2.3 <0.3.0-0`
287+
* `~0.2` := `>=0.2.0 <0.(2+1).0` := `>=0.2.0 <0.3.0-0` (Same as `0.2.x`)
288+
* `~0` := `>=0.0.0 <(0+1).0.0` := `>=0.0.0 <1.0.0-0` (Same as `0.x`)
289+
* `~1.2.3-beta.2` := `>=1.2.3-beta.2 <1.3.0-0` Note that prereleases in
290290
the `1.2.3` version will be allowed, if they are greater than or
291291
equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but
292292
`1.2.4-beta.2` would not, because it is a prerelease of a
@@ -308,32 +308,32 @@ However, it presumes that there will *not* be breaking changes between
308308
`0.2.4` and `0.2.5`. It allows for changes that are presumed to be
309309
additive (but non-breaking), according to commonly observed practices.
310310

311-
* `^1.2.3` := `>=1.2.3 <2.0.0`
312-
* `^0.2.3` := `>=0.2.3 <0.3.0`
313-
* `^0.0.3` := `>=0.0.3 <0.0.4`
314-
* `^1.2.3-beta.2` := `>=1.2.3-beta.2 <2.0.0` Note that prereleases in
311+
* `^1.2.3` := `>=1.2.3 <2.0.0-0`
312+
* `^0.2.3` := `>=0.2.3 <0.3.0-0`
313+
* `^0.0.3` := `>=0.0.3 <0.0.4-0`
314+
* `^1.2.3-beta.2` := `>=1.2.3-beta.2 <2.0.0-0` Note that prereleases in
315315
the `1.2.3` version will be allowed, if they are greater than or
316316
equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but
317317
`1.2.4-beta.2` would not, because it is a prerelease of a
318318
different `[major, minor, patch]` tuple.
319-
* `^0.0.3-beta` := `>=0.0.3-beta <0.0.4` Note that prereleases in the
319+
* `^0.0.3-beta` := `>=0.0.3-beta <0.0.4-0` Note that prereleases in the
320320
`0.0.3` version *only* will be allowed, if they are greater than or
321321
equal to `beta`. So, `0.0.3-pr.2` would be allowed.
322322

323323
When parsing caret ranges, a missing `patch` value desugars to the
324324
number `0`, but will allow flexibility within that value, even if the
325325
major and minor versions are both `0`.
326326

327-
* `^1.2.x` := `>=1.2.0 <2.0.0`
328-
* `^0.0.x` := `>=0.0.0 <0.1.0`
329-
* `^0.0` := `>=0.0.0 <0.1.0`
327+
* `^1.2.x` := `>=1.2.0 <2.0.0-0`
328+
* `^0.0.x` := `>=0.0.0 <0.1.0-0`
329+
* `^0.0` := `>=0.0.0 <0.1.0-0`
330330

331331
A missing `minor` and `patch` values will desugar to zero, but also
332332
allow flexibility within those values, even if the major version is
333333
zero.
334334

335-
* `^1.x` := `>=1.0.0 <2.0.0`
336-
* `^0.x` := `>=0.0.0 <1.0.0`
335+
* `^1.x` := `>=1.0.0 <2.0.0-0`
336+
* `^0.x` := `>=0.0.0 <1.0.0-0`
337337

338338
### Range Grammar
339339

classes/range.js

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,11 @@ const parseComparator = (comp, options) => {
192192
const isX = id => !id || id.toLowerCase() === 'x' || id === '*'
193193

194194
// ~, ~> --> * (any, kinda silly)
195-
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0
196-
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0
197-
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0
198-
// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0
199-
// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0
195+
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0
196+
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0
197+
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0
198+
// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0
199+
// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0
200200
const replaceTildes = (comp, options) =>
201201
comp.trim().split(/\s+/).map((comp) => {
202202
return replaceTilde(comp, options)
@@ -211,18 +211,18 @@ const replaceTilde = (comp, options) => {
211211
if (isX(M)) {
212212
ret = ''
213213
} else if (isX(m)) {
214-
ret = `>=${M}.0.0 <${+M + 1}.0.0`
214+
ret = `>=${M}.0.0 <${+M + 1}.0.0-0`
215215
} else if (isX(p)) {
216-
// ~1.2 == >=1.2.0 <1.3.0
217-
ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0`
216+
// ~1.2 == >=1.2.0 <1.3.0-0
217+
ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`
218218
} else if (pr) {
219219
debug('replaceTilde pr', pr)
220220
ret = `>=${M}.${m}.${p}-${pr
221-
} <${M}.${+m + 1}.0`
221+
} <${M}.${+m + 1}.0-0`
222222
} else {
223-
// ~1.2.3 == >=1.2.3 <1.3.0
223+
// ~1.2.3 == >=1.2.3 <1.3.0-0
224224
ret = `>=${M}.${m}.${p
225-
} <${M}.${+m + 1}.0`
225+
} <${M}.${+m + 1}.0-0`
226226
}
227227

228228
debug('tilde return', ret)
@@ -231,11 +231,11 @@ const replaceTilde = (comp, options) => {
231231
}
232232

233233
// ^ --> * (any, kinda silly)
234-
// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0
235-
// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0
236-
// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0
237-
// ^1.2.3 --> >=1.2.3 <2.0.0
238-
// ^1.2.0 --> >=1.2.0 <2.0.0
234+
// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0-0
235+
// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0-0
236+
// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0
237+
// ^1.2.3 --> >=1.2.3 <2.0.0-0
238+
// ^1.2.0 --> >=1.2.0 <2.0.0-0
239239
const replaceCarets = (comp, options) =>
240240
comp.trim().split(/\s+/).map((comp) => {
241241
return replaceCaret(comp, options)
@@ -252,40 +252,40 @@ const replaceCaret = (comp, options) => {
252252
if (isX(M)) {
253253
ret = ''
254254
} else if (isX(m)) {
255-
ret = `>=${M}.0.0${z} <${+M + 1}.0.0${z}`
255+
ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`
256256
} else if (isX(p)) {
257257
if (M === '0') {
258-
ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0${z}`
258+
ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`
259259
} else {
260-
ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0${z}`
260+
ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`
261261
}
262262
} else if (pr) {
263263
debug('replaceCaret pr', pr)
264264
if (M === '0') {
265265
if (m === '0') {
266266
ret = `>=${M}.${m}.${p}-${pr
267-
} <${M}.${m}.${+p + 1}${z}`
267+
} <${M}.${m}.${+p + 1}-0`
268268
} else {
269269
ret = `>=${M}.${m}.${p}-${pr
270-
} <${M}.${+m + 1}.0${z}`
270+
} <${M}.${+m + 1}.0-0`
271271
}
272272
} else {
273273
ret = `>=${M}.${m}.${p}-${pr
274-
} <${+M + 1}.0.0${z}`
274+
} <${+M + 1}.0.0-0`
275275
}
276276
} else {
277277
debug('no pr')
278278
if (M === '0') {
279279
if (m === '0') {
280280
ret = `>=${M}.${m}.${p
281-
}${z} <${M}.${m}.${+p + 1}${z}`
281+
}${z} <${M}.${m}.${+p + 1}-0`
282282
} else {
283283
ret = `>=${M}.${m}.${p
284-
}${z} <${M}.${+m + 1}.0${z}`
284+
}${z} <${M}.${+m + 1}.0-0`
285285
}
286286
} else {
287287
ret = `>=${M}.${m}.${p
288-
} <${+M + 1}.0.0${z}`
288+
} <${+M + 1}.0.0-0`
289289
}
290290
}
291291

@@ -360,10 +360,10 @@ const replaceXRange = (comp, options) => {
360360

361361
ret = `${gtlt + M}.${m}.${p}${pr}`
362362
} else if (xm) {
363-
ret = `>=${M}.0.0${pr} <${+M + 1}.0.0${pr}`
363+
ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`
364364
} else if (xp) {
365365
ret = `>=${M}.${m}.0${pr
366-
} <${M}.${+m + 1}.0${pr}`
366+
} <${M}.${+m + 1}.0-0`
367367
}
368368

369369
debug('xRange return', ret)
@@ -389,8 +389,8 @@ const replaceGTE0 = (comp, options) => {
389389
// This function is passed to string.replace(re[t.HYPHENRANGE])
390390
// M, m, patch, prerelease, build
391391
// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
392-
// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
393-
// 1.2 - 3.4 => >=1.2.0 <3.5.0
392+
// 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do
393+
// 1.2 - 3.4 => >=1.2.0 <3.5.0-0
394394
const hyphenReplace = incPr => ($0,
395395
from, fM, fm, fp, fpr, fb,
396396
to, tM, tm, tp, tpr, tb) => {
@@ -409,9 +409,9 @@ const hyphenReplace = incPr => ($0,
409409
if (isX(tM)) {
410410
to = ''
411411
} else if (isX(tm)) {
412-
to = `<${+tM + 1}.0.0${incPr ? '-0' : ''}`
412+
to = `<${+tM + 1}.0.0-0`
413413
} else if (isX(tp)) {
414-
to = `<${tM}.${+tm + 1}.0${incPr ? '-0' : ''}`
414+
to = `<${tM}.${+tm + 1}.0-0`
415415
} else if (tpr) {
416416
to = `<=${tM}.${tm}.${tp}-${tpr}`
417417
} else if (incPr) {

test/classes/range.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ test('negative range tests', t => {
6969
test('strict vs loose ranges', (t) => {
7070
[
7171
['>=01.02.03', '>=1.2.3'],
72-
['~1.02.03beta', '>=1.2.3-beta <1.3.0']
72+
['~1.02.03beta', '>=1.2.3-beta <1.3.0-0']
7373
].forEach(([loose, comps]) => {
7474
t.throws(() => new Range(loose))
7575
t.equal(new Range(loose, true).range, comps)

test/fixtures/range-exclude.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ module.exports = [
7878

7979
['2.x', '3.0.0-pre.0', { includePrerelease: true }],
8080
['^1.0.0', '1.0.0-rc1', { includePrerelease: true }],
81+
['^1.0.0', '2.0.0-rc1', { includePrerelease: true }],
8182
['^1.2.3-rc2', '2.0.0', { includePrerelease: true }],
8283
['^1.0.0', '2.0.0-rc1', { includePrerelease: true }],
8384
['^1.0.0', '2.0.0-rc1'],

test/fixtures/range-parse.js

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
module.exports = [
66
['1.0.0 - 2.0.0', '>=1.0.0 <=2.0.0'],
77
['1.0.0 - 2.0.0', '>=1.0.0-0 <2.0.1-0', { includePrerelease: true }],
8-
['1 - 2', '>=1.0.0 <3.0.0'],
8+
['1 - 2', '>=1.0.0 <3.0.0-0'],
99
['1 - 2', '>=1.0.0-0 <3.0.0-0', { includePrerelease: true }],
10-
['1.0 - 2.0', '>=1.0.0 <2.1.0'],
10+
['1.0 - 2.0', '>=1.0.0 <2.1.0-0'],
1111
['1.0 - 2.0', '>=1.0.0-0 <2.1.0-0', { includePrerelease: true }],
1212
['1.0.0', '1.0.0', { loose: false }],
1313
['>=*', '*'],
@@ -17,7 +17,7 @@ module.exports = [
1717
['>=1.0.0', '>=1.0.0'],
1818
['>1.0.0', '>1.0.0'],
1919
['<=2.0.0', '<=2.0.0'],
20-
['1', '>=1.0.0 <2.0.0'],
20+
['1', '>=1.0.0 <2.0.0-0'],
2121
['<=2.0.0', '<=2.0.0'],
2222
['<=2.0.0', '<=2.0.0'],
2323
['<2.0.0', '<2.0.0'],
@@ -39,50 +39,50 @@ module.exports = [
3939
['>=0.2.3 || <0.0.1', '>=0.2.3||<0.0.1'],
4040
['>=0.2.3 || <0.0.1', '>=0.2.3||<0.0.1'],
4141
['||', '||'],
42-
['2.x.x', '>=2.0.0 <3.0.0'],
43-
['1.2.x', '>=1.2.0 <1.3.0'],
44-
['1.2.x || 2.x', '>=1.2.0 <1.3.0||>=2.0.0 <3.0.0'],
45-
['1.2.x || 2.x', '>=1.2.0 <1.3.0||>=2.0.0 <3.0.0'],
42+
['2.x.x', '>=2.0.0 <3.0.0-0'],
43+
['1.2.x', '>=1.2.0 <1.3.0-0'],
44+
['1.2.x || 2.x', '>=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0'],
45+
['1.2.x || 2.x', '>=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0'],
4646
['x', '*'],
47-
['2.*.*', '>=2.0.0 <3.0.0'],
48-
['1.2.*', '>=1.2.0 <1.3.0'],
49-
['1.2.* || 2.*', '>=1.2.0 <1.3.0||>=2.0.0 <3.0.0'],
47+
['2.*.*', '>=2.0.0 <3.0.0-0'],
48+
['1.2.*', '>=1.2.0 <1.3.0-0'],
49+
['1.2.* || 2.*', '>=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0'],
5050
['*', '*'],
51-
['2', '>=2.0.0 <3.0.0'],
52-
['2.3', '>=2.3.0 <2.4.0'],
53-
['~2.4', '>=2.4.0 <2.5.0'],
54-
['~2.4', '>=2.4.0 <2.5.0'],
55-
['~>3.2.1', '>=3.2.1 <3.3.0'],
56-
['~1', '>=1.0.0 <2.0.0'],
57-
['~>1', '>=1.0.0 <2.0.0'],
58-
['~> 1', '>=1.0.0 <2.0.0'],
59-
['~1.0', '>=1.0.0 <1.1.0'],
60-
['~ 1.0', '>=1.0.0 <1.1.0'],
61-
['^0', '<1.0.0'],
62-
['^ 1', '>=1.0.0 <2.0.0'],
63-
['^0.1', '>=0.1.0 <0.2.0'],
64-
['^1.0', '>=1.0.0 <2.0.0'],
65-
['^1.2', '>=1.2.0 <2.0.0'],
66-
['^0.0.1', '>=0.0.1 <0.0.2'],
67-
['^0.0.1-beta', '>=0.0.1-beta <0.0.2'],
68-
['^0.1.2', '>=0.1.2 <0.2.0'],
69-
['^1.2.3', '>=1.2.3 <2.0.0'],
70-
['^1.2.3-beta.4', '>=1.2.3-beta.4 <2.0.0'],
51+
['2', '>=2.0.0 <3.0.0-0'],
52+
['2.3', '>=2.3.0 <2.4.0-0'],
53+
['~2.4', '>=2.4.0 <2.5.0-0'],
54+
['~2.4', '>=2.4.0 <2.5.0-0'],
55+
['~>3.2.1', '>=3.2.1 <3.3.0-0'],
56+
['~1', '>=1.0.0 <2.0.0-0'],
57+
['~>1', '>=1.0.0 <2.0.0-0'],
58+
['~> 1', '>=1.0.0 <2.0.0-0'],
59+
['~1.0', '>=1.0.0 <1.1.0-0'],
60+
['~ 1.0', '>=1.0.0 <1.1.0-0'],
61+
['^0', '<1.0.0-0'],
62+
['^ 1', '>=1.0.0 <2.0.0-0'],
63+
['^0.1', '>=0.1.0 <0.2.0-0'],
64+
['^1.0', '>=1.0.0 <2.0.0-0'],
65+
['^1.2', '>=1.2.0 <2.0.0-0'],
66+
['^0.0.1', '>=0.0.1 <0.0.2-0'],
67+
['^0.0.1-beta', '>=0.0.1-beta <0.0.2-0'],
68+
['^0.1.2', '>=0.1.2 <0.2.0-0'],
69+
['^1.2.3', '>=1.2.3 <2.0.0-0'],
70+
['^1.2.3-beta.4', '>=1.2.3-beta.4 <2.0.0-0'],
7171
['<1', '<1.0.0'],
7272
['< 1', '<1.0.0'],
7373
['>=1', '>=1.0.0'],
7474
['>= 1', '>=1.0.0'],
7575
['<1.2', '<1.2.0'],
7676
['< 1.2', '<1.2.0'],
77-
['1', '>=1.0.0 <2.0.0'],
77+
['1', '>=1.0.0 <2.0.0-0'],
7878
['>01.02.03', '>1.2.3', true],
7979
['>01.02.03', null],
80-
['~1.2.3beta', '>=1.2.3-beta <1.3.0', { loose: true }],
80+
['~1.2.3beta', '>=1.2.3-beta <1.3.0-0', { loose: true }],
8181
['~1.2.3beta', null],
82-
['^ 1.2 ^ 1', '>=1.2.0 <2.0.0 >=1.0.0 <2.0.0'],
82+
['^ 1.2 ^ 1', '>=1.2.0 <2.0.0-0 >=1.0.0 <2.0.0-0'],
8383
['1.2 - 3.4.5', '>=1.2.0 <=3.4.5'],
84-
['1.2.3 - 3.4', '>=1.2.3 <3.5.0'],
85-
['1.2 - 3.4', '>=1.2.0 <3.5.0'],
84+
['1.2.3 - 3.4', '>=1.2.3 <3.5.0-0'],
85+
['1.2 - 3.4', '>=1.2.0 <3.5.0-0'],
8686
['>1', '>=2.0.0'],
8787
['>1.2', '>=1.3.0'],
8888
['>X', '<0.0.0-0'],

0 commit comments

Comments
 (0)