Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / semver / classes / range.js
1 // hoisted class for cyclic dependency
2 class Range {
3   constructor (range, options) {
4     options = parseOptions(options)
5
6     if (range instanceof Range) {
7       if (
8         range.loose === !!options.loose &&
9         range.includePrerelease === !!options.includePrerelease
10       ) {
11         return range
12       } else {
13         return new Range(range.raw, options)
14       }
15     }
16
17     if (range instanceof Comparator) {
18       // just put it in the set and return
19       this.raw = range.value
20       this.set = [[range]]
21       this.format()
22       return this
23     }
24
25     this.options = options
26     this.loose = !!options.loose
27     this.includePrerelease = !!options.includePrerelease
28
29     // First, split based on boolean or ||
30     this.raw = range
31     this.set = range
32       .split(/\s*\|\|\s*/)
33       // map the range to a 2d array of comparators
34       .map(range => this.parseRange(range.trim()))
35       // throw out any comparator lists that are empty
36       // this generally means that it was not a valid range, which is allowed
37       // in loose mode, but will still throw if the WHOLE range is invalid.
38       .filter(c => c.length)
39
40     if (!this.set.length) {
41       throw new TypeError(`Invalid SemVer Range: ${range}`)
42     }
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
62     this.format()
63   }
64
65   format () {
66     this.range = this.set
67       .map((comps) => {
68         return comps.join(' ').trim()
69       })
70       .join('||')
71       .trim()
72     return this.range
73   }
74
75   toString () {
76     return this.range
77   }
78
79   parseRange (range) {
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
91     // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
92     const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]
93     range = range.replace(hr, hyphenReplace(this.options.includePrerelease))
94     debug('hyphen replace', range)
95     // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
96     range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)
97     debug('comparator trim', range, re[t.COMPARATORTRIM])
98
99     // `~ 1.2.3` => `~1.2.3`
100     range = range.replace(re[t.TILDETRIM], tildeTrimReplace)
101
102     // `^ 1.2.3` => `^1.2.3`
103     range = range.replace(re[t.CARETTRIM], caretTrimReplace)
104
105     // normalize spaces
106     range = range.split(/\s+/).join(' ')
107
108     // At this point, the range is completely trimmed and
109     // ready to be split into comparators.
110
111     const compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]
112     const rangeList = range
113       .split(' ')
114       .map(comp => parseComparator(comp, this.options))
115       .join(' ')
116       .split(/\s+/)
117       // >=0.0.0 is equivalent to *
118       .map(comp => replaceGTE0(comp, this.options))
119       // in loose mode, throw out any that are not valid comparators
120       .filter(this.options.loose ? comp => !!comp.match(compRe) : () => true)
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
139   }
140
141   intersects (range, options) {
142     if (!(range instanceof Range)) {
143       throw new TypeError('a Range is required')
144     }
145
146     return this.set.some((thisComparators) => {
147       return (
148         isSatisfiable(thisComparators, options) &&
149         range.set.some((rangeComparators) => {
150           return (
151             isSatisfiable(rangeComparators, options) &&
152             thisComparators.every((thisComparator) => {
153               return rangeComparators.every((rangeComparator) => {
154                 return thisComparator.intersects(rangeComparator, options)
155               })
156             })
157           )
158         })
159       )
160     })
161   }
162
163   // if ANY of the sets match ALL of its comparators, then pass
164   test (version) {
165     if (!version) {
166       return false
167     }
168
169     if (typeof version === 'string') {
170       try {
171         version = new SemVer(version, this.options)
172       } catch (er) {
173         return false
174       }
175     }
176
177     for (let i = 0; i < this.set.length; i++) {
178       if (testSet(this.set[i], version, this.options)) {
179         return true
180       }
181     }
182     return false
183   }
184 }
185 module.exports = Range
186
187 const LRU = require('lru-cache')
188 const cache = new LRU({ max: 1000 })
189
190 const parseOptions = require('../internal/parse-options')
191 const Comparator = require('./comparator')
192 const debug = require('../internal/debug')
193 const SemVer = require('./semver')
194 const {
195   re,
196   t,
197   comparatorTrimReplace,
198   tildeTrimReplace,
199   caretTrimReplace
200 } = require('../internal/re')
201
202 const isNullSet = c => c.value === '<0.0.0-0'
203 const isAny = c => c.value === ''
204
205 // take a set of comparators and determine whether there
206 // exists a version which can satisfy it
207 const isSatisfiable = (comparators, options) => {
208   let result = true
209   const remainingComparators = comparators.slice()
210   let testComparator = remainingComparators.pop()
211
212   while (result && remainingComparators.length) {
213     result = remainingComparators.every((otherComparator) => {
214       return testComparator.intersects(otherComparator, options)
215     })
216
217     testComparator = remainingComparators.pop()
218   }
219
220   return result
221 }
222
223 // comprised of xranges, tildes, stars, and gtlt's at this point.
224 // already replaced the hyphen ranges
225 // turn into a set of JUST comparators.
226 const parseComparator = (comp, options) => {
227   debug('comp', comp, options)
228   comp = replaceCarets(comp, options)
229   debug('caret', comp)
230   comp = replaceTildes(comp, options)
231   debug('tildes', comp)
232   comp = replaceXRanges(comp, options)
233   debug('xrange', comp)
234   comp = replaceStars(comp, options)
235   debug('stars', comp)
236   return comp
237 }
238
239 const isX = id => !id || id.toLowerCase() === 'x' || id === '*'
240
241 // ~, ~> --> * (any, kinda silly)
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
247 const replaceTildes = (comp, options) =>
248   comp.trim().split(/\s+/).map((comp) => {
249     return replaceTilde(comp, options)
250   }).join(' ')
251
252 const replaceTilde = (comp, options) => {
253   const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]
254   return comp.replace(r, (_, M, m, p, pr) => {
255     debug('tilde', comp, _, M, m, p, pr)
256     let ret
257
258     if (isX(M)) {
259       ret = ''
260     } else if (isX(m)) {
261       ret = `>=${M}.0.0 <${+M + 1}.0.0-0`
262     } else if (isX(p)) {
263       // ~1.2 == >=1.2.0 <1.3.0-0
264       ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`
265     } else if (pr) {
266       debug('replaceTilde pr', pr)
267       ret = `>=${M}.${m}.${p}-${pr
268       } <${M}.${+m + 1}.0-0`
269     } else {
270       // ~1.2.3 == >=1.2.3 <1.3.0-0
271       ret = `>=${M}.${m}.${p
272       } <${M}.${+m + 1}.0-0`
273     }
274
275     debug('tilde return', ret)
276     return ret
277   })
278 }
279
280 // ^ --> * (any, kinda silly)
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
286 const replaceCarets = (comp, options) =>
287   comp.trim().split(/\s+/).map((comp) => {
288     return replaceCaret(comp, options)
289   }).join(' ')
290
291 const replaceCaret = (comp, options) => {
292   debug('caret', comp, options)
293   const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET]
294   const z = options.includePrerelease ? '-0' : ''
295   return comp.replace(r, (_, M, m, p, pr) => {
296     debug('caret', comp, _, M, m, p, pr)
297     let ret
298
299     if (isX(M)) {
300       ret = ''
301     } else if (isX(m)) {
302       ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`
303     } else if (isX(p)) {
304       if (M === '0') {
305         ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`
306       } else {
307         ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`
308       }
309     } else if (pr) {
310       debug('replaceCaret pr', pr)
311       if (M === '0') {
312         if (m === '0') {
313           ret = `>=${M}.${m}.${p}-${pr
314           } <${M}.${m}.${+p + 1}-0`
315         } else {
316           ret = `>=${M}.${m}.${p}-${pr
317           } <${M}.${+m + 1}.0-0`
318         }
319       } else {
320         ret = `>=${M}.${m}.${p}-${pr
321         } <${+M + 1}.0.0-0`
322       }
323     } else {
324       debug('no pr')
325       if (M === '0') {
326         if (m === '0') {
327           ret = `>=${M}.${m}.${p
328           }${z} <${M}.${m}.${+p + 1}-0`
329         } else {
330           ret = `>=${M}.${m}.${p
331           }${z} <${M}.${+m + 1}.0-0`
332         }
333       } else {
334         ret = `>=${M}.${m}.${p
335         } <${+M + 1}.0.0-0`
336       }
337     }
338
339     debug('caret return', ret)
340     return ret
341   })
342 }
343
344 const replaceXRanges = (comp, options) => {
345   debug('replaceXRanges', comp, options)
346   return comp.split(/\s+/).map((comp) => {
347     return replaceXRange(comp, options)
348   }).join(' ')
349 }
350
351 const replaceXRange = (comp, options) => {
352   comp = comp.trim()
353   const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE]
354   return comp.replace(r, (ret, gtlt, M, m, p, pr) => {
355     debug('xRange', comp, ret, gtlt, M, m, p, pr)
356     const xM = isX(M)
357     const xm = xM || isX(m)
358     const xp = xm || isX(p)
359     const anyX = xp
360
361     if (gtlt === '=' && anyX) {
362       gtlt = ''
363     }
364
365     // if we're including prereleases in the match, then we need
366     // to fix this to -0, the lowest possible prerelease value
367     pr = options.includePrerelease ? '-0' : ''
368
369     if (xM) {
370       if (gtlt === '>' || gtlt === '<') {
371         // nothing is allowed
372         ret = '<0.0.0-0'
373       } else {
374         // nothing is forbidden
375         ret = '*'
376       }
377     } else if (gtlt && anyX) {
378       // we know patch is an x, because we have any x at all.
379       // replace X with 0
380       if (xm) {
381         m = 0
382       }
383       p = 0
384
385       if (gtlt === '>') {
386         // >1 => >=2.0.0
387         // >1.2 => >=1.3.0
388         gtlt = '>='
389         if (xm) {
390           M = +M + 1
391           m = 0
392           p = 0
393         } else {
394           m = +m + 1
395           p = 0
396         }
397       } else if (gtlt === '<=') {
398         // <=0.7.x is actually <0.8.0, since any 0.7.x should
399         // pass.  Similarly, <=7.x is actually <8.0.0, etc.
400         gtlt = '<'
401         if (xm) {
402           M = +M + 1
403         } else {
404           m = +m + 1
405         }
406       }
407
408       if (gtlt === '<')
409         pr = '-0'
410
411       ret = `${gtlt + M}.${m}.${p}${pr}`
412     } else if (xm) {
413       ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`
414     } else if (xp) {
415       ret = `>=${M}.${m}.0${pr
416       } <${M}.${+m + 1}.0-0`
417     }
418
419     debug('xRange return', ret)
420
421     return ret
422   })
423 }
424
425 // Because * is AND-ed with everything else in the comparator,
426 // and '' means "any version", just remove the *s entirely.
427 const replaceStars = (comp, options) => {
428   debug('replaceStars', comp, options)
429   // Looseness is ignored here.  star is always as loose as it gets!
430   return comp.trim().replace(re[t.STAR], '')
431 }
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
439 // This function is passed to string.replace(re[t.HYPHENRANGE])
440 // M, m, patch, prerelease, build
441 // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
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
444 const hyphenReplace = incPr => ($0,
445   from, fM, fm, fp, fpr, fb,
446   to, tM, tm, tp, tpr, tb) => {
447   if (isX(fM)) {
448     from = ''
449   } else if (isX(fm)) {
450     from = `>=${fM}.0.0${incPr ? '-0' : ''}`
451   } else if (isX(fp)) {
452     from = `>=${fM}.${fm}.0${incPr ? '-0' : ''}`
453   } else if (fpr) {
454     from = `>=${from}`
455   } else {
456     from = `>=${from}${incPr ? '-0' : ''}`
457   }
458
459   if (isX(tM)) {
460     to = ''
461   } else if (isX(tm)) {
462     to = `<${+tM + 1}.0.0-0`
463   } else if (isX(tp)) {
464     to = `<${tM}.${+tm + 1}.0-0`
465   } else if (tpr) {
466     to = `<=${tM}.${tm}.${tp}-${tpr}`
467   } else if (incPr) {
468     to = `<${tM}.${tm}.${+tp + 1}-0`
469   } else {
470     to = `<=${to}`
471   }
472
473   return (`${from} ${to}`).trim()
474 }
475
476 const testSet = (set, version, options) => {
477   for (let i = 0; i < set.length; i++) {
478     if (!set[i].test(version)) {
479       return false
480     }
481   }
482
483   if (version.prerelease.length && !options.includePrerelease) {
484     // Find the set of versions that are allowed to have prereleases
485     // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
486     // That should allow `1.2.3-pr.2` to pass.
487     // However, `1.2.4-alpha.notready` should NOT be allowed,
488     // even though it's within the range set by the comparators.
489     for (let i = 0; i < set.length; i++) {
490       debug(set[i].semver)
491       if (set[i].semver === Comparator.ANY) {
492         continue
493       }
494
495       if (set[i].semver.prerelease.length > 0) {
496         const allowed = set[i].semver
497         if (allowed.major === version.major &&
498             allowed.minor === version.minor &&
499             allowed.patch === version.patch) {
500           return true
501         }
502       }
503     }
504
505     // Version has a -pre, but it's not one of the ones we like.
506     return false
507   }
508
509   return true
510 }