/ node_modules / semver / ranges / subset.js
subset.js
  1  'use strict'
  2  
  3  const Range = require('../classes/range.js')
  4  const Comparator = require('../classes/comparator.js')
  5  const { ANY } = Comparator
  6  const satisfies = require('../functions/satisfies.js')
  7  const compare = require('../functions/compare.js')
  8  
  9  // Complex range `r1 || r2 || ...` is a subset of `R1 || R2 || ...` iff:
 10  // - Every simple range `r1, r2, ...` is a null set, OR
 11  // - Every simple range `r1, r2, ...` which is not a null set is a subset of
 12  //   some `R1, R2, ...`
 13  //
 14  // Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff:
 15  // - If c is only the ANY comparator
 16  //   - If C is only the ANY comparator, return true
 17  //   - Else if in prerelease mode, return false
 18  //   - else replace c with `[>=0.0.0]`
 19  // - If C is only the ANY comparator
 20  //   - if in prerelease mode, return true
 21  //   - else replace C with `[>=0.0.0]`
 22  // - Let EQ be the set of = comparators in c
 23  // - If EQ is more than one, return true (null set)
 24  // - Let GT be the highest > or >= comparator in c
 25  // - Let LT be the lowest < or <= comparator in c
 26  // - If GT and LT, and GT.semver > LT.semver, return true (null set)
 27  // - If any C is a = range, and GT or LT are set, return false
 28  // - If EQ
 29  //   - If GT, and EQ does not satisfy GT, return true (null set)
 30  //   - If LT, and EQ does not satisfy LT, return true (null set)
 31  //   - If EQ satisfies every C, return true
 32  //   - Else return false
 33  // - If GT
 34  //   - If GT.semver is lower than any > or >= comp in C, return false
 35  //   - If GT is >=, and GT.semver does not satisfy every C, return false
 36  //   - If GT.semver has a prerelease, and not in prerelease mode
 37  //     - If no C has a prerelease and the GT.semver tuple, return false
 38  // - If LT
 39  //   - If LT.semver is greater than any < or <= comp in C, return false
 40  //   - If LT is <=, and LT.semver does not satisfy every C, return false
 41  //   - If GT.semver has a prerelease, and not in prerelease mode
 42  //     - If no C has a prerelease and the LT.semver tuple, return false
 43  // - Else return true
 44  
 45  const subset = (sub, dom, options = {}) => {
 46    if (sub === dom) {
 47      return true
 48    }
 49  
 50    sub = new Range(sub, options)
 51    dom = new Range(dom, options)
 52    let sawNonNull = false
 53  
 54    OUTER: for (const simpleSub of sub.set) {
 55      for (const simpleDom of dom.set) {
 56        const isSub = simpleSubset(simpleSub, simpleDom, options)
 57        sawNonNull = sawNonNull || isSub !== null
 58        if (isSub) {
 59          continue OUTER
 60        }
 61      }
 62      // the null set is a subset of everything, but null simple ranges in
 63      // a complex range should be ignored.  so if we saw a non-null range,
 64      // then we know this isn't a subset, but if EVERY simple range was null,
 65      // then it is a subset.
 66      if (sawNonNull) {
 67        return false
 68      }
 69    }
 70    return true
 71  }
 72  
 73  const minimumVersionWithPreRelease = [new Comparator('>=0.0.0-0')]
 74  const minimumVersion = [new Comparator('>=0.0.0')]
 75  
 76  const simpleSubset = (sub, dom, options) => {
 77    if (sub === dom) {
 78      return true
 79    }
 80  
 81    if (sub.length === 1 && sub[0].semver === ANY) {
 82      if (dom.length === 1 && dom[0].semver === ANY) {
 83        return true
 84      } else if (options.includePrerelease) {
 85        sub = minimumVersionWithPreRelease
 86      } else {
 87        sub = minimumVersion
 88      }
 89    }
 90  
 91    if (dom.length === 1 && dom[0].semver === ANY) {
 92      if (options.includePrerelease) {
 93        return true
 94      } else {
 95        dom = minimumVersion
 96      }
 97    }
 98  
 99    const eqSet = new Set()
100    let gt, lt
101    for (const c of sub) {
102      if (c.operator === '>' || c.operator === '>=') {
103        gt = higherGT(gt, c, options)
104      } else if (c.operator === '<' || c.operator === '<=') {
105        lt = lowerLT(lt, c, options)
106      } else {
107        eqSet.add(c.semver)
108      }
109    }
110  
111    if (eqSet.size > 1) {
112      return null
113    }
114  
115    let gtltComp
116    if (gt && lt) {
117      gtltComp = compare(gt.semver, lt.semver, options)
118      if (gtltComp > 0) {
119        return null
120      } else if (gtltComp === 0 && (gt.operator !== '>=' || lt.operator !== '<=')) {
121        return null
122      }
123    }
124  
125    // will iterate one or zero times
126    for (const eq of eqSet) {
127      if (gt && !satisfies(eq, String(gt), options)) {
128        return null
129      }
130  
131      if (lt && !satisfies(eq, String(lt), options)) {
132        return null
133      }
134  
135      for (const c of dom) {
136        if (!satisfies(eq, String(c), options)) {
137          return false
138        }
139      }
140  
141      return true
142    }
143  
144    let higher, lower
145    let hasDomLT, hasDomGT
146    // if the subset has a prerelease, we need a comparator in the superset
147    // with the same tuple and a prerelease, or it's not a subset
148    let needDomLTPre = lt &&
149      !options.includePrerelease &&
150      lt.semver.prerelease.length ? lt.semver : false
151    let needDomGTPre = gt &&
152      !options.includePrerelease &&
153      gt.semver.prerelease.length ? gt.semver : false
154    // exception: <1.2.3-0 is the same as <1.2.3
155    if (needDomLTPre && needDomLTPre.prerelease.length === 1 &&
156        lt.operator === '<' && needDomLTPre.prerelease[0] === 0) {
157      needDomLTPre = false
158    }
159  
160    for (const c of dom) {
161      hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>='
162      hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<='
163      if (gt) {
164        if (needDomGTPre) {
165          if (c.semver.prerelease && c.semver.prerelease.length &&
166              c.semver.major === needDomGTPre.major &&
167              c.semver.minor === needDomGTPre.minor &&
168              c.semver.patch === needDomGTPre.patch) {
169            needDomGTPre = false
170          }
171        }
172        if (c.operator === '>' || c.operator === '>=') {
173          higher = higherGT(gt, c, options)
174          if (higher === c && higher !== gt) {
175            return false
176          }
177        } else if (gt.operator === '>=' && !satisfies(gt.semver, String(c), options)) {
178          return false
179        }
180      }
181      if (lt) {
182        if (needDomLTPre) {
183          if (c.semver.prerelease && c.semver.prerelease.length &&
184              c.semver.major === needDomLTPre.major &&
185              c.semver.minor === needDomLTPre.minor &&
186              c.semver.patch === needDomLTPre.patch) {
187            needDomLTPre = false
188          }
189        }
190        if (c.operator === '<' || c.operator === '<=') {
191          lower = lowerLT(lt, c, options)
192          if (lower === c && lower !== lt) {
193            return false
194          }
195        } else if (lt.operator === '<=' && !satisfies(lt.semver, String(c), options)) {
196          return false
197        }
198      }
199      if (!c.operator && (lt || gt) && gtltComp !== 0) {
200        return false
201      }
202    }
203  
204    // if there was a < or >, and nothing in the dom, then must be false
205    // UNLESS it was limited by another range in the other direction.
206    // Eg, >1.0.0 <1.0.1 is still a subset of <2.0.0
207    if (gt && hasDomLT && !lt && gtltComp !== 0) {
208      return false
209    }
210  
211    if (lt && hasDomGT && !gt && gtltComp !== 0) {
212      return false
213    }
214  
215    // we needed a prerelease range in a specific tuple, but didn't get one
216    // then this isn't a subset.  eg >=1.2.3-pre is not a subset of >=1.0.0,
217    // because it includes prereleases in the 1.2.3 tuple
218    if (needDomGTPre || needDomLTPre) {
219      return false
220    }
221  
222    return true
223  }
224  
225  // >=1.2.3 is lower than >1.2.3
226  const higherGT = (a, b, options) => {
227    if (!a) {
228      return b
229    }
230    const comp = compare(a.semver, b.semver, options)
231    return comp > 0 ? a
232      : comp < 0 ? b
233      : b.operator === '>' && a.operator === '>=' ? b
234      : a
235  }
236  
237  // <=1.2.3 is higher than <1.2.3
238  const lowerLT = (a, b, options) => {
239    if (!a) {
240      return b
241    }
242    const comp = compare(a.semver, b.semver, options)
243    return comp < 0 ? a
244      : comp > 0 ? b
245      : b.operator === '<' && a.operator === '<=' ? b
246      : a
247  }
248  
249  module.exports = subset