├── .gitignore ├── .npmignore ├── .travis.yml ├── package.json ├── LICENSE ├── convert-range.js ├── README.md ├── sver.js └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | test 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | git: 2 | depth: 1 3 | language: node_js 4 | node_js: 5 | - '8' 6 | 7 | before_install: 8 | - npm install 9 | script: 10 | - npm run test 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sver", 3 | "version": "1.8.4", 4 | "description": "Simple Semver and SemverRange classes", 5 | "main": "sver.js", 6 | "scripts": { 7 | "test": "mocha -u tdd -R dot" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+ssh://git@github.com/guybedford/sver.git" 12 | }, 13 | "keywords": [ 14 | "semver" 15 | ], 16 | "author": "Guy Bedford", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/guybedford/sver/issues" 20 | }, 21 | "homepage": "https://github.com/guybedford/sver#readme", 22 | "devDependencies": { 23 | "mocha": "^10.2.0", 24 | "semver": "^6.3.0" 25 | }, 26 | "optionalDependencies": { 27 | "semver": "^6.3.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | ----------- 3 | 4 | Copyright (C) 2017 Guy Bedford 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /convert-range.js: -------------------------------------------------------------------------------- 1 | const nodeSemver = require('semver'); 2 | const { Semver, SemverRange } = require('./sver'); 3 | 4 | module.exports = function nodeRangeToSemverRange (range) { 5 | let parsed = nodeSemver.validRange(range); 6 | 7 | // tag version 8 | if (!parsed) 9 | return new SemverRange(range); 10 | 11 | if (parsed === '*') 12 | return new SemverRange(parsed); 13 | 14 | try { 15 | let semverRange = new SemverRange(range); 16 | if (!semverRange.version.tag) 17 | return semverRange; 18 | } 19 | catch (e) { 20 | if (e.code !== 'ENOTSEMVER') 21 | throw e; 22 | } 23 | 24 | let outRange; 25 | for (let union of parsed.split('||')) { 26 | // compute the intersection into a lowest upper bound and a highest lower bound 27 | let upperBound, lowerBound, upperEq, lowerEq; 28 | for (let intersection of union.split(' ')) { 29 | let lt = intersection[0] === '<'; 30 | let gt = intersection[0] === '>'; 31 | if (!lt && !gt) { 32 | upperBound = intersection; 33 | upperEq = true; 34 | break; 35 | } 36 | let eq = intersection[1] === '='; 37 | if (!gt) { 38 | let version = new Semver(intersection.substr(1 + eq)); 39 | if (!upperBound || upperBound.gt(version)) { 40 | upperBound = version; 41 | upperEq = eq; 42 | } 43 | } 44 | else if (!lt) { 45 | let eq = intersection[1] === '='; 46 | let version = new Semver(intersection.substr(1 + eq)); 47 | if (!lowerBound || lowerBound.lt(version)) { 48 | lowerBound = version; 49 | lowerEq = eq; 50 | } 51 | } 52 | } 53 | 54 | // no upper bound -> wildcard 55 | if (!upperBound) { 56 | outRange = new SemverRange('*'); 57 | continue; 58 | } 59 | 60 | // if the lower bound is greater than the upper bound then just return the lower bound exactly 61 | if (lowerBound && upperBound && lowerBound.gt(upperBound)) { 62 | let curRange = new SemverRange(lowerBound.toString()); 63 | // the largest or highest union range wins 64 | if (!outRange || !outRange.contains(curRange) && (curRange.gt(outRange) || curRange.contains(outRange))) 65 | outRange = curRange; 66 | continue; 67 | } 68 | 69 | // determine the largest semver range satisfying the upper bound 70 | let upperRange; 71 | if (upperBound) { 72 | // if the upper bound has an equality then we return it directly 73 | if (upperEq) { 74 | let curRange = new SemverRange(upperBound.toString()); 75 | // the largest or highest union range wins 76 | if (!outRange || !outRange.contains(curRange) && (curRange.gt(outRange) || curRange.contains(outRange))) 77 | outRange = curRange; 78 | continue; 79 | } 80 | 81 | let major = 0, minor = 0, patch = 0, rangeType = ''; 82 | 83 | // if an exact prerelease range, match the lower bound as a range 84 | if (upperBound.pre && lowerBound.major === upperBound.major && lowerBound.minor === upperBound.minor && lowerBound.patch === upperBound.patch) { 85 | outRange = new SemverRange('~' + lowerBound.toString()); 86 | continue; 87 | } 88 | 89 | // <2.0.0 -> ^1.0.0 90 | else if (upperBound.patch === 0) { 91 | if (upperBound.minor === 0) { 92 | if (upperBound.major > 0) { 93 | major = upperBound.major - 1; 94 | rangeType = '^'; 95 | } 96 | } 97 | // <1.2.0 -> ~1.1.0 98 | else { 99 | major = upperBound.major; 100 | minor = upperBound.minor - 1; 101 | rangeType = '~'; 102 | } 103 | } 104 | // <1.2.3 -> ~1.2.0 105 | else { 106 | major = upperBound.major; 107 | minor = upperBound.minor; 108 | patch = 0; 109 | rangeType = '~'; 110 | } 111 | 112 | if (major === 0 && rangeType === '^') 113 | upperRange = new SemverRange('0'); 114 | else 115 | upperRange = new SemverRange(rangeType + major + '.' + minor + '.' + patch); 116 | } 117 | 118 | if (!lowerBound) { 119 | outRange = upperRange; 120 | continue; 121 | } 122 | 123 | // determine the lower range semver range 124 | let lowerRange; 125 | if (!lowerEq) { 126 | if (lowerBound.pre) 127 | lowerRange = new SemverRange('^^' + lowerBound.major + '.' + lowerBound.minor + '.' + lowerBound.patch + '-' + [...lowerBound.pre, 1].join('.')); 128 | else 129 | lowerRange = new SemverRange('^^' + lowerBound.major + '.' + lowerBound.minor + '.' + (lowerBound.patch + 1)); 130 | } 131 | else { 132 | lowerRange = new SemverRange('^^' + lowerBound.toString()); 133 | } 134 | 135 | // we then intersect the upper semver range with the lower semver range 136 | // if the intersection is empty, we return the upper range only 137 | let curRange = upperRange ? lowerRange.intersect(upperRange) || upperRange : lowerRange; 138 | 139 | // the largest or highest union range wins 140 | if (!outRange || !outRange.contains(curRange) && (curRange.gt(outRange) || curRange.contains(outRange))) 141 | outRange = curRange; 142 | } 143 | return outRange; 144 | } 145 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sver 2 | 3 | [![Build Status](https://travis-ci.org/guybedford/sver.svg?branch=master)](https://travis-ci.org/guybedford/sver) 4 | 5 | Another Semver utility library. Supports NodeJS 6+ only. No maintenance guarantees. 6 | 7 | This is the semver library used by jspm. 8 | 9 | ``` 10 | npm install sver 11 | ``` 12 | 13 | ```js 14 | const { Semver, SemverRange } = require('sver'); 15 | 16 | // Static usage: 17 | SemverRange.match('^1.2.3', '1.2.4'); // true 18 | 19 | // Class usage: 20 | let range = new SemverRange('^1.2.3'); 21 | let version = new Semver('1.2.4'); 22 | version.matches(range); // true 23 | range.has(version); // true 24 | ``` 25 | 26 | ### Range support 27 | 28 | Restricts version ranges to the simplified cases: 29 | * `*`: Wildcard range 30 | * `MAJOR`: Match exact major 31 | * `MAJOR.MINOR` Match exact major and minor 32 | * `MAJOR.MINOR.PATCH[-PRE]` Match exact semver 33 | * `~MAJOR.MINOR.PATCH[-PRE]`: Match patch bumps 34 | * `^MAJOR.MINOR.PATCH[-PRE]`: Match minor and patch bumps 35 | 36 | Invalid ranges will fallback to being detected as exact string matches. 37 | 38 | ### Prerelease Matching 39 | 40 | By default, as per convention, ranges like `^1.2.3-alpha` only match prerelease ranges on the same patch (`1.2.3-alpha.4`), but 41 | not prerelease ranges from further patches (`1.3.4-alpha`). 42 | 43 | To alter this matching, a third boolean argument can be provided to the match function to support these unstable matches: 44 | 45 | ```js 46 | SemverRange.match('^1.2.3', '1.5.6-beta'); // false 47 | SemverRange.match('^1.2.3', '1.5.6-beta', true); // true 48 | ``` 49 | 50 | ### Best Version Match 51 | 52 | ```js 53 | let versions = ['1.2.3', '1.3.4-alpha', '1.3.4-alpha.1', '1.3.4-beta']; 54 | let range = new SemverRange('*'); 55 | 56 | let bestStableMatch = range.bestMatch(versions); 57 | bestStableMatch.toString(); // 1.2.3 58 | 59 | let bestUnstableMatch = range.bestMatch(versions, true); 60 | bestUnstableMatch.toString(); // 1.3.4-beta 61 | ``` 62 | 63 | ### Version and Range Sorting 64 | 65 | ```js 66 | let versions = ['2.4.5', '2.3.4-alpha', '1.2.3', '2.3.4-alpha.2']; 67 | let ranges = ['^1.2.3', '1.2', '2.3.4']; 68 | 69 | versions.sort(Semver.compare); // [1.2.3, 2.3.4-alpha, 2.3.4-alpha.2, 2.4.5] 70 | ranges.sort(SemverRange.compare) // [1.2, ^1.2.3, 2.3.4] 71 | ``` 72 | 73 | ### Conversion from Node Semver Ranges 74 | 75 | A utility function is included to convert Node Semver ranges into Semver ranges. 76 | 77 | This requires `semver` to be installed in the application running this process. 78 | 79 | _Note this conversion is lossy by definition._ 80 | 81 | ```js 82 | const convertRange = require('sver/convert-range'); 83 | 84 | convertRange('>=2.3.4 <3.0.0').toString(); // ^2.3.4 85 | convertRange('1 || 2 || 3').toString(); // ^3.0.0 86 | ``` 87 | 88 | ### Semver and Semver Range Validation 89 | 90 | When a version string fails semver validation it falls back to being treated as a tag, still as a `Semver` instance. 91 | 92 | For example: 93 | 94 | ```js 95 | let version = new Semver('x.y.z'); 96 | version.tag === 'x.y.z'; // true 97 | 98 | version = new Semver('^1.2.3'); 99 | version.major === undefined; // true 100 | version.tag === '^1.2.3'; // true 101 | ``` 102 | 103 | For validation, rather use `Semver.isValid` and `SemverRange.isValid`: 104 | 105 | ```js 106 | Semver.isValid('x.y.z'); // false 107 | Semver.isValid('^1.2.3'); // false 108 | SemverRange.isValid('^1.2.3'); // true 109 | ``` 110 | 111 | ## API 112 | 113 | ### Semver 114 | 115 | Static methods: 116 | 117 | * `Semver.isValid(version: string): boolean`: Whether the given string is a valid semver. 118 | * `Semver.compare(v1: Semver|string, v2: Semver|string): number`: 1 if v1 > v2, -1 if v1 < v2, 0 if equal. 119 | 120 | For a given Semver instance `version = new Semver('X.Y.Z')`, 121 | 122 | * `version.major`: The major version number. 123 | * `version.minor`: The minor version number. 124 | * `version.patch`: The patch version number. 125 | * `version.pre`: The prerelease identifer, as an array of strings (`.`-separated). 126 | * `version.build`: The build identifier, as a string. 127 | * `version.tag`: If not a valid semver, the full tag string. 128 | * `version.gt(otherVersion: Semver|string): bool`: Whether this version is greater than the other version. 129 | * `version.lt(otherVersion: Semver|string): bool`: Whether this version is less than the other version. 130 | * `version.eq(otherVerion: Semver|string): bool`: Whether this version equals the other version. 131 | * `version.matches(range: SemverRange|string, unstable?: bool): bool`: Whether this version matches the given version range. 132 | * `version.toString(): string`: Convert the version back to a string. 133 | 134 | ### SemverRange 135 | 136 | Static methods: 137 | 138 | * `SemverRange.match(range: SemverRange|string, version: Semver|string, unstable = false): bool`: Whether the version matches the range. 139 | * `SemverRange.isValid(range: string): bool`: Whether the given range string is a valid semver range (in this simplified grammar). 140 | * `SemverRange.compare(r1: SemverRange|string, r2: SemverRange|string): number`: 1 if r1 > r2, -1 if r1 < r2, 0 if equal. 141 | 142 | For a given SemverRange instance `range = new SemverRange('^X.Y.Z')`, 143 | 144 | * `range.type: string`: Returns `'wildcard'`, `'major'`, `'stable'` or `'exact'`. 145 | * `range.version: Smever`: Returns the `Semver` instance corresponding to the range. 146 | * `range.isExact: string`: Returns true if the range is an exact version only. 147 | * `range.isStable: string`: Returns true if the range is a stable version range. 148 | * `range.isMajor: string`: Returns true if the range is a major version range. 149 | * `range.isWildcard: string`: Returns true if the range is the wildcard version range. 150 | * `range.gt(otherRange: SemverRange|string): bool`: Whether the range is greater than the other range. 151 | * `range.lt(otherRange: SemverRange|string): bool`: Whether the range is less than the other range. 152 | * `range.eq(otherRange: SemverRange|string): bool`: Whether the range is exactly the same as the other range. 153 | * `range.has(version: Semver|string, unstable = false): bool`: Whether the range includes the given version. 154 | * `range.contains(otherRange: SemverRange|string): bool`: Whether the range fully contains the other range. 155 | * `range.intersect(otherRange: SemverRange|string): SemverRange|undefined`: The intersection range, if any. 156 | * `range.bestMatch(versions: (Semver|string)[], unstable = false): Semver|undefined`: The intersection range, if any. 157 | * `range.toString()`: Convert the range back to a string. 158 | 159 | ## License 160 | 161 | MIT 162 | -------------------------------------------------------------------------------- /sver.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const shortSemverRegEx = /^([~\^])?(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?$/; 4 | const semverRegEx = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([\da-z-]+(?:\.[\da-z-]+)*))?(\+[\da-z-]+)?$/i; 5 | exports.semverRegEx = semverRegEx; 6 | exports.shortSemverRegEx = shortSemverRegEx; 7 | 8 | const MAJOR = Symbol('major'); 9 | const MINOR = Symbol('minor'); 10 | const PATCH = Symbol('patch'); 11 | const PRE = Symbol('pre'); 12 | const BUILD = Symbol('build'); 13 | const TAG = Symbol('tag'); 14 | 15 | let numRegEx = /^\d+$/; 16 | class Semver { 17 | constructor (version) { 18 | let semver = version.match(semverRegEx); 19 | if (!semver) { 20 | this[TAG] = version; 21 | return; 22 | } 23 | this[MAJOR] = parseInt(semver[1], 10); 24 | this[MINOR] = parseInt(semver[2], 10); 25 | this[PATCH] = parseInt(semver[3], 10); 26 | this[PRE] = semver[4] && semver[4].split('.'); 27 | this[BUILD] = semver[5]; 28 | } 29 | get major () { 30 | return this[MAJOR]; 31 | } 32 | get minor () { 33 | return this[MINOR]; 34 | } 35 | get patch () { 36 | return this[PATCH]; 37 | } 38 | get pre () { 39 | return this[PRE]; 40 | } 41 | get build () { 42 | return this[BUILD]; 43 | } 44 | get tag () { 45 | return this[TAG]; 46 | } 47 | gt (version) { 48 | return Semver.compare(this, version) === 1; 49 | } 50 | lt (version) { 51 | return Semver.compare(this, version) === -1; 52 | } 53 | eq (version) { 54 | if (!(version instanceof Semver)) 55 | version = new Semver(version); 56 | 57 | if (this[TAG] && version[TAG]) 58 | return this[TAG] === version[TAG]; 59 | if (this[TAG] || version[TAG]) 60 | return false; 61 | if (this[MAJOR] !== version[MAJOR]) 62 | return false; 63 | if (this[MINOR] !== version[MINOR]) 64 | return false; 65 | if (this[PATCH] !== version[PATCH]) 66 | return false; 67 | if (this[PRE] === undefined && version[PRE] === undefined) 68 | return true; 69 | if (this[PRE] === undefined || version[PRE] === undefined) 70 | return false; 71 | if (this[PRE].length !== version[PRE].length) 72 | return false; 73 | for (let i = 0; i < this[PRE].length; i++) { 74 | if (this[PRE][i] !== version[PRE][i]) 75 | return false; 76 | } 77 | return this[BUILD] === version[BUILD]; 78 | } 79 | matches (range, unstable = false) { 80 | if (!(range instanceof SemverRange)) 81 | range = new SemverRange(range); 82 | return range.has(this, unstable); 83 | } 84 | toString () { 85 | if (this[TAG]) 86 | return this[TAG]; 87 | return this[MAJOR] + '.' + this[MINOR] + '.' + this[PATCH] + (this[PRE] ? '-' + this[PRE].join('.') : '') + (this[BUILD] ? this[BUILD] : ''); 88 | } 89 | toJSON() { 90 | return this.toString(); 91 | } 92 | static isValid (version) { 93 | let semver = version.match(semverRegEx); 94 | return semver && semver[2] !== undefined && semver[3] !== undefined; 95 | } 96 | static compare (v1, v2) { 97 | if (!(v1 instanceof Semver)) 98 | v1 = new Semver(v1); 99 | if (!(v2 instanceof Semver)) 100 | v2 = new Semver(v2); 101 | 102 | // not semvers - tags have equal precedence 103 | if (v1[TAG] && v2[TAG]) 104 | return 0; 105 | // semver beats tag version 106 | if (v1[TAG]) 107 | return -1; 108 | if (v2[TAG]) 109 | return 1; 110 | // compare version numbers 111 | if (v1[MAJOR] !== v2[MAJOR]) 112 | return v1[MAJOR] > v2[MAJOR] ? 1 : -1; 113 | if (v1[MINOR] !== v2[MINOR]) 114 | return v1[MINOR] > v2[MINOR] ? 1 : -1; 115 | if (v1[PATCH] !== v2[PATCH]) 116 | return v1[PATCH] > v2[PATCH] ? 1 : -1; 117 | if (!v1[PRE] && !v2[PRE]) 118 | return 0; 119 | if (!v1[PRE]) 120 | return 1; 121 | if (!v2[PRE]) 122 | return -1; 123 | // prerelease comparison 124 | return prereleaseCompare(v1[PRE], v2[PRE]); 125 | } 126 | } 127 | exports.Semver = Semver; 128 | 129 | function prereleaseCompare (v1Pre, v2Pre) { 130 | for (let i = 0, l = Math.min(v1Pre.length, v2Pre.length); i < l; i++) { 131 | if (v1Pre[i] !== v2Pre[i]) { 132 | let isNum1 = v1Pre[i].match(numRegEx); 133 | let isNum2 = v2Pre[i].match(numRegEx); 134 | // numeric has lower precedence 135 | if (isNum1 && !isNum2) 136 | return -1; 137 | if (isNum2 && !isNum1) 138 | return 1; 139 | // compare parts 140 | if (isNum1 && isNum2) 141 | return parseInt(v1Pre[i], 10) > parseInt(v2Pre[i], 10) ? 1 : -1; 142 | else 143 | return v1Pre[i] > v2Pre[i] ? 1 : -1; 144 | } 145 | } 146 | if (v1Pre.length === v2Pre.length) 147 | return 0; 148 | // more pre-release fields win if equal 149 | return v1Pre.length > v2Pre.length ? 1 : -1; 150 | 151 | } 152 | 153 | const WILDCARD_RANGE = 0; 154 | const MAJOR_RANGE = 1; 155 | const STABLE_RANGE = 2; 156 | const EXACT_RANGE = 3; 157 | 158 | const TYPE = Symbol('type'); 159 | const VERSION = Symbol('version'); 160 | 161 | class SemverRange { 162 | constructor (versionRange) { 163 | if (versionRange === '*' || versionRange === '') { 164 | this[TYPE] = WILDCARD_RANGE; 165 | return; 166 | } 167 | let shortSemver = versionRange.match(shortSemverRegEx); 168 | if (shortSemver) { 169 | if (shortSemver[1]) 170 | versionRange = versionRange.substr(1); 171 | if (shortSemver[3] === undefined) { 172 | // ^, ~ mean the same thing for a single major 173 | this[VERSION] = new Semver(versionRange + '.0.0'); 174 | this[TYPE] = MAJOR_RANGE; 175 | } 176 | else { 177 | this[VERSION] = new Semver(versionRange + '.0'); 178 | // ^ only becomes major range for major > 0 179 | if (shortSemver[1] === '^' && shortSemver[2] !== '0') 180 | this[TYPE] = MAJOR_RANGE; 181 | else 182 | this[TYPE] = STABLE_RANGE; 183 | } 184 | // empty pre array === support prerelease ranges 185 | this[VERSION][PRE] = this[VERSION][PRE] || []; 186 | } 187 | // forces hat on 0.x versions 188 | else if (versionRange.startsWith('^^')) { 189 | this[VERSION] = new Semver(versionRange.substr(2)); 190 | this[TYPE] = MAJOR_RANGE; 191 | } 192 | else if (versionRange[0] === '^') { 193 | this[VERSION] = new Semver(versionRange.substr(1)); 194 | if (this[VERSION][MAJOR] === 0) { 195 | if (this[VERSION][MINOR] === 0) 196 | this[TYPE] = EXACT_RANGE; 197 | else 198 | this[TYPE] = STABLE_RANGE; 199 | } 200 | else { 201 | this[TYPE] = MAJOR_RANGE; 202 | } 203 | } 204 | else if (versionRange[0] === '~') { 205 | this[VERSION] = new Semver(versionRange.substr(1)); 206 | this[TYPE] = STABLE_RANGE; 207 | } 208 | else { 209 | this[VERSION] = new Semver(versionRange); 210 | this[TYPE] = EXACT_RANGE; 211 | } 212 | if (this[VERSION][TAG] && this[TYPE] !== EXACT_RANGE) 213 | this[TYPE] = EXACT_RANGE; 214 | } 215 | get isExact () { 216 | return this[TYPE] === EXACT_RANGE; 217 | } 218 | get isExactSemver () { 219 | return this[TYPE] === EXACT_RANGE && this.version[TAG] === undefined; 220 | } 221 | get isExactTag () { 222 | return this[TYPE] === EXACT_RANGE && this.version[TAG] !== undefined; 223 | } 224 | get isStable () { 225 | return this[TYPE] === STABLE_RANGE; 226 | } 227 | get isMajor () { 228 | return this[TYPE] === MAJOR_RANGE; 229 | } 230 | get isWildcard () { 231 | return this[TYPE] === WILDCARD_RANGE; 232 | } 233 | get type () { 234 | switch (this[TYPE]) { 235 | case WILDCARD_RANGE: 236 | return 'wildcard'; 237 | case MAJOR_RANGE: 238 | return 'major'; 239 | case STABLE_RANGE: 240 | return 'stable'; 241 | case EXACT_RANGE: 242 | return 'exact'; 243 | } 244 | } 245 | get version () { 246 | return this[VERSION]; 247 | } 248 | gt (range) { 249 | return SemverRange.compare(this, range) === 1; 250 | } 251 | lt (range) { 252 | return SemverRange.compare(this, range) === -1; 253 | } 254 | eq (range) { 255 | return SemverRange.compare(this, range) === 0; 256 | } 257 | has (version, unstable = false) { 258 | if (!(version instanceof Semver)) 259 | version = new Semver(version); 260 | if (this[TYPE] === WILDCARD_RANGE) 261 | return unstable || (!version[PRE] && !version[TAG]); 262 | if (this[TYPE] === EXACT_RANGE) 263 | return this[VERSION].eq(version); 264 | if (version[TAG]) 265 | return false; 266 | if (this[VERSION][MAJOR] !== version[MAJOR]) 267 | return false; 268 | if (this[TYPE] === MAJOR_RANGE ? this[VERSION][MINOR] > version[MINOR] : this[VERSION][MINOR] !== version[MINOR]) 269 | return false; 270 | if ((this[TYPE] === MAJOR_RANGE ? this[VERSION][MINOR] === version[MINOR] : true) && this[VERSION][PATCH] > version[PATCH]) 271 | return false; 272 | if (version[PRE] === undefined || version[PRE].length === 0) 273 | return true; 274 | if (this[VERSION][PRE] === undefined || this[VERSION][PRE].length === 0) 275 | return unstable; 276 | if (unstable === false && (this[VERSION][MINOR] !== version[MINOR] || this[VERSION][PATCH] !== version[PATCH])) 277 | return false; 278 | return prereleaseCompare(this[VERSION][PRE], version[PRE]) !== 1; 279 | } 280 | contains (range) { 281 | if (!(range instanceof SemverRange)) 282 | range = new SemverRange(range); 283 | if (this[TYPE] === WILDCARD_RANGE) 284 | return true; 285 | if (range[TYPE] === WILDCARD_RANGE) 286 | return false; 287 | return range[TYPE] >= this[TYPE] && this.has(range[VERSION], true); 288 | } 289 | intersect (range) { 290 | if (!(range instanceof SemverRange)) 291 | range = new SemverRange(range); 292 | 293 | if (this[TYPE] === WILDCARD_RANGE && range[TYPE] === WILDCARD_RANGE) 294 | return this; 295 | if (this[TYPE] === WILDCARD_RANGE) 296 | return range; 297 | if (range[TYPE] === WILDCARD_RANGE) 298 | return this; 299 | 300 | if (this[TYPE] === EXACT_RANGE) 301 | return range.has(this[VERSION], true) ? this : undefined; 302 | if (range[TYPE] === EXACT_RANGE) 303 | return this.has(range[VERSION], true) ? range : undefined; 304 | 305 | let higherRange, lowerRange, polarity; 306 | if (range[VERSION].gt(this[VERSION])) { 307 | higherRange = range; 308 | lowerRange = this; 309 | polarity = true; 310 | } 311 | else { 312 | higherRange = this; 313 | lowerRange = range; 314 | polarity = false; 315 | } 316 | 317 | if (!lowerRange.has(higherRange[VERSION], true)) 318 | return; 319 | 320 | if (lowerRange[TYPE] === MAJOR_RANGE) 321 | return polarity ? range : this; 322 | 323 | let intersection = new SemverRange(higherRange[VERSION].toString()); 324 | intersection[TYPE] = STABLE_RANGE; 325 | return intersection; 326 | } 327 | bestMatch (versions, unstable = false) { 328 | let maxSemver; 329 | versions.forEach(version => { 330 | if (!(version instanceof Semver)) 331 | version = new Semver(version); 332 | if (!this.has(version, unstable)) 333 | return; 334 | if (!maxSemver) 335 | maxSemver = version; 336 | else if (Semver.compare(version, maxSemver) === 1) 337 | maxSemver = version; 338 | }); 339 | return maxSemver; 340 | } 341 | toString () { 342 | let version = this[VERSION]; 343 | switch (this[TYPE]) { 344 | case WILDCARD_RANGE: 345 | return '*'; 346 | case MAJOR_RANGE: 347 | if (version[MAJOR] === 0 && version[MINOR] === 0 && version[PATCH] === 0) 348 | return '0'; 349 | if (version[PRE] && version[PRE].length === 0 && version[PATCH] === 0) 350 | return '^' + version[MAJOR] + '.' + version[MINOR]; 351 | return '^' + version.toString(); 352 | case STABLE_RANGE: 353 | if (version[PRE] && version[PRE].length === 0 && version[PATCH] === 0 || version[MAJOR] === 0 && version[MINOR] === 0) 354 | return version[MAJOR] + '.' + version[MINOR]; 355 | return '~' + version.toString(); 356 | case EXACT_RANGE: 357 | return version.toString(); 358 | } 359 | } 360 | toJSON() { 361 | return this.toString(); 362 | } 363 | static match (range, version, unstable = false) { 364 | if (!(version instanceof Semver)) 365 | version = new Semver(version); 366 | return version.matches(range, unstable); 367 | } 368 | static isValid (range) { 369 | let semverRange = new SemverRange(range); 370 | return semverRange[TYPE] !== EXACT_RANGE || semverRange[VERSION][TAG] === undefined; 371 | } 372 | static compare (r1, r2) { 373 | if (!(r1 instanceof SemverRange)) 374 | r1 = new SemverRange(r1); 375 | if (!(r2 instanceof SemverRange)) 376 | r2 = new SemverRange(r2); 377 | if (r1[TYPE] === WILDCARD_RANGE && r2[TYPE] === WILDCARD_RANGE) 378 | return 0; 379 | if (r1[TYPE] === WILDCARD_RANGE) 380 | return 1; 381 | if (r2[TYPE] === WILDCARD_RANGE) 382 | return -1; 383 | let cmp = Semver.compare(r1[VERSION], r2[VERSION]); 384 | if (cmp !== 0) { 385 | return cmp; 386 | } 387 | if (r1[TYPE] === r2[TYPE]) 388 | return 0; 389 | return r1[TYPE] > r2[TYPE] ? 1 : -1; 390 | } 391 | } 392 | exports.SemverRange = SemverRange; 393 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const { Semver, SemverRange } = require('../sver.js'); 2 | const convertRange = require('../convert-range.js'); 3 | const assert = require('assert'); 4 | 5 | suite('Semver Major and Minor Ranges', () => { 6 | 7 | test('Range test 1', () => { 8 | assert.equal(SemverRange.match('0.0.1', '0.0.1'), true); 9 | assert.equal(SemverRange.match('0.0.1', '0.0.0'), false); 10 | assert.equal(SemverRange.match('0.0.1', '0.0.2'), false); 11 | assert.equal(SemverRange.match('0.0.1', '0.0.1-betaasdf-asdf'), false); 12 | assert.equal(new SemverRange('0.1').toString(), '0.1'); 13 | assert.equal(new SemverRange('~0.1').toString(), '0.1'); 14 | assert.equal(new SemverRange('^0.1').toString(), '0.1'); 15 | }); 16 | test('Range test 2', () => { 17 | assert.equal(new Semver('0.1.0-').lt('0.1.0'), true); 18 | assert.equal(SemverRange.match('0.1', '0.1.1'), true); 19 | assert.equal(SemverRange.match('0.1', '0.1.4'), true); 20 | assert.equal(SemverRange.match('0.1', '0.1.23423-sdf'), false); 21 | assert.equal(SemverRange.match('0.1', '0.1'), false); 22 | assert.equal(SemverRange.match('0.1', '1.1.1'), false); 23 | assert.equal(SemverRange.match('0.1', '0.0.1'), false); 24 | }); 25 | test('Range test 3', () => { 26 | let range = new SemverRange('0'); 27 | assert.equal(range.contains('0.0.1'), true); 28 | assert.equal(range.contains('0.1.1'), true); 29 | assert.equal(range.contains('0.1.1-beta'), true); 30 | assert.equal(range.contains('1.1.1-beta'), false); 31 | }); 32 | test('Range test 4', () => { 33 | let range = new SemverRange('1'); 34 | assert.equal(range.contains('1.5'), true); 35 | assert.equal(range.contains('1.5.2'), true); 36 | assert.equal(range.contains('1.0.0'), true); 37 | assert.equal(range.contains('1.5.3-beta1'), true); 38 | assert.equal(range.contains('2.0.0-beta1'), false); 39 | assert.equal(range.contains('0.1.1'), false); 40 | }); 41 | test('Range test 5', () => { 42 | assert.equal(SemverRange.match('1.2', '1.2.0'), true); 43 | assert.equal(SemverRange.match('1.2', '1.2.1'), true); 44 | assert.equal(SemverRange.match('1.2', '1.2.1-beta'), false); 45 | assert.equal(SemverRange.match('1.2', '1.2.1-beta', true), true); 46 | assert.equal(SemverRange.match('2.0', '2.1.0'), false); 47 | }); 48 | test('Range test 6', () => { 49 | assert.equal(SemverRange.match('4.3.2', '4.3.2-beta'), false); 50 | assert.equal(SemverRange.match('4.3.2', '4.3.2'), true); 51 | assert.equal(SemverRange.match('4.3.2', '4.3.3'), false); 52 | }); 53 | test('Range test 7', () => { 54 | assert.equal(SemverRange.match('4.3.2-beta.4', '4.3.2-beta.4'), true); 55 | assert.equal(SemverRange.match('^4.3.2-beta.4', '4.3.2-beta.5'), true); 56 | assert.equal(SemverRange.match('^4.3.2-beta.4', '4.3.2-beta.3'), false); 57 | assert.equal(SemverRange.match('^4.3.2-beta.4', '4.3.3-beta.5'), false); 58 | assert.equal(SemverRange.match('4.3.2-beta.4', '4.3.2'), false); 59 | assert.equal(SemverRange.match('^4.3.2-beta.4', 'asdfa'), false); 60 | }); 61 | test('Range test 8', () => { 62 | assert.equal(SemverRange.match('ksdufh8234-', 'asdfa'), false); 63 | assert.equal(SemverRange.match('ksdufh8234-', 'ksdufh8234-'), true); 64 | }); 65 | test('Range test 9', () => { 66 | assert.equal(Semver.compare('0.2.0', 'master'), 1); 67 | assert.equal(Semver.compare('wip/here/some-thing', '0.3.0-alpha'), -1); 68 | assert.equal(Semver.compare('wip%2Fhere%2Fsome%2Fthing', '0.3.0-alpha'), -1); 69 | assert.equal(Semver.compare('1.2.a', '0.0.1'), -1); 70 | assert.equal(Semver.compare('1.2.3-beta', '1.2.3-alpha'), 1); 71 | assert.equal(Semver.compare('1.2.3-beta.1', '1.2.3-beta.11'), -1); 72 | assert.equal(Semver.compare('1.2.3-beta.1', '1.2.3-beta.11'), -1); 73 | assert.equal(Semver.compare('1.2.3-beta.2', '1.2.3-beta.1'), 1); 74 | assert.equal(Semver.compare('1.2.3', '1.2.3-beta.1'), 1); 75 | }); 76 | test('Range test 10', () => { 77 | assert.equal(Semver.compare('1.0.0-alpha', '1.0.0-alpha.1'), -1); 78 | assert.equal(Semver.compare('1.0.0-alpha.1', '1.0.0-alpha.beta'), -1); 79 | assert.equal(Semver.compare('1.0.0-alpha.beta', '1.0.0-beta'), -1); 80 | assert.equal(Semver.compare('1.0.0-beta', '1.0.0-beta.2'), -1); 81 | assert.equal(Semver.compare('1.0.0-beta.2', '1.0.0-beta.11'), -1); 82 | assert.equal(Semver.compare('1.0.0-beta.11', '1.0.0-rc.1'), -1); 83 | assert.equal(Semver.compare('1.0.0-rc.1', '1.0.0'), -1); 84 | }); 85 | test('Range test 11', () => { 86 | let versions = ['1.2.3', '1.3.4-alpha', '1.3.4-alpha.1', '1.3.4-beta', '1.2.3+b', '1.2.3+a']; 87 | let range = new SemverRange('*'); 88 | 89 | let bestStableMatch = range.bestMatch(versions); 90 | assert.equal(bestStableMatch.toString(), '1.2.3'); 91 | 92 | range = new SemverRange(''); 93 | 94 | let bestUnstableMatch = range.bestMatch(versions, true); 95 | assert.equal(bestUnstableMatch.toString(), '1.3.4-beta'); 96 | 97 | range = new SemverRange('^7.0.0'); 98 | bestUnstableMatch = range.bestMatch(['7.0.0-beta.1', '7.0.0-alpha', 'latest'], true); 99 | assert.equal(bestUnstableMatch.toString(), '7.0.0-beta.1'); 100 | 101 | range = new SemverRange('^7.0.0'); 102 | bestStableMatch = range.bestMatch(['7.0.0-beta.1', '7.0.0-alpha', 'latest'], false); 103 | assert.equal(bestStableMatch, undefined); 104 | 105 | range = new SemverRange('*'); 106 | bestStableMatch = range.bestMatch(['7.0.0-beta.1', '7.0.0-alpha', 'latest'], false); 107 | assert.equal(bestStableMatch, undefined); 108 | 109 | range = new SemverRange('*'); 110 | bestStableMatch = range.bestMatch(['7.0.0-beta.1', '7.0.0-alpha', 'latest'], true); 111 | assert.equal(bestUnstableMatch.toString(), '7.0.0-beta.1'); 112 | }); 113 | }); 114 | 115 | suite('Semver Compare', () => { 116 | 117 | test('Version Compare 1', () => { 118 | assert.equal(Semver.compare('1.0.1', '1.0.11'), -1); 119 | assert.equal(Semver.compare('1.0.3', '1.2.11'), -1); 120 | assert.equal(Semver.compare('1.2.11', '1.2.1'), 1); 121 | assert.equal(Semver.compare('1.2.10', '1.2.1'), 1); 122 | }); 123 | 124 | test('Range Compare 1', () => { 125 | assert.equal(SemverRange.compare('1.4', '1.4.5'), -1); 126 | assert.equal(SemverRange.compare('1.4.5', '1.4'), 1); 127 | assert.equal(SemverRange.compare('1.4', '2.0.0'), -1); 128 | assert.equal(SemverRange.compare('1.0.0', '1.4'), -1); 129 | assert.equal(SemverRange.compare('1', '2'), -1); 130 | assert.equal(SemverRange.compare('1.4.0', '1'), 1); 131 | assert.equal(SemverRange.compare('1.0.1', '1.0.11'), -1); 132 | assert.equal(SemverRange.compare('1.0.3', '1.2.11'), -1); 133 | assert.equal(SemverRange.compare('1.2.11', '1.2.1'), 1); 134 | assert.equal(SemverRange.compare('1.2.10', '1.2.1'), 1); 135 | }); 136 | 137 | test('Compare 2', () => { 138 | assert.equal(Semver.compare('2.0', '2.1.0'), -1); 139 | }); 140 | 141 | test('Semver sort', () => { 142 | var versions = ['1.0.3', '1.0.4', '1.0.5', '1.0.6', '1.0.7', '1.0.8', '1.2.0', '1.2.1', '1.2.10', '1.2.11', '1.2.12', '1.2.2', '1.2.3', '1.2.4', '1.2.5', '1.2.6', '1.2.7', '1.2.8', '1.2.9']; 143 | versions.sort(Semver.compare); 144 | assert.equal(versions.pop(), '1.2.12'); 145 | }); 146 | 147 | test('Compare with build numbers', () => { 148 | assert.equal(Semver.compare('0.2.0-build.2+sha.c4e21ef', '0.2.0-build.1+sha.9db70d3'), 0); 149 | }); 150 | 151 | test('Contains', () => { 152 | assert.equal(SemverRange.match('0.1', '0.1.0-alpha', true), true); 153 | assert.equal(new SemverRange('0.1').contains('0.1.0-beta'), true); 154 | }); 155 | 156 | test('Tag range', () => { 157 | const tagRange = new SemverRange('asdf'); 158 | assert.equal(tagRange.isExact, true); 159 | assert.equal(tagRange.isExactSemver, false); 160 | assert.equal(tagRange.isExactTag, true); 161 | assert.equal(tagRange.isStable, false); 162 | }); 163 | 164 | test('Sorting', () => { 165 | var ranges1 = ['0.0.1-beta', '0.0.1', '0.1.0', '0.1', '0.1.3-beta.1', '^0.1.0', '*', '2.0.1', '2.1', '2.1.0-beta', '~2.1.0', '^2.0.0']; 166 | assert.equal(JSON.stringify(ranges1.sort(SemverRange.compare)), JSON.stringify([ 167 | '0.0.1-beta', '0.0.1', '0.1', '^0.1.0', '0.1.0', '0.1.3-beta.1', '^2.0.0', '2.0.1', '2.1', '2.1.0-beta', '~2.1.0', '*' 168 | ])); 169 | 170 | let versions = ['2.4.5', '2.3.4-alpha', '1.2.3', '2.3.4-alpha.2']; 171 | let ranges2 = ['^1.2.3', '1.2', '2.3.4']; 172 | 173 | assert.equal(JSON.stringify(versions.sort(Semver.compare)), JSON.stringify([ 174 | '1.2.3', '2.3.4-alpha', '2.3.4-alpha.2', '2.4.5' 175 | ])); 176 | assert.equal(JSON.stringify(ranges2.sort(SemverRange.compare)), JSON.stringify([ 177 | '1.2', '^1.2.3', '2.3.4' 178 | ])); 179 | }); 180 | 181 | }); 182 | 183 | 184 | suite('Semver Compatibility Ranges', () => { 185 | 186 | test('Basic compatibility', () => { 187 | assert.equal(SemverRange.match('^1.5.2', '1.4.0'), false); 188 | assert.equal(SemverRange.match('1', '1.4.0'), true); 189 | assert.equal(SemverRange.match('0.1', '0.1.0'), true); 190 | assert.equal(SemverRange.match('0.1', '0.2.0'), false); 191 | assert.equal(SemverRange.match('1.1', '1.2.0'), false); 192 | assert.equal(SemverRange.match('1.1', '1.1.0'), true); 193 | assert.equal(SemverRange.match('^0.0.2', '1.0.2'), false); 194 | assert.equal(SemverRange.match('^0.0.1', '0.0.1'), true); 195 | assert.equal(SemverRange.match('^0.0.1', '0.0.2'), false); 196 | assert.equal(SemverRange.match('^0.0.1', '1.0.2'), false); 197 | assert.equal(SemverRange.match('^0.0.1', '1.0.1'), false); 198 | assert.equal(SemverRange.match('^0.1.0', '0.1.0'), true); 199 | assert.equal(SemverRange.match('^0.1.0', '0.2.0'), false); 200 | assert.equal(SemverRange.match('^0.1.0', '1.1.0'), false); 201 | assert.equal(SemverRange.match('1.0.0-beta.13', '1.0.0-beta.5b'), false); 202 | }); 203 | 204 | test('Semver compatibility', () => { 205 | assert.equal(SemverRange.match('^1.1.12', '1.1.12'), true); 206 | assert.equal(SemverRange.match('^1.1.12', '1.1.11'), false); 207 | assert.equal(SemverRange.match('^1.1.12', '1.1.345'), true); 208 | assert.equal(SemverRange.match('^1.1.12', '1.10.345'), true); 209 | assert.equal(SemverRange.match('^1.1.12', '2.10.345'), false); 210 | }); 211 | 212 | test('Prerelease ranges', () => { 213 | assert.equal(SemverRange.match('^1.0.4-alpha.1', '1.0.4-alpha.1'), true); 214 | assert.equal(SemverRange.match('^1.0.4-alpha.1', '1.0.4-alpha.2'), true); 215 | assert.equal(SemverRange.match('^1.0.4-alpha.1', '1.0.4-beta'), true); 216 | assert.equal(SemverRange.match('^1.0.4-alpha.1', '1.0.4-beta.10'), true); 217 | assert.equal(SemverRange.match('^1.0.4-alpha.1', '1.0.4'), true); 218 | }); 219 | 220 | }); 221 | 222 | suite('Fuzzy Compatibility Ranges', () => { 223 | 224 | test('Basic compatibility', () => { 225 | assert.equal(SemverRange.match('^1.5.2', '1.4.0'), false); 226 | assert.equal(SemverRange.match('~0.0.0', '0.1.0'), false); 227 | assert.equal(SemverRange.match('~0.0.0', '1.2.0'), false); 228 | assert.equal(SemverRange.match('~1.0.0', '1.2.0'), false); 229 | assert.equal(SemverRange.match('~1.0.0', '2.4.0'), false); 230 | assert.equal(SemverRange.match('~0.1.0', '0.1.0'), true); 231 | assert.equal(SemverRange.match('~0.1.0', '0.2.0'), false); 232 | assert.equal(SemverRange.match('~1.1.0', '1.2.0'), false); 233 | assert.equal(SemverRange.match('~1.1.0', '1.1.0'), true); 234 | assert.equal(SemverRange.match('~0.0.2', '1.0.2'), false); 235 | assert.equal(SemverRange.match('~0.0.1', '0.0.1'), true); 236 | assert.equal(SemverRange.match('~0.0.1', '0.0.2'), true); 237 | assert.equal(SemverRange.match('~0.0.1', '1.0.2'), false); 238 | assert.equal(SemverRange.match('~0.0.1', '1.0.1'), false); 239 | assert.equal(SemverRange.match('~0.1.0', '0.1.0'), true); 240 | assert.equal(SemverRange.match('~0.1.0', '0.2.0'), false); 241 | assert.equal(SemverRange.match('~0.1.0', '1.1.0'), false); 242 | }); 243 | 244 | test('Semver compatibility', () => { 245 | assert.equal(SemverRange.match('~1.1.12', '1.1.12'), true); 246 | assert.equal(SemverRange.match('~1.1.12', '1.1.11'), false); 247 | assert.equal(SemverRange.match('~1.1.12', '1.1.345'), true); 248 | assert.equal(SemverRange.match('~1.1.12', '1.10.345'), false); 249 | assert.equal(SemverRange.match('~1.1.12', '2.10.345'), false); 250 | }); 251 | 252 | test('Prerelease ranges', () => { 253 | assert.equal(SemverRange.match('~1.0.4-alpha.1', '1.0.4-alpha.1'), true); 254 | assert.equal(SemverRange.match('~1.0.4-alpha.1', '1.0.4-alpha.2'), true); 255 | assert.equal(SemverRange.match('~1.0.4-alpha.1', '1.0.4-beta'), true); 256 | assert.equal(SemverRange.match('~1.0.4-alpha.1', '1.0.4-beta.10'), true); 257 | assert.equal(SemverRange.match('~1.0.4-alpha.1', '1.0.4'), true); 258 | assert.equal(SemverRange.match('~1.0.4-alpha.1', '1.0.5-alpha.1', true), true); 259 | assert.equal(SemverRange.match('~1.0.4-alpha.1', '1.0.5-alpha.2', true), true); 260 | assert.equal(SemverRange.match('~1.0.4-alpha.1', '1.0.4-alpha', true), false); 261 | assert.equal(SemverRange.match('~1.0.4-alpha.1', '1.0.4-alpha.0', true), false); 262 | assert.equal(SemverRange.match('~1.0.4-alpha.1', '1.1.4-alpha', true), false); 263 | }); 264 | 265 | }); 266 | 267 | suite('Range functions', () => { 268 | test('Range contains', () => { 269 | assert.equal(new SemverRange('~1.1.0').contains('~1.1.1'), true); 270 | assert.equal(new SemverRange('~1.1.0').contains('~1.1.1-alpha'), true); 271 | assert.equal(new SemverRange('~1.1.0').contains('1.1.100'), true); 272 | assert.equal(new SemverRange('~1.1.0').contains('~1.2.1'), false); 273 | assert.equal(new SemverRange('^1.1.0').contains('1.2.1-alpha'), true); 274 | assert.equal(new SemverRange('^1.1.0').contains('~1.0.1'), false); 275 | assert.equal(new SemverRange('^1.1.0').contains('1.3'), true); 276 | assert.equal(new SemverRange('^1.1.0').contains('^1.2.1'), true); 277 | assert.equal(new SemverRange('^1.1.0').contains('~1.3.1-alpha'), true); 278 | assert.equal(new SemverRange('^1.1.0').contains('1.3.3-alpha'), true); 279 | }); 280 | 281 | test('Range intersections', () => { 282 | assert.equal(new SemverRange('~1.1.0').intersect('~1.1.1').toString(), '~1.1.1'); 283 | assert.equal(new SemverRange('~1.1.0').intersect('~1.1.1-alpha').toString(), '~1.1.1-alpha'); 284 | assert.equal(new SemverRange('~1.1.0').intersect('1.1.100').toString(), '1.1.100'); 285 | assert.equal(new SemverRange('~1.1.0').intersect('~1.2.1'), undefined); 286 | assert.equal(new SemverRange('^1.1.0').intersect('1.2.1-alpha').toString(), '1.2.1-alpha'); 287 | assert.equal(new SemverRange('^1.1.0').intersect('~1.0.1'), undefined); 288 | assert.equal(new SemverRange('^1.1.0').intersect('1.3').toString(), '1.3'); 289 | assert.equal(new SemverRange('^1.1.0').intersect('~1.3').toString(), '1.3'); 290 | assert.equal(new SemverRange('^1.1.0').intersect('^1.3').toString(), '^1.3'); 291 | assert.equal(new SemverRange('^1.1.0').intersect('^1.3.0').toString(), '^1.3.0'); 292 | assert.equal(new SemverRange('^1.1.0').intersect('^1.2.1').toString(), '^1.2.1'); 293 | assert.equal(new SemverRange('^1.1.0').intersect('~1.3.1-alpha').toString(), '~1.3.1-alpha'); 294 | assert.equal(new SemverRange('^1.1.0').intersect('1.3.3-alpha').toString(), '1.3.3-alpha'); 295 | assert.equal(new SemverRange('^1.0.5').intersect('~1.0.7').toString(), '~1.0.7'); 296 | assert.equal(new SemverRange('^1.0.5').intersect('~1.0.3').toString(), '~1.0.5'); 297 | assert.equal(new SemverRange('~2.5.0').intersect('^2.5.0-alpha.1').toString(), '~2.5.0'); 298 | assert.equal(new SemverRange('~2.5.0').intersect('^2.5.1-alpha.1').toString(), '~2.5.1-alpha.1'); 299 | assert.equal(new SemverRange('~2.5.0').intersect('^2.5.0-alpha.1').toString(), '~2.5.0'); 300 | assert.equal(new SemverRange('^7.0.0').intersect('^7.0.0-rc.1').toString(), '^7.0.0'); 301 | }); 302 | 303 | test('Has', () => { 304 | assert.equal(new SemverRange('^2.3.4').has('2.5.0'), true); 305 | }); 306 | }); 307 | 308 | suite('Range conversion', () => { 309 | test('Semver conversions', () => { 310 | assert.equal(convertRange('>=2.3.4 <3.0.0').toString(), '^2.3.4'); 311 | assert.equal(convertRange('1 || 2 || 3 || 2.1').toString(), '^3.0.0'); 312 | assert.equal(convertRange('>=2.3.4 <2.4.0').toString(), '~2.3.4'); 313 | assert.equal(convertRange('1 2 3').toString(), '3.0.0'); 314 | assert.equal(convertRange('hello-world').toString(), 'hello-world'); 315 | assert.equal(convertRange('^0.2.3').toString(), '~0.2.3'); 316 | assert.equal(convertRange('^0.0.3').toString(), '0.0.3'); 317 | assert.equal(convertRange('>2.0.0 <=2.5.3').toString(), '2.5.3'); 318 | assert.equal(convertRange('>2.0.0 <2.5.3').toString(), '~2.5.0'); 319 | assert.equal(convertRange('>2.5.0 <2.5.3').toString(), '~2.5.1'); 320 | assert.equal(convertRange('>2.5.1-alpha <2.5.3').toString(), '~2.5.1-alpha.1'); 321 | assert.equal(convertRange('^2.3.4 || ~2.5.0').toString(), '^2.3.4'); 322 | assert.equal(convertRange('> =3.1.10').toString(), '*'); 323 | assert.equal(convertRange('=0.1.20').toString(), '0.1.20'); 324 | assert.equal(convertRange('=0.1.20 || =0.1.21 || 0.1.22 || 0.0.1').toString(), '0.1.22'); 325 | assert.equal(convertRange('=0.1.20 || =0.1.21 || 0.1.22 || ^2 || 0.0.1').toString(), '^2.0.0'); 326 | assert.equal(convertRange('=0.1.20 || ^0.1').toString(), '~0.1.0'); 327 | assert.equal(convertRange('=1.1.20 || 1 || ^1.1').toString(), '^1.0.0'); 328 | assert.equal(convertRange('2.1 2 || 1').toString(), '~2.1.0'); 329 | assert.equal(convertRange('1.2.x').toString(), '~1.2.0'); 330 | assert.equal(convertRange('1.x.x').toString(), '^1.0.0'); 331 | assert.equal(convertRange('x.x.x').toString(), '*'); 332 | assert.equal(convertRange('0').toString(), '0'); 333 | assert.equal(convertRange('>=0.5').toString(), '*'); 334 | assert.equal(new SemverRange('0').has('0.5.0'), true); 335 | assert.equal(new SemverRange('0.5').has('0.5.4'), true); 336 | assert.equal(convertRange('>=0.5 0').toString(), '^0.5.0'); 337 | assert.equal(convertRange('>=7.0.0-beta.50 <7.0.0-rc.0').toString(), '~7.0.0-beta.50'); 338 | assert.equal(convertRange('>=14.x').toString(), '*'); 339 | assert.equal(convertRange('<14.x').toString(), '^13.0.0'); 340 | assert.equal(convertRange('<=14').toString(), '^14.0.0'); 341 | assert.equal(convertRange('0.3.3').toString(), '0.3.3'); 342 | assert.equal(convertRange('0.x').toString(), '0'); 343 | assert.equal(convertRange('0.0.x').toString(), '0.0'); 344 | assert.equal(convertRange('7 || ^7.0.0-rc.2').toString(), '^7.0.0'); 345 | }); 346 | }); 347 | --------------------------------------------------------------------------------