semver 7.3.1 → 7.3.5

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.
@@ -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,15 +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 *
95
118
  .map(comp => replaceGTE0(comp, this.options))
96
119
  // in loose mode, throw out any that are not valid comparators
97
120
  .filter(this.options.loose ? comp => !!comp.match(compRe) : () => true)
98
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
99
139
  }
100
140
 
101
141
  intersects (range, options) {
@@ -144,6 +184,10 @@ class Range {
144
184
  }
145
185
  module.exports = Range
146
186
 
187
+ const LRU = require('lru-cache')
188
+ const cache = new LRU({ max: 1000 })
189
+
190
+ const parseOptions = require('../internal/parse-options')
147
191
  const Comparator = require('./comparator')
148
192
  const debug = require('../internal/debug')
149
193
  const SemVer = require('./semver')
@@ -155,6 +199,9 @@ const {
155
199
  caretTrimReplace
156
200
  } = require('../internal/re')
157
201
 
202
+ const isNullSet = c => c.value === '<0.0.0-0'
203
+ const isAny = c => c.value === ''
204
+
158
205
  // take a set of comparators and determine whether there
159
206
  // exists a version which can satisfy it
160
207
  const isSatisfiable = (comparators, options) => {
@@ -358,6 +405,9 @@ const replaceXRange = (comp, options) => {
358
405
  }
359
406
  }
360
407
 
408
+ if (gtlt === '<')
409
+ pr = '-0'
410
+
361
411
  ret = `${gtlt + M}.${m}.${p}${pr}`
362
412
  } else if (xm) {
363
413
  ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`
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
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semver",
3
- "version": "7.3.1",
3
+ "version": "7.3.5",
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": "^6.0.0"
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
  }
package/ranges/subset.js CHANGED
@@ -1,35 +1,49 @@
1
1
  const Range = require('../classes/range.js')
2
- const { ANY } = require('../classes/comparator.js')
2
+ const Comparator = require('../classes/comparator.js')
3
+ const { ANY } = Comparator
3
4
  const satisfies = require('../functions/satisfies.js')
4
5
  const compare = require('../functions/compare.js')
5
6
 
6
7
  // 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
+ // - Every simple range `r1, r2, ...` is a null set, OR
9
+ // - Every simple range `r1, r2, ...` which is not a null set is a subset of
10
+ // some `R1, R2, ...`
8
11
  //
9
12
  // Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff:
10
13
  // - If c is only the ANY comparator
11
14
  // - If C is only the ANY comparator, return true
12
- // - Else return false
15
+ // - Else if in prerelease mode, return false
16
+ // - else replace c with `[>=0.0.0]`
17
+ // - If C is only the ANY comparator
18
+ // - if in prerelease mode, return true
19
+ // - else replace C with `[>=0.0.0]`
13
20
  // - Let EQ be the set of = comparators in c
14
21
  // - If EQ is more than one, return true (null set)
15
22
  // - Let GT be the highest > or >= comparator in c
16
23
  // - Let LT be the lowest < or <= comparator in c
17
24
  // - If GT and LT, and GT.semver > LT.semver, return true (null set)
25
+ // - If any C is a = range, and GT or LT are set, return false
18
26
  // - If EQ
19
27
  // - If GT, and EQ does not satisfy GT, return true (null set)
20
28
  // - If LT, and EQ does not satisfy LT, return true (null set)
21
29
  // - If EQ satisfies every C, return true
22
30
  // - Else return false
23
31
  // - If GT
24
- // - If GT is lower than any > or >= comp in C, return false
32
+ // - If GT.semver is lower than any > or >= comp in C, return false
25
33
  // - If GT is >=, and GT.semver does not satisfy every C, return false
34
+ // - If GT.semver has a prerelease, and not in prerelease mode
35
+ // - If no C has a prerelease and the GT.semver tuple, return false
26
36
  // - If LT
27
- // - If LT.semver is greater than that of any > comp in C, return false
37
+ // - If LT.semver is greater than any < or <= comp in C, return false
28
38
  // - 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
39
+ // - If GT.semver has a prerelease, and not in prerelease mode
40
+ // - If no C has a prerelease and the LT.semver tuple, return false
30
41
  // - Else return true
31
42
 
32
- const subset = (sub, dom, options) => {
43
+ const subset = (sub, dom, options = {}) => {
44
+ if (sub === dom)
45
+ return true
46
+
33
47
  sub = new Range(sub, options)
34
48
  dom = new Range(dom, options)
35
49
  let sawNonNull = false
@@ -52,8 +66,24 @@ const subset = (sub, dom, options) => {
52
66
  }
53
67
 
54
68
  const simpleSubset = (sub, dom, options) => {
55
- if (sub.length === 1 && sub[0].semver === ANY)
56
- return dom.length === 1 && dom[0].semver === ANY
69
+ if (sub === dom)
70
+ return true
71
+
72
+ if (sub.length === 1 && sub[0].semver === ANY) {
73
+ if (dom.length === 1 && dom[0].semver === ANY)
74
+ return true
75
+ else if (options.includePrerelease)
76
+ sub = [ new Comparator('>=0.0.0-0') ]
77
+ else
78
+ sub = [ new Comparator('>=0.0.0') ]
79
+ }
80
+
81
+ if (dom.length === 1 && dom[0].semver === ANY) {
82
+ if (options.includePrerelease)
83
+ return true
84
+ else
85
+ dom = [ new Comparator('>=0.0.0') ]
86
+ }
57
87
 
58
88
  const eqSet = new Set()
59
89
  let gt, lt
@@ -90,26 +120,57 @@ const simpleSubset = (sub, dom, options) => {
90
120
  if (!satisfies(eq, String(c), options))
91
121
  return false
92
122
  }
123
+
93
124
  return true
94
125
  }
95
126
 
96
127
  let higher, lower
97
128
  let hasDomLT, hasDomGT
129
+ // if the subset has a prerelease, we need a comparator in the superset
130
+ // with the same tuple and a prerelease, or it's not a subset
131
+ let needDomLTPre = lt &&
132
+ !options.includePrerelease &&
133
+ lt.semver.prerelease.length ? lt.semver : false
134
+ let needDomGTPre = gt &&
135
+ !options.includePrerelease &&
136
+ gt.semver.prerelease.length ? gt.semver : false
137
+ // exception: <1.2.3-0 is the same as <1.2.3
138
+ if (needDomLTPre && needDomLTPre.prerelease.length === 1 &&
139
+ lt.operator === '<' && needDomLTPre.prerelease[0] === 0) {
140
+ needDomLTPre = false
141
+ }
142
+
98
143
  for (const c of dom) {
99
144
  hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>='
100
145
  hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<='
101
146
  if (gt) {
147
+ if (needDomGTPre) {
148
+ if (c.semver.prerelease && c.semver.prerelease.length &&
149
+ c.semver.major === needDomGTPre.major &&
150
+ c.semver.minor === needDomGTPre.minor &&
151
+ c.semver.patch === needDomGTPre.patch) {
152
+ needDomGTPre = false
153
+ }
154
+ }
102
155
  if (c.operator === '>' || c.operator === '>=') {
103
156
  higher = higherGT(gt, c, options)
104
- if (higher === c)
157
+ if (higher === c && higher !== gt)
105
158
  return false
106
159
  } else if (gt.operator === '>=' && !satisfies(gt.semver, String(c), options))
107
160
  return false
108
161
  }
109
162
  if (lt) {
163
+ if (needDomLTPre) {
164
+ if (c.semver.prerelease && c.semver.prerelease.length &&
165
+ c.semver.major === needDomLTPre.major &&
166
+ c.semver.minor === needDomLTPre.minor &&
167
+ c.semver.patch === needDomLTPre.patch) {
168
+ needDomLTPre = false
169
+ }
170
+ }
110
171
  if (c.operator === '<' || c.operator === '<=') {
111
172
  lower = lowerLT(lt, c, options)
112
- if (lower === c)
173
+ if (lower === c && lower !== lt)
113
174
  return false
114
175
  } else if (lt.operator === '<=' && !satisfies(lt.semver, String(c), options))
115
176
  return false
@@ -127,6 +188,12 @@ const simpleSubset = (sub, dom, options) => {
127
188
  if (lt && hasDomGT && !gt && gtltComp !== 0)
128
189
  return false
129
190
 
191
+ // we needed a prerelease range in a specific tuple, but didn't get one
192
+ // then this isn't a subset. eg >=1.2.3-pre is not a subset of >=1.0.0,
193
+ // because it includes prereleases in the 1.2.3 tuple
194
+ if (needDomGTPre || needDomLTPre)
195
+ return false
196
+
130
197
  return true
131
198
  }
132
199