1const ANY = Symbol('SemVer ANY') 2// hoisted class for cyclic dependency 3class Comparator { 4 static get ANY () { 5 return ANY 6 } 7 8 constructor (comp, options) { 9 options = parseOptions(options) 10 11 if (comp instanceof Comparator) { 12 if (comp.loose === !!options.loose) { 13 return comp 14 } else { 15 comp = comp.value 16 } 17 } 18 19 comp = comp.trim().split(/\s+/).join(' ') 20 debug('comparator', comp, options) 21 this.options = options 22 this.loose = !!options.loose 23 this.parse(comp) 24 25 if (this.semver === ANY) { 26 this.value = '' 27 } else { 28 this.value = this.operator + this.semver.version 29 } 30 31 debug('comp', this) 32 } 33 34 parse (comp) { 35 const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] 36 const m = comp.match(r) 37 38 if (!m) { 39 throw new TypeError(`Invalid comparator: ${comp}`) 40 } 41 42 this.operator = m[1] !== undefined ? m[1] : '' 43 if (this.operator === '=') { 44 this.operator = '' 45 } 46 47 // if it literally is just '>' or '' then allow anything. 48 if (!m[2]) { 49 this.semver = ANY 50 } else { 51 this.semver = new SemVer(m[2], this.options.loose) 52 } 53 } 54 55 toString () { 56 return this.value 57 } 58 59 test (version) { 60 debug('Comparator.test', version, this.options.loose) 61 62 if (this.semver === ANY || version === ANY) { 63 return true 64 } 65 66 if (typeof version === 'string') { 67 try { 68 version = new SemVer(version, this.options) 69 } catch (er) { 70 return false 71 } 72 } 73 74 return cmp(version, this.operator, this.semver, this.options) 75 } 76 77 intersects (comp, options) { 78 if (!(comp instanceof Comparator)) { 79 throw new TypeError('a Comparator is required') 80 } 81 82 if (this.operator === '') { 83 if (this.value === '') { 84 return true 85 } 86 return new Range(comp.value, options).test(this.value) 87 } else if (comp.operator === '') { 88 if (comp.value === '') { 89 return true 90 } 91 return new Range(this.value, options).test(comp.semver) 92 } 93 94 options = parseOptions(options) 95 96 // Special cases where nothing can possibly be lower 97 if (options.includePrerelease && 98 (this.value === '<0.0.0-0' || comp.value === '<0.0.0-0')) { 99 return false 100 } 101 if (!options.includePrerelease && 102 (this.value.startsWith('<0.0.0') || comp.value.startsWith('<0.0.0'))) { 103 return false 104 } 105 106 // Same direction increasing (> or >=) 107 if (this.operator.startsWith('>') && comp.operator.startsWith('>')) { 108 return true 109 } 110 // Same direction decreasing (< or <=) 111 if (this.operator.startsWith('<') && comp.operator.startsWith('<')) { 112 return true 113 } 114 // same SemVer and both sides are inclusive (<= or >=) 115 if ( 116 (this.semver.version === comp.semver.version) && 117 this.operator.includes('=') && comp.operator.includes('=')) { 118 return true 119 } 120 // opposite directions less than 121 if (cmp(this.semver, '<', comp.semver, options) && 122 this.operator.startsWith('>') && comp.operator.startsWith('<')) { 123 return true 124 } 125 // opposite directions greater than 126 if (cmp(this.semver, '>', comp.semver, options) && 127 this.operator.startsWith('<') && comp.operator.startsWith('>')) { 128 return true 129 } 130 return false 131 } 132} 133 134module.exports = Comparator 135 136const parseOptions = require('../internal/parse-options') 137const { safeRe: re, t } = require('../internal/re') 138const cmp = require('../functions/cmp') 139const debug = require('../internal/debug') 140const SemVer = require('./semver') 141const Range = require('./range') 142