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