semver 7.2.3 → 7.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # changes log
2
2
 
3
+ ## 7.3.0
4
+
5
+ * Add `subset(r1, r2)` method to determine if `r1` range is entirely
6
+ contained by `r2` range.
7
+
8
+ ## 7.2.3
9
+
10
+ * Fix handling of `includePrelease` mode where version ranges like `1.0.0 -
11
+ 2.0.0` would include `3.0.0-pre` and not `1.0.0-pre`.
12
+
3
13
  ## 7.2.2
4
14
 
5
15
  * Fix bug where `2.0.0-pre` would be included in `^1.0.0` if
package/README.md CHANGED
@@ -79,6 +79,7 @@ const semverGtr = require('semver/ranges/gtr')
79
79
  const semverLtr = require('semver/ranges/ltr')
80
80
  const semverIntersects = require('semver/ranges/intersects')
81
81
  const simplifyRange = require('semver/ranges/simplify')
82
+ const rangeSubset = require('semver/ranges/subset')
82
83
  ```
83
84
 
84
85
  As a command-line utility:
@@ -255,8 +256,8 @@ inclusive range, then all versions that start with the supplied parts
255
256
  of the tuple are accepted, but nothing that would be greater than the
256
257
  provided tuple parts.
257
258
 
258
- * `1.2.3 - 2.3` := `>=1.2.3 <2.4.0`
259
- * `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`
260
261
 
261
262
  #### X-Ranges `1.2.x` `1.X` `1.2.*` `*`
262
263
 
@@ -264,28 +265,28 @@ Any of `X`, `x`, or `*` may be used to "stand in" for one of the
264
265
  numeric values in the `[major, minor, patch]` tuple.
265
266
 
266
267
  * `*` := `>=0.0.0` (Any version satisfies)
267
- * `1.x` := `>=1.0.0 <2.0.0` (Matching major version)
268
- * `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)
269
270
 
270
271
  A partial version range is treated as an X-Range, so the special
271
272
  character is in fact optional.
272
273
 
273
274
  * `""` (empty string) := `*` := `>=0.0.0`
274
- * `1` := `1.x.x` := `>=1.0.0 <2.0.0`
275
- * `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`
276
277
 
277
278
  #### Tilde Ranges `~1.2.3` `~1.2` `~1`
278
279
 
279
280
  Allows patch-level changes if a minor version is specified on the
280
281
  comparator. Allows minor-level changes if not.
281
282
 
282
- * `~1.2.3` := `>=1.2.3 <1.(2+1).0` := `>=1.2.3 <1.3.0`
283
- * `~1.2` := `>=1.2.0 <1.(2+1).0` := `>=1.2.0 <1.3.0` (Same as `1.2.x`)
284
- * `~1` := `>=1.0.0 <(1+1).0.0` := `>=1.0.0 <2.0.0` (Same as `1.x`)
285
- * `~0.2.3` := `>=0.2.3 <0.(2+1).0` := `>=0.2.3 <0.3.0`
286
- * `~0.2` := `>=0.2.0 <0.(2+1).0` := `>=0.2.0 <0.3.0` (Same as `0.2.x`)
287
- * `~0` := `>=0.0.0 <(0+1).0.0` := `>=0.0.0 <1.0.0` (Same as `0.x`)
288
- * `~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
289
290
  the `1.2.3` version will be allowed, if they are greater than or
290
291
  equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but
291
292
  `1.2.4-beta.2` would not, because it is a prerelease of a
@@ -307,15 +308,15 @@ However, it presumes that there will *not* be breaking changes between
307
308
  `0.2.4` and `0.2.5`. It allows for changes that are presumed to be
308
309
  additive (but non-breaking), according to commonly observed practices.
309
310
 
310
- * `^1.2.3` := `>=1.2.3 <2.0.0`
311
- * `^0.2.3` := `>=0.2.3 <0.3.0`
312
- * `^0.0.3` := `>=0.0.3 <0.0.4`
313
- * `^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
314
315
  the `1.2.3` version will be allowed, if they are greater than or
315
316
  equal to `beta.2`. So, `1.2.3-beta.4` would be allowed, but
316
317
  `1.2.4-beta.2` would not, because it is a prerelease of a
317
318
  different `[major, minor, patch]` tuple.
318
- * `^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
319
320
  `0.0.3` version *only* will be allowed, if they are greater than or
320
321
  equal to `beta`. So, `0.0.3-pr.2` would be allowed.
321
322
 
@@ -323,16 +324,16 @@ When parsing caret ranges, a missing `patch` value desugars to the
323
324
  number `0`, but will allow flexibility within that value, even if the
324
325
  major and minor versions are both `0`.
325
326
 
326
- * `^1.2.x` := `>=1.2.0 <2.0.0`
327
- * `^0.0.x` := `>=0.0.0 <0.1.0`
328
- * `^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`
329
330
 
330
331
  A missing `minor` and `patch` values will desugar to zero, but also
331
332
  allow flexibility within those values, even if the major version is
332
333
  zero.
333
334
 
334
- * `^1.x` := `>=1.0.0 <2.0.0`
335
- * `^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`
336
337
 
337
338
  ### Range Grammar
338
339
 
@@ -455,6 +456,8 @@ strings that they parse.
455
456
  programmatically, to provide the user with something a bit more
456
457
  ergonomic. If the provided range is shorter in string-length than the
457
458
  generated range, then that is returned.
459
+ * `subset(subRange, superRange)`: Return `true` if the `subRange` range is
460
+ entirely contained by the `superRange` range.
458
461
 
459
462
  Note that, since ranges may be non-contiguous, a version might not be
460
463
  greater than a range, less than a range, *or* satisfy a range! For
@@ -5,12 +5,7 @@ class Comparator {
5
5
  return ANY
6
6
  }
7
7
  constructor (comp, options) {
8
- if (!options || typeof options !== 'object') {
9
- options = {
10
- loose: !!options,
11
- includePrerelease: false
12
- }
13
- }
8
+ options = parseOptions(options)
14
9
 
15
10
  if (comp instanceof Comparator) {
16
11
  if (comp.loose === !!options.loose) {
@@ -132,6 +127,7 @@ class Comparator {
132
127
 
133
128
  module.exports = Comparator
134
129
 
130
+ const parseOptions = require('../internal/parse-options')
135
131
  const {re, t} = require('../internal/re')
136
132
  const cmp = require('../functions/cmp')
137
133
  const debug = require('../internal/debug')
package/classes/range.js CHANGED
@@ -1,12 +1,7 @@
1
1
  // hoisted class for cyclic dependency
2
2
  class Range {
3
3
  constructor (range, options) {
4
- if (!options || typeof options !== 'object') {
5
- options = {
6
- loose: !!options,
7
- includePrerelease: false
8
- }
9
- }
4
+ options = parseOptions(options)
10
5
 
11
6
  if (range instanceof Range) {
12
7
  if (
@@ -46,6 +41,24 @@ class Range {
46
41
  throw new TypeError(`Invalid SemVer Range: ${range}`)
47
42
  }
48
43
 
44
+ // if we have any that are not the null set, throw out null sets.
45
+ if (this.set.length > 1) {
46
+ // keep the first one, in case they're all null sets
47
+ const first = this.set[0]
48
+ this.set = this.set.filter(c => !isNullSet(c[0]))
49
+ if (this.set.length === 0)
50
+ this.set = [first]
51
+ else if (this.set.length > 1) {
52
+ // if we have any that are *, then the range is just *
53
+ for (const c of this.set) {
54
+ if (c.length === 1 && isAny(c[0])) {
55
+ this.set = [c]
56
+ break
57
+ }
58
+ }
59
+ }
60
+ }
61
+
49
62
  this.format()
50
63
  }
51
64
 
@@ -64,8 +77,17 @@ class Range {
64
77
  }
65
78
 
66
79
  parseRange (range) {
67
- const loose = this.options.loose
68
80
  range = range.trim()
81
+
82
+ // memoize range parsing for performance.
83
+ // this is a very hot path, and fully deterministic.
84
+ const memoOpts = Object.keys(this.options).join(',')
85
+ const memoKey = `parseRange:${memoOpts}:${range}`
86
+ const cached = cache.get(memoKey)
87
+ if (cached)
88
+ return cached
89
+
90
+ const loose = this.options.loose
69
91
  // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
70
92
  const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]
71
93
  range = range.replace(hr, hyphenReplace(this.options.includePrerelease))
@@ -87,14 +109,33 @@ class Range {
87
109
  // ready to be split into comparators.
88
110
 
89
111
  const compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
90
- return range
112
+ const rangeList = range
91
113
  .split(' ')
92
114
  .map(comp => parseComparator(comp, this.options))
93
115
  .join(' ')
94
116
  .split(/\s+/)
117
+ // >=0.0.0 is equivalent to *
118
+ .map(comp => replaceGTE0(comp, this.options))
95
119
  // in loose mode, throw out any that are not valid comparators
96
120
  .filter(this.options.loose ? comp => !!comp.match(compRe) : () => true)
97
121
  .map(comp => new Comparator(comp, this.options))
122
+
123
+ // if any comparators are the null set, then replace with JUST null set
124
+ // if more than one comparator, remove any * comparators
125
+ // also, don't include the same comparator more than once
126
+ const l = rangeList.length
127
+ const rangeMap = new Map()
128
+ for (const comp of rangeList) {
129
+ if (isNullSet(comp))
130
+ return [comp]
131
+ rangeMap.set(comp.value, comp)
132
+ }
133
+ if (rangeMap.size > 1 && rangeMap.has(''))
134
+ rangeMap.delete('')
135
+
136
+ const result = [...rangeMap.values()]
137
+ cache.set(memoKey, result)
138
+ return result
98
139
  }
99
140
 
100
141
  intersects (range, options) {
@@ -143,6 +184,10 @@ class Range {
143
184
  }
144
185
  module.exports = Range
145
186
 
187
+ const LRU = require('lru-cache')
188
+ const cache = new LRU({ max: 1000 })
189
+
190
+ const parseOptions = require('../internal/parse-options')
146
191
  const Comparator = require('./comparator')
147
192
  const debug = require('../internal/debug')
148
193
  const SemVer = require('./semver')
@@ -154,6 +199,9 @@ const {
154
199
  caretTrimReplace
155
200
  } = require('../internal/re')
156
201
 
202
+ const isNullSet = c => c.value === '<0.0.0-0'
203
+ const isAny = c => c.value === ''
204
+
157
205
  // take a set of comparators and determine whether there
158
206
  // exists a version which can satisfy it
159
207
  const isSatisfiable = (comparators, options) => {
@@ -191,11 +239,11 @@ const parseComparator = (comp, options) => {
191
239
  const isX = id => !id || id.toLowerCase() === 'x' || id === '*'
192
240
 
193
241
  // ~, ~> --> * (any, kinda silly)
194
- // ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0
195
- // ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0
196
- // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0
197
- // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0
198
- // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0
242
+ // ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0
243
+ // ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0
244
+ // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0
245
+ // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0
246
+ // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0
199
247
  const replaceTildes = (comp, options) =>
200
248
  comp.trim().split(/\s+/).map((comp) => {
201
249
  return replaceTilde(comp, options)
@@ -210,18 +258,18 @@ const replaceTilde = (comp, options) => {
210
258
  if (isX(M)) {
211
259
  ret = ''
212
260
  } else if (isX(m)) {
213
- ret = `>=${M}.0.0 <${+M + 1}.0.0`
261
+ ret = `>=${M}.0.0 <${+M + 1}.0.0-0`
214
262
  } else if (isX(p)) {
215
- // ~1.2 == >=1.2.0 <1.3.0
216
- ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0`
263
+ // ~1.2 == >=1.2.0 <1.3.0-0
264
+ ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`
217
265
  } else if (pr) {
218
266
  debug('replaceTilde pr', pr)
219
267
  ret = `>=${M}.${m}.${p}-${pr
220
- } <${M}.${+m + 1}.0`
268
+ } <${M}.${+m + 1}.0-0`
221
269
  } else {
222
- // ~1.2.3 == >=1.2.3 <1.3.0
270
+ // ~1.2.3 == >=1.2.3 <1.3.0-0
223
271
  ret = `>=${M}.${m}.${p
224
- } <${M}.${+m + 1}.0`
272
+ } <${M}.${+m + 1}.0-0`
225
273
  }
226
274
 
227
275
  debug('tilde return', ret)
@@ -230,11 +278,11 @@ const replaceTilde = (comp, options) => {
230
278
  }
231
279
 
232
280
  // ^ --> * (any, kinda silly)
233
- // ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0
234
- // ^2.0, ^2.0.x --> >=2.0.0 <3.0.0
235
- // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0
236
- // ^1.2.3 --> >=1.2.3 <2.0.0
237
- // ^1.2.0 --> >=1.2.0 <2.0.0
281
+ // ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0-0
282
+ // ^2.0, ^2.0.x --> >=2.0.0 <3.0.0-0
283
+ // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0
284
+ // ^1.2.3 --> >=1.2.3 <2.0.0-0
285
+ // ^1.2.0 --> >=1.2.0 <2.0.0-0
238
286
  const replaceCarets = (comp, options) =>
239
287
  comp.trim().split(/\s+/).map((comp) => {
240
288
  return replaceCaret(comp, options)
@@ -251,40 +299,40 @@ const replaceCaret = (comp, options) => {
251
299
  if (isX(M)) {
252
300
  ret = ''
253
301
  } else if (isX(m)) {
254
- ret = `>=${M}.0.0${z} <${+M + 1}.0.0${z}`
302
+ ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`
255
303
  } else if (isX(p)) {
256
304
  if (M === '0') {
257
- ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0${z}`
305
+ ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`
258
306
  } else {
259
- ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0${z}`
307
+ ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`
260
308
  }
261
309
  } else if (pr) {
262
310
  debug('replaceCaret pr', pr)
263
311
  if (M === '0') {
264
312
  if (m === '0') {
265
313
  ret = `>=${M}.${m}.${p}-${pr
266
- } <${M}.${m}.${+p + 1}${z}`
314
+ } <${M}.${m}.${+p + 1}-0`
267
315
  } else {
268
316
  ret = `>=${M}.${m}.${p}-${pr
269
- } <${M}.${+m + 1}.0${z}`
317
+ } <${M}.${+m + 1}.0-0`
270
318
  }
271
319
  } else {
272
320
  ret = `>=${M}.${m}.${p}-${pr
273
- } <${+M + 1}.0.0${z}`
321
+ } <${+M + 1}.0.0-0`
274
322
  }
275
323
  } else {
276
324
  debug('no pr')
277
325
  if (M === '0') {
278
326
  if (m === '0') {
279
327
  ret = `>=${M}.${m}.${p
280
- }${z} <${M}.${m}.${+p + 1}${z}`
328
+ }${z} <${M}.${m}.${+p + 1}-0`
281
329
  } else {
282
330
  ret = `>=${M}.${m}.${p
283
- }${z} <${M}.${+m + 1}.0${z}`
331
+ }${z} <${M}.${+m + 1}.0-0`
284
332
  }
285
333
  } else {
286
334
  ret = `>=${M}.${m}.${p
287
- } <${+M + 1}.0.0${z}`
335
+ } <${+M + 1}.0.0-0`
288
336
  }
289
337
  }
290
338
 
@@ -357,12 +405,15 @@ const replaceXRange = (comp, options) => {
357
405
  }
358
406
  }
359
407
 
408
+ if (gtlt === '<')
409
+ pr = '-0'
410
+
360
411
  ret = `${gtlt + M}.${m}.${p}${pr}`
361
412
  } else if (xm) {
362
- ret = `>=${M}.0.0${pr} <${+M + 1}.0.0${pr}`
413
+ ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`
363
414
  } else if (xp) {
364
415
  ret = `>=${M}.${m}.0${pr
365
- } <${M}.${+m + 1}.0${pr}`
416
+ } <${M}.${+m + 1}.0-0`
366
417
  }
367
418
 
368
419
  debug('xRange return', ret)
@@ -379,11 +430,17 @@ const replaceStars = (comp, options) => {
379
430
  return comp.trim().replace(re[t.STAR], '')
380
431
  }
381
432
 
433
+ const replaceGTE0 = (comp, options) => {
434
+ debug('replaceGTE0', comp, options)
435
+ return comp.trim()
436
+ .replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '')
437
+ }
438
+
382
439
  // This function is passed to string.replace(re[t.HYPHENRANGE])
383
440
  // M, m, patch, prerelease, build
384
441
  // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
385
- // 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
386
- // 1.2 - 3.4 => >=1.2.0 <3.5.0
442
+ // 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do
443
+ // 1.2 - 3.4 => >=1.2.0 <3.5.0-0
387
444
  const hyphenReplace = incPr => ($0,
388
445
  from, fM, fm, fp, fpr, fb,
389
446
  to, tM, tm, tp, tpr, tb) => {
@@ -402,9 +459,9 @@ const hyphenReplace = incPr => ($0,
402
459
  if (isX(tM)) {
403
460
  to = ''
404
461
  } else if (isX(tm)) {
405
- to = `<${+tM + 1}.0.0${incPr ? '-0' : ''}`
462
+ to = `<${+tM + 1}.0.0-0`
406
463
  } else if (isX(tp)) {
407
- to = `<${tM}.${+tm + 1}.0${incPr ? '-0' : ''}`
464
+ to = `<${tM}.${+tm + 1}.0-0`
408
465
  } else if (tpr) {
409
466
  to = `<=${tM}.${tm}.${tp}-${tpr}`
410
467
  } else if (incPr) {
package/classes/semver.js CHANGED
@@ -2,15 +2,12 @@ const debug = require('../internal/debug')
2
2
  const { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants')
3
3
  const { re, t } = require('../internal/re')
4
4
 
5
+ const parseOptions = require('../internal/parse-options')
5
6
  const { compareIdentifiers } = require('../internal/identifiers')
6
7
  class SemVer {
7
8
  constructor (version, options) {
8
- if (!options || typeof options !== 'object') {
9
- options = {
10
- loose: !!options,
11
- includePrerelease: false
12
- }
13
- }
9
+ options = parseOptions(options)
10
+
14
11
  if (version instanceof SemVer) {
15
12
  if (version.loose === !!options.loose &&
16
13
  version.includePrerelease === !!options.includePrerelease) {
@@ -2,13 +2,9 @@ const {MAX_LENGTH} = require('../internal/constants')
2
2
  const { re, t } = require('../internal/re')
3
3
  const SemVer = require('../classes/semver')
4
4
 
5
+ const parseOptions = require('../internal/parse-options')
5
6
  const parse = (version, options) => {
6
- if (!options || typeof options !== 'object') {
7
- options = {
8
- loose: !!options,
9
- includePrerelease: false
10
- }
11
- }
7
+ options = parseOptions(options)
12
8
 
13
9
  if (version instanceof SemVer) {
14
10
  return version
package/index.js CHANGED
@@ -44,4 +44,5 @@ module.exports = {
44
44
  ltr: require('./ranges/ltr'),
45
45
  intersects: require('./ranges/intersects'),
46
46
  simplifyRange: require('./ranges/simplify'),
47
+ subset: require('./ranges/subset'),
47
48
  }
@@ -0,0 +1,11 @@
1
+ // parse out just the options we care about so we always get a consistent
2
+ // obj with keys in a consistent order.
3
+ const opts = ['includePrerelease', 'loose', 'rtl']
4
+ const parseOptions = options =>
5
+ !options ? {}
6
+ : typeof options !== 'object' ? { loose: true }
7
+ : opts.filter(k => options[k]).reduce((options, k) => {
8
+ options[k] = true
9
+ return options
10
+ }, {})
11
+ module.exports = parseOptions
package/internal/re.js CHANGED
@@ -177,3 +177,6 @@ createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` +
177
177
 
178
178
  // Star ranges basically just allow anything at all.
179
179
  createToken('STAR', '(<|>)?=?\\s*\\*')
180
+ // >=0.0.0 is like a star
181
+ createToken('GTE0', '^\\s*>=\\s*0\.0\.0\\s*$')
182
+ createToken('GTE0PRE', '^\\s*>=\\s*0\.0\.0-0\\s*$')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semver",
3
- "version": "7.2.3",
3
+ "version": "7.3.3",
4
4
  "description": "The semantic version parser used by npm.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -16,7 +16,7 @@
16
16
  "license": "ISC",
17
17
  "repository": "https://github.com/npm/node-semver",
18
18
  "bin": {
19
- "semver": "./bin/semver.js"
19
+ "semver": "bin/semver.js"
20
20
  },
21
21
  "files": [
22
22
  "bin/**/*.js",
@@ -34,5 +34,8 @@
34
34
  },
35
35
  "engines": {
36
36
  "node": ">=10"
37
+ },
38
+ "dependencies": {
39
+ "lru-cache": "^4.1.5"
37
40
  }
38
41
  }
@@ -19,6 +19,7 @@ const minVersion = (range, loose) => {
19
19
  for (let i = 0; i < range.set.length; ++i) {
20
20
  const comparators = range.set[i]
21
21
 
22
+ let setMin = null
22
23
  comparators.forEach((comparator) => {
23
24
  // Clone to avoid manipulating the comparator's semver object.
24
25
  const compver = new SemVer(comparator.semver.version)
@@ -33,8 +34,8 @@ const minVersion = (range, loose) => {
33
34
  /* fallthrough */
34
35
  case '':
35
36
  case '>=':
36
- if (!minver || gt(minver, compver)) {
37
- minver = compver
37
+ if (!setMin || gt(compver, setMin)) {
38
+ setMin = compver
38
39
  }
39
40
  break
40
41
  case '<':
@@ -46,6 +47,8 @@ const minVersion = (range, loose) => {
46
47
  throw new Error(`Unexpected operation: ${comparator.operator}`)
47
48
  }
48
49
  })
50
+ if (setMin && (!minver || gt(minver, setMin)))
51
+ minver = setMin
49
52
  }
50
53
 
51
54
  if (minver && range.test(minver)) {
package/ranges/outside.js CHANGED
@@ -32,7 +32,7 @@ const outside = (version, range, hilo, options) => {
32
32
  throw new TypeError('Must provide a hilo val of "<" or ">"')
33
33
  }
34
34
 
35
- // If it satisifes the range it is not outside
35
+ // If it satisfies the range it is not outside
36
36
  if (satisfies(version, range, options)) {
37
37
  return false
38
38
  }
@@ -0,0 +1,162 @@
1
+ const Range = require('../classes/range.js')
2
+ const { ANY } = require('../classes/comparator.js')
3
+ const satisfies = require('../functions/satisfies.js')
4
+ const compare = require('../functions/compare.js')
5
+
6
+ // Complex range `r1 || r2 || ...` is a subset of `R1 || R2 || ...` iff:
7
+ // - Every simple range `r1, r2, ...` is a subset of some `R1, R2, ...`
8
+ //
9
+ // Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff:
10
+ // - If c is only the ANY comparator
11
+ // - If C is only the ANY comparator, return true
12
+ // - Else return false
13
+ // - Let EQ be the set of = comparators in c
14
+ // - If EQ is more than one, return true (null set)
15
+ // - Let GT be the highest > or >= comparator in c
16
+ // - Let LT be the lowest < or <= comparator in c
17
+ // - If GT and LT, and GT.semver > LT.semver, return true (null set)
18
+ // - If EQ
19
+ // - If GT, and EQ does not satisfy GT, return true (null set)
20
+ // - If LT, and EQ does not satisfy LT, return true (null set)
21
+ // - If EQ satisfies every C, return true
22
+ // - Else return false
23
+ // - If GT
24
+ // - If GT.semver is lower than any > or >= comp in C, return false
25
+ // - If GT is >=, and GT.semver does not satisfy every C, return false
26
+ // - If LT
27
+ // - If LT.semver is greater than any < or <= comp in C, return false
28
+ // - If LT is <=, and LT.semver does not satisfy every C, return false
29
+ // - If any C is a = range, and GT or LT are set, return false
30
+ // - Else return true
31
+
32
+ const subset = (sub, dom, options) => {
33
+ if (sub === dom)
34
+ return true
35
+
36
+ sub = new Range(sub, options)
37
+ dom = new Range(dom, options)
38
+ let sawNonNull = false
39
+
40
+ OUTER: for (const simpleSub of sub.set) {
41
+ for (const simpleDom of dom.set) {
42
+ const isSub = simpleSubset(simpleSub, simpleDom, options)
43
+ sawNonNull = sawNonNull || isSub !== null
44
+ if (isSub)
45
+ continue OUTER
46
+ }
47
+ // the null set is a subset of everything, but null simple ranges in
48
+ // a complex range should be ignored. so if we saw a non-null range,
49
+ // then we know this isn't a subset, but if EVERY simple range was null,
50
+ // then it is a subset.
51
+ if (sawNonNull)
52
+ return false
53
+ }
54
+ return true
55
+ }
56
+
57
+ const simpleSubset = (sub, dom, options) => {
58
+ if (sub === dom)
59
+ return true
60
+
61
+ if (sub.length === 1 && sub[0].semver === ANY)
62
+ return dom.length === 1 && dom[0].semver === ANY
63
+
64
+ const eqSet = new Set()
65
+ let gt, lt
66
+ for (const c of sub) {
67
+ if (c.operator === '>' || c.operator === '>=')
68
+ gt = higherGT(gt, c, options)
69
+ else if (c.operator === '<' || c.operator === '<=')
70
+ lt = lowerLT(lt, c, options)
71
+ else
72
+ eqSet.add(c.semver)
73
+ }
74
+
75
+ if (eqSet.size > 1)
76
+ return null
77
+
78
+ let gtltComp
79
+ if (gt && lt) {
80
+ gtltComp = compare(gt.semver, lt.semver, options)
81
+ if (gtltComp > 0)
82
+ return null
83
+ else if (gtltComp === 0 && (gt.operator !== '>=' || lt.operator !== '<='))
84
+ return null
85
+ }
86
+
87
+ // will iterate one or zero times
88
+ for (const eq of eqSet) {
89
+ if (gt && !satisfies(eq, String(gt), options))
90
+ return null
91
+
92
+ if (lt && !satisfies(eq, String(lt), options))
93
+ return null
94
+
95
+ for (const c of dom) {
96
+ if (!satisfies(eq, String(c), options))
97
+ return false
98
+ }
99
+
100
+ return true
101
+ }
102
+
103
+ let higher, lower
104
+ let hasDomLT, hasDomGT
105
+ for (const c of dom) {
106
+ hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>='
107
+ hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<='
108
+ if (gt) {
109
+ if (c.operator === '>' || c.operator === '>=') {
110
+ higher = higherGT(gt, c, options)
111
+ if (higher === c && higher !== gt)
112
+ return false
113
+ } else if (gt.operator === '>=' && !satisfies(gt.semver, String(c), options))
114
+ return false
115
+ }
116
+ if (lt) {
117
+ if (c.operator === '<' || c.operator === '<=') {
118
+ lower = lowerLT(lt, c, options)
119
+ if (lower === c && lower !== lt)
120
+ return false
121
+ } else if (lt.operator === '<=' && !satisfies(lt.semver, String(c), options))
122
+ return false
123
+ }
124
+ if (!c.operator && (lt || gt) && gtltComp !== 0)
125
+ return false
126
+ }
127
+
128
+ // if there was a < or >, and nothing in the dom, then must be false
129
+ // UNLESS it was limited by another range in the other direction.
130
+ // Eg, >1.0.0 <1.0.1 is still a subset of <2.0.0
131
+ if (gt && hasDomLT && !lt && gtltComp !== 0)
132
+ return false
133
+
134
+ if (lt && hasDomGT && !gt && gtltComp !== 0)
135
+ return false
136
+
137
+ return true
138
+ }
139
+
140
+ // >=1.2.3 is lower than >1.2.3
141
+ const higherGT = (a, b, options) => {
142
+ if (!a)
143
+ return b
144
+ const comp = compare(a.semver, b.semver, options)
145
+ return comp > 0 ? a
146
+ : comp < 0 ? b
147
+ : b.operator === '>' && a.operator === '>=' ? b
148
+ : a
149
+ }
150
+
151
+ // <=1.2.3 is higher than <1.2.3
152
+ const lowerLT = (a, b, options) => {
153
+ if (!a)
154
+ return b
155
+ const comp = compare(a.semver, b.semver, options)
156
+ return comp < 0 ? a
157
+ : comp > 0 ? b
158
+ : b.operator === '<' && a.operator === '<=' ? b
159
+ : a
160
+ }
161
+
162
+ module.exports = subset