| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554 | const SPACE_CHARACTERS = /\s+/g// hoisted class for cyclic dependencyclass Range {  constructor (range, options) {    options = parseOptions(options)    if (range instanceof Range) {      if (        range.loose === !!options.loose &&        range.includePrerelease === !!options.includePrerelease      ) {        return range      } else {        return new Range(range.raw, options)      }    }    if (range instanceof Comparator) {      // just put it in the set and return      this.raw = range.value      this.set = [[range]]      this.formatted = undefined      return this    }    this.options = options    this.loose = !!options.loose    this.includePrerelease = !!options.includePrerelease    // First reduce all whitespace as much as possible so we do not have to rely    // on potentially slow regexes like \s*. This is then stored and used for    // future error messages as well.    this.raw = range.trim().replace(SPACE_CHARACTERS, ' ')    // First, split on ||    this.set = this.raw      .split('||')      // map the range to a 2d array of comparators      .map(r => this.parseRange(r.trim()))      // throw out any comparator lists that are empty      // this generally means that it was not a valid range, which is allowed      // in loose mode, but will still throw if the WHOLE range is invalid.      .filter(c => c.length)    if (!this.set.length) {      throw new TypeError(`Invalid SemVer Range: ${this.raw}`)    }    // if we have any that are not the null set, throw out null sets.    if (this.set.length > 1) {      // keep the first one, in case they're all null sets      const first = this.set[0]      this.set = this.set.filter(c => !isNullSet(c[0]))      if (this.set.length === 0) {        this.set = [first]      } else if (this.set.length > 1) {        // if we have any that are *, then the range is just *        for (const c of this.set) {          if (c.length === 1 && isAny(c[0])) {            this.set = [c]            break          }        }      }    }    this.formatted = undefined  }  get range () {    if (this.formatted === undefined) {      this.formatted = ''      for (let i = 0; i < this.set.length; i++) {        if (i > 0) {          this.formatted += '||'        }        const comps = this.set[i]        for (let k = 0; k < comps.length; k++) {          if (k > 0) {            this.formatted += ' '          }          this.formatted += comps[k].toString().trim()        }      }    }    return this.formatted  }  format () {    return this.range  }  toString () {    return this.range  }  parseRange (range) {    // memoize range parsing for performance.    // this is a very hot path, and fully deterministic.    const memoOpts =      (this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) |      (this.options.loose && FLAG_LOOSE)    const memoKey = memoOpts + ':' + range    const cached = cache.get(memoKey)    if (cached) {      return cached    }    const loose = this.options.loose    // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`    const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]    range = range.replace(hr, hyphenReplace(this.options.includePrerelease))    debug('hyphen replace', range)    // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`    range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)    debug('comparator trim', range)    // `~ 1.2.3` => `~1.2.3`    range = range.replace(re[t.TILDETRIM], tildeTrimReplace)    debug('tilde trim', range)    // `^ 1.2.3` => `^1.2.3`    range = range.replace(re[t.CARETTRIM], caretTrimReplace)    debug('caret trim', range)    // At this point, the range is completely trimmed and    // ready to be split into comparators.    let rangeList = range      .split(' ')      .map(comp => parseComparator(comp, this.options))      .join(' ')      .split(/\s+/)      // >=0.0.0 is equivalent to *      .map(comp => replaceGTE0(comp, this.options))    if (loose) {      // in loose mode, throw out any that are not valid comparators      rangeList = rangeList.filter(comp => {        debug('loose invalid filter', comp, this.options)        return !!comp.match(re[t.COMPARATORLOOSE])      })    }    debug('range list', rangeList)    // if any comparators are the null set, then replace with JUST null set    // if more than one comparator, remove any * comparators    // also, don't include the same comparator more than once    const rangeMap = new Map()    const comparators = rangeList.map(comp => new Comparator(comp, this.options))    for (const comp of comparators) {      if (isNullSet(comp)) {        return [comp]      }      rangeMap.set(comp.value, comp)    }    if (rangeMap.size > 1 && rangeMap.has('')) {      rangeMap.delete('')    }    const result = [...rangeMap.values()]    cache.set(memoKey, result)    return result  }  intersects (range, options) {    if (!(range instanceof Range)) {      throw new TypeError('a Range is required')    }    return this.set.some((thisComparators) => {      return (        isSatisfiable(thisComparators, options) &&        range.set.some((rangeComparators) => {          return (            isSatisfiable(rangeComparators, options) &&            thisComparators.every((thisComparator) => {              return rangeComparators.every((rangeComparator) => {                return thisComparator.intersects(rangeComparator, options)              })            })          )        })      )    })  }  // if ANY of the sets match ALL of its comparators, then pass  test (version) {    if (!version) {      return false    }    if (typeof version === 'string') {      try {        version = new SemVer(version, this.options)      } catch (er) {        return false      }    }    for (let i = 0; i < this.set.length; i++) {      if (testSet(this.set[i], version, this.options)) {        return true      }    }    return false  }}module.exports = Rangeconst LRU = require('../internal/lrucache')const cache = new LRU()const parseOptions = require('../internal/parse-options')const Comparator = require('./comparator')const debug = require('../internal/debug')const SemVer = require('./semver')const {  safeRe: re,  t,  comparatorTrimReplace,  tildeTrimReplace,  caretTrimReplace,} = require('../internal/re')const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require('../internal/constants')const isNullSet = c => c.value === '<0.0.0-0'const isAny = c => c.value === ''// take a set of comparators and determine whether there// exists a version which can satisfy itconst isSatisfiable = (comparators, options) => {  let result = true  const remainingComparators = comparators.slice()  let testComparator = remainingComparators.pop()  while (result && remainingComparators.length) {    result = remainingComparators.every((otherComparator) => {      return testComparator.intersects(otherComparator, options)    })    testComparator = remainingComparators.pop()  }  return result}// comprised of xranges, tildes, stars, and gtlt's at this point.// already replaced the hyphen ranges// turn into a set of JUST comparators.const parseComparator = (comp, options) => {  debug('comp', comp, options)  comp = replaceCarets(comp, options)  debug('caret', comp)  comp = replaceTildes(comp, options)  debug('tildes', comp)  comp = replaceXRanges(comp, options)  debug('xrange', comp)  comp = replaceStars(comp, options)  debug('stars', comp)  return comp}const isX = id => !id || id.toLowerCase() === 'x' || id === '*'// ~, ~> --> * (any, kinda silly)// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0// ~0.0.1 --> >=0.0.1 <0.1.0-0const replaceTildes = (comp, options) => {  return comp    .trim()    .split(/\s+/)    .map((c) => replaceTilde(c, options))    .join(' ')}const replaceTilde = (comp, options) => {  const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]  return comp.replace(r, (_, M, m, p, pr) => {    debug('tilde', comp, _, M, m, p, pr)    let ret    if (isX(M)) {      ret = ''    } else if (isX(m)) {      ret = `>=${M}.0.0 <${+M + 1}.0.0-0`    } else if (isX(p)) {      // ~1.2 == >=1.2.0 <1.3.0-0      ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`    } else if (pr) {      debug('replaceTilde pr', pr)      ret = `>=${M}.${m}.${p}-${pr      } <${M}.${+m + 1}.0-0`    } else {      // ~1.2.3 == >=1.2.3 <1.3.0-0      ret = `>=${M}.${m}.${p      } <${M}.${+m + 1}.0-0`    }    debug('tilde return', ret)    return ret  })}// ^ --> * (any, kinda silly)// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0-0// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0-0// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0// ^1.2.3 --> >=1.2.3 <2.0.0-0// ^1.2.0 --> >=1.2.0 <2.0.0-0// ^0.0.1 --> >=0.0.1 <0.0.2-0// ^0.1.0 --> >=0.1.0 <0.2.0-0const replaceCarets = (comp, options) => {  return comp    .trim()    .split(/\s+/)    .map((c) => replaceCaret(c, options))    .join(' ')}const replaceCaret = (comp, options) => {  debug('caret', comp, options)  const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET]  const z = options.includePrerelease ? '-0' : ''  return comp.replace(r, (_, M, m, p, pr) => {    debug('caret', comp, _, M, m, p, pr)    let ret    if (isX(M)) {      ret = ''    } else if (isX(m)) {      ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`    } else if (isX(p)) {      if (M === '0') {        ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`      } else {        ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`      }    } else if (pr) {      debug('replaceCaret pr', pr)      if (M === '0') {        if (m === '0') {          ret = `>=${M}.${m}.${p}-${pr          } <${M}.${m}.${+p + 1}-0`        } else {          ret = `>=${M}.${m}.${p}-${pr          } <${M}.${+m + 1}.0-0`        }      } else {        ret = `>=${M}.${m}.${p}-${pr        } <${+M + 1}.0.0-0`      }    } else {      debug('no pr')      if (M === '0') {        if (m === '0') {          ret = `>=${M}.${m}.${p          }${z} <${M}.${m}.${+p + 1}-0`        } else {          ret = `>=${M}.${m}.${p          }${z} <${M}.${+m + 1}.0-0`        }      } else {        ret = `>=${M}.${m}.${p        } <${+M + 1}.0.0-0`      }    }    debug('caret return', ret)    return ret  })}const replaceXRanges = (comp, options) => {  debug('replaceXRanges', comp, options)  return comp    .split(/\s+/)    .map((c) => replaceXRange(c, options))    .join(' ')}const replaceXRange = (comp, options) => {  comp = comp.trim()  const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE]  return comp.replace(r, (ret, gtlt, M, m, p, pr) => {    debug('xRange', comp, ret, gtlt, M, m, p, pr)    const xM = isX(M)    const xm = xM || isX(m)    const xp = xm || isX(p)    const anyX = xp    if (gtlt === '=' && anyX) {      gtlt = ''    }    // if we're including prereleases in the match, then we need    // to fix this to -0, the lowest possible prerelease value    pr = options.includePrerelease ? '-0' : ''    if (xM) {      if (gtlt === '>' || gtlt === '<') {        // nothing is allowed        ret = '<0.0.0-0'      } else {        // nothing is forbidden        ret = '*'      }    } else if (gtlt && anyX) {      // we know patch is an x, because we have any x at all.      // replace X with 0      if (xm) {        m = 0      }      p = 0      if (gtlt === '>') {        // >1 => >=2.0.0        // >1.2 => >=1.3.0        gtlt = '>='        if (xm) {          M = +M + 1          m = 0          p = 0        } else {          m = +m + 1          p = 0        }      } else if (gtlt === '<=') {        // <=0.7.x is actually <0.8.0, since any 0.7.x should        // pass.  Similarly, <=7.x is actually <8.0.0, etc.        gtlt = '<'        if (xm) {          M = +M + 1        } else {          m = +m + 1        }      }      if (gtlt === '<') {        pr = '-0'      }      ret = `${gtlt + M}.${m}.${p}${pr}`    } else if (xm) {      ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`    } else if (xp) {      ret = `>=${M}.${m}.0${pr      } <${M}.${+m + 1}.0-0`    }    debug('xRange return', ret)    return ret  })}// Because * is AND-ed with everything else in the comparator,// and '' means "any version", just remove the *s entirely.const replaceStars = (comp, options) => {  debug('replaceStars', comp, options)  // Looseness is ignored here.  star is always as loose as it gets!  return comp    .trim()    .replace(re[t.STAR], '')}const replaceGTE0 = (comp, options) => {  debug('replaceGTE0', comp, options)  return comp    .trim()    .replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '')}// This function is passed to string.replace(re[t.HYPHENRANGE])// M, m, patch, prerelease, build// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5// 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do// 1.2 - 3.4 => >=1.2.0 <3.5.0-0// TODO build?const hyphenReplace = incPr => ($0,  from, fM, fm, fp, fpr, fb,  to, tM, tm, tp, tpr) => {  if (isX(fM)) {    from = ''  } else if (isX(fm)) {    from = `>=${fM}.0.0${incPr ? '-0' : ''}`  } else if (isX(fp)) {    from = `>=${fM}.${fm}.0${incPr ? '-0' : ''}`  } else if (fpr) {    from = `>=${from}`  } else {    from = `>=${from}${incPr ? '-0' : ''}`  }  if (isX(tM)) {    to = ''  } else if (isX(tm)) {    to = `<${+tM + 1}.0.0-0`  } else if (isX(tp)) {    to = `<${tM}.${+tm + 1}.0-0`  } else if (tpr) {    to = `<=${tM}.${tm}.${tp}-${tpr}`  } else if (incPr) {    to = `<${tM}.${tm}.${+tp + 1}-0`  } else {    to = `<=${to}`  }  return `${from} ${to}`.trim()}const testSet = (set, version, options) => {  for (let i = 0; i < set.length; i++) {    if (!set[i].test(version)) {      return false    }  }  if (version.prerelease.length && !options.includePrerelease) {    // Find the set of versions that are allowed to have prereleases    // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0    // That should allow `1.2.3-pr.2` to pass.    // However, `1.2.4-alpha.notready` should NOT be allowed,    // even though it's within the range set by the comparators.    for (let i = 0; i < set.length; i++) {      debug(set[i].semver)      if (set[i].semver === Comparator.ANY) {        continue      }      if (set[i].semver.prerelease.length > 0) {        const allowed = set[i].semver        if (allowed.major === version.major &&            allowed.minor === version.minor &&            allowed.patch === version.patch) {          return true        }      }    }    // Version has a -pre, but it's not one of the ones we like.    return false  }  return true}
 |