2017-05-03 15:35:00 +02:00
/ * *
* Copyright ( c ) 2013 - present , Facebook , Inc .
*
2017-10-14 18:40:54 +02:00
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree .
2017-05-03 15:35:00 +02:00
*
* /
'use strict' ;
var invariant = require ( './invariant' ) ;
var componentRegex = /\./ ;
var orRegex = /\|\|/ ;
var rangeRegex = /\s+\-\s+/ ;
var modifierRegex = /^(<=|<|=|>=|~>|~|>|)?\s*(.+)/ ;
var numericRegex = /^(\d*)(.*)/ ;
/ * *
* Splits input ` range ` on "||" and returns true if any subrange matches
* ` version ` .
*
* @ param { string } range
* @ param { string } version
* @ returns { boolean }
* /
function checkOrExpression ( range , version ) {
var expressions = range . split ( orRegex ) ;
if ( expressions . length > 1 ) {
return expressions . some ( function ( range ) {
return VersionRange . contains ( range , version ) ;
} ) ;
} else {
range = expressions [ 0 ] . trim ( ) ;
return checkRangeExpression ( range , version ) ;
}
}
/ * *
* Splits input ` range ` on " - " ( the surrounding whitespace is required ) and
* returns true if version falls between the two operands .
*
* @ param { string } range
* @ param { string } version
* @ returns { boolean }
* /
function checkRangeExpression ( range , version ) {
var expressions = range . split ( rangeRegex ) ;
! ( expressions . length > 0 && expressions . length <= 2 ) ? process . env . NODE _ENV !== 'production' ? invariant ( false , 'the "-" operator expects exactly 2 operands' ) : invariant ( false ) : void 0 ;
if ( expressions . length === 1 ) {
return checkSimpleExpression ( expressions [ 0 ] , version ) ;
} else {
var startVersion = expressions [ 0 ] ,
endVersion = expressions [ 1 ] ;
! ( isSimpleVersion ( startVersion ) && isSimpleVersion ( endVersion ) ) ? process . env . NODE _ENV !== 'production' ? invariant ( false , 'operands to the "-" operator must be simple (no modifiers)' ) : invariant ( false ) : void 0 ;
return checkSimpleExpression ( '>=' + startVersion , version ) && checkSimpleExpression ( '<=' + endVersion , version ) ;
}
}
/ * *
* Checks if ` range ` matches ` version ` . ` range ` should be a "simple" range ( ie .
* not a compound range using the " - " or "||" operators ) .
*
* @ param { string } range
* @ param { string } version
* @ returns { boolean }
* /
function checkSimpleExpression ( range , version ) {
range = range . trim ( ) ;
if ( range === '' ) {
return true ;
}
var versionComponents = version . split ( componentRegex ) ;
var _getModifierAndCompon = getModifierAndComponents ( range ) ,
modifier = _getModifierAndCompon . modifier ,
rangeComponents = _getModifierAndCompon . rangeComponents ;
switch ( modifier ) {
case '<' :
return checkLessThan ( versionComponents , rangeComponents ) ;
case '<=' :
return checkLessThanOrEqual ( versionComponents , rangeComponents ) ;
case '>=' :
return checkGreaterThanOrEqual ( versionComponents , rangeComponents ) ;
case '>' :
return checkGreaterThan ( versionComponents , rangeComponents ) ;
case '~' :
case '~>' :
return checkApproximateVersion ( versionComponents , rangeComponents ) ;
default :
return checkEqual ( versionComponents , rangeComponents ) ;
}
}
/ * *
* Checks whether ` a ` is less than ` b ` .
*
* @ param { array < string > } a
* @ param { array < string > } b
* @ returns { boolean }
* /
function checkLessThan ( a , b ) {
return compareComponents ( a , b ) === - 1 ;
}
/ * *
* Checks whether ` a ` is less than or equal to ` b ` .
*
* @ param { array < string > } a
* @ param { array < string > } b
* @ returns { boolean }
* /
function checkLessThanOrEqual ( a , b ) {
var result = compareComponents ( a , b ) ;
return result === - 1 || result === 0 ;
}
/ * *
* Checks whether ` a ` is equal to ` b ` .
*
* @ param { array < string > } a
* @ param { array < string > } b
* @ returns { boolean }
* /
function checkEqual ( a , b ) {
return compareComponents ( a , b ) === 0 ;
}
/ * *
* Checks whether ` a ` is greater than or equal to ` b ` .
*
* @ param { array < string > } a
* @ param { array < string > } b
* @ returns { boolean }
* /
function checkGreaterThanOrEqual ( a , b ) {
var result = compareComponents ( a , b ) ;
return result === 1 || result === 0 ;
}
/ * *
* Checks whether ` a ` is greater than ` b ` .
*
* @ param { array < string > } a
* @ param { array < string > } b
* @ returns { boolean }
* /
function checkGreaterThan ( a , b ) {
return compareComponents ( a , b ) === 1 ;
}
/ * *
* Checks whether ` a ` is "reasonably close" to ` b ` ( as described in
* https : //www.npmjs.org/doc/misc/semver.html). For example, if `b` is "1.3.1"
* then "reasonably close" is defined as ">= 1.3.1 and < 1.4" .
*
* @ param { array < string > } a
* @ param { array < string > } b
* @ returns { boolean }
* /
function checkApproximateVersion ( a , b ) {
var lowerBound = b . slice ( ) ;
var upperBound = b . slice ( ) ;
if ( upperBound . length > 1 ) {
upperBound . pop ( ) ;
}
var lastIndex = upperBound . length - 1 ;
var numeric = parseInt ( upperBound [ lastIndex ] , 10 ) ;
if ( isNumber ( numeric ) ) {
upperBound [ lastIndex ] = numeric + 1 + '' ;
}
return checkGreaterThanOrEqual ( a , lowerBound ) && checkLessThan ( a , upperBound ) ;
}
/ * *
* Extracts the optional modifier ( < , <= , = , >= , > , ~ , ~ > ) and version
* components from ` range ` .
*
* For example , given ` range ` ">= 1.2.3" returns an object with a ` modifier ` of
* ` ">=" ` and ` components ` of ` [1, 2, 3] ` .
*
* @ param { string } range
* @ returns { object }
* /
function getModifierAndComponents ( range ) {
var rangeComponents = range . split ( componentRegex ) ;
var matches = rangeComponents [ 0 ] . match ( modifierRegex ) ;
! matches ? process . env . NODE _ENV !== 'production' ? invariant ( false , 'expected regex to match but it did not' ) : invariant ( false ) : void 0 ;
return {
modifier : matches [ 1 ] ,
rangeComponents : [ matches [ 2 ] ] . concat ( rangeComponents . slice ( 1 ) )
} ;
}
/ * *
* Determines if ` number ` is a number .
*
* @ param { mixed } number
* @ returns { boolean }
* /
function isNumber ( number ) {
return ! isNaN ( number ) && isFinite ( number ) ;
}
/ * *
* Tests whether ` range ` is a "simple" version number without any modifiers
* ( ">" , "~" etc ) .
*
* @ param { string } range
* @ returns { boolean }
* /
function isSimpleVersion ( range ) {
return ! getModifierAndComponents ( range ) . modifier ;
}
/ * *
* Zero - pads array ` array ` until it is at least ` length ` long .
*
* @ param { array } array
* @ param { number } length
* /
function zeroPad ( array , length ) {
for ( var i = array . length ; i < length ; i ++ ) {
array [ i ] = '0' ;
}
}
/ * *
* Normalizes ` a ` and ` b ` in preparation for comparison by doing the following :
*
* - zero - pads ` a ` and ` b `
* - marks any "x" , "X" or "*" component in ` b ` as equivalent by zero - ing it out
* in both ` a ` and ` b `
* - marks any final "*" component in ` b ` as a greedy wildcard by zero - ing it
* and all of its successors in ` a `
*
* @ param { array < string > } a
* @ param { array < string > } b
* @ returns { array < array < string >> }
* /
function normalizeVersions ( a , b ) {
a = a . slice ( ) ;
b = b . slice ( ) ;
zeroPad ( a , b . length ) ;
// mark "x" and "*" components as equal
for ( var i = 0 ; i < b . length ; i ++ ) {
var matches = b [ i ] . match ( /^[x*]$/i ) ;
if ( matches ) {
b [ i ] = a [ i ] = '0' ;
// final "*" greedily zeros all remaining components
if ( matches [ 0 ] === '*' && i === b . length - 1 ) {
for ( var j = i ; j < a . length ; j ++ ) {
a [ j ] = '0' ;
}
}
}
}
zeroPad ( b , a . length ) ;
return [ a , b ] ;
}
/ * *
* Returns the numerical -- not the lexicographical -- ordering of ` a ` and ` b ` .
*
* For example , ` 10-alpha ` is greater than ` 2-beta ` .
*
* @ param { string } a
* @ param { string } b
* @ returns { number } - 1 , 0 or 1 to indicate whether ` a ` is less than , equal to ,
* or greater than ` b ` , respectively
* /
function compareNumeric ( a , b ) {
var aPrefix = a . match ( numericRegex ) [ 1 ] ;
var bPrefix = b . match ( numericRegex ) [ 1 ] ;
var aNumeric = parseInt ( aPrefix , 10 ) ;
var bNumeric = parseInt ( bPrefix , 10 ) ;
if ( isNumber ( aNumeric ) && isNumber ( bNumeric ) && aNumeric !== bNumeric ) {
return compare ( aNumeric , bNumeric ) ;
} else {
return compare ( a , b ) ;
}
}
/ * *
* Returns the ordering of ` a ` and ` b ` .
*
* @ param { string | number } a
* @ param { string | number } b
* @ returns { number } - 1 , 0 or 1 to indicate whether ` a ` is less than , equal to ,
* or greater than ` b ` , respectively
* /
function compare ( a , b ) {
! ( typeof a === typeof b ) ? process . env . NODE _ENV !== 'production' ? invariant ( false , '"a" and "b" must be of the same type' ) : invariant ( false ) : void 0 ;
if ( a > b ) {
return 1 ;
} else if ( a < b ) {
return - 1 ;
} else {
return 0 ;
}
}
/ * *
* Compares arrays of version components .
*
* @ param { array < string > } a
* @ param { array < string > } b
* @ returns { number } - 1 , 0 or 1 to indicate whether ` a ` is less than , equal to ,
* or greater than ` b ` , respectively
* /
function compareComponents ( a , b ) {
var _normalizeVersions = normalizeVersions ( a , b ) ,
aNormalized = _normalizeVersions [ 0 ] ,
bNormalized = _normalizeVersions [ 1 ] ;
for ( var i = 0 ; i < bNormalized . length ; i ++ ) {
var result = compareNumeric ( aNormalized [ i ] , bNormalized [ i ] ) ;
if ( result ) {
return result ;
}
}
return 0 ;
}
var VersionRange = {
/ * *
* Checks whether ` version ` satisfies the ` range ` specification .
*
* We support a subset of the expressions defined in
* https : //www.npmjs.org/doc/misc/semver.html:
*
* version Must match version exactly
* = version Same as just version
* > version Must be greater than version
* >= version Must be greater than or equal to version
* < version Must be less than version
* <= version Must be less than or equal to version
* ~ version Must be at least version , but less than the next significant
* revision above version :
* "~1.2.3" is equivalent to ">= 1.2.3 and < 1.3"
* ~ > version Equivalent to ~ version
* 1.2 . x Must match "1.2.x" , where "x" is a wildcard that matches
* anything
* 1.2 . * Similar to "1.2.x" , but "*" in the trailing position is a
* "greedy" wildcard , so will match any number of additional
* components :
* "1.2.*" will match "1.2.1" , "1.2.1.1" , "1.2.1.1.1" etc
* * Any version
* "" ( Empty string ) Same as *
* v1 - v2 Equivalent to ">= v1 and <= v2"
* r1 || r2 Passes if either r1 or r2 are satisfied
*
* @ param { string } range
* @ param { string } version
* @ returns { boolean }
* /
contains : function contains ( range , version ) {
return checkOrExpression ( range . trim ( ) , version . trim ( ) ) ;
}
} ;
module . exports = VersionRange ;