source: frontend/node_modules/semver/classes/range.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 14.6 KB
Line 
1'use strict'
2
3const SPACE_CHARACTERS = /\s+/g
4
5// hoisted class for cyclic dependency
6class Range {
7 constructor (range, options) {
8 options = parseOptions(options)
9
10 if (range instanceof Range) {
11 if (
12 range.loose === !!options.loose &&
13 range.includePrerelease === !!options.includePrerelease
14 ) {
15 return range
16 } else {
17 return new Range(range.raw, options)
18 }
19 }
20
21 if (range instanceof Comparator) {
22 // just put it in the set and return
23 this.raw = range.value
24 this.set = [[range]]
25 this.formatted = undefined
26 return this
27 }
28
29 this.options = options
30 this.loose = !!options.loose
31 this.includePrerelease = !!options.includePrerelease
32
33 // First reduce all whitespace as much as possible so we do not have to rely
34 // on potentially slow regexes like \s*. This is then stored and used for
35 // future error messages as well.
36 this.raw = range.trim().replace(SPACE_CHARACTERS, ' ')
37
38 // First, split on ||
39 this.set = this.raw
40 .split('||')
41 // map the range to a 2d array of comparators
42 .map(r => this.parseRange(r.trim()))
43 // throw out any comparator lists that are empty
44 // this generally means that it was not a valid range, which is allowed
45 // in loose mode, but will still throw if the WHOLE range is invalid.
46 .filter(c => c.length)
47
48 if (!this.set.length) {
49 throw new TypeError(`Invalid SemVer Range: ${this.raw}`)
50 }
51
52 // if we have any that are not the null set, throw out null sets.
53 if (this.set.length > 1) {
54 // keep the first one, in case they're all null sets
55 const first = this.set[0]
56 this.set = this.set.filter(c => !isNullSet(c[0]))
57 if (this.set.length === 0) {
58 this.set = [first]
59 } else if (this.set.length > 1) {
60 // if we have any that are *, then the range is just *
61 for (const c of this.set) {
62 if (c.length === 1 && isAny(c[0])) {
63 this.set = [c]
64 break
65 }
66 }
67 }
68 }
69
70 this.formatted = undefined
71 }
72
73 get range () {
74 if (this.formatted === undefined) {
75 this.formatted = ''
76 for (let i = 0; i < this.set.length; i++) {
77 if (i > 0) {
78 this.formatted += '||'
79 }
80 const comps = this.set[i]
81 for (let k = 0; k < comps.length; k++) {
82 if (k > 0) {
83 this.formatted += ' '
84 }
85 this.formatted += comps[k].toString().trim()
86 }
87 }
88 }
89 return this.formatted
90 }
91
92 format () {
93 return this.range
94 }
95
96 toString () {
97 return this.range
98 }
99
100 parseRange (range) {
101 // memoize range parsing for performance.
102 // this is a very hot path, and fully deterministic.
103 const memoOpts =
104 (this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) |
105 (this.options.loose && FLAG_LOOSE)
106 const memoKey = memoOpts + ':' + range
107 const cached = cache.get(memoKey)
108 if (cached) {
109 return cached
110 }
111
112 const loose = this.options.loose
113 // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
114 const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]
115 range = range.replace(hr, hyphenReplace(this.options.includePrerelease))
116 debug('hyphen replace', range)
117
118 // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
119 range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)
120 debug('comparator trim', range)
121
122 // `~ 1.2.3` => `~1.2.3`
123 range = range.replace(re[t.TILDETRIM], tildeTrimReplace)
124 debug('tilde trim', range)
125
126 // `^ 1.2.3` => `^1.2.3`
127 range = range.replace(re[t.CARETTRIM], caretTrimReplace)
128 debug('caret trim', range)
129
130 // At this point, the range is completely trimmed and
131 // ready to be split into comparators.
132
133 let rangeList = range
134 .split(' ')
135 .map(comp => parseComparator(comp, this.options))
136 .join(' ')
137 .split(/\s+/)
138 // >=0.0.0 is equivalent to *
139 .map(comp => replaceGTE0(comp, this.options))
140
141 if (loose) {
142 // in loose mode, throw out any that are not valid comparators
143 rangeList = rangeList.filter(comp => {
144 debug('loose invalid filter', comp, this.options)
145 return !!comp.match(re[t.COMPARATORLOOSE])
146 })
147 }
148 debug('range list', rangeList)
149
150 // if any comparators are the null set, then replace with JUST null set
151 // if more than one comparator, remove any * comparators
152 // also, don't include the same comparator more than once
153 const rangeMap = new Map()
154 const comparators = rangeList.map(comp => new Comparator(comp, this.options))
155 for (const comp of comparators) {
156 if (isNullSet(comp)) {
157 return [comp]
158 }
159 rangeMap.set(comp.value, comp)
160 }
161 if (rangeMap.size > 1 && rangeMap.has('')) {
162 rangeMap.delete('')
163 }
164
165 const result = [...rangeMap.values()]
166 cache.set(memoKey, result)
167 return result
168 }
169
170 intersects (range, options) {
171 if (!(range instanceof Range)) {
172 throw new TypeError('a Range is required')
173 }
174
175 return this.set.some((thisComparators) => {
176 return (
177 isSatisfiable(thisComparators, options) &&
178 range.set.some((rangeComparators) => {
179 return (
180 isSatisfiable(rangeComparators, options) &&
181 thisComparators.every((thisComparator) => {
182 return rangeComparators.every((rangeComparator) => {
183 return thisComparator.intersects(rangeComparator, options)
184 })
185 })
186 )
187 })
188 )
189 })
190 }
191
192 // if ANY of the sets match ALL of its comparators, then pass
193 test (version) {
194 if (!version) {
195 return false
196 }
197
198 if (typeof version === 'string') {
199 try {
200 version = new SemVer(version, this.options)
201 } catch (er) {
202 return false
203 }
204 }
205
206 for (let i = 0; i < this.set.length; i++) {
207 if (testSet(this.set[i], version, this.options)) {
208 return true
209 }
210 }
211 return false
212 }
213}
214
215module.exports = Range
216
217const LRU = require('../internal/lrucache')
218const cache = new LRU()
219
220const parseOptions = require('../internal/parse-options')
221const Comparator = require('./comparator')
222const debug = require('../internal/debug')
223const SemVer = require('./semver')
224const {
225 safeRe: re,
226 t,
227 comparatorTrimReplace,
228 tildeTrimReplace,
229 caretTrimReplace,
230} = require('../internal/re')
231const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require('../internal/constants')
232
233const isNullSet = c => c.value === '<0.0.0-0'
234const isAny = c => c.value === ''
235
236// take a set of comparators and determine whether there
237// exists a version which can satisfy it
238const isSatisfiable = (comparators, options) => {
239 let result = true
240 const remainingComparators = comparators.slice()
241 let testComparator = remainingComparators.pop()
242
243 while (result && remainingComparators.length) {
244 result = remainingComparators.every((otherComparator) => {
245 return testComparator.intersects(otherComparator, options)
246 })
247
248 testComparator = remainingComparators.pop()
249 }
250
251 return result
252}
253
254// comprised of xranges, tildes, stars, and gtlt's at this point.
255// already replaced the hyphen ranges
256// turn into a set of JUST comparators.
257const parseComparator = (comp, options) => {
258 comp = comp.replace(re[t.BUILD], '')
259 debug('comp', comp, options)
260 comp = replaceCarets(comp, options)
261 debug('caret', comp)
262 comp = replaceTildes(comp, options)
263 debug('tildes', comp)
264 comp = replaceXRanges(comp, options)
265 debug('xrange', comp)
266 comp = replaceStars(comp, options)
267 debug('stars', comp)
268 return comp
269}
270
271const isX = id => !id || id.toLowerCase() === 'x' || id === '*'
272
273// ~, ~> --> * (any, kinda silly)
274// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0
275// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0
276// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0
277// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0
278// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0
279// ~0.0.1 --> >=0.0.1 <0.1.0-0
280const replaceTildes = (comp, options) => {
281 return comp
282 .trim()
283 .split(/\s+/)
284 .map((c) => replaceTilde(c, options))
285 .join(' ')
286}
287
288const replaceTilde = (comp, options) => {
289 const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]
290 return comp.replace(r, (_, M, m, p, pr) => {
291 debug('tilde', comp, _, M, m, p, pr)
292 let ret
293
294 if (isX(M)) {
295 ret = ''
296 } else if (isX(m)) {
297 ret = `>=${M}.0.0 <${+M + 1}.0.0-0`
298 } else if (isX(p)) {
299 // ~1.2 == >=1.2.0 <1.3.0-0
300 ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`
301 } else if (pr) {
302 debug('replaceTilde pr', pr)
303 ret = `>=${M}.${m}.${p}-${pr
304 } <${M}.${+m + 1}.0-0`
305 } else {
306 // ~1.2.3 == >=1.2.3 <1.3.0-0
307 ret = `>=${M}.${m}.${p
308 } <${M}.${+m + 1}.0-0`
309 }
310
311 debug('tilde return', ret)
312 return ret
313 })
314}
315
316// ^ --> * (any, kinda silly)
317// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0-0
318// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0-0
319// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0
320// ^1.2.3 --> >=1.2.3 <2.0.0-0
321// ^1.2.0 --> >=1.2.0 <2.0.0-0
322// ^0.0.1 --> >=0.0.1 <0.0.2-0
323// ^0.1.0 --> >=0.1.0 <0.2.0-0
324const replaceCarets = (comp, options) => {
325 return comp
326 .trim()
327 .split(/\s+/)
328 .map((c) => replaceCaret(c, options))
329 .join(' ')
330}
331
332const replaceCaret = (comp, options) => {
333 debug('caret', comp, options)
334 const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET]
335 const z = options.includePrerelease ? '-0' : ''
336 return comp.replace(r, (_, M, m, p, pr) => {
337 debug('caret', comp, _, M, m, p, pr)
338 let ret
339
340 if (isX(M)) {
341 ret = ''
342 } else if (isX(m)) {
343 ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`
344 } else if (isX(p)) {
345 if (M === '0') {
346 ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`
347 } else {
348 ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`
349 }
350 } else if (pr) {
351 debug('replaceCaret pr', pr)
352 if (M === '0') {
353 if (m === '0') {
354 ret = `>=${M}.${m}.${p}-${pr
355 } <${M}.${m}.${+p + 1}-0`
356 } else {
357 ret = `>=${M}.${m}.${p}-${pr
358 } <${M}.${+m + 1}.0-0`
359 }
360 } else {
361 ret = `>=${M}.${m}.${p}-${pr
362 } <${+M + 1}.0.0-0`
363 }
364 } else {
365 debug('no pr')
366 if (M === '0') {
367 if (m === '0') {
368 ret = `>=${M}.${m}.${p
369 }${z} <${M}.${m}.${+p + 1}-0`
370 } else {
371 ret = `>=${M}.${m}.${p
372 }${z} <${M}.${+m + 1}.0-0`
373 }
374 } else {
375 ret = `>=${M}.${m}.${p
376 } <${+M + 1}.0.0-0`
377 }
378 }
379
380 debug('caret return', ret)
381 return ret
382 })
383}
384
385const replaceXRanges = (comp, options) => {
386 debug('replaceXRanges', comp, options)
387 return comp
388 .split(/\s+/)
389 .map((c) => replaceXRange(c, options))
390 .join(' ')
391}
392
393const replaceXRange = (comp, options) => {
394 comp = comp.trim()
395 const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE]
396 return comp.replace(r, (ret, gtlt, M, m, p, pr) => {
397 debug('xRange', comp, ret, gtlt, M, m, p, pr)
398 const xM = isX(M)
399 const xm = xM || isX(m)
400 const xp = xm || isX(p)
401 const anyX = xp
402
403 if (gtlt === '=' && anyX) {
404 gtlt = ''
405 }
406
407 // if we're including prereleases in the match, then we need
408 // to fix this to -0, the lowest possible prerelease value
409 pr = options.includePrerelease ? '-0' : ''
410
411 if (xM) {
412 if (gtlt === '>' || gtlt === '<') {
413 // nothing is allowed
414 ret = '<0.0.0-0'
415 } else {
416 // nothing is forbidden
417 ret = '*'
418 }
419 } else if (gtlt && anyX) {
420 // we know patch is an x, because we have any x at all.
421 // replace X with 0
422 if (xm) {
423 m = 0
424 }
425 p = 0
426
427 if (gtlt === '>') {
428 // >1 => >=2.0.0
429 // >1.2 => >=1.3.0
430 gtlt = '>='
431 if (xm) {
432 M = +M + 1
433 m = 0
434 p = 0
435 } else {
436 m = +m + 1
437 p = 0
438 }
439 } else if (gtlt === '<=') {
440 // <=0.7.x is actually <0.8.0, since any 0.7.x should
441 // pass. Similarly, <=7.x is actually <8.0.0, etc.
442 gtlt = '<'
443 if (xm) {
444 M = +M + 1
445 } else {
446 m = +m + 1
447 }
448 }
449
450 if (gtlt === '<') {
451 pr = '-0'
452 }
453
454 ret = `${gtlt + M}.${m}.${p}${pr}`
455 } else if (xm) {
456 ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`
457 } else if (xp) {
458 ret = `>=${M}.${m}.0${pr
459 } <${M}.${+m + 1}.0-0`
460 }
461
462 debug('xRange return', ret)
463
464 return ret
465 })
466}
467
468// Because * is AND-ed with everything else in the comparator,
469// and '' means "any version", just remove the *s entirely.
470const replaceStars = (comp, options) => {
471 debug('replaceStars', comp, options)
472 // Looseness is ignored here. star is always as loose as it gets!
473 return comp
474 .trim()
475 .replace(re[t.STAR], '')
476}
477
478const replaceGTE0 = (comp, options) => {
479 debug('replaceGTE0', comp, options)
480 return comp
481 .trim()
482 .replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '')
483}
484
485// This function is passed to string.replace(re[t.HYPHENRANGE])
486// M, m, patch, prerelease, build
487// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
488// 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do
489// 1.2 - 3.4 => >=1.2.0 <3.5.0-0
490// TODO build?
491const hyphenReplace = incPr => ($0,
492 from, fM, fm, fp, fpr, fb,
493 to, tM, tm, tp, tpr) => {
494 if (isX(fM)) {
495 from = ''
496 } else if (isX(fm)) {
497 from = `>=${fM}.0.0${incPr ? '-0' : ''}`
498 } else if (isX(fp)) {
499 from = `>=${fM}.${fm}.0${incPr ? '-0' : ''}`
500 } else if (fpr) {
501 from = `>=${from}`
502 } else {
503 from = `>=${from}${incPr ? '-0' : ''}`
504 }
505
506 if (isX(tM)) {
507 to = ''
508 } else if (isX(tm)) {
509 to = `<${+tM + 1}.0.0-0`
510 } else if (isX(tp)) {
511 to = `<${tM}.${+tm + 1}.0-0`
512 } else if (tpr) {
513 to = `<=${tM}.${tm}.${tp}-${tpr}`
514 } else if (incPr) {
515 to = `<${tM}.${tm}.${+tp + 1}-0`
516 } else {
517 to = `<=${to}`
518 }
519
520 return `${from} ${to}`.trim()
521}
522
523const testSet = (set, version, options) => {
524 for (let i = 0; i < set.length; i++) {
525 if (!set[i].test(version)) {
526 return false
527 }
528 }
529
530 if (version.prerelease.length && !options.includePrerelease) {
531 // Find the set of versions that are allowed to have prereleases
532 // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
533 // That should allow `1.2.3-pr.2` to pass.
534 // However, `1.2.4-alpha.notready` should NOT be allowed,
535 // even though it's within the range set by the comparators.
536 for (let i = 0; i < set.length; i++) {
537 debug(set[i].semver)
538 if (set[i].semver === Comparator.ANY) {
539 continue
540 }
541
542 if (set[i].semver.prerelease.length > 0) {
543 const allowed = set[i].semver
544 if (allowed.major === version.major &&
545 allowed.minor === version.minor &&
546 allowed.patch === version.patch) {
547 return true
548 }
549 }
550 }
551
552 // Version has a -pre, but it's not one of the ones we like.
553 return false
554 }
555
556 return true
557}
Note: See TracBrowser for help on using the repository browser.