Upstream npm/node-semver state of the touched files before f6bf826ecc
This commit is contained in:
commit
d4354cc38b
3 files changed
+856
No files matched your search
@@ -0,0 +1,577 @@
|
||||
'use strict'
|
||||
|
||||
const SPACE_CHARACTERS = /\s+/g
|
||||
|
||||
// hoisted class for cyclic dependency
|
||||
class 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) {
|
||||
// strip build metadata so it can't bleed into the version
|
||||
range = range.replace(BUILDSTRIPRE, '')
|
||||
|
||||
// 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 = Range
|
||||
|
||||
const 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,
|
||||
src,
|
||||
t,
|
||||
comparatorTrimReplace,
|
||||
tildeTrimReplace,
|
||||
caretTrimReplace,
|
||||
} = require('../internal/re')
|
||||
const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require('../internal/constants')
|
||||
|
||||
// unbounded global build-metadata stripper used by parseRange
|
||||
const BUILDSTRIPRE = new RegExp(src[t.BUILD], 'g')
|
||||
|
||||
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 it
|
||||
const 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) => {
|
||||
comp = comp.replace(re[t.BUILD], '')
|
||||
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 === '*'
|
||||
|
||||
const invalidXRangeOrder = (M, m, p) => (
|
||||
(isX(M) && !isX(m)) ||
|
||||
(isX(m) && p && !isX(p))
|
||||
)
|
||||
|
||||
// ~, ~> --> * (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-0
|
||||
const 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]
|
||||
// if we're including prereleases in the match, then the lower bound is
|
||||
// -0, the lowest possible prerelease value, just like x-ranges and carets.
|
||||
// this keeps `~1.2` equivalent to the `1.2.x` x-range it's documented as.
|
||||
const z = options.includePrerelease ? '-0' : ''
|
||||
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${z} <${+M + 1}.0.0-0`
|
||||
} else if (isX(p)) {
|
||||
// ~1.2 == >=1.2.0 <1.3.0-0
|
||||
ret = `>=${M}.${m}.0${z} <${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-0
|
||||
const 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
|
||||
} <${M}.${m}.${+p + 1}-0`
|
||||
} else {
|
||||
ret = `>=${M}.${m}.${p
|
||||
} <${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)
|
||||
if (invalidXRangeOrder(M, m, p)) {
|
||||
return comp
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const Range = require('../../classes/range')
|
||||
const Comparator = require('../../classes/comparator')
|
||||
const rangeIntersection = require('../fixtures/range-intersection.js')
|
||||
|
||||
const rangeInclude = require('../fixtures/range-include.js')
|
||||
const rangeExclude = require('../fixtures/range-exclude.js')
|
||||
const rangeParse = require('../fixtures/range-parse.js')
|
||||
|
||||
test('range tests', t => {
|
||||
t.plan(rangeInclude.length)
|
||||
rangeInclude.forEach(([range, ver, options]) => {
|
||||
const r = new Range(range, options)
|
||||
t.ok(r.test(ver), `${range} satisfied by ${ver}`)
|
||||
})
|
||||
})
|
||||
|
||||
test('range parsing', t => {
|
||||
t.plan(rangeParse.length)
|
||||
rangeParse.forEach(([range, expect, options]) =>
|
||||
t.test(`${range} ${expect} ${JSON.stringify(options)}`, t => {
|
||||
if (expect === null) {
|
||||
t.throws(() => new Range(range, options), TypeError, `invalid range: ${range}`)
|
||||
} else {
|
||||
t.equal(new Range(range, options).range || '*', expect, `${range} => ${expect}`)
|
||||
t.equal(new Range(range, options).range, new Range(expect).range,
|
||||
'parsing both yields same result')
|
||||
}
|
||||
t.end()
|
||||
}))
|
||||
})
|
||||
|
||||
test('throw for empty comparator set, even in loose mode', t => {
|
||||
t.throws(() => new Range('sadf||asdf', { loose: true }),
|
||||
TypeError('Invalid SemVer Range: sadf||asdf'))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('convert comparator to range', t => {
|
||||
const c = new Comparator('>=1.2.3')
|
||||
const r = new Range(c)
|
||||
t.equal(r.raw, c.value, 'created range from comparator')
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('range as argument to range ctor', t => {
|
||||
const loose = new Range('1.2.3', { loose: true })
|
||||
t.equal(new Range(loose, { loose: true }), loose, 'loose option')
|
||||
t.equal(new Range(loose, true), loose, 'loose boolean')
|
||||
t.not(new Range(loose), loose, 'created new range if not matched')
|
||||
|
||||
const incPre = new Range('1.2.3', { includePrerelease: true })
|
||||
t.equal(new Range(incPre, { includePrerelease: true }), incPre,
|
||||
'include prerelease, option match returns argument')
|
||||
t.not(new Range(incPre), incPre,
|
||||
'include prerelease, option mismatch does not return argument')
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('negative range tests', t => {
|
||||
t.plan(rangeExclude.length)
|
||||
rangeExclude.forEach(([range, ver, options]) => {
|
||||
const r = new Range(range, options)
|
||||
t.notOk(r.test(ver), `${range} not satisfied by ${ver}`)
|
||||
})
|
||||
})
|
||||
|
||||
test('strict vs loose ranges', (t) => {
|
||||
[
|
||||
['>=01.02.03', '>=1.2.3'],
|
||||
['~1.02.03beta', '>=1.2.3-beta <1.3.0-0'],
|
||||
].forEach(([loose, comps]) => {
|
||||
t.throws(() => new Range(loose))
|
||||
t.equal(new Range(loose, true).range, comps)
|
||||
})
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('tostrings', (t) => {
|
||||
t.equal(new Range('>= v1.2.3').toString(), '>=1.2.3')
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('formatted value is calculated lazily and cached', (t) => {
|
||||
const r = new Range('>= v1.2.3')
|
||||
t.equal(r.formatted, undefined)
|
||||
t.equal(r.format(), '>=1.2.3')
|
||||
t.equal(r.formatted, '>=1.2.3')
|
||||
t.equal(r.format(), '>=1.2.3')
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('ranges intersect', (t) => {
|
||||
rangeIntersection.forEach(([r0, r1, expect]) => {
|
||||
t.test(`${r0} <~> ${r1}`, t => {
|
||||
const range0 = new Range(r0)
|
||||
const range1 = new Range(r1)
|
||||
|
||||
t.equal(range0.intersects(range1), expect,
|
||||
`${r0} <~> ${r1} objects`)
|
||||
t.equal(range1.intersects(range0), expect,
|
||||
`${r1} <~> ${r0} objects`)
|
||||
t.end()
|
||||
})
|
||||
})
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('missing range parameter in range intersect', (t) => {
|
||||
t.throws(() => {
|
||||
new Range('1.0.0').intersects()
|
||||
}, new TypeError('a Range is required'),
|
||||
'throws type error')
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('cache', (t) => {
|
||||
const cached = Symbol('cached')
|
||||
const r1 = new Range('1.0.0')
|
||||
r1.set[0][cached] = true
|
||||
const r2 = new Range('1.0.0')
|
||||
t.equal(r1.set[0][cached], true)
|
||||
t.equal(r2.set[0][cached], true) // Will be true, showing it's cached.
|
||||
t.end()
|
||||
})
|
||||
Vendored
+151
@@ -0,0 +1,151 @@
|
||||
'use strict'
|
||||
|
||||
// [range, canonical result, options]
|
||||
// null result means it's not a valid range
|
||||
// '*' is the return value from functions.validRange(), but
|
||||
// new Range().range will be '' in those cases
|
||||
const { MAX_SAFE_INTEGER } = require('../../internal/constants')
|
||||
module.exports = [
|
||||
['1.0.0 - 2.0.0', '>=1.0.0 <=2.0.0'],
|
||||
['1.0.0 - 2.0.0', '>=1.0.0-0 <2.0.1-0', { includePrerelease: true }],
|
||||
['1 - 2', '>=1.0.0 <3.0.0-0'],
|
||||
['1 - 2', '>=1.0.0-0 <3.0.0-0', { includePrerelease: true }],
|
||||
['1.0 - 2.0', '>=1.0.0 <2.1.0-0'],
|
||||
['1.0 - 2.0', '>=1.0.0-0 <2.1.0-0', { includePrerelease: true }],
|
||||
['1.0.0', '1.0.0', { loose: false }],
|
||||
['>=*', '*'],
|
||||
['', '*'],
|
||||
['*', '*'],
|
||||
['>=1.0.0', '>=1.0.0'],
|
||||
['>1.0.0', '>1.0.0'],
|
||||
['<=2.0.0', '<=2.0.0'],
|
||||
['1', '>=1.0.0 <2.0.0-0'],
|
||||
['<2.0.0', '<2.0.0'],
|
||||
['>= 1.0.0', '>=1.0.0'],
|
||||
['>= 1.0.0', '>=1.0.0'],
|
||||
['>= 1.0.0', '>=1.0.0'],
|
||||
['> 1.0.0', '>1.0.0'],
|
||||
['> 1.0.0', '>1.0.0'],
|
||||
['<= 2.0.0', '<=2.0.0'],
|
||||
['<= 2.0.0', '<=2.0.0'],
|
||||
['<= 2.0.0', '<=2.0.0'],
|
||||
['< 2.0.0', '<2.0.0'],
|
||||
['<\t2.0.0', '<2.0.0'],
|
||||
['>=0.1.97', '>=0.1.97'],
|
||||
['0.1.20 || 1.2.4', '0.1.20||1.2.4'],
|
||||
['>=0.2.3 || <0.0.1', '>=0.2.3||<0.0.1'],
|
||||
['||', '*'],
|
||||
['2.x.x', '>=2.0.0 <3.0.0-0'],
|
||||
['1.2.x', '>=1.2.0 <1.3.0-0'],
|
||||
['1.2.x || 2.x', '>=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0'],
|
||||
['1.x.5', null],
|
||||
['1.*.5', null],
|
||||
['1.x.5 || 2.x', null],
|
||||
['x.1', null],
|
||||
['x.1.2', null],
|
||||
['x.x.1', null],
|
||||
['x', '*'],
|
||||
['2.*.*', '>=2.0.0 <3.0.0-0'],
|
||||
['1.2.*', '>=1.2.0 <1.3.0-0'],
|
||||
['1.2.* || 2.*', '>=1.2.0 <1.3.0-0||>=2.0.0 <3.0.0-0'],
|
||||
['2', '>=2.0.0 <3.0.0-0'],
|
||||
['2.3', '>=2.3.0 <2.4.0-0'],
|
||||
['~2.4', '>=2.4.0 <2.5.0-0'],
|
||||
['~>3.2.1', '>=3.2.1 <3.3.0-0'],
|
||||
['~1', '>=1.0.0 <2.0.0-0'],
|
||||
['~>1', '>=1.0.0 <2.0.0-0'],
|
||||
['~> 1', '>=1.0.0 <2.0.0-0'],
|
||||
['~1.0', '>=1.0.0 <1.1.0-0'],
|
||||
['~ 1.0', '>=1.0.0 <1.1.0-0'],
|
||||
// tilde with includePrerelease must lower-bound at -0 just like the
|
||||
// equivalent x-range (e.g. ~1.2 is documented as the same as 1.2.x),
|
||||
// so that prereleases such as 1.2.0-rc match. See #512.
|
||||
['~1', '>=1.0.0-0 <2.0.0-0', { includePrerelease: true }],
|
||||
['~1.x', '>=1.0.0-0 <2.0.0-0', { includePrerelease: true }],
|
||||
['~1.2', '>=1.2.0-0 <1.3.0-0', { includePrerelease: true }],
|
||||
['~1.2.x', '>=1.2.0-0 <1.3.0-0', { includePrerelease: true }],
|
||||
['~0.0', '<0.1.0-0', { includePrerelease: true }],
|
||||
// a fully-specified tilde keeps its exact lower bound (matches caret),
|
||||
// and an explicit prerelease is preserved as-is
|
||||
['~1.2.3', '>=1.2.3 <1.3.0-0', { includePrerelease: true }],
|
||||
['~1.2.3-beta.4', '>=1.2.3-beta.4 <1.3.0-0', { includePrerelease: true }],
|
||||
['^0', '<1.0.0-0'],
|
||||
['^ 1', '>=1.0.0 <2.0.0-0'],
|
||||
['^0.1', '>=0.1.0 <0.2.0-0'],
|
||||
['^1.0', '>=1.0.0 <2.0.0-0'],
|
||||
['^1.2', '>=1.2.0 <2.0.0-0'],
|
||||
['^0.0.1', '>=0.0.1 <0.0.2-0'],
|
||||
['^0.0.1-beta', '>=0.0.1-beta <0.0.2-0'],
|
||||
['^0.1.2', '>=0.1.2 <0.2.0-0'],
|
||||
['^1.2.3', '>=1.2.3 <2.0.0-0'],
|
||||
['^1.2.3-beta.4', '>=1.2.3-beta.4 <2.0.0-0'],
|
||||
['<1', '<1.0.0-0'],
|
||||
['< 1', '<1.0.0-0'],
|
||||
['>=1', '>=1.0.0'],
|
||||
['>= 1', '>=1.0.0'],
|
||||
['<1.2', '<1.2.0-0'],
|
||||
['< 1.2', '<1.2.0-0'],
|
||||
['>01.02.03', '>1.2.3', true],
|
||||
['>01.02.03', null],
|
||||
['~1.2.3beta', '>=1.2.3-beta <1.3.0-0', { loose: true }],
|
||||
['~1.2.3beta', null],
|
||||
['^ 1.2 ^ 1', '>=1.2.0 <2.0.0-0 >=1.0.0'],
|
||||
['1.2 - 3.4.5', '>=1.2.0 <=3.4.5'],
|
||||
['1.2.3 - 3.4', '>=1.2.3 <3.5.0-0'],
|
||||
['1.2 - 3.4', '>=1.2.0 <3.5.0-0'],
|
||||
['>1', '>=2.0.0'],
|
||||
['>1.2', '>=1.3.0'],
|
||||
['>X', '<0.0.0-0'],
|
||||
['<X', '<0.0.0-0'],
|
||||
['<x <* || >* 2.x', '<0.0.0-0'],
|
||||
['>x 2.x || * || <x', '*'],
|
||||
['>=09090', null],
|
||||
['>=09090', '>=9090.0.0', true],
|
||||
['>=09090-0', null, { includePrerelease: true }],
|
||||
['>=09090-0', null, { loose: true, includePrerelease: true }],
|
||||
[`^${MAX_SAFE_INTEGER}.0.0`, null],
|
||||
[`=${MAX_SAFE_INTEGER}.0.0`, `${MAX_SAFE_INTEGER}.0.0`],
|
||||
[`^${MAX_SAFE_INTEGER - 1}.0.0`, `>=${MAX_SAFE_INTEGER - 1}.0.0 <${MAX_SAFE_INTEGER}.0.0-0`],
|
||||
// x-ranges with build metadata
|
||||
['1.x.x+build >2.x+build', '>=1.0.0 <2.0.0-0 >=3.0.0', null],
|
||||
['>=1.x+build <2.x.x+build', '>=1.0.0 <2.0.0-0', null],
|
||||
['1.x.x+build || 2.x.x+build', '>=1.0.0 <2.0.0-0||>=2.0.0 <3.0.0-0', null],
|
||||
['1.x+build.123', '>=1.0.0 <2.0.0-0', null],
|
||||
['1.x.x+meta-data', '>=1.0.0 <2.0.0-0', null],
|
||||
['1.x.x+build.123 >2.x.x+meta-data', '>=1.0.0 <2.0.0-0 >=3.0.0', null],
|
||||
['1.x.x+build <2.x.x+meta', '>=1.0.0 <2.0.0-0', null],
|
||||
['>1.x+build <=2.x.x+meta', '>=2.0.0 <3.0.0-0', null],
|
||||
[' 1.x.x+build >2.x.x+build ', '>=1.0.0 <2.0.0-0 >=3.0.0', null],
|
||||
['^1.x+build', '>=1.0.0 <2.0.0-0', null],
|
||||
['^1.x.x+build', '>=1.0.0 <2.0.0-0', null],
|
||||
['^1.2.x+build', '>=1.2.0 <2.0.0-0', null],
|
||||
['^1.x+meta-data', '>=1.0.0 <2.0.0-0', null],
|
||||
['^1.x.x+build.123', '>=1.0.0 <2.0.0-0', null],
|
||||
['~1.x+build', '>=1.0.0 <2.0.0-0', null],
|
||||
['~1.x.x+build', '>=1.0.0 <2.0.0-0', null],
|
||||
['~1.2.x+build', '>=1.2.0 <1.3.0-0', null],
|
||||
['~1.x+meta-data', '>=1.0.0 <2.0.0-0', null],
|
||||
['~1.x.x+build.123', '>=1.0.0 <2.0.0-0', null],
|
||||
['^1.x.x+build || ~2.x.x+meta', '>=1.0.0 <2.0.0-0||>=2.0.0 <3.0.0-0', null],
|
||||
['~1.x.x+build >2.x+meta', '>=1.0.0 <2.0.0-0 >=3.0.0', null],
|
||||
['^1.x+build.123 <2.x.x+meta-data', '>=1.0.0 <2.0.0-0', null],
|
||||
// // x-ranges with prerelease and build
|
||||
['1.x.x-alpha+build', '>=1.0.0 <2.0.0-0', null],
|
||||
['>1.x.x-alpha+build', '>=2.0.0', null],
|
||||
['>=1.x.x-alpha+build <2.x.x+build', '>=1.0.0 <2.0.0-0', null],
|
||||
['1.x.x-alpha+build || 2.x.x+build', '>=1.0.0 <2.0.0-0||>=2.0.0 <3.0.0-0', null],
|
||||
// long build metadata must be stripped, not bled into the version
|
||||
['4.17.0+' + 'a'.repeat(250) + '3', '4.17.0', { loose: true }],
|
||||
['4.17.0+' + 'a'.repeat(251), '4.17.0', { loose: true }],
|
||||
['v1.0+' + 'a'.repeat(249) + 'x6', '>=1.0.0 <1.1.0-0', null],
|
||||
['1.2.3+' + 'a'.repeat(251) + ' - 2.0.0', '>=1.2.3 <=2.0.0', null],
|
||||
['1.2.3+' + 'a'.repeat(251) + ' - 2.0.0', '>=1.2.3 <=2.0.0', { loose: true }],
|
||||
['> 1.2.3+' + 'a'.repeat(251), '>1.2.3', null],
|
||||
['>= 1.2.3+' + 'a'.repeat(251), '>=1.2.3', { loose: true }],
|
||||
['~1.2.3+' + 'a'.repeat(251), '>=1.2.3 <1.3.0-0', null],
|
||||
['^1.2.3+' + 'a'.repeat(251), '>=1.2.3 <2.0.0-0', null],
|
||||
['1.2.3+sha512.' + 'a'.repeat(251), '1.2.3', { loose: true }],
|
||||
['1.2.3+sha256.' + 'a'.repeat(200) + '.' + 'b'.repeat(200), '1.2.3', { loose: true }],
|
||||
['1.2.3+' + 'a'.repeat(251) + ' || 2.0.0+' + 'b'.repeat(251),
|
||||
'1.2.3||2.0.0', { loose: true }],
|
||||
]
|
||||
Reference in new issue
Block a user