├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── cli.ts ├── mod.ts ├── range.bnf └── test ├── clean.test.ts ├── cli.test.ts ├── coerce.test.ts ├── comparators.test.ts ├── comparison.test.ts ├── deps.ts ├── diff.test.ts ├── equality.test.ts ├── gtr.test.ts ├── increment.test.ts ├── ltr.test.ts ├── major.test.ts ├── minVersion.test.ts ├── minor.test.ts ├── patch.test.ts ├── prerelease.test.ts ├── range.test.ts ├── semver.test.ts ├── test.ts └── tooLong.test.ts /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.bat text eol=crlf 3 | *.cmd text eol=crlf 4 | *.ps1 text eol=crlf 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: ${{ matrix.kind }} ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [macOS-latest, ubuntu-latest, windows-latest] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Setup Deno 16 | uses: denoland/setup-deno@v1 17 | 18 | - name: Format 19 | run: deno fmt --check 20 | 21 | # - name: Lint 22 | # run: deno lint 23 | 24 | - name: Tests 25 | run: deno test --allow-run 26 | 27 | - name: Release 28 | uses: softprops/action-gh-release@v1 29 | if: | 30 | matrix.os == 'ubuntu-latest' && 31 | startsWith(github.repository, 'justjavac') && 32 | startsWith(github.ref, 'refs/tags/') 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | with: 36 | draft: true 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .cache 3 | .DS_Store 4 | *bak 5 | .history 6 | .temp/** 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["denoland.vscode-deno"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.lint": false, 4 | "deno.unstable": false 5 | } 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.4.0 - [2021-05-31] 4 | 5 | - Fix type error for latest deno canary (#12) 6 | - fix typo (#13) 7 | 8 | ## 1.2.0 - [2021-01-12] 9 | 10 | - add cli 11 | 12 | ## 1.1.0 - [2019-09-06] 13 | 14 | - upgrade deno@1.3.3 15 | - upgrade std@0.68.0 16 | 17 | ## 1.0.0 - [2019-06-06] 18 | 19 | - Initial release 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The ISC License 2 | 3 | Copyright (c) justjavac 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The project is no longer maintained. move to [official Deno standard library](https://deno.land/std/semver/README.md) 2 | -------------------------------------------------------------------------------- /cli.ts: -------------------------------------------------------------------------------- 1 | import * as semver from "./mod.ts"; 2 | 3 | const [command, ...args] = Deno.args; 4 | 5 | const output = async (content: string) => 6 | await Deno.stdout.write(new TextEncoder().encode(content?.toString() + "\n")); 7 | 8 | const callFunction = async (command: string, args: string[]) => 9 | //@ts-ignore 10 | await output(semver[command](...args)); 11 | 12 | switch (command) { 13 | case "valid": 14 | case "clean": 15 | case "satisfies": 16 | case "gt": 17 | case "lt": 18 | case "minVersion": 19 | await callFunction(command, args); 20 | break; 21 | default: 22 | await output(`valid 1.2.3 // "1.2.3" 23 | valid a.b.c // null 24 | clean " =v1.2.3 " // "1.2.3" 25 | satisfies "1.2.3" "1.x || >=2.5.0 || 5.0.0 - 7.2.3" // true 26 | gt "1.2.3" "9.8.7" // false 27 | lt "1.2.3" "9.8.7" // true 28 | minVersion ">=1.0.0" // "1.0.0"`); 29 | } 30 | 31 | Deno.exit(); 32 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | export type ReleaseType = 2 | | "pre" 3 | | "major" 4 | | "premajor" 5 | | "minor" 6 | | "preminor" 7 | | "patch" 8 | | "prepatch" 9 | | "prerelease"; 10 | 11 | export type Operator = 12 | | "===" 13 | | "!==" 14 | | "" 15 | | "=" 16 | | "==" 17 | | "!=" 18 | | ">" 19 | | ">=" 20 | | "<" 21 | | "<="; 22 | 23 | export interface Options { 24 | loose?: boolean; 25 | includePrerelease?: boolean; 26 | } 27 | 28 | // Note: this is the semver.org version of the spec that it implements 29 | // Not necessarily the package version of this code. 30 | export const SEMVER_SPEC_VERSION = "2.0.0"; 31 | 32 | const MAX_LENGTH: number = 256; 33 | 34 | // Max safe segment length for coercion. 35 | const MAX_SAFE_COMPONENT_LENGTH: number = 16; 36 | 37 | // The actual regexps 38 | const re: RegExp[] = []; 39 | const src: string[] = []; 40 | let R: number = 0; 41 | 42 | // The following Regular Expressions can be used for tokenizing, 43 | // validating, and parsing SemVer version strings. 44 | 45 | // ## Numeric Identifier 46 | // A single `0`, or a non-zero digit followed by zero or more digits. 47 | 48 | const NUMERICIDENTIFIER: number = R++; 49 | src[NUMERICIDENTIFIER] = "0|[1-9]\\d*"; 50 | const NUMERICIDENTIFIERLOOSE: number = R++; 51 | src[NUMERICIDENTIFIERLOOSE] = "[0-9]+"; 52 | 53 | // ## Non-numeric Identifier 54 | // Zero or more digits, followed by a letter or hyphen, and then zero or 55 | // more letters, digits, or hyphens. 56 | 57 | const NONNUMERICIDENTIFIER: number = R++; 58 | src[NONNUMERICIDENTIFIER] = "\\d*[a-zA-Z-][a-zA-Z0-9-]*"; 59 | 60 | // ## Main Version 61 | // Three dot-separated numeric identifiers. 62 | 63 | const MAINVERSION: number = R++; 64 | const nid = src[NUMERICIDENTIFIER]; 65 | src[MAINVERSION] = `(${nid})\\.(${nid})\\.(${nid})`; 66 | 67 | const MAINVERSIONLOOSE: number = R++; 68 | const nidl = src[NUMERICIDENTIFIERLOOSE]; 69 | src[MAINVERSIONLOOSE] = `(${nidl})\\.(${nidl})\\.(${nidl})`; 70 | 71 | // ## Pre-release Version Identifier 72 | // A numeric identifier, or a non-numeric identifier. 73 | 74 | const PRERELEASEIDENTIFIER: number = R++; 75 | src[PRERELEASEIDENTIFIER] = "(?:" + src[NUMERICIDENTIFIER] + "|" + 76 | src[NONNUMERICIDENTIFIER] + ")"; 77 | 78 | const PRERELEASEIDENTIFIERLOOSE: number = R++; 79 | src[PRERELEASEIDENTIFIERLOOSE] = "(?:" + src[NUMERICIDENTIFIERLOOSE] + "|" + 80 | src[NONNUMERICIDENTIFIER] + ")"; 81 | 82 | // ## Pre-release Version 83 | // Hyphen, followed by one or more dot-separated pre-release version 84 | // identifiers. 85 | 86 | const PRERELEASE: number = R++; 87 | src[PRERELEASE] = "(?:-(" + 88 | src[PRERELEASEIDENTIFIER] + 89 | "(?:\\." + 90 | src[PRERELEASEIDENTIFIER] + 91 | ")*))"; 92 | 93 | const PRERELEASELOOSE: number = R++; 94 | src[PRERELEASELOOSE] = "(?:-?(" + 95 | src[PRERELEASEIDENTIFIERLOOSE] + 96 | "(?:\\." + 97 | src[PRERELEASEIDENTIFIERLOOSE] + 98 | ")*))"; 99 | 100 | // ## Build Metadata Identifier 101 | // Any combination of digits, letters, or hyphens. 102 | 103 | const BUILDIDENTIFIER: number = R++; 104 | src[BUILDIDENTIFIER] = "[0-9A-Za-z-]+"; 105 | 106 | // ## Build Metadata 107 | // Plus sign, followed by one or more period-separated build metadata 108 | // identifiers. 109 | 110 | const BUILD: number = R++; 111 | src[BUILD] = "(?:\\+(" + src[BUILDIDENTIFIER] + "(?:\\." + 112 | src[BUILDIDENTIFIER] + ")*))"; 113 | 114 | // ## Full Version String 115 | // A main version, followed optionally by a pre-release version and 116 | // build metadata. 117 | 118 | // Note that the only major, minor, patch, and pre-release sections of 119 | // the version string are capturing groups. The build metadata is not a 120 | // capturing group, because it should not ever be used in version 121 | // comparison. 122 | 123 | const FULL: number = R++; 124 | const FULLPLAIN = "v?" + src[MAINVERSION] + src[PRERELEASE] + "?" + src[BUILD] + 125 | "?"; 126 | 127 | src[FULL] = "^" + FULLPLAIN + "$"; 128 | 129 | // like full, but allows v1.2.3 and =1.2.3, which people do sometimes. 130 | // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty 131 | // common in the npm registry. 132 | const LOOSEPLAIN: string = "[v=\\s]*" + 133 | src[MAINVERSIONLOOSE] + 134 | src[PRERELEASELOOSE] + 135 | "?" + 136 | src[BUILD] + 137 | "?"; 138 | 139 | const LOOSE: number = R++; 140 | src[LOOSE] = "^" + LOOSEPLAIN + "$"; 141 | 142 | const GTLT: number = R++; 143 | src[GTLT] = "((?:<|>)?=?)"; 144 | 145 | // Something like "2.*" or "1.2.x". 146 | // Note that "x.x" is a valid xRange identifer, meaning "any version" 147 | // Only the first item is strictly required. 148 | const XRANGEIDENTIFIERLOOSE: number = R++; 149 | src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + "|x|X|\\*"; 150 | const XRANGEIDENTIFIER: number = R++; 151 | src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + "|x|X|\\*"; 152 | 153 | const XRANGEPLAIN: number = R++; 154 | src[XRANGEPLAIN] = "[v=\\s]*(" + 155 | src[XRANGEIDENTIFIER] + 156 | ")" + 157 | "(?:\\.(" + 158 | src[XRANGEIDENTIFIER] + 159 | ")" + 160 | "(?:\\.(" + 161 | src[XRANGEIDENTIFIER] + 162 | ")" + 163 | "(?:" + 164 | src[PRERELEASE] + 165 | ")?" + 166 | src[BUILD] + 167 | "?" + 168 | ")?)?"; 169 | 170 | const XRANGEPLAINLOOSE: number = R++; 171 | src[XRANGEPLAINLOOSE] = "[v=\\s]*(" + 172 | src[XRANGEIDENTIFIERLOOSE] + 173 | ")" + 174 | "(?:\\.(" + 175 | src[XRANGEIDENTIFIERLOOSE] + 176 | ")" + 177 | "(?:\\.(" + 178 | src[XRANGEIDENTIFIERLOOSE] + 179 | ")" + 180 | "(?:" + 181 | src[PRERELEASELOOSE] + 182 | ")?" + 183 | src[BUILD] + 184 | "?" + 185 | ")?)?"; 186 | 187 | const XRANGE: number = R++; 188 | src[XRANGE] = "^" + src[GTLT] + "\\s*" + src[XRANGEPLAIN] + "$"; 189 | const XRANGELOOSE = R++; 190 | src[XRANGELOOSE] = "^" + src[GTLT] + "\\s*" + src[XRANGEPLAINLOOSE] + "$"; 191 | 192 | // Coercion. 193 | // Extract anything that could conceivably be a part of a valid semver 194 | const COERCE: number = R++; 195 | src[COERCE] = "(?:^|[^\\d])" + 196 | "(\\d{1," + 197 | MAX_SAFE_COMPONENT_LENGTH + 198 | "})" + 199 | "(?:\\.(\\d{1," + 200 | MAX_SAFE_COMPONENT_LENGTH + 201 | "}))?" + 202 | "(?:\\.(\\d{1," + 203 | MAX_SAFE_COMPONENT_LENGTH + 204 | "}))?" + 205 | "(?:$|[^\\d])"; 206 | 207 | // Tilde ranges. 208 | // Meaning is "reasonably at or greater than" 209 | const LONETILDE: number = R++; 210 | src[LONETILDE] = "(?:~>?)"; 211 | 212 | const TILDETRIM: number = R++; 213 | src[TILDETRIM] = "(\\s*)" + src[LONETILDE] + "\\s+"; 214 | re[TILDETRIM] = new RegExp(src[TILDETRIM], "g"); 215 | const tildeTrimReplace: string = "$1~"; 216 | 217 | const TILDE: number = R++; 218 | src[TILDE] = "^" + src[LONETILDE] + src[XRANGEPLAIN] + "$"; 219 | const TILDELOOSE: number = R++; 220 | src[TILDELOOSE] = "^" + src[LONETILDE] + src[XRANGEPLAINLOOSE] + "$"; 221 | 222 | // Caret ranges. 223 | // Meaning is "at least and backwards compatible with" 224 | const LONECARET: number = R++; 225 | src[LONECARET] = "(?:\\^)"; 226 | 227 | const CARETTRIM: number = R++; 228 | src[CARETTRIM] = "(\\s*)" + src[LONECARET] + "\\s+"; 229 | re[CARETTRIM] = new RegExp(src[CARETTRIM], "g"); 230 | const caretTrimReplace: string = "$1^"; 231 | 232 | const CARET: number = R++; 233 | src[CARET] = "^" + src[LONECARET] + src[XRANGEPLAIN] + "$"; 234 | const CARETLOOSE: number = R++; 235 | src[CARETLOOSE] = "^" + src[LONECARET] + src[XRANGEPLAINLOOSE] + "$"; 236 | 237 | // A simple gt/lt/eq thing, or just "" to indicate "any version" 238 | const COMPARATORLOOSE: number = R++; 239 | src[COMPARATORLOOSE] = "^" + src[GTLT] + "\\s*(" + LOOSEPLAIN + ")$|^$"; 240 | const COMPARATOR: number = R++; 241 | src[COMPARATOR] = "^" + src[GTLT] + "\\s*(" + FULLPLAIN + ")$|^$"; 242 | 243 | // An expression to strip any whitespace between the gtlt and the thing 244 | // it modifies, so that `> 1.2.3` ==> `>1.2.3` 245 | const COMPARATORTRIM: number = R++; 246 | src[COMPARATORTRIM] = "(\\s*)" + src[GTLT] + "\\s*(" + LOOSEPLAIN + "|" + 247 | src[XRANGEPLAIN] + ")"; 248 | 249 | // this one has to use the /g flag 250 | re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], "g"); 251 | const comparatorTrimReplace: string = "$1$2$3"; 252 | 253 | // Something like `1.2.3 - 1.2.4` 254 | // Note that these all use the loose form, because they'll be 255 | // checked against either the strict or loose comparator form 256 | // later. 257 | const HYPHENRANGE: number = R++; 258 | src[HYPHENRANGE] = "^\\s*(" + 259 | src[XRANGEPLAIN] + 260 | ")" + 261 | "\\s+-\\s+" + 262 | "(" + 263 | src[XRANGEPLAIN] + 264 | ")" + 265 | "\\s*$"; 266 | 267 | const HYPHENRANGELOOSE: number = R++; 268 | src[HYPHENRANGELOOSE] = "^\\s*(" + 269 | src[XRANGEPLAINLOOSE] + 270 | ")" + 271 | "\\s+-\\s+" + 272 | "(" + 273 | src[XRANGEPLAINLOOSE] + 274 | ")" + 275 | "\\s*$"; 276 | 277 | // Star ranges basically just allow anything at all. 278 | const STAR: number = R++; 279 | src[STAR] = "(<|>)?=?\\s*\\*"; 280 | 281 | // Compile to actual regexp objects. 282 | // All are flag-free, unless they were created above with a flag. 283 | for (let i: number = 0; i < R; i++) { 284 | if (!re[i]) { 285 | re[i] = new RegExp(src[i]); 286 | } 287 | } 288 | 289 | export function parse( 290 | version: string | SemVer | null, 291 | optionsOrLoose?: boolean | Options, 292 | ): SemVer | null { 293 | if (!optionsOrLoose || typeof optionsOrLoose !== "object") { 294 | optionsOrLoose = { 295 | loose: !!optionsOrLoose, 296 | includePrerelease: false, 297 | }; 298 | } 299 | 300 | if (version instanceof SemVer) { 301 | return version; 302 | } 303 | 304 | if (typeof version !== "string") { 305 | return null; 306 | } 307 | 308 | if (version.length > MAX_LENGTH) { 309 | return null; 310 | } 311 | 312 | const r: RegExp = optionsOrLoose.loose ? re[LOOSE] : re[FULL]; 313 | if (!r.test(version)) { 314 | return null; 315 | } 316 | 317 | try { 318 | return new SemVer(version, optionsOrLoose); 319 | } catch (er) { 320 | return null; 321 | } 322 | } 323 | 324 | export function valid( 325 | version: string | SemVer | null, 326 | optionsOrLoose?: boolean | Options, 327 | ): string | null { 328 | if (version === null) return null; 329 | const v: SemVer | null = parse(version, optionsOrLoose); 330 | return v ? v.version : null; 331 | } 332 | 333 | export function clean( 334 | version: string, 335 | optionsOrLoose?: boolean | Options, 336 | ): string | null { 337 | const s: SemVer | null = parse( 338 | version.trim().replace(/^[=v]+/, ""), 339 | optionsOrLoose, 340 | ); 341 | return s ? s.version : null; 342 | } 343 | 344 | export class SemVer { 345 | raw!: string; 346 | loose!: boolean; 347 | options!: Options; 348 | 349 | major!: number; 350 | minor!: number; 351 | patch!: number; 352 | version!: string; 353 | build!: ReadonlyArray; 354 | prerelease!: Array; 355 | 356 | constructor(version: string | SemVer, optionsOrLoose?: boolean | Options) { 357 | if (!optionsOrLoose || typeof optionsOrLoose !== "object") { 358 | optionsOrLoose = { 359 | loose: !!optionsOrLoose, 360 | includePrerelease: false, 361 | }; 362 | } 363 | if (version instanceof SemVer) { 364 | if (version.loose === optionsOrLoose.loose) { 365 | return version; 366 | } else { 367 | version = version.version; 368 | } 369 | } else if (typeof version !== "string") { 370 | throw new TypeError("Invalid Version: " + version); 371 | } 372 | 373 | if (version.length > MAX_LENGTH) { 374 | throw new TypeError( 375 | "version is longer than " + MAX_LENGTH + " characters", 376 | ); 377 | } 378 | 379 | if (!(this instanceof SemVer)) { 380 | return new SemVer(version, optionsOrLoose); 381 | } 382 | 383 | this.options = optionsOrLoose; 384 | this.loose = !!optionsOrLoose.loose; 385 | 386 | const m = version.trim().match(optionsOrLoose.loose ? re[LOOSE] : re[FULL]); 387 | 388 | if (!m) { 389 | throw new TypeError("Invalid Version: " + version); 390 | } 391 | 392 | this.raw = version; 393 | 394 | // these are actually numbers 395 | this.major = +m[1]; 396 | this.minor = +m[2]; 397 | this.patch = +m[3]; 398 | 399 | if (this.major > Number.MAX_SAFE_INTEGER || this.major < 0) { 400 | throw new TypeError("Invalid major version"); 401 | } 402 | 403 | if (this.minor > Number.MAX_SAFE_INTEGER || this.minor < 0) { 404 | throw new TypeError("Invalid minor version"); 405 | } 406 | 407 | if (this.patch > Number.MAX_SAFE_INTEGER || this.patch < 0) { 408 | throw new TypeError("Invalid patch version"); 409 | } 410 | 411 | // numberify any prerelease numeric ids 412 | if (!m[4]) { 413 | this.prerelease = []; 414 | } else { 415 | this.prerelease = m[4].split(".").map((id: string) => { 416 | if (/^[0-9]+$/.test(id)) { 417 | const num: number = +id; 418 | if (num >= 0 && num < Number.MAX_SAFE_INTEGER) { 419 | return num; 420 | } 421 | } 422 | return id; 423 | }); 424 | } 425 | 426 | this.build = m[5] ? m[5].split(".") : []; 427 | this.format(); 428 | } 429 | 430 | format(): string { 431 | this.version = this.major + "." + this.minor + "." + this.patch; 432 | if (this.prerelease.length) { 433 | this.version += "-" + this.prerelease.join("."); 434 | } 435 | return this.version; 436 | } 437 | 438 | compare(other: string | SemVer): 1 | 0 | -1 { 439 | if (!(other instanceof SemVer)) { 440 | other = new SemVer(other, this.options); 441 | } 442 | 443 | return this.compareMain(other) || this.comparePre(other); 444 | } 445 | 446 | compareMain(other: string | SemVer): 1 | 0 | -1 { 447 | if (!(other instanceof SemVer)) { 448 | other = new SemVer(other, this.options); 449 | } 450 | 451 | return ( 452 | compareIdentifiers(this.major, other.major) || 453 | compareIdentifiers(this.minor, other.minor) || 454 | compareIdentifiers(this.patch, other.patch) 455 | ); 456 | } 457 | 458 | comparePre(other: string | SemVer): 1 | 0 | -1 { 459 | if (!(other instanceof SemVer)) { 460 | other = new SemVer(other, this.options); 461 | } 462 | 463 | // NOT having a prerelease is > having one 464 | if (this.prerelease.length && !other.prerelease.length) { 465 | return -1; 466 | } else if (!this.prerelease.length && other.prerelease.length) { 467 | return 1; 468 | } else if (!this.prerelease.length && !other.prerelease.length) { 469 | return 0; 470 | } 471 | 472 | let i: number = 0; 473 | do { 474 | const a: string | number = this.prerelease[i]; 475 | const b: string | number = other.prerelease[i]; 476 | if (a === undefined && b === undefined) { 477 | return 0; 478 | } else if (b === undefined) { 479 | return 1; 480 | } else if (a === undefined) { 481 | return -1; 482 | } else if (a === b) { 483 | continue; 484 | } else { 485 | return compareIdentifiers(a, b); 486 | } 487 | } while (++i); 488 | return 1; 489 | } 490 | 491 | compareBuild(other: string | SemVer): 1 | 0 | -1 { 492 | if (!(other instanceof SemVer)) { 493 | other = new SemVer(other, this.options); 494 | } 495 | 496 | let i: number = 0; 497 | do { 498 | const a: string = this.build[i]; 499 | const b: string = other.build[i]; 500 | if (a === undefined && b === undefined) { 501 | return 0; 502 | } else if (b === undefined) { 503 | return 1; 504 | } else if (a === undefined) { 505 | return -1; 506 | } else if (a === b) { 507 | continue; 508 | } else { 509 | return compareIdentifiers(a, b); 510 | } 511 | } while (++i); 512 | return 1; 513 | } 514 | 515 | inc(release: ReleaseType, identifier?: string): SemVer { 516 | switch (release) { 517 | case "premajor": 518 | this.prerelease.length = 0; 519 | this.patch = 0; 520 | this.minor = 0; 521 | this.major++; 522 | this.inc("pre", identifier); 523 | break; 524 | case "preminor": 525 | this.prerelease.length = 0; 526 | this.patch = 0; 527 | this.minor++; 528 | this.inc("pre", identifier); 529 | break; 530 | case "prepatch": 531 | // If this is already a prerelease, it will bump to the next version 532 | // drop any prereleases that might already exist, since they are not 533 | // relevant at this point. 534 | this.prerelease.length = 0; 535 | this.inc("patch", identifier); 536 | this.inc("pre", identifier); 537 | break; 538 | // If the input is a non-prerelease version, this acts the same as 539 | // prepatch. 540 | case "prerelease": 541 | if (this.prerelease.length === 0) { 542 | this.inc("patch", identifier); 543 | } 544 | this.inc("pre", identifier); 545 | break; 546 | 547 | case "major": 548 | // If this is a pre-major version, bump up to the same major version. 549 | // Otherwise increment major. 550 | // 1.0.0-5 bumps to 1.0.0 551 | // 1.1.0 bumps to 2.0.0 552 | if ( 553 | this.minor !== 0 || 554 | this.patch !== 0 || 555 | this.prerelease.length === 0 556 | ) { 557 | this.major++; 558 | } 559 | this.minor = 0; 560 | this.patch = 0; 561 | this.prerelease = []; 562 | break; 563 | case "minor": 564 | // If this is a pre-minor version, bump up to the same minor version. 565 | // Otherwise increment minor. 566 | // 1.2.0-5 bumps to 1.2.0 567 | // 1.2.1 bumps to 1.3.0 568 | if (this.patch !== 0 || this.prerelease.length === 0) { 569 | this.minor++; 570 | } 571 | this.patch = 0; 572 | this.prerelease = []; 573 | break; 574 | case "patch": 575 | // If this is not a pre-release version, it will increment the patch. 576 | // If it is a pre-release it will bump up to the same patch version. 577 | // 1.2.0-5 patches to 1.2.0 578 | // 1.2.0 patches to 1.2.1 579 | if (this.prerelease.length === 0) { 580 | this.patch++; 581 | } 582 | this.prerelease = []; 583 | break; 584 | // This probably shouldn't be used publicly. 585 | // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. 586 | case "pre": 587 | if (this.prerelease.length === 0) { 588 | this.prerelease = [0]; 589 | } else { 590 | let i: number = this.prerelease.length; 591 | while (--i >= 0) { 592 | if (typeof this.prerelease[i] === "number") { 593 | // deno-fmt-ignore 594 | (this.prerelease[i] as number)++; 595 | i = -2; 596 | } 597 | } 598 | if (i === -1) { 599 | // didn't increment anything 600 | this.prerelease.push(0); 601 | } 602 | } 603 | if (identifier) { 604 | // 1.2.0-beta.1 bumps to 1.2.0-beta.2, 605 | // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 606 | if (this.prerelease[0] === identifier) { 607 | if (isNaN(this.prerelease[1] as number)) { 608 | this.prerelease = [identifier, 0]; 609 | } 610 | } else { 611 | this.prerelease = [identifier, 0]; 612 | } 613 | } 614 | break; 615 | 616 | default: 617 | throw new Error("invalid increment argument: " + release); 618 | } 619 | this.format(); 620 | this.raw = this.version; 621 | return this; 622 | } 623 | 624 | toString(): string { 625 | return this.version; 626 | } 627 | } 628 | 629 | /** 630 | * Return the version incremented by the release type (major, minor, patch, or prerelease), or null if it's not valid. 631 | */ 632 | export function inc( 633 | version: string | SemVer, 634 | release: ReleaseType, 635 | optionsOrLoose?: boolean | Options, 636 | identifier?: string, 637 | ): string | null { 638 | if (typeof optionsOrLoose === "string") { 639 | identifier = optionsOrLoose; 640 | optionsOrLoose = undefined; 641 | } 642 | try { 643 | return new SemVer(version, optionsOrLoose).inc(release, identifier).version; 644 | } catch (er) { 645 | return null; 646 | } 647 | } 648 | 649 | export function diff( 650 | version1: string | SemVer, 651 | version2: string | SemVer, 652 | optionsOrLoose?: boolean | Options, 653 | ): ReleaseType | null { 654 | if (eq(version1, version2, optionsOrLoose)) { 655 | return null; 656 | } else { 657 | const v1: SemVer | null = parse(version1); 658 | const v2: SemVer | null = parse(version2); 659 | let prefix: string = ""; 660 | let defaultResult: ReleaseType | null = null; 661 | 662 | if (v1 && v2) { 663 | if (v1.prerelease.length || v2.prerelease.length) { 664 | prefix = "pre"; 665 | defaultResult = "prerelease"; 666 | } 667 | 668 | for (const key in v1) { 669 | if (key === "major" || key === "minor" || key === "patch") { 670 | if (v1[key] !== v2[key]) { 671 | return (prefix + key) as ReleaseType; 672 | } 673 | } 674 | } 675 | } 676 | return defaultResult; // may be undefined 677 | } 678 | } 679 | 680 | const numeric: RegExp = /^[0-9]+$/; 681 | 682 | export function compareIdentifiers( 683 | a: string | number | null, 684 | b: string | number | null, 685 | ): 1 | 0 | -1 { 686 | const anum: boolean = numeric.test(a as string); 687 | const bnum: boolean = numeric.test(b as string); 688 | 689 | if (a === null || b === null) throw "Comparison against null invalid"; 690 | 691 | if (anum && bnum) { 692 | a = +a; 693 | b = +b; 694 | } 695 | 696 | return a === b ? 0 : anum && !bnum ? -1 : bnum && !anum ? 1 : a < b ? -1 : 1; 697 | } 698 | 699 | export function rcompareIdentifiers( 700 | a: string | null, 701 | b: string | null, 702 | ): 1 | 0 | -1 { 703 | return compareIdentifiers(b, a); 704 | } 705 | 706 | /** 707 | * Return the major version number. 708 | */ 709 | export function major( 710 | v: string | SemVer, 711 | optionsOrLoose?: boolean | Options, 712 | ): number { 713 | return new SemVer(v, optionsOrLoose).major; 714 | } 715 | 716 | /** 717 | * Return the minor version number. 718 | */ 719 | export function minor( 720 | v: string | SemVer, 721 | optionsOrLoose?: boolean | Options, 722 | ): number { 723 | return new SemVer(v, optionsOrLoose).minor; 724 | } 725 | 726 | /** 727 | * Return the patch version number. 728 | */ 729 | export function patch( 730 | v: string | SemVer, 731 | optionsOrLoose?: boolean | Options, 732 | ): number { 733 | return new SemVer(v, optionsOrLoose).patch; 734 | } 735 | 736 | export function compare( 737 | v1: string | SemVer, 738 | v2: string | SemVer, 739 | optionsOrLoose?: boolean | Options, 740 | ): 1 | 0 | -1 { 741 | return new SemVer(v1, optionsOrLoose).compare(new SemVer(v2, optionsOrLoose)); 742 | } 743 | 744 | export function compareLoose( 745 | a: string | SemVer, 746 | b: string | SemVer, 747 | ): 1 | 0 | -1 { 748 | return compare(a, b, true); 749 | } 750 | 751 | export function compareBuild( 752 | a: string | SemVer, 753 | b: string | SemVer, 754 | loose?: boolean | Options, 755 | ): 1 | 0 | -1 { 756 | var versionA = new SemVer(a, loose); 757 | var versionB = new SemVer(b, loose); 758 | return versionA.compare(versionB) || versionA.compareBuild(versionB); 759 | } 760 | 761 | export function rcompare( 762 | v1: string | SemVer, 763 | v2: string | SemVer, 764 | optionsOrLoose?: boolean | Options, 765 | ): 1 | 0 | -1 { 766 | return compare(v2, v1, optionsOrLoose); 767 | } 768 | 769 | export function sort( 770 | list: T[], 771 | optionsOrLoose?: boolean | Options, 772 | ): T[] { 773 | return list.sort((a, b) => { 774 | return compareBuild(a, b, optionsOrLoose); 775 | }); 776 | } 777 | 778 | export function rsort( 779 | list: T[], 780 | optionsOrLoose?: boolean | Options, 781 | ): T[] { 782 | return list.sort((a, b) => { 783 | return compareBuild(b, a, optionsOrLoose); 784 | }); 785 | } 786 | 787 | export function gt( 788 | v1: string | SemVer, 789 | v2: string | SemVer, 790 | optionsOrLoose?: boolean | Options, 791 | ): boolean { 792 | return compare(v1, v2, optionsOrLoose) > 0; 793 | } 794 | 795 | export function lt( 796 | v1: string | SemVer, 797 | v2: string | SemVer, 798 | optionsOrLoose?: boolean | Options, 799 | ): boolean { 800 | return compare(v1, v2, optionsOrLoose) < 0; 801 | } 802 | 803 | export function eq( 804 | v1: string | SemVer, 805 | v2: string | SemVer, 806 | optionsOrLoose?: boolean | Options, 807 | ): boolean { 808 | return compare(v1, v2, optionsOrLoose) === 0; 809 | } 810 | 811 | export function neq( 812 | v1: string | SemVer, 813 | v2: string | SemVer, 814 | optionsOrLoose?: boolean | Options, 815 | ): boolean { 816 | return compare(v1, v2, optionsOrLoose) !== 0; 817 | } 818 | 819 | export function gte( 820 | v1: string | SemVer, 821 | v2: string | SemVer, 822 | optionsOrLoose?: boolean | Options, 823 | ): boolean { 824 | return compare(v1, v2, optionsOrLoose) >= 0; 825 | } 826 | 827 | export function lte( 828 | v1: string | SemVer, 829 | v2: string | SemVer, 830 | optionsOrLoose?: boolean | Options, 831 | ): boolean { 832 | return compare(v1, v2, optionsOrLoose) <= 0; 833 | } 834 | 835 | export function cmp( 836 | v1: string | SemVer, 837 | operator: Operator, 838 | v2: string | SemVer, 839 | optionsOrLoose?: boolean | Options, 840 | ): boolean { 841 | switch (operator) { 842 | case "===": 843 | if (typeof v1 === "object") v1 = v1.version; 844 | if (typeof v2 === "object") v2 = v2.version; 845 | return v1 === v2; 846 | 847 | case "!==": 848 | if (typeof v1 === "object") v1 = v1.version; 849 | if (typeof v2 === "object") v2 = v2.version; 850 | return v1 !== v2; 851 | 852 | case "": 853 | case "=": 854 | case "==": 855 | return eq(v1, v2, optionsOrLoose); 856 | 857 | case "!=": 858 | return neq(v1, v2, optionsOrLoose); 859 | 860 | case ">": 861 | return gt(v1, v2, optionsOrLoose); 862 | 863 | case ">=": 864 | return gte(v1, v2, optionsOrLoose); 865 | 866 | case "<": 867 | return lt(v1, v2, optionsOrLoose); 868 | 869 | case "<=": 870 | return lte(v1, v2, optionsOrLoose); 871 | 872 | default: 873 | throw new TypeError("Invalid operator: " + operator); 874 | } 875 | } 876 | 877 | const ANY: SemVer = {} as SemVer; 878 | 879 | export class Comparator { 880 | semver!: SemVer; 881 | operator!: "" | "=" | "<" | ">" | "<=" | ">="; 882 | value!: string; 883 | loose!: boolean; 884 | options!: Options; 885 | 886 | constructor(comp: string | Comparator, optionsOrLoose?: boolean | Options) { 887 | if (!optionsOrLoose || typeof optionsOrLoose !== "object") { 888 | optionsOrLoose = { 889 | loose: !!optionsOrLoose, 890 | includePrerelease: false, 891 | }; 892 | } 893 | 894 | if (comp instanceof Comparator) { 895 | if (comp.loose === !!optionsOrLoose.loose) { 896 | return comp; 897 | } else { 898 | comp = comp.value; 899 | } 900 | } 901 | 902 | if (!(this instanceof Comparator)) { 903 | return new Comparator(comp, optionsOrLoose); 904 | } 905 | 906 | this.options = optionsOrLoose; 907 | this.loose = !!optionsOrLoose.loose; 908 | this.parse(comp); 909 | 910 | if (this.semver === ANY) { 911 | this.value = ""; 912 | } else { 913 | this.value = this.operator + this.semver.version; 914 | } 915 | } 916 | 917 | parse(comp: string): void { 918 | const r = this.options.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; 919 | const m = comp.match(r); 920 | 921 | if (!m) { 922 | throw new TypeError("Invalid comparator: " + comp); 923 | } 924 | 925 | const m1 = m[1] as "" | "=" | "<" | ">" | "<=" | ">="; 926 | this.operator = m1 !== undefined ? m1 : ""; 927 | 928 | if (this.operator === "=") { 929 | this.operator = ""; 930 | } 931 | 932 | // if it literally is just '>' or '' then allow anything. 933 | if (!m[2]) { 934 | this.semver = ANY; 935 | } else { 936 | this.semver = new SemVer(m[2], this.options.loose); 937 | } 938 | } 939 | 940 | test(version: string | SemVer): boolean { 941 | if (this.semver === ANY || version === ANY) { 942 | return true; 943 | } 944 | 945 | if (typeof version === "string") { 946 | version = new SemVer(version, this.options); 947 | } 948 | 949 | return cmp(version, this.operator, this.semver, this.options); 950 | } 951 | 952 | intersects(comp: Comparator, optionsOrLoose?: boolean | Options): boolean { 953 | if (!(comp instanceof Comparator)) { 954 | throw new TypeError("a Comparator is required"); 955 | } 956 | 957 | if (!optionsOrLoose || typeof optionsOrLoose !== "object") { 958 | optionsOrLoose = { 959 | loose: !!optionsOrLoose, 960 | includePrerelease: false, 961 | }; 962 | } 963 | 964 | let rangeTmp: Range; 965 | 966 | if (this.operator === "") { 967 | if (this.value === "") { 968 | return true; 969 | } 970 | rangeTmp = new Range(comp.value, optionsOrLoose); 971 | return satisfies(this.value, rangeTmp, optionsOrLoose); 972 | } else if (comp.operator === "") { 973 | if (comp.value === "") { 974 | return true; 975 | } 976 | rangeTmp = new Range(this.value, optionsOrLoose); 977 | return satisfies(comp.semver, rangeTmp, optionsOrLoose); 978 | } 979 | 980 | const sameDirectionIncreasing: boolean = 981 | (this.operator === ">=" || this.operator === ">") && 982 | (comp.operator === ">=" || comp.operator === ">"); 983 | const sameDirectionDecreasing: boolean = 984 | (this.operator === "<=" || this.operator === "<") && 985 | (comp.operator === "<=" || comp.operator === "<"); 986 | const sameSemVer: boolean = this.semver.version === comp.semver.version; 987 | const differentDirectionsInclusive: boolean = 988 | (this.operator === ">=" || this.operator === "<=") && 989 | (comp.operator === ">=" || comp.operator === "<="); 990 | const oppositeDirectionsLessThan: boolean = 991 | cmp(this.semver, "<", comp.semver, optionsOrLoose) && 992 | (this.operator === ">=" || this.operator === ">") && 993 | (comp.operator === "<=" || comp.operator === "<"); 994 | const oppositeDirectionsGreaterThan: boolean = 995 | cmp(this.semver, ">", comp.semver, optionsOrLoose) && 996 | (this.operator === "<=" || this.operator === "<") && 997 | (comp.operator === ">=" || comp.operator === ">"); 998 | 999 | return ( 1000 | sameDirectionIncreasing || 1001 | sameDirectionDecreasing || 1002 | (sameSemVer && differentDirectionsInclusive) || 1003 | oppositeDirectionsLessThan || 1004 | oppositeDirectionsGreaterThan 1005 | ); 1006 | } 1007 | 1008 | toString(): string { 1009 | return this.value; 1010 | } 1011 | } 1012 | 1013 | export class Range { 1014 | range!: string; 1015 | raw!: string; 1016 | loose!: boolean; 1017 | options!: Options; 1018 | includePrerelease!: boolean; 1019 | set!: ReadonlyArray>; 1020 | 1021 | constructor( 1022 | range: string | Range | Comparator, 1023 | optionsOrLoose?: boolean | Options, 1024 | ) { 1025 | if (!optionsOrLoose || typeof optionsOrLoose !== "object") { 1026 | optionsOrLoose = { 1027 | loose: !!optionsOrLoose, 1028 | includePrerelease: false, 1029 | }; 1030 | } 1031 | 1032 | if (range instanceof Range) { 1033 | if ( 1034 | range.loose === !!optionsOrLoose.loose && 1035 | range.includePrerelease === !!optionsOrLoose.includePrerelease 1036 | ) { 1037 | return range; 1038 | } else { 1039 | return new Range(range.raw, optionsOrLoose); 1040 | } 1041 | } 1042 | 1043 | if (range instanceof Comparator) { 1044 | return new Range(range.value, optionsOrLoose); 1045 | } 1046 | 1047 | if (!(this instanceof Range)) { 1048 | return new Range(range, optionsOrLoose); 1049 | } 1050 | 1051 | this.options = optionsOrLoose; 1052 | this.loose = !!optionsOrLoose.loose; 1053 | this.includePrerelease = !!optionsOrLoose.includePrerelease; 1054 | 1055 | // First, split based on boolean or || 1056 | this.raw = range; 1057 | this.set = range 1058 | .split(/\s*\|\|\s*/) 1059 | .map((range) => this.parseRange(range.trim())) 1060 | .filter((c) => { 1061 | // throw out any that are not relevant for whatever reason 1062 | return c.length; 1063 | }); 1064 | 1065 | if (!this.set.length) { 1066 | throw new TypeError("Invalid SemVer Range: " + range); 1067 | } 1068 | 1069 | this.format(); 1070 | } 1071 | 1072 | format(): string { 1073 | this.range = this.set 1074 | .map((comps) => comps.join(" ").trim()) 1075 | .join("||") 1076 | .trim(); 1077 | return this.range; 1078 | } 1079 | 1080 | parseRange(range: string): ReadonlyArray { 1081 | const loose = this.options.loose; 1082 | range = range.trim(); 1083 | // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` 1084 | const hr: RegExp = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]; 1085 | range = range.replace(hr, hyphenReplace); 1086 | 1087 | // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` 1088 | range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace); 1089 | 1090 | // `~ 1.2.3` => `~1.2.3` 1091 | range = range.replace(re[TILDETRIM], tildeTrimReplace); 1092 | 1093 | // `^ 1.2.3` => `^1.2.3` 1094 | range = range.replace(re[CARETTRIM], caretTrimReplace); 1095 | 1096 | // normalize spaces 1097 | range = range.split(/\s+/).join(" "); 1098 | 1099 | // At this point, the range is completely trimmed and 1100 | // ready to be split into comparators. 1101 | 1102 | const compRe: RegExp = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; 1103 | let set: string[] = range 1104 | .split(" ") 1105 | .map((comp) => parseComparator(comp, this.options)) 1106 | .join(" ") 1107 | .split(/\s+/); 1108 | if (this.options.loose) { 1109 | // in loose mode, throw out any that are not valid comparators 1110 | set = set.filter((comp) => { 1111 | return !!comp.match(compRe); 1112 | }); 1113 | } 1114 | 1115 | return set.map((comp) => new Comparator(comp, this.options)); 1116 | } 1117 | 1118 | test(version: string | SemVer): boolean { 1119 | if (typeof version === "string") { 1120 | version = new SemVer(version, this.options); 1121 | } 1122 | 1123 | for (var i = 0; i < this.set.length; i++) { 1124 | if (testSet(this.set[i], version, this.options)) { 1125 | return true; 1126 | } 1127 | } 1128 | return false; 1129 | } 1130 | 1131 | intersects(range?: Range, optionsOrLoose?: boolean | Options): boolean { 1132 | if (!(range instanceof Range)) { 1133 | throw new TypeError("a Range is required"); 1134 | } 1135 | 1136 | return this.set.some((thisComparators) => { 1137 | return ( 1138 | isSatisfiable(thisComparators, optionsOrLoose) && 1139 | range.set.some((rangeComparators) => { 1140 | return ( 1141 | isSatisfiable(rangeComparators, optionsOrLoose) && 1142 | thisComparators.every((thisComparator) => { 1143 | return rangeComparators.every((rangeComparator) => { 1144 | return thisComparator.intersects( 1145 | rangeComparator, 1146 | optionsOrLoose, 1147 | ); 1148 | }); 1149 | }) 1150 | ); 1151 | }) 1152 | ); 1153 | }); 1154 | } 1155 | 1156 | toString(): string { 1157 | return this.range; 1158 | } 1159 | } 1160 | 1161 | function testSet( 1162 | set: ReadonlyArray, 1163 | version: SemVer, 1164 | options: Options, 1165 | ): boolean { 1166 | for (let i: number = 0; i < set.length; i++) { 1167 | if (!set[i].test(version)) { 1168 | return false; 1169 | } 1170 | } 1171 | 1172 | if (version.prerelease.length && !options.includePrerelease) { 1173 | // Find the set of versions that are allowed to have prereleases 1174 | // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 1175 | // That should allow `1.2.3-pr.2` to pass. 1176 | // However, `1.2.4-alpha.notready` should NOT be allowed, 1177 | // even though it's within the range set by the comparators. 1178 | for (let i: number = 0; i < set.length; i++) { 1179 | if (set[i].semver === ANY) { 1180 | continue; 1181 | } 1182 | 1183 | if (set[i].semver.prerelease.length > 0) { 1184 | const allowed: SemVer = set[i].semver; 1185 | if ( 1186 | allowed.major === version.major && 1187 | allowed.minor === version.minor && 1188 | allowed.patch === version.patch 1189 | ) { 1190 | return true; 1191 | } 1192 | } 1193 | } 1194 | 1195 | // Version has a -pre, but it's not one of the ones we like. 1196 | return false; 1197 | } 1198 | 1199 | return true; 1200 | } 1201 | 1202 | // take a set of comparators and determine whether there 1203 | // exists a version which can satisfy it 1204 | function isSatisfiable( 1205 | comparators: readonly Comparator[], 1206 | options?: boolean | Options, 1207 | ): boolean { 1208 | let result: boolean = true; 1209 | const remainingComparators: Comparator[] = comparators.slice(); 1210 | let testComparator = remainingComparators.pop(); 1211 | 1212 | while (result && remainingComparators.length) { 1213 | result = remainingComparators.every((otherComparator) => { 1214 | return testComparator?.intersects(otherComparator, options); 1215 | }); 1216 | 1217 | testComparator = remainingComparators.pop(); 1218 | } 1219 | 1220 | return result; 1221 | } 1222 | 1223 | // Mostly just for testing and legacy API reasons 1224 | export function toComparators( 1225 | range: string | Range, 1226 | optionsOrLoose?: boolean | Options, 1227 | ): string[][] { 1228 | return new Range(range, optionsOrLoose).set.map((comp) => { 1229 | return comp 1230 | .map((c) => c.value) 1231 | .join(" ") 1232 | .trim() 1233 | .split(" "); 1234 | }); 1235 | } 1236 | 1237 | // comprised of xranges, tildes, stars, and gtlt's at this point. 1238 | // already replaced the hyphen ranges 1239 | // turn into a set of JUST comparators. 1240 | function parseComparator(comp: string, options: Options): string { 1241 | comp = replaceCarets(comp, options); 1242 | comp = replaceTildes(comp, options); 1243 | comp = replaceXRanges(comp, options); 1244 | comp = replaceStars(comp, options); 1245 | return comp; 1246 | } 1247 | 1248 | function isX(id: string): boolean { 1249 | return !id || id.toLowerCase() === "x" || id === "*"; 1250 | } 1251 | 1252 | // ~, ~> --> * (any, kinda silly) 1253 | // ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 1254 | // ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 1255 | // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 1256 | // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 1257 | // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 1258 | function replaceTildes(comp: string, options: Options): string { 1259 | return comp 1260 | .trim() 1261 | .split(/\s+/) 1262 | .map((comp) => replaceTilde(comp, options)) 1263 | .join(" "); 1264 | } 1265 | 1266 | function replaceTilde(comp: string, options: Options): string { 1267 | const r: RegExp = options.loose ? re[TILDELOOSE] : re[TILDE]; 1268 | return comp.replace( 1269 | r, 1270 | (_: string, M: string, m: string, p: string, pr: string) => { 1271 | let ret: string; 1272 | 1273 | if (isX(M)) { 1274 | ret = ""; 1275 | } else if (isX(m)) { 1276 | ret = ">=" + M + ".0.0 <" + (+M + 1) + ".0.0"; 1277 | } else if (isX(p)) { 1278 | // ~1.2 == >=1.2.0 <1.3.0 1279 | ret = ">=" + M + "." + m + ".0 <" + M + "." + (+m + 1) + ".0"; 1280 | } else if (pr) { 1281 | ret = ">=" + 1282 | M + 1283 | "." + 1284 | m + 1285 | "." + 1286 | p + 1287 | "-" + 1288 | pr + 1289 | " <" + 1290 | M + 1291 | "." + 1292 | (+m + 1) + 1293 | ".0"; 1294 | } else { 1295 | // ~1.2.3 == >=1.2.3 <1.3.0 1296 | ret = ">=" + M + "." + m + "." + p + " <" + M + "." + (+m + 1) + ".0"; 1297 | } 1298 | 1299 | return ret; 1300 | }, 1301 | ); 1302 | } 1303 | 1304 | // ^ --> * (any, kinda silly) 1305 | // ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 1306 | // ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 1307 | // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 1308 | // ^1.2.3 --> >=1.2.3 <2.0.0 1309 | // ^1.2.0 --> >=1.2.0 <2.0.0 1310 | function replaceCarets(comp: string, options: Options): string { 1311 | return comp 1312 | .trim() 1313 | .split(/\s+/) 1314 | .map((comp) => replaceCaret(comp, options)) 1315 | .join(" "); 1316 | } 1317 | 1318 | function replaceCaret(comp: string, options: Options): string { 1319 | const r: RegExp = options.loose ? re[CARETLOOSE] : re[CARET]; 1320 | return comp.replace(r, (_: string, M, m, p, pr) => { 1321 | let ret: string; 1322 | 1323 | if (isX(M)) { 1324 | ret = ""; 1325 | } else if (isX(m)) { 1326 | ret = ">=" + M + ".0.0 <" + (+M + 1) + ".0.0"; 1327 | } else if (isX(p)) { 1328 | if (M === "0") { 1329 | ret = ">=" + M + "." + m + ".0 <" + M + "." + (+m + 1) + ".0"; 1330 | } else { 1331 | ret = ">=" + M + "." + m + ".0 <" + (+M + 1) + ".0.0"; 1332 | } 1333 | } else if (pr) { 1334 | if (M === "0") { 1335 | if (m === "0") { 1336 | ret = ">=" + 1337 | M + 1338 | "." + 1339 | m + 1340 | "." + 1341 | p + 1342 | "-" + 1343 | pr + 1344 | " <" + 1345 | M + 1346 | "." + 1347 | m + 1348 | "." + 1349 | (+p + 1); 1350 | } else { 1351 | ret = ">=" + 1352 | M + 1353 | "." + 1354 | m + 1355 | "." + 1356 | p + 1357 | "-" + 1358 | pr + 1359 | " <" + 1360 | M + 1361 | "." + 1362 | (+m + 1) + 1363 | ".0"; 1364 | } 1365 | } else { 1366 | ret = ">=" + M + "." + m + "." + p + "-" + pr + " <" + (+M + 1) + 1367 | ".0.0"; 1368 | } 1369 | } else { 1370 | if (M === "0") { 1371 | if (m === "0") { 1372 | ret = ">=" + M + "." + m + "." + p + " <" + M + "." + m + "." + 1373 | (+p + 1); 1374 | } else { 1375 | ret = ">=" + M + "." + m + "." + p + " <" + M + "." + (+m + 1) + ".0"; 1376 | } 1377 | } else { 1378 | ret = ">=" + M + "." + m + "." + p + " <" + (+M + 1) + ".0.0"; 1379 | } 1380 | } 1381 | 1382 | return ret; 1383 | }); 1384 | } 1385 | 1386 | function replaceXRanges(comp: string, options: Options): string { 1387 | return comp 1388 | .split(/\s+/) 1389 | .map((comp) => replaceXRange(comp, options)) 1390 | .join(" "); 1391 | } 1392 | 1393 | function replaceXRange(comp: string, options: Options): string { 1394 | comp = comp.trim(); 1395 | const r: RegExp = options.loose ? re[XRANGELOOSE] : re[XRANGE]; 1396 | return comp.replace(r, (ret: string, gtlt, M, m, p, pr) => { 1397 | const xM: boolean = isX(M); 1398 | const xm: boolean = xM || isX(m); 1399 | const xp: boolean = xm || isX(p); 1400 | const anyX: boolean = xp; 1401 | 1402 | if (gtlt === "=" && anyX) { 1403 | gtlt = ""; 1404 | } 1405 | 1406 | if (xM) { 1407 | if (gtlt === ">" || gtlt === "<") { 1408 | // nothing is allowed 1409 | ret = "<0.0.0"; 1410 | } else { 1411 | // nothing is forbidden 1412 | ret = "*"; 1413 | } 1414 | } else if (gtlt && anyX) { 1415 | // we know patch is an x, because we have any x at all. 1416 | // replace X with 0 1417 | if (xm) { 1418 | m = 0; 1419 | } 1420 | p = 0; 1421 | 1422 | if (gtlt === ">") { 1423 | // >1 => >=2.0.0 1424 | // >1.2 => >=1.3.0 1425 | // >1.2.3 => >= 1.2.4 1426 | gtlt = ">="; 1427 | if (xm) { 1428 | M = +M + 1; 1429 | m = 0; 1430 | p = 0; 1431 | } else { 1432 | m = +m + 1; 1433 | p = 0; 1434 | } 1435 | } else if (gtlt === "<=") { 1436 | // <=0.7.x is actually <0.8.0, since any 0.7.x should 1437 | // pass. Similarly, <=7.x is actually <8.0.0, etc. 1438 | gtlt = "<"; 1439 | if (xm) { 1440 | M = +M + 1; 1441 | } else { 1442 | m = +m + 1; 1443 | } 1444 | } 1445 | 1446 | ret = gtlt + M + "." + m + "." + p; 1447 | } else if (xm) { 1448 | ret = ">=" + M + ".0.0 <" + (+M + 1) + ".0.0"; 1449 | } else if (xp) { 1450 | ret = ">=" + M + "." + m + ".0 <" + M + "." + (+m + 1) + ".0"; 1451 | } 1452 | 1453 | return ret; 1454 | }); 1455 | } 1456 | 1457 | // Because * is AND-ed with everything else in the comparator, 1458 | // and '' means "any version", just remove the *s entirely. 1459 | function replaceStars(comp: string, options: Options): string { 1460 | // Looseness is ignored here. star is always as loose as it gets! 1461 | return comp.trim().replace(re[STAR], ""); 1462 | } 1463 | 1464 | // This function is passed to string.replace(re[HYPHENRANGE]) 1465 | // M, m, patch, prerelease, build 1466 | // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 1467 | // 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do 1468 | // 1.2 - 3.4 => >=1.2.0 <3.5.0 1469 | function hyphenReplace( 1470 | $0: any, 1471 | from: any, 1472 | fM: any, 1473 | fm: any, 1474 | fp: any, 1475 | fpr: any, 1476 | fb: any, 1477 | to: any, 1478 | tM: any, 1479 | tm: any, 1480 | tp: any, 1481 | tpr: any, 1482 | tb: any, 1483 | ) { 1484 | if (isX(fM)) { 1485 | from = ""; 1486 | } else if (isX(fm)) { 1487 | from = ">=" + fM + ".0.0"; 1488 | } else if (isX(fp)) { 1489 | from = ">=" + fM + "." + fm + ".0"; 1490 | } else { 1491 | from = ">=" + from; 1492 | } 1493 | 1494 | if (isX(tM)) { 1495 | to = ""; 1496 | } else if (isX(tm)) { 1497 | to = "<" + (+tM + 1) + ".0.0"; 1498 | } else if (isX(tp)) { 1499 | to = "<" + tM + "." + (+tm + 1) + ".0"; 1500 | } else if (tpr) { 1501 | to = "<=" + tM + "." + tm + "." + tp + "-" + tpr; 1502 | } else { 1503 | to = "<=" + to; 1504 | } 1505 | 1506 | return (from + " " + to).trim(); 1507 | } 1508 | 1509 | export function satisfies( 1510 | version: string | SemVer, 1511 | range: string | Range, 1512 | optionsOrLoose?: boolean | Options, 1513 | ): boolean { 1514 | try { 1515 | range = new Range(range, optionsOrLoose); 1516 | } catch (er) { 1517 | return false; 1518 | } 1519 | return range.test(version); 1520 | } 1521 | 1522 | export function maxSatisfying( 1523 | versions: ReadonlyArray, 1524 | range: string | Range, 1525 | optionsOrLoose?: boolean | Options, 1526 | ): T | null { 1527 | //todo 1528 | var max: T | SemVer | null = null; 1529 | var maxSV: SemVer | null = null; 1530 | try { 1531 | var rangeObj = new Range(range, optionsOrLoose); 1532 | } catch (er) { 1533 | return null; 1534 | } 1535 | versions.forEach((v) => { 1536 | if (rangeObj.test(v)) { 1537 | // satisfies(v, range, options) 1538 | if (!max || (maxSV && maxSV.compare(v) === -1)) { 1539 | // compare(max, v, true) 1540 | max = v; 1541 | maxSV = new SemVer(max, optionsOrLoose); 1542 | } 1543 | } 1544 | }); 1545 | return max; 1546 | } 1547 | 1548 | export function minSatisfying( 1549 | versions: ReadonlyArray, 1550 | range: string | Range, 1551 | optionsOrLoose?: boolean | Options, 1552 | ): T | null { 1553 | //todo 1554 | var min: any = null; 1555 | var minSV: any = null; 1556 | try { 1557 | var rangeObj = new Range(range, optionsOrLoose); 1558 | } catch (er) { 1559 | return null; 1560 | } 1561 | versions.forEach((v) => { 1562 | if (rangeObj.test(v)) { 1563 | // satisfies(v, range, options) 1564 | if (!min || minSV.compare(v) === 1) { 1565 | // compare(min, v, true) 1566 | min = v; 1567 | minSV = new SemVer(min, optionsOrLoose); 1568 | } 1569 | } 1570 | }); 1571 | return min; 1572 | } 1573 | 1574 | export function minVersion( 1575 | range: string | Range, 1576 | optionsOrLoose?: boolean | Options, 1577 | ): SemVer | null { 1578 | range = new Range(range, optionsOrLoose); 1579 | 1580 | var minver: SemVer | null = new SemVer("0.0.0"); 1581 | if (range.test(minver)) { 1582 | return minver; 1583 | } 1584 | 1585 | minver = new SemVer("0.0.0-0"); 1586 | if (range.test(minver)) { 1587 | return minver; 1588 | } 1589 | 1590 | minver = null; 1591 | for (var i = 0; i < range.set.length; ++i) { 1592 | var comparators = range.set[i]; 1593 | 1594 | comparators.forEach((comparator) => { 1595 | // Clone to avoid manipulating the comparator's semver object. 1596 | var compver = new SemVer(comparator.semver.version); 1597 | switch (comparator.operator) { 1598 | case ">": 1599 | if (compver.prerelease.length === 0) { 1600 | compver.patch++; 1601 | } else { 1602 | compver.prerelease.push(0); 1603 | } 1604 | compver.raw = compver.format(); 1605 | /* fallthrough */ 1606 | case "": 1607 | case ">=": 1608 | if (!minver || gt(minver, compver)) { 1609 | minver = compver; 1610 | } 1611 | break; 1612 | case "<": 1613 | case "<=": 1614 | /* Ignore maximum versions */ 1615 | break; 1616 | /* istanbul ignore next */ 1617 | default: 1618 | throw new Error("Unexpected operation: " + comparator.operator); 1619 | } 1620 | }); 1621 | } 1622 | 1623 | if (minver && range.test(minver)) { 1624 | return minver; 1625 | } 1626 | 1627 | return null; 1628 | } 1629 | 1630 | export function validRange( 1631 | range: string | Range | null, 1632 | optionsOrLoose?: boolean | Options, 1633 | ): string | null { 1634 | try { 1635 | if (range === null) return null; 1636 | // Return '*' instead of '' so that truthiness works. 1637 | // This will throw if it's invalid anyway 1638 | return new Range(range, optionsOrLoose).range || "*"; 1639 | } catch (er) { 1640 | return null; 1641 | } 1642 | } 1643 | 1644 | /** 1645 | * Return true if version is less than all the versions possible in the range. 1646 | */ 1647 | export function ltr( 1648 | version: string | SemVer, 1649 | range: string | Range, 1650 | optionsOrLoose?: boolean | Options, 1651 | ): boolean { 1652 | return outside(version, range, "<", optionsOrLoose); 1653 | } 1654 | 1655 | /** 1656 | * Return true if version is greater than all the versions possible in the range. 1657 | */ 1658 | export function gtr( 1659 | version: string | SemVer, 1660 | range: string | Range, 1661 | optionsOrLoose?: boolean | Options, 1662 | ): boolean { 1663 | return outside(version, range, ">", optionsOrLoose); 1664 | } 1665 | 1666 | /** 1667 | * Return true if the version is outside the bounds of the range in either the high or low direction. 1668 | * The hilo argument must be either the string '>' or '<'. (This is the function called by gtr and ltr.) 1669 | */ 1670 | export function outside( 1671 | version: string | SemVer, 1672 | range: string | Range, 1673 | hilo: ">" | "<", 1674 | optionsOrLoose?: boolean | Options, 1675 | ): boolean { 1676 | version = new SemVer(version, optionsOrLoose); 1677 | range = new Range(range, optionsOrLoose); 1678 | 1679 | let gtfn: typeof gt; 1680 | let ltefn: typeof lte; 1681 | let ltfn: typeof lt; 1682 | let comp: string; 1683 | let ecomp: string; 1684 | switch (hilo) { 1685 | case ">": 1686 | gtfn = gt; 1687 | ltefn = lte; 1688 | ltfn = lt; 1689 | comp = ">"; 1690 | ecomp = ">="; 1691 | break; 1692 | case "<": 1693 | gtfn = lt; 1694 | ltefn = gte; 1695 | ltfn = gt; 1696 | comp = "<"; 1697 | ecomp = "<="; 1698 | break; 1699 | default: 1700 | throw new TypeError('Must provide a hilo val of "<" or ">"'); 1701 | } 1702 | 1703 | // If it satisifes the range it is not outside 1704 | if (satisfies(version, range, optionsOrLoose)) { 1705 | return false; 1706 | } 1707 | 1708 | // From now on, variable terms are as if we're in "gtr" mode. 1709 | // but note that everything is flipped for the "ltr" function. 1710 | 1711 | for (let i: number = 0; i < range.set.length; ++i) { 1712 | const comparators: readonly Comparator[] = range.set[i]; 1713 | 1714 | let high: Comparator | null = null; 1715 | let low: Comparator | null = null; 1716 | 1717 | for (let comparator of comparators) { 1718 | if (comparator.semver === ANY) { 1719 | comparator = new Comparator(">=0.0.0"); 1720 | } 1721 | high = high || comparator; 1722 | low = low || comparator; 1723 | if (gtfn(comparator.semver, high.semver, optionsOrLoose)) { 1724 | high = comparator; 1725 | } else if (ltfn(comparator.semver, low.semver, optionsOrLoose)) { 1726 | low = comparator; 1727 | } 1728 | } 1729 | 1730 | if (high === null || low === null) return true; 1731 | 1732 | // If the edge version comparator has a operator then our version 1733 | // isn't outside it 1734 | if (high!.operator === comp || high!.operator === ecomp) { 1735 | return false; 1736 | } 1737 | 1738 | // If the lowest version comparator has an operator and our version 1739 | // is less than it then it isn't higher than the range 1740 | if ( 1741 | (!low!.operator || low!.operator === comp) && 1742 | ltefn(version, low!.semver) 1743 | ) { 1744 | return false; 1745 | } else if (low!.operator === ecomp && ltfn(version, low!.semver)) { 1746 | return false; 1747 | } 1748 | } 1749 | return true; 1750 | } 1751 | 1752 | export function prerelease( 1753 | version: string | SemVer, 1754 | optionsOrLoose?: boolean | Options, 1755 | ): ReadonlyArray | null { 1756 | var parsed = parse(version, optionsOrLoose); 1757 | return parsed && parsed.prerelease.length ? parsed.prerelease : null; 1758 | } 1759 | 1760 | /** 1761 | * Return true if any of the ranges comparators intersect 1762 | */ 1763 | export function intersects( 1764 | range1: string | Range | Comparator, 1765 | range2: string | Range | Comparator, 1766 | optionsOrLoose?: boolean | Options, 1767 | ): boolean { 1768 | range1 = new Range(range1, optionsOrLoose); 1769 | range2 = new Range(range2, optionsOrLoose); 1770 | return range1.intersects(range2); 1771 | } 1772 | 1773 | /** 1774 | * Coerces a string to semver if possible 1775 | */ 1776 | export function coerce( 1777 | version: string | SemVer, 1778 | optionsOrLoose?: boolean | Options, 1779 | ): SemVer | null { 1780 | if (version instanceof SemVer) { 1781 | return version; 1782 | } 1783 | 1784 | if (typeof version !== "string") { 1785 | return null; 1786 | } 1787 | 1788 | const match = version.match(re[COERCE]); 1789 | 1790 | if (match == null) { 1791 | return null; 1792 | } 1793 | 1794 | return parse( 1795 | match[1] + "." + (match[2] || "0") + "." + (match[3] || "0"), 1796 | optionsOrLoose, 1797 | ); 1798 | } 1799 | 1800 | export default SemVer; 1801 | -------------------------------------------------------------------------------- /range.bnf: -------------------------------------------------------------------------------- 1 | range-set ::= range ( logical-or range ) * 2 | logical-or ::= ( ' ' ) * '||' ( ' ' ) * 3 | range ::= hyphen | simple ( ' ' simple ) * | '' 4 | hyphen ::= partial ' - ' partial 5 | simple ::= primitive | partial | tilde | caret 6 | primitive ::= ( '<' | '>' | '>=' | '<=' | '=' ) partial 7 | partial ::= xr ( '.' xr ( '.' xr qualifier ? )? )? 8 | xr ::= 'x' | 'X' | '*' | nr 9 | nr ::= '0' | [1-9] ( [0-9] ) * 10 | tilde ::= '~' partial 11 | caret ::= '^' partial 12 | qualifier ::= ( '-' pre )? ( '+' build )? 13 | pre ::= parts 14 | build ::= parts 15 | parts ::= part ( '.' part ) * 16 | part ::= nr | [-0-9A-Za-z]+ 17 | -------------------------------------------------------------------------------- /test/clean.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | Deno.test("clean", function (): void { 6 | // [range, version] 7 | // Version should be detectable despite extra characters 8 | const versions: [string, string | null][] = [ 9 | ["1.2.3", "1.2.3"], 10 | [" 1.2.3 ", "1.2.3"], 11 | [" 1.2.3-4 ", "1.2.3-4"], 12 | [" 1.2.3-pre ", "1.2.3-pre"], 13 | [" =v1.2.3 ", "1.2.3"], 14 | ["v1.2.3", "1.2.3"], 15 | [" v1.2.3 ", "1.2.3"], 16 | ["\t1.2.3", "1.2.3"], 17 | [">1.2.3", null], 18 | ["~1.2.3", null], 19 | ["<=1.2.3", null], 20 | ["1.2.x", null], 21 | ]; 22 | 23 | versions.forEach(function (tuple) { 24 | const range: string = tuple[0]; 25 | const version: string | null = tuple[1]; 26 | const msg = `clean(${range})=${version}`; 27 | assertEquals(semver.clean(range), version, msg); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/cli.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "./deps.ts"; 2 | 3 | Deno.test("cli", async function (): Promise { 4 | const cmds = [ 5 | { commands: ["valid", "1.2.3"], expected: "1.2.3\n" }, 6 | { commands: ["clean", " =v1.2.3 "], expected: "1.2.3\n" }, 7 | { 8 | commands: ["satisfies", "1.2.3", "1.x || >=2.5.0 || 5.0.0 - 7.2.3"], 9 | expected: "true\n", 10 | }, 11 | { commands: ["gt", "1.2.3", "9.8.7"], expected: "false\n" }, 12 | { commands: ["lt", "1.2.3", "9.8.7"], expected: "true\n" }, 13 | { commands: ["minVersion", ">=1.0.0"], expected: "1.0.0\n" }, 14 | ]; 15 | 16 | const errorMsg = (command: string, expected: string, output: string) => 17 | `Command '${command}' failed; Expected: ${expected}; Was: ${output}`; 18 | 19 | for await (const { commands, expected } of cmds) { 20 | const p = Deno.run({ 21 | cmd: [ 22 | "deno", 23 | "run", 24 | "cli.ts", 25 | ...commands, 26 | ], 27 | stdout: "piped", 28 | }); 29 | 30 | const { code } = await p.status(); 31 | 32 | assertEquals( 33 | code, 34 | 0, 35 | errorMsg(commands[0] + " code", "0", code.toString()), 36 | ); 37 | 38 | const rawOutput = await p.output(); 39 | 40 | p.close(); 41 | 42 | const output = new TextDecoder().decode(rawOutput); 43 | 44 | assertEquals(output, expected, errorMsg(commands[0], expected, output)); 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /test/coerce.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | function r(text: string) { 6 | return function (count: number): string { 7 | return text.repeat(count); 8 | }; 9 | } 10 | 11 | Deno.test("coerce", function (): void { 12 | // Expected to be null (cannot be coerced). 13 | [ 14 | null, 15 | { version: "1.2.3" }, 16 | function () { 17 | return "1.2.3"; 18 | }, 19 | "", 20 | ".", 21 | "version one", 22 | r("9")(16), 23 | r("1")(17), 24 | "a" + r("9")(16), 25 | "a" + r("1")(17), 26 | r("9")(16) + "a", 27 | r("1")(17) + "a", 28 | r("9")(16) + ".4.7.4", 29 | r("9")(16) + "." + r("2")(16) + "." + r("3")(16), 30 | r("1")(16) + "." + r("9")(16) + "." + r("3")(16), 31 | r("1")(16) + "." + r("2")(16) + "." + r("9")(16), 32 | ].forEach(function (input) { 33 | var msg = "coerce(" + input + ") should be null"; 34 | assertEquals(semver.coerce(input as any), null, msg); 35 | }); 36 | 37 | // Expected to be the valid. 38 | [ 39 | [semver.parse("1.2.3")!.version, "1.2.3"], 40 | [".1", "1.0.0"], 41 | [".1.", "1.0.0"], 42 | ["..1", "1.0.0"], 43 | [".1.1", "1.1.0"], 44 | ["1.", "1.0.0"], 45 | ["1.0", "1.0.0"], 46 | ["1.0.0", "1.0.0"], 47 | ["0", "0.0.0"], 48 | ["0.0", "0.0.0"], 49 | ["0.0.0", "0.0.0"], 50 | ["0.1", "0.1.0"], 51 | ["0.0.1", "0.0.1"], 52 | ["0.1.1", "0.1.1"], 53 | ["1", "1.0.0"], 54 | ["1.2", "1.2.0"], 55 | ["1.2.3", "1.2.3"], 56 | ["1.2.3.4", "1.2.3"], 57 | ["13", "13.0.0"], 58 | ["35.12", "35.12.0"], 59 | ["35.12.18", "35.12.18"], 60 | ["35.12.18.24", "35.12.18"], 61 | ["v1", "1.0.0"], 62 | ["v1.2", "1.2.0"], 63 | ["v1.2.3", "1.2.3"], 64 | ["v1.2.3.4", "1.2.3"], 65 | [" 1", "1.0.0"], 66 | ["1 ", "1.0.0"], 67 | ["1 0", "1.0.0"], 68 | ["1 1", "1.0.0"], 69 | ["1.1 1", "1.1.0"], 70 | ["1.1-1", "1.1.0"], 71 | ["1.1-1", "1.1.0"], 72 | ["a1", "1.0.0"], 73 | ["a1a", "1.0.0"], 74 | ["1a", "1.0.0"], 75 | ["version 1", "1.0.0"], 76 | ["version1", "1.0.0"], 77 | ["version1.0", "1.0.0"], 78 | ["version1.1", "1.1.0"], 79 | ["42.6.7.9.3-alpha", "42.6.7"], 80 | ["v2", "2.0.0"], 81 | ["v3.4 replaces v3.3.1", "3.4.0"], 82 | ["4.6.3.9.2-alpha2", "4.6.3"], 83 | [r("1")(17) + ".2", "2.0.0"], 84 | [r("1")(17) + ".2.3", "2.3.0"], 85 | ["1." + r("2")(17) + ".3", "1.0.0"], 86 | ["1.2." + r("3")(17), "1.2.0"], 87 | [r("1")(17) + ".2.3.4", "2.3.4"], 88 | ["1." + r("2")(17) + ".3.4", "1.0.0"], 89 | ["1.2." + r("3")(17) + ".4", "1.2.0"], 90 | [ 91 | r("1")(17) + "." + r("2")(16) + "." + r("3")(16), 92 | r("2")(16) + "." + r("3")(16) + ".0", 93 | ], 94 | [r("1")(16) + "." + r("2")(17) + "." + r("3")(16), r("1")(16) + ".0.0"], 95 | [ 96 | r("1")(16) + "." + r("2")(16) + "." + r("3")(17), 97 | r("1")(16) + "." + r("2")(16) + ".0", 98 | ], 99 | ["11" + r(".1")(126), "11.1.1"], 100 | [r("1")(16), r("1")(16) + ".0.0"], 101 | ["a" + r("1")(16), r("1")(16) + ".0.0"], 102 | [r("1")(16) + ".2.3.4", r("1")(16) + ".2.3"], 103 | ["1." + r("2")(16) + ".3.4", "1." + r("2")(16) + ".3"], 104 | ["1.2." + r("3")(16) + ".4", "1.2." + r("3")(16)], 105 | [ 106 | r("1")(16) + "." + r("2")(16) + "." + r("3")(16), 107 | r("1")(16) + "." + r("2")(16) + "." + r("3")(16), 108 | ], 109 | ["1.2.3." + r("4")(252) + ".5", "1.2.3"], 110 | ["1.2.3." + r("4")(1024), "1.2.3"], 111 | [r("1")(17) + ".4.7.4", "4.7.4"], 112 | ].forEach(function (tuple: string[]) { 113 | var input = tuple[0]; 114 | var expected = tuple[1]; 115 | var msg = "coerce(" + input + ") should become " + expected; 116 | assertEquals(semver.coerce(input)!.version, expected, msg); 117 | }); 118 | 119 | assertEquals(semver.valid(semver.coerce("42.6.7.9.3-alpha")), "42.6.7"); 120 | assertEquals(semver.valid(semver.coerce("v2")), "2.0.0"); 121 | }); 122 | -------------------------------------------------------------------------------- /test/comparators.test.ts: -------------------------------------------------------------------------------- 1 | import { assert, assertEquals, assertThrows } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | type Version = string; 6 | type Options = semver.Options | boolean; 7 | 8 | Deno.test("comparators", function (): void { 9 | // [range, comparators] 10 | // turn range into a set of individual comparators 11 | const versions: [Version, string[][]][] = [ 12 | ["1.0.0 - 2.0.0", [[">=1.0.0", "<=2.0.0"]]], 13 | ["1.0.0", [["1.0.0"]]], 14 | [">=*", [[""]]], 15 | ["", [[""]]], 16 | ["*", [[""]]], 17 | ["*", [[""]]], 18 | [">=1.0.0", [[">=1.0.0"]]], 19 | [">=1.0.0", [[">=1.0.0"]]], 20 | [">=1.0.0", [[">=1.0.0"]]], 21 | [">1.0.0", [[">1.0.0"]]], 22 | [">1.0.0", [[">1.0.0"]]], 23 | ["<=2.0.0", [["<=2.0.0"]]], 24 | ["1", [[">=1.0.0", "<2.0.0"]]], 25 | ["<=2.0.0", [["<=2.0.0"]]], 26 | ["<=2.0.0", [["<=2.0.0"]]], 27 | ["<2.0.0", [["<2.0.0"]]], 28 | ["<2.0.0", [["<2.0.0"]]], 29 | [">= 1.0.0", [[">=1.0.0"]]], 30 | [">= 1.0.0", [[">=1.0.0"]]], 31 | [">= 1.0.0", [[">=1.0.0"]]], 32 | ["> 1.0.0", [[">1.0.0"]]], 33 | ["> 1.0.0", [[">1.0.0"]]], 34 | ["<= 2.0.0", [["<=2.0.0"]]], 35 | ["<= 2.0.0", [["<=2.0.0"]]], 36 | ["<= 2.0.0", [["<=2.0.0"]]], 37 | ["< 2.0.0", [["<2.0.0"]]], 38 | ["<\t2.0.0", [["<2.0.0"]]], 39 | [">=0.1.97", [[">=0.1.97"]]], 40 | [">=0.1.97", [[">=0.1.97"]]], 41 | ["0.1.20 || 1.2.4", [["0.1.20"], ["1.2.4"]]], 42 | [">=0.2.3 || <0.0.1", [[">=0.2.3"], ["<0.0.1"]]], 43 | [">=0.2.3 || <0.0.1", [[">=0.2.3"], ["<0.0.1"]]], 44 | [">=0.2.3 || <0.0.1", [[">=0.2.3"], ["<0.0.1"]]], 45 | ["||", [[""], [""]]], 46 | ["2.x.x", [[">=2.0.0", "<3.0.0"]]], 47 | ["1.2.x", [[">=1.2.0", "<1.3.0"]]], 48 | [ 49 | "1.2.x || 2.x", 50 | [ 51 | [">=1.2.0", "<1.3.0"], 52 | [">=2.0.0", "<3.0.0"], 53 | ], 54 | ], 55 | [ 56 | "1.2.x || 2.x", 57 | [ 58 | [">=1.2.0", "<1.3.0"], 59 | [">=2.0.0", "<3.0.0"], 60 | ], 61 | ], 62 | ["x", [[""]]], 63 | ["2.*.*", [[">=2.0.0", "<3.0.0"]]], 64 | ["1.2.*", [[">=1.2.0", "<1.3.0"]]], 65 | [ 66 | "1.2.* || 2.*", 67 | [ 68 | [">=1.2.0", "<1.3.0"], 69 | [">=2.0.0", "<3.0.0"], 70 | ], 71 | ], 72 | [ 73 | "1.2.* || 2.*", 74 | [ 75 | [">=1.2.0", "<1.3.0"], 76 | [">=2.0.0", "<3.0.0"], 77 | ], 78 | ], 79 | ["*", [[""]]], 80 | ["2", [[">=2.0.0", "<3.0.0"]]], 81 | ["2.3", [[">=2.3.0", "<2.4.0"]]], 82 | ["~2.4", [[">=2.4.0", "<2.5.0"]]], 83 | ["~2.4", [[">=2.4.0", "<2.5.0"]]], 84 | ["~>3.2.1", [[">=3.2.1", "<3.3.0"]]], 85 | ["~1", [[">=1.0.0", "<2.0.0"]]], 86 | ["~>1", [[">=1.0.0", "<2.0.0"]]], 87 | ["~> 1", [[">=1.0.0", "<2.0.0"]]], 88 | ["~1.0", [[">=1.0.0", "<1.1.0"]]], 89 | ["~ 1.0", [[">=1.0.0", "<1.1.0"]]], 90 | ["~ 1.0.3", [[">=1.0.3", "<1.1.0"]]], 91 | ["~> 1.0.3", [[">=1.0.3", "<1.1.0"]]], 92 | ["<1", [["<1.0.0"]]], 93 | ["< 1", [["<1.0.0"]]], 94 | [">=1", [[">=1.0.0"]]], 95 | [">= 1", [[">=1.0.0"]]], 96 | ["<1.2", [["<1.2.0"]]], 97 | ["< 1.2", [["<1.2.0"]]], 98 | ["1", [[">=1.0.0", "<2.0.0"]]], 99 | ["1 2", [[">=1.0.0", "<2.0.0", ">=2.0.0", "<3.0.0"]]], 100 | ["1.2 - 3.4.5", [[">=1.2.0", "<=3.4.5"]]], 101 | ["1.2.3 - 3.4", [[">=1.2.3", "<3.5.0"]]], 102 | ["1.2.3 - 3", [[">=1.2.3", "<4.0.0"]]], 103 | [">*", [["<0.0.0"]]], 104 | ["<*", [["<0.0.0"]]], 105 | ]; 106 | 107 | versions.forEach(function (v) { 108 | const pre = v[0]; 109 | const wanted = v[1]; 110 | const found = semver.toComparators(v[0]); 111 | const jw = JSON.stringify(wanted); 112 | assertEquals(found, wanted, "toComparators(" + pre + ") === " + jw); 113 | }); 114 | }); 115 | 116 | Deno.test("test", function (): void { 117 | const c = new semver.Comparator(">=1.2.3"); 118 | assert(c.test("1.2.4")); 119 | const c2 = new semver.Comparator(c); 120 | assert(c2.test("1.2.4")); 121 | const c3 = new semver.Comparator(c, true); 122 | assert(c3.test("1.2.4")); 123 | }); 124 | 125 | Deno.test("intersect", function (): void { 126 | const versions: [string, string, boolean][] = [ 127 | // One is a Version 128 | ["1.3.0", ">=1.3.0", true], 129 | ["1.3.0", ">1.3.0", false], 130 | [">=1.3.0", "1.3.0", true], 131 | [">1.3.0", "1.3.0", false], 132 | // Same direction increasing 133 | [">1.3.0", ">1.2.0", true], 134 | [">1.2.0", ">1.3.0", true], 135 | [">=1.2.0", ">1.3.0", true], 136 | [">1.2.0", ">=1.3.0", true], 137 | // Same direction decreasing 138 | ["<1.3.0", "<1.2.0", true], 139 | ["<1.2.0", "<1.3.0", true], 140 | ["<=1.2.0", "<1.3.0", true], 141 | ["<1.2.0", "<=1.3.0", true], 142 | // Different directions, same semver and inclusive operator 143 | [">=1.3.0", "<=1.3.0", true], 144 | [">=v1.3.0", "<=1.3.0", true], 145 | [">=1.3.0", ">=1.3.0", true], 146 | ["<=1.3.0", "<=1.3.0", true], 147 | ["<=1.3.0", "<=v1.3.0", true], 148 | [">1.3.0", "<=1.3.0", false], 149 | [">=1.3.0", "<1.3.0", false], 150 | // Opposite matching directions 151 | [">1.0.0", "<2.0.0", true], 152 | [">=1.0.0", "<2.0.0", true], 153 | [">=1.0.0", "<=2.0.0", true], 154 | [">1.0.0", "<=2.0.0", true], 155 | ["<=2.0.0", ">1.0.0", true], 156 | ["<=1.0.0", ">=2.0.0", false], 157 | ]; 158 | 159 | versions.forEach(function (v) { 160 | const comparator1 = new semver.Comparator(v[0]); 161 | const comparator2 = new semver.Comparator(v[1]); 162 | const expect = v[2]; 163 | 164 | const actual1 = comparator1.intersects(comparator2, false); 165 | const actual2 = comparator2.intersects(comparator1, { loose: false }); 166 | const actual3 = semver.intersects(comparator1, comparator2); 167 | const actual4 = semver.intersects(comparator2, comparator1); 168 | const actual5 = semver.intersects(comparator1, comparator2, true); 169 | const actual6 = semver.intersects(comparator2, comparator1, true); 170 | const actual7 = semver.intersects(v[0], v[1]); 171 | const actual8 = semver.intersects(v[1], v[0]); 172 | const actual9 = semver.intersects(v[0], v[1], true); 173 | const actual10 = semver.intersects(v[1], v[0], true); 174 | 175 | assertEquals(actual1, expect); 176 | assertEquals(actual2, expect); 177 | assertEquals(actual3, expect); 178 | assertEquals(actual4, expect); 179 | assertEquals(actual5, expect); 180 | assertEquals(actual6, expect); 181 | assertEquals(actual7, expect); 182 | assertEquals(actual8, expect); 183 | assertEquals(actual9, expect); 184 | assertEquals(actual10, expect); 185 | }); 186 | }); 187 | 188 | Deno.test("tostrings", function (): void { 189 | assertEquals(new semver.Comparator(">= v1.2.3").toString(), ">=1.2.3"); 190 | }); 191 | -------------------------------------------------------------------------------- /test/comparison.test.ts: -------------------------------------------------------------------------------- 1 | import { assert, assertEquals, assertThrows } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | type Version = string; 6 | type Options = semver.Options | boolean; 7 | 8 | Deno.test("comparison", function (): void { 9 | // [version1, version2, loose] 10 | // version1 should be greater than version2 11 | const versions: [Version, Version, Options?][] = [ 12 | ["0.0.0", "0.0.0-foo"], 13 | ["0.0.1", "0.0.0"], 14 | ["1.0.0", "0.9.9"], 15 | ["0.10.0", "0.9.0"], 16 | ["0.99.0", "0.10.0", {}], 17 | ["2.0.0", "1.2.3", { loose: false }], 18 | ["v0.0.0", "0.0.0-foo", true], 19 | ["v0.0.1", "0.0.0", { loose: true }], 20 | ["v1.0.0", "0.9.9", true], 21 | ["v0.10.0", "0.9.0", true], 22 | ["v0.99.0", "0.10.0", true], 23 | ["v2.0.0", "1.2.3", true], 24 | ["0.0.0", "v0.0.0-foo", true], 25 | ["0.0.1", "v0.0.0", true], 26 | ["1.0.0", "v0.9.9", true], 27 | ["0.10.0", "v0.9.0", true], 28 | ["0.99.0", "v0.10.0", true], 29 | ["2.0.0", "v1.2.3", true], 30 | ["1.2.3", "1.2.3-asdf"], 31 | ["1.2.3", "1.2.3-4"], 32 | ["1.2.3", "1.2.3-4-foo"], 33 | ["1.2.3-5-foo", "1.2.3-5"], 34 | ["1.2.3-5", "1.2.3-4"], 35 | ["1.2.3-5-foo", "1.2.3-5-Foo"], 36 | ["3.0.0", "2.7.2+asdf"], 37 | ["1.2.3-a.10", "1.2.3-a.5"], 38 | ["1.2.3-a.b", "1.2.3-a.5"], 39 | ["1.2.3-a.b", "1.2.3-a"], 40 | ["1.2.3-a.b.c.10.d.5", "1.2.3-a.b.c.5.d.100"], 41 | ["1.2.3-r2", "1.2.3-r100"], 42 | ["1.2.3-r100", "1.2.3-R2"], 43 | ]; 44 | 45 | versions.forEach(function (v) { 46 | const v0 = v[0]; 47 | const v1 = v[1]; 48 | const loose = v[2]; 49 | assert(semver.gt(v0, v1, loose), "gt('" + v0 + "', '" + v1 + "')"); 50 | assert(semver.lt(v1, v0, loose), "lt('" + v1 + "', '" + v0 + "')"); 51 | assert(!semver.gt(v1, v0, loose), "!gt('" + v1 + "', '" + v0 + "')"); 52 | assert(!semver.lt(v0, v1, loose), "!lt('" + v0 + "', '" + v1 + "')"); 53 | assert(semver.eq(v0, v0, loose), "eq('" + v0 + "', '" + v0 + "')"); 54 | assert(semver.eq(v1, v1, loose), "eq('" + v1 + "', '" + v1 + "')"); 55 | assert(semver.neq(v0, v1, loose), "neq('" + v0 + "', '" + v1 + "')"); 56 | assert( 57 | semver.cmp(v1, "==", v1, loose), 58 | "cmp('" + v1 + "' == '" + v1 + "')", 59 | ); 60 | assert( 61 | semver.cmp(v0, ">=", v1, loose), 62 | "cmp('" + v0 + "' >= '" + v1 + "')", 63 | ); 64 | assert( 65 | semver.cmp(v1, "<=", v0, loose), 66 | "cmp('" + v1 + "' <= '" + v0 + "')", 67 | ); 68 | assert( 69 | semver.cmp(v0, "!=", v1, loose), 70 | "cmp('" + v0 + "' != '" + v1 + "')", 71 | ); 72 | }); 73 | }); 74 | 75 | Deno.test("compareBuild", function (): void { 76 | const noBuild = new semver.SemVer("1.0.0"); 77 | const build0 = new semver.SemVer("1.0.0+0"); 78 | const build1 = new semver.SemVer("1.0.0+1"); 79 | const build10 = new semver.SemVer("1.0.0+1.0"); 80 | assertEquals(noBuild.compareBuild(build0), -1); 81 | assertEquals(build0.compareBuild(build0), 0); 82 | assertEquals(build0.compareBuild(noBuild), 1); 83 | 84 | assertEquals(build0.compareBuild("1.0.0+0.0"), -1); 85 | assertEquals(build0.compareBuild(build1), -1); 86 | assertEquals(build1.compareBuild(build0), 1); 87 | assertEquals(build10.compareBuild(build1), 1); 88 | }); 89 | 90 | Deno.test("rcompare", function (): void { 91 | assertEquals(semver.rcompare("1.0.0", "1.0.1"), 1); 92 | assertEquals(semver.rcompare("1.0.0", "1.0.0"), 0); 93 | assertEquals(semver.rcompare("1.0.0+0", "1.0.0"), 0); 94 | assertEquals(semver.rcompare("1.0.1", "1.0.0"), -1); 95 | }); 96 | 97 | Deno.test("compareMainVsPre", function (): void { 98 | const s = new semver.SemVer("1.2.3"); 99 | assertEquals(s.compareMain("2.3.4"), -1); 100 | assertEquals(s.compareMain("1.2.4"), -1); 101 | assertEquals(s.compareMain("0.1.2"), 1); 102 | assertEquals(s.compareMain("1.2.2"), 1); 103 | assertEquals(s.compareMain("1.2.3-pre"), 0); 104 | 105 | const p = new semver.SemVer("1.2.3-alpha.0.pr.1"); 106 | assertEquals(p.comparePre("9.9.9-alpha.0.pr.1"), 0); 107 | assertEquals(p.comparePre("1.2.3"), -1); 108 | assertEquals(p.comparePre("1.2.3-alpha.0.pr.2"), -1); 109 | assertEquals(p.comparePre("1.2.3-alpha.0.2"), 1); 110 | }); 111 | 112 | Deno.test("compareIdentifierst", function (): void { 113 | const set = [ 114 | ["1", "2"], 115 | ["alpha", "beta"], 116 | ["0", "beta"], 117 | ]; 118 | set.forEach(function (ab) { 119 | const a = ab[0]; 120 | const b = ab[1]; 121 | assertEquals(semver.compareIdentifiers(a, b), -1); 122 | assertEquals(semver.rcompareIdentifiers(a, b), 1); 123 | }); 124 | assertEquals(semver.compareIdentifiers("0", "0"), 0); 125 | assertEquals(semver.rcompareIdentifiers("0", "0"), 0); 126 | }); 127 | 128 | Deno.test("strictVsLooseVersion", function (): void { 129 | [ 130 | ["=1.2.3", "1.2.3"], 131 | ["01.02.03", "1.2.3"], 132 | ["1.2.3-beta.01", "1.2.3-beta.1"], 133 | [" =1.2.3", "1.2.3"], 134 | ["1.2.3foo", "1.2.3-foo"], 135 | ].forEach(function (v) { 136 | const loose = v[0]; 137 | const strict = v[1]; 138 | assertThrows(function () { 139 | new semver.SemVer(loose); 140 | }); 141 | const lv = new semver.SemVer(loose, true); 142 | assertEquals(lv.version, strict); 143 | assert(semver.eq(loose, strict, true)); 144 | assertThrows(function () { 145 | semver.eq(loose, strict); 146 | }); 147 | assertThrows(function () { 148 | new semver.SemVer(strict).compare(loose); 149 | }); 150 | assertEquals(semver.compareLoose(v[0], v[1]), 0); 151 | }); 152 | }); 153 | 154 | Deno.test("invalidCmpUsage", function (): void { 155 | assertThrows( 156 | function () { 157 | semver.cmp("1.2.3", "a frog" as any, "4.5.6"); 158 | }, 159 | TypeError, 160 | "Invalid operator: a frog", 161 | ); 162 | }); 163 | -------------------------------------------------------------------------------- /test/deps.ts: -------------------------------------------------------------------------------- 1 | export { 2 | assert, 3 | assertEquals, 4 | assertNotEquals, 5 | assertThrows, 6 | assertThrowsAsync, 7 | } from "https://deno.land/std@0.68.0/testing/asserts.ts"; 8 | -------------------------------------------------------------------------------- /test/diff.test.ts: -------------------------------------------------------------------------------- 1 | import { assert, assertEquals, assertThrows } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | type Version = string; 6 | 7 | Deno.test("diff", function (): void { 8 | // [version1, version2, result] 9 | // diff(version1, version2) -> result 10 | const versions: [Version, Version, semver.ReleaseType | null][] = [ 11 | ["1.2.3", "0.2.3", "major"], 12 | ["1.4.5", "0.2.3", "major"], 13 | ["1.2.3", "2.0.0-pre", "premajor"], 14 | ["1.2.3", "1.3.3", "minor"], 15 | ["1.0.1", "1.1.0-pre", "preminor"], 16 | ["1.2.3", "1.2.4", "patch"], 17 | ["1.2.3", "1.2.4-pre", "prepatch"], 18 | ["0.0.1", "0.0.1-pre", "prerelease"], 19 | ["0.0.1", "0.0.1-pre-2", "prerelease"], 20 | ["1.1.0", "1.1.0-pre", "prerelease"], 21 | ["1.1.0-pre-1", "1.1.0-pre-2", "prerelease"], 22 | ["1.0.0", "1.0.0", null], 23 | ]; 24 | 25 | versions.forEach(function (v) { 26 | const version1 = v[0]; 27 | const version2 = v[1]; 28 | const wanted = v[2]; 29 | const found = semver.diff(version1, version2); 30 | const cmd = "diff(" + version1 + ", " + version2 + ")"; 31 | assertEquals(found, wanted, cmd + " === " + wanted); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /test/equality.test.ts: -------------------------------------------------------------------------------- 1 | import { assert, assertEquals } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | type Version = string; 6 | 7 | Deno.test("equality", function (): void { 8 | // [version1, version2, loose] 9 | // version1 should be equivalent to version2 10 | const versions: [Version, Version, boolean?][] = [ 11 | ["1.2.3", "v1.2.3", true], 12 | ["1.2.3", "=1.2.3", true], 13 | ["1.2.3", "v 1.2.3", true], 14 | ["1.2.3", "= 1.2.3", true], 15 | ["1.2.3", " v1.2.3", true], 16 | ["1.2.3", " =1.2.3", true], 17 | ["1.2.3", " v 1.2.3", true], 18 | ["1.2.3", " = 1.2.3", true], 19 | ["1.2.3-0", "v1.2.3-0", true], 20 | ["1.2.3-0", "=1.2.3-0", true], 21 | ["1.2.3-0", "v 1.2.3-0", true], 22 | ["1.2.3-0", "= 1.2.3-0", true], 23 | ["1.2.3-0", " v1.2.3-0", true], 24 | ["1.2.3-0", " =1.2.3-0", true], 25 | ["1.2.3-0", " v 1.2.3-0", true], 26 | ["1.2.3-0", " = 1.2.3-0", true], 27 | ["1.2.3-1", "v1.2.3-1", true], 28 | ["1.2.3-1", "=1.2.3-1", true], 29 | ["1.2.3-1", "v 1.2.3-1", true], 30 | ["1.2.3-1", "= 1.2.3-1", true], 31 | ["1.2.3-1", " v1.2.3-1", true], 32 | ["1.2.3-1", " =1.2.3-1", true], 33 | ["1.2.3-1", " v 1.2.3-1", true], 34 | ["1.2.3-1", " = 1.2.3-1", true], 35 | ["1.2.3-beta", "v1.2.3-beta", true], 36 | ["1.2.3-beta", "=1.2.3-beta", true], 37 | ["1.2.3-beta", "v 1.2.3-beta", true], 38 | ["1.2.3-beta", "= 1.2.3-beta", true], 39 | ["1.2.3-beta", " v1.2.3-beta", true], 40 | ["1.2.3-beta", " =1.2.3-beta", true], 41 | ["1.2.3-beta", " v 1.2.3-beta", true], 42 | ["1.2.3-beta", " = 1.2.3-beta", true], 43 | ["1.2.3-beta+build", " = 1.2.3-beta+otherbuild", true], 44 | ["1.2.3+build", " = 1.2.3+otherbuild", true], 45 | ["1.2.3-beta+build", "1.2.3-beta+otherbuild"], 46 | ["1.2.3+build", "1.2.3+otherbuild"], 47 | [" v1.2.3+build", "1.2.3+otherbuild"], 48 | ]; 49 | 50 | versions.forEach(function (v) { 51 | const v0 = v[0]; 52 | const v1 = v[1]; 53 | const loose: boolean | undefined = v[2]; 54 | 55 | assert(semver.eq(v0, v1, loose), "eq('" + v0 + "', '" + v1 + "')"); 56 | assert(!semver.neq(v0, v1, loose), "!neq('" + v0 + "', '" + v1 + "')"); 57 | assert(semver.cmp(v0, "==", v1, loose), "cmp(" + v0 + "==" + v1 + ")"); 58 | assert(!semver.cmp(v0, "!=", v1, loose), "!cmp(" + v0 + "!=" + v1 + ")"); 59 | assert(!semver.cmp(v0, "===", v1, loose), "!cmp(" + v0 + "===" + v1 + ")"); 60 | 61 | // also test with an object. they are === because obj.version matches 62 | assert( 63 | semver.cmp( 64 | new semver.SemVer(v0, { loose: loose }), 65 | "===", 66 | new semver.SemVer(v1, { loose: loose }), 67 | ), 68 | "!cmp(" + v0 + "===" + v1 + ") object", 69 | ); 70 | 71 | assert(semver.cmp(v0, "!==", v1, loose), "cmp(" + v0 + "!==" + v1 + ")"); 72 | 73 | assert( 74 | !semver.cmp( 75 | new semver.SemVer(v0, loose), 76 | "!==", 77 | new semver.SemVer(v1, loose), 78 | ), 79 | "cmp(" + v0 + "!==" + v1 + ") object", 80 | ); 81 | 82 | assert(!semver.gt(v0, v1, loose), "!gt('" + v0 + "', '" + v1 + "')"); 83 | assert(semver.gte(v0, v1, loose), "gte('" + v0 + "', '" + v1 + "')"); 84 | assert(!semver.lt(v0, v1, loose), "!lt('" + v0 + "', '" + v1 + "')"); 85 | assert(semver.lte(v0, v1, loose), "lte('" + v0 + "', '" + v1 + "')"); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/gtr.test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | Deno.test("gtr", function (): void { 6 | // [range, version, loose] 7 | // Version should be greater than range 8 | const versions: ReadonlyArray<[string, string, boolean?]> = [ 9 | ["~1.2.2", "1.3.0"], 10 | ["~0.6.1-1", "0.7.1-1"], 11 | ["1.0.0 - 2.0.0", "2.0.1"], 12 | ["1.0.0", "1.0.1-beta1"], 13 | ["1.0.0", "2.0.0"], 14 | ["<=2.0.0", "2.1.1"], 15 | ["<=2.0.0", "3.2.9"], 16 | ["<2.0.0", "2.0.0"], 17 | ["0.1.20 || 1.2.4", "1.2.5"], 18 | ["2.x.x", "3.0.0"], 19 | ["1.2.x", "1.3.0"], 20 | ["1.2.x || 2.x", "3.0.0"], 21 | ["2.*.*", "5.0.1"], 22 | ["1.2.*", "1.3.3"], 23 | ["1.2.* || 2.*", "4.0.0"], 24 | ["2", "3.0.0"], 25 | ["2.3", "2.4.2"], 26 | ["~2.4", "2.5.0"], // >=2.4.0 <2.5.0 27 | ["~2.4", "2.5.5"], 28 | ["~>3.2.1", "3.3.0"], // >=3.2.1 <3.3.0 29 | ["~1", "2.2.3"], // >=1.0.0 <2.0.0 30 | ["~>1", "2.2.4"], 31 | ["~> 1", "3.2.3"], 32 | ["~1.0", "1.1.2"], // >=1.0.0 <1.1.0 33 | ["~ 1.0", "1.1.0"], 34 | ["<1.2", "1.2.0"], 35 | ["< 1.2", "1.2.1"], 36 | ["1", "2.0.0beta", true], 37 | ["~v0.5.4-pre", "0.6.0"], 38 | ["~v0.5.4-pre", "0.6.1-pre"], 39 | ["=0.7.x", "0.8.0"], 40 | ["=0.7.x", "0.8.0-asdf"], 41 | ["<0.7.x", "0.7.0"], 42 | ["~1.2.2", "1.3.0"], 43 | ["1.0.0 - 2.0.0", "2.2.3"], 44 | ["1.0.0", "1.0.1"], 45 | ["<=2.0.0", "3.0.0"], 46 | ["<=2.0.0", "2.9999.9999"], 47 | ["<=2.0.0", "2.2.9"], 48 | ["<2.0.0", "2.9999.9999"], 49 | ["<2.0.0", "2.2.9"], 50 | ["2.x.x", "3.1.3"], 51 | ["1.2.x", "1.3.3"], 52 | ["1.2.x || 2.x", "3.1.3"], 53 | ["2.*.*", "3.1.3"], 54 | ["1.2.*", "1.3.3"], 55 | ["1.2.* || 2.*", "3.1.3"], 56 | ["2", "3.1.2"], 57 | ["2.3", "2.4.1"], 58 | ["~2.4", "2.5.0"], // >=2.4.0 <2.5.0 59 | ["~>3.2.1", "3.3.2"], // >=3.2.1 <3.3.0 60 | ["~1", "2.2.3"], // >=1.0.0 <2.0.0 61 | ["~>1", "2.2.3"], 62 | ["~1.0", "1.1.0"], // >=1.0.0 <1.1.0 63 | ["<1", "1.0.0"], 64 | ["1", "2.0.0beta", true], 65 | ["<1", "1.0.0beta", true], 66 | ["< 1", "1.0.0beta", true], 67 | ["=0.7.x", "0.8.2"], 68 | ["<0.7.x", "0.7.2"], 69 | ]; 70 | 71 | versions.forEach(function (tuple) { 72 | const range = tuple[0]; 73 | const version = tuple[1]; 74 | const loose = tuple[2] || false; 75 | const msg = `gtr(${version}, ${range}, ${loose})`; 76 | assert(semver.gtr(version, range, loose), msg); 77 | }); 78 | }); 79 | 80 | Deno.test("gtrNegative", function (): void { 81 | // [range, version, loose] 82 | // Version should be greater than range 83 | const versions: ReadonlyArray<[string, string, boolean?]> = [ 84 | ["~0.6.1-1", "0.6.1-1"], 85 | ["1.0.0 - 2.0.0", "1.2.3"], 86 | ["1.0.0 - 2.0.0", "0.9.9"], 87 | ["1.0.0", "1.0.0"], 88 | [">=*", "0.2.4"], 89 | ["", "1.0.0", true], 90 | ["*", "1.2.3"], 91 | ["*", "v1.2.3-foo"], 92 | [">=1.0.0", "1.0.0"], 93 | [">=1.0.0", "1.0.1"], 94 | [">=1.0.0", "1.1.0"], 95 | [">1.0.0", "1.0.1"], 96 | [">1.0.0", "1.1.0"], 97 | ["<=2.0.0", "2.0.0"], 98 | ["<=2.0.0", "1.9999.9999"], 99 | ["<=2.0.0", "0.2.9"], 100 | ["<2.0.0", "1.9999.9999"], 101 | ["<2.0.0", "0.2.9"], 102 | [">= 1.0.0", "1.0.0"], 103 | [">= 1.0.0", "1.0.1"], 104 | [">= 1.0.0", "1.1.0"], 105 | ["> 1.0.0", "1.0.1"], 106 | ["> 1.0.0", "1.1.0"], 107 | ["<= 2.0.0", "2.0.0"], 108 | ["<= 2.0.0", "1.9999.9999"], 109 | ["<= 2.0.0", "0.2.9"], 110 | ["< 2.0.0", "1.9999.9999"], 111 | ["<\t2.0.0", "0.2.9"], 112 | [">=0.1.97", "v0.1.97"], 113 | [">=0.1.97", "0.1.97"], 114 | ["0.1.20 || 1.2.4", "1.2.4"], 115 | ["0.1.20 || >1.2.4", "1.2.4"], 116 | ["0.1.20 || 1.2.4", "1.2.3"], 117 | ["0.1.20 || 1.2.4", "0.1.20"], 118 | [">=0.2.3 || <0.0.1", "0.0.0"], 119 | [">=0.2.3 || <0.0.1", "0.2.3"], 120 | [">=0.2.3 || <0.0.1", "0.2.4"], 121 | ["||", "1.3.4"], 122 | ["2.x.x", "2.1.3"], 123 | ["1.2.x", "1.2.3"], 124 | ["1.2.x || 2.x", "2.1.3"], 125 | ["1.2.x || 2.x", "1.2.3"], 126 | ["x", "1.2.3"], 127 | ["2.*.*", "2.1.3"], 128 | ["1.2.*", "1.2.3"], 129 | ["1.2.* || 2.*", "2.1.3"], 130 | ["1.2.* || 2.*", "1.2.3"], 131 | ["1.2.* || 2.*", "1.2.3"], 132 | ["*", "1.2.3"], 133 | ["2", "2.1.2"], 134 | ["2.3", "2.3.1"], 135 | ["~2.4", "2.4.0"], // >=2.4.0 <2.5.0 136 | ["~2.4", "2.4.5"], 137 | ["~>3.2.1", "3.2.2"], // >=3.2.1 <3.3.0 138 | ["~1", "1.2.3"], // >=1.0.0 <2.0.0 139 | ["~>1", "1.2.3"], 140 | ["~> 1", "1.2.3"], 141 | ["~1.0", "1.0.2"], // >=1.0.0 <1.1.0 142 | ["~ 1.0", "1.0.2"], 143 | [">=1", "1.0.0"], 144 | [">= 1", "1.0.0"], 145 | ["<1.2", "1.1.1"], 146 | ["< 1.2", "1.1.1"], 147 | ["1", "1.0.0beta", true], 148 | ["~v0.5.4-pre", "0.5.5"], 149 | ["~v0.5.4-pre", "0.5.4"], 150 | ["=0.7.x", "0.7.2"], 151 | [">=0.7.x", "0.7.2"], 152 | ["=0.7.x", "0.7.0-asdf"], 153 | [">=0.7.x", "0.7.0-asdf"], 154 | ["<=0.7.x", "0.6.2"], 155 | [">0.2.3 >0.2.4 <=0.2.5", "0.2.5"], 156 | [">=0.2.3 <=0.2.4", "0.2.4"], 157 | ["1.0.0 - 2.0.0", "2.0.0"], 158 | ["^1", "0.0.0-0"], 159 | ["^3.0.0", "2.0.0"], 160 | ["^1.0.0 || ~2.0.1", "2.0.0"], 161 | ["^0.1.0 || ~3.0.1 || 5.0.0", "3.2.0"], 162 | ["^0.1.0 || ~3.0.1 || 5.0.0", "1.0.0beta", true], 163 | ["^0.1.0 || ~3.0.1 || 5.0.0", "5.0.0-0", true], 164 | ["^0.1.0 || ~3.0.1 || >4 <=5.0.0", "3.5.0"], 165 | ]; 166 | 167 | versions.forEach(function (tuple) { 168 | const range = tuple[0]; 169 | const version = tuple[1]; 170 | const loose = tuple[2] || false; 171 | const msg = `!gtr(${version}, ${range}, ${loose})`; 172 | assert(!semver.gtr(version, range, loose), msg); 173 | }); 174 | }); 175 | -------------------------------------------------------------------------------- /test/increment.test.ts: -------------------------------------------------------------------------------- 1 | import { assert, assertEquals, assertThrows } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | type Inc = semver.ReleaseType; 6 | type Version = string; 7 | type Options = semver.Options | boolean; 8 | 9 | Deno.test("increment", function (): void { 10 | // [version, inc, result, identifier] 11 | // inc(version, inc) -> result 12 | const versions: [Version, Inc, Version | null, Options?, string?][] = [ 13 | ["1.2.3", "major", "2.0.0"], 14 | ["1.2.3", "minor", "1.3.0"], 15 | ["1.2.3", "patch", "1.2.4"], 16 | ["1.2.3tag", "major", "2.0.0", true], 17 | ["1.2.3-tag", "major", "2.0.0"], 18 | ["1.2.3", "fake" as any, null], 19 | ["1.2.0-0", "patch", "1.2.0"], 20 | ["fake", "major", null], 21 | ["1.2.3-4", "major", "2.0.0"], 22 | ["1.2.3-4", "minor", "1.3.0"], 23 | ["1.2.3-4", "patch", "1.2.3"], 24 | ["1.2.3-alpha.0.beta", "major", "2.0.0"], 25 | ["1.2.3-alpha.0.beta", "minor", "1.3.0"], 26 | ["1.2.3-alpha.0.beta", "patch", "1.2.3"], 27 | ["1.2.4", "prerelease", "1.2.5-0"], 28 | ["1.2.3-0", "prerelease", "1.2.3-1"], 29 | ["1.2.3-alpha.0", "prerelease", "1.2.3-alpha.1"], 30 | ["1.2.3-alpha.1", "prerelease", "1.2.3-alpha.2"], 31 | ["1.2.3-alpha.2", "prerelease", "1.2.3-alpha.3"], 32 | ["1.2.3-alpha.0.beta", "prerelease", "1.2.3-alpha.1.beta"], 33 | ["1.2.3-alpha.1.beta", "prerelease", "1.2.3-alpha.2.beta"], 34 | ["1.2.3-alpha.2.beta", "prerelease", "1.2.3-alpha.3.beta"], 35 | ["1.2.3-alpha.10.0.beta", "prerelease", "1.2.3-alpha.10.1.beta"], 36 | ["1.2.3-alpha.10.1.beta", "prerelease", "1.2.3-alpha.10.2.beta"], 37 | ["1.2.3-alpha.10.2.beta", "prerelease", "1.2.3-alpha.10.3.beta"], 38 | ["1.2.3-alpha.10.beta.0", "prerelease", "1.2.3-alpha.10.beta.1"], 39 | ["1.2.3-alpha.10.beta.1", "prerelease", "1.2.3-alpha.10.beta.2"], 40 | ["1.2.3-alpha.10.beta.2", "prerelease", "1.2.3-alpha.10.beta.3"], 41 | ["1.2.3-alpha.9.beta", "prerelease", "1.2.3-alpha.10.beta"], 42 | ["1.2.3-alpha.10.beta", "prerelease", "1.2.3-alpha.11.beta"], 43 | ["1.2.3-alpha.11.beta", "prerelease", "1.2.3-alpha.12.beta"], 44 | ["1.2.0", "prepatch", "1.2.1-0"], 45 | ["1.2.0-1", "prepatch", "1.2.1-0"], 46 | ["1.2.0", "preminor", "1.3.0-0"], 47 | ["1.2.3-1", "preminor", "1.3.0-0"], 48 | ["1.2.0", "premajor", "2.0.0-0"], 49 | ["1.2.3-1", "premajor", "2.0.0-0"], 50 | ["1.2.0-1", "minor", "1.2.0"], 51 | ["1.0.0-1", "major", "1.0.0"], 52 | 53 | ["1.2.3", "major", "2.0.0", false, "dev"], 54 | ["1.2.3", "minor", "1.3.0", false, "dev"], 55 | ["1.2.3", "patch", "1.2.4", false, "dev"], 56 | ["1.2.3tag", "major", "2.0.0", true, "dev"], 57 | ["1.2.3-tag", "major", "2.0.0", false, "dev"], 58 | ["1.2.3", "fake" as any, null, false, "dev"], 59 | ["1.2.0-0", "patch", "1.2.0", false, "dev"], 60 | ["fake", "major", null, false, "dev"], 61 | ["1.2.3-4", "major", "2.0.0", false, "dev"], 62 | ["1.2.3-4", "minor", "1.3.0", false, "dev"], 63 | ["1.2.3-4", "patch", "1.2.3", false, "dev"], 64 | ["1.2.3-alpha.0.beta", "major", "2.0.0", false, "dev"], 65 | ["1.2.3-alpha.0.beta", "minor", "1.3.0", false, "dev"], 66 | ["1.2.3-alpha.0.beta", "patch", "1.2.3", false, "dev"], 67 | ["1.2.4", "prerelease", "1.2.5-dev.0", false, "dev"], 68 | ["1.2.3-0", "prerelease", "1.2.3-dev.0", false, "dev"], 69 | ["1.2.3-alpha.0", "prerelease", "1.2.3-dev.0", false, "dev"], 70 | ["1.2.3-alpha.0", "prerelease", "1.2.3-alpha.1", false, "alpha"], 71 | ["1.2.3-alpha.0.beta", "prerelease", "1.2.3-dev.0", false, "dev"], 72 | ["1.2.3-alpha.0.beta", "prerelease", "1.2.3-alpha.1.beta", false, "alpha"], 73 | ["1.2.3-alpha.10.0.beta", "prerelease", "1.2.3-dev.0", false, "dev"], 74 | [ 75 | "1.2.3-alpha.10.0.beta", 76 | "prerelease", 77 | "1.2.3-alpha.10.1.beta", 78 | false, 79 | "alpha", 80 | ], 81 | [ 82 | "1.2.3-alpha.10.1.beta", 83 | "prerelease", 84 | "1.2.3-alpha.10.2.beta", 85 | false, 86 | "alpha", 87 | ], 88 | [ 89 | "1.2.3-alpha.10.2.beta", 90 | "prerelease", 91 | "1.2.3-alpha.10.3.beta", 92 | false, 93 | "alpha", 94 | ], 95 | ["1.2.3-alpha.10.beta.0", "prerelease", "1.2.3-dev.0", false, "dev"], 96 | [ 97 | "1.2.3-alpha.10.beta.0", 98 | "prerelease", 99 | "1.2.3-alpha.10.beta.1", 100 | false, 101 | "alpha", 102 | ], 103 | [ 104 | "1.2.3-alpha.10.beta.1", 105 | "prerelease", 106 | "1.2.3-alpha.10.beta.2", 107 | false, 108 | "alpha", 109 | ], 110 | [ 111 | "1.2.3-alpha.10.beta.2", 112 | "prerelease", 113 | "1.2.3-alpha.10.beta.3", 114 | false, 115 | "alpha", 116 | ], 117 | ["1.2.3-alpha.9.beta", "prerelease", "1.2.3-dev.0", false, "dev"], 118 | ["1.2.3-alpha.9.beta", "prerelease", "1.2.3-alpha.10.beta", false, "alpha"], 119 | [ 120 | "1.2.3-alpha.10.beta", 121 | "prerelease", 122 | "1.2.3-alpha.11.beta", 123 | false, 124 | "alpha", 125 | ], 126 | [ 127 | "1.2.3-alpha.11.beta", 128 | "prerelease", 129 | "1.2.3-alpha.12.beta", 130 | false, 131 | "alpha", 132 | ], 133 | ["1.2.0", "prepatch", "1.2.1-dev.0", false, "dev"], 134 | ["1.2.0-1", "prepatch", "1.2.1-dev.0", false, "dev"], 135 | ["1.2.0", "preminor", "1.3.0-dev.0", false, "dev"], 136 | ["1.2.3-1", "preminor", "1.3.0-dev.0", false, "dev"], 137 | ["1.2.0", "premajor", "2.0.0-dev.0", false, "dev"], 138 | ["1.2.3-1", "premajor", "2.0.0-dev.0", false, "dev"], 139 | ["1.2.0-1", "minor", "1.2.0", false, "dev"], 140 | ["1.0.0-1", "major", "1.0.0", "dev" as any], 141 | ["1.2.3-dev.bar", "prerelease", "1.2.3-dev.0", false, "dev"], 142 | ]; 143 | 144 | versions.forEach(function (v) { 145 | const pre = v[0]; 146 | const what = v[1]; 147 | const wanted = v[2]; 148 | const loose = v[3]; 149 | const id = v[4]; 150 | const found = semver.inc(pre, what, loose, id); 151 | const cmd = "inc(" + pre + ", " + what + ", " + id + ")"; 152 | assertEquals(found, wanted, cmd + " === " + wanted); 153 | 154 | const parsed = semver.parse(pre, loose); 155 | if (wanted && parsed) { 156 | //todo ? 157 | parsed.inc(what, id); 158 | assertEquals(parsed.version, wanted, cmd + " object version updated"); 159 | assertEquals(parsed.raw, wanted, cmd + " object raw field updated"); 160 | } else if (parsed) { 161 | assertThrows(function () { 162 | parsed.inc(what, id); 163 | }); 164 | } else { 165 | assertEquals(parsed, null); 166 | } 167 | }); 168 | }); 169 | -------------------------------------------------------------------------------- /test/ltr.test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | Deno.test("ltr", function (): void { 6 | // [range, version, loose] 7 | // Version should be greater than range 8 | const versions: ReadonlyArray<[string, string, boolean?]> = [ 9 | ["~1.2.2", "1.2.1"], 10 | ["~0.6.1-1", "0.6.1-0"], 11 | ["1.0.0 - 2.0.0", "0.0.1"], 12 | ["1.0.0-beta.2", "1.0.0-beta.1"], 13 | ["1.0.0", "0.0.0"], 14 | [">=2.0.0", "1.1.1"], 15 | [">=2.0.0", "1.2.9"], 16 | [">2.0.0", "2.0.0"], 17 | ["0.1.20 || 1.2.4", "0.1.5"], 18 | ["2.x.x", "1.0.0"], 19 | ["1.2.x", "1.1.0"], 20 | ["1.2.x || 2.x", "1.0.0"], 21 | ["2.*.*", "1.0.1"], 22 | ["1.2.*", "1.1.3"], 23 | ["1.2.* || 2.*", "1.1.9999"], 24 | ["2", "1.0.0"], 25 | ["2.3", "2.2.2"], 26 | ["~2.4", "2.3.0"], // >=2.4.0 <2.5.0 27 | ["~2.4", "2.3.5"], 28 | ["~>3.2.1", "3.2.0"], // >=3.2.1 <3.3.0 29 | ["~1", "0.2.3"], // >=1.0.0 <2.0.0 30 | ["~>1", "0.2.4"], 31 | ["~> 1", "0.2.3"], 32 | ["~1.0", "0.1.2"], // >=1.0.0 <1.1.0 33 | ["~ 1.0", "0.1.0"], 34 | [">1.2", "1.2.0"], 35 | ["> 1.2", "1.2.1"], 36 | ["1", "0.0.0beta", true], 37 | ["~v0.5.4-pre", "0.5.4-alpha"], 38 | ["~v0.5.4-pre", "0.5.4-alpha"], 39 | ["=0.7.x", "0.6.0"], 40 | ["=0.7.x", "0.6.0-asdf"], 41 | [">=0.7.x", "0.6.0"], 42 | ["~1.2.2", "1.2.1"], 43 | ["1.0.0 - 2.0.0", "0.2.3"], 44 | ["1.0.0", "0.0.1"], 45 | [">=2.0.0", "1.0.0"], 46 | [">=2.0.0", "1.9999.9999"], 47 | [">=2.0.0", "1.2.9"], 48 | [">2.0.0", "2.0.0"], 49 | [">2.0.0", "1.2.9"], 50 | ["2.x.x", "1.1.3"], 51 | ["1.2.x", "1.1.3"], 52 | ["1.2.x || 2.x", "1.1.3"], 53 | ["2.*.*", "1.1.3"], 54 | ["1.2.*", "1.1.3"], 55 | ["1.2.* || 2.*", "1.1.3"], 56 | ["2", "1.9999.9999"], 57 | ["2.3", "2.2.1"], 58 | ["~2.4", "2.3.0"], // >=2.4.0 <2.5.0 59 | ["~>3.2.1", "2.3.2"], // >=3.2.1 <3.3.0 60 | ["~1", "0.2.3"], // >=1.0.0 <2.0.0 61 | ["~>1", "0.2.3"], 62 | ["~1.0", "0.0.0"], // >=1.0.0 <1.1.0 63 | [">1", "1.0.0"], 64 | ["2", "1.0.0beta", true], 65 | [">1", "1.0.0beta", true], 66 | ["> 1", "1.0.0beta", true], 67 | ["=0.7.x", "0.6.2"], 68 | ["=0.7.x", "0.7.0-asdf"], 69 | ["^1", "1.0.0-0"], 70 | [">=0.7.x", "0.7.0-asdf"], 71 | ["1", "1.0.0beta", true], 72 | [">=0.7.x", "0.6.2"], 73 | [">1.2.3", "1.3.0-alpha"], 74 | ]; 75 | 76 | versions.forEach(function (tuple) { 77 | const range = tuple[0]; 78 | const version = tuple[1]; 79 | const loose = tuple[2] || false; 80 | const msg = `ltr(${version}, ${range}, ${loose})`; 81 | assert(semver.ltr(version, range, loose), msg); 82 | }); 83 | }); 84 | 85 | Deno.test("ltrNegative", function (): void { 86 | // [range, version, loose] 87 | // Version should be greater than range 88 | const versions: ReadonlyArray<[string, string, boolean?]> = [ 89 | ["~ 1.0", "1.1.0"], 90 | ["~0.6.1-1", "0.6.1-1"], 91 | ["1.0.0 - 2.0.0", "1.2.3"], 92 | ["1.0.0 - 2.0.0", "2.9.9"], 93 | ["1.0.0", "1.0.0"], 94 | [">=*", "0.2.4"], 95 | ["", "1.0.0", true], 96 | ["*", "1.2.3"], 97 | [">=1.0.0", "1.0.0"], 98 | [">=1.0.0", "1.0.1"], 99 | [">=1.0.0", "1.1.0"], 100 | [">1.0.0", "1.0.1"], 101 | [">1.0.0", "1.1.0"], 102 | ["<=2.0.0", "2.0.0"], 103 | ["<=2.0.0", "1.9999.9999"], 104 | ["<=2.0.0", "0.2.9"], 105 | ["<2.0.0", "1.9999.9999"], 106 | ["<2.0.0", "0.2.9"], 107 | [">= 1.0.0", "1.0.0"], 108 | [">= 1.0.0", "1.0.1"], 109 | [">= 1.0.0", "1.1.0"], 110 | ["> 1.0.0", "1.0.1"], 111 | ["> 1.0.0", "1.1.0"], 112 | ["<= 2.0.0", "2.0.0"], 113 | ["<= 2.0.0", "1.9999.9999"], 114 | ["<= 2.0.0", "0.2.9"], 115 | ["< 2.0.0", "1.9999.9999"], 116 | ["<\t2.0.0", "0.2.9"], 117 | [">=0.1.97", "v0.1.97"], 118 | [">=0.1.97", "0.1.97"], 119 | ["0.1.20 || 1.2.4", "1.2.4"], 120 | ["0.1.20 || >1.2.4", "1.2.4"], 121 | ["0.1.20 || 1.2.4", "1.2.3"], 122 | ["0.1.20 || 1.2.4", "0.1.20"], 123 | [">=0.2.3 || <0.0.1", "0.0.0"], 124 | [">=0.2.3 || <0.0.1", "0.2.3"], 125 | [">=0.2.3 || <0.0.1", "0.2.4"], 126 | ["||", "1.3.4"], 127 | ["2.x.x", "2.1.3"], 128 | ["1.2.x", "1.2.3"], 129 | ["1.2.x || 2.x", "2.1.3"], 130 | ["1.2.x || 2.x", "1.2.3"], 131 | ["x", "1.2.3"], 132 | ["2.*.*", "2.1.3"], 133 | ["1.2.*", "1.2.3"], 134 | ["1.2.* || 2.*", "2.1.3"], 135 | ["1.2.* || 2.*", "1.2.3"], 136 | ["1.2.* || 2.*", "1.2.3"], 137 | ["*", "1.2.3"], 138 | ["2", "2.1.2"], 139 | ["2.3", "2.3.1"], 140 | ["~2.4", "2.4.0"], // >=2.4.0 <2.5.0 141 | ["~2.4", "2.4.5"], 142 | ["~>3.2.1", "3.2.2"], // >=3.2.1 <3.3.0 143 | ["~1", "1.2.3"], // >=1.0.0 <2.0.0 144 | ["~>1", "1.2.3"], 145 | ["~> 1", "1.2.3"], 146 | ["~1.0", "1.0.2"], // >=1.0.0 <1.1.0 147 | ["~ 1.0", "1.0.2"], 148 | [">=1", "1.0.0"], 149 | [">= 1", "1.0.0"], 150 | ["<1.2", "1.1.1"], 151 | ["< 1.2", "1.1.1"], 152 | ["~v0.5.4-pre", "0.5.5"], 153 | ["~v0.5.4-pre", "0.5.4"], 154 | ["=0.7.x", "0.7.2"], 155 | [">=0.7.x", "0.7.2"], 156 | ["<=0.7.x", "0.6.2"], 157 | [">0.2.3 >0.2.4 <=0.2.5", "0.2.5"], 158 | [">=0.2.3 <=0.2.4", "0.2.4"], 159 | ["1.0.0 - 2.0.0", "2.0.0"], 160 | ["^3.0.0", "4.0.0"], 161 | ["^1.0.0 || ~2.0.1", "2.0.0"], 162 | ["^0.1.0 || ~3.0.1 || 5.0.0", "3.2.0"], 163 | ["^0.1.0 || ~3.0.1 || 5.0.0", "1.0.0beta", true], 164 | ["^0.1.0 || ~3.0.1 || 5.0.0", "5.0.0-0", true], 165 | ["^0.1.0 || ~3.0.1 || >4 <=5.0.0", "3.5.0"], 166 | ["^1.0.0alpha", "1.0.0beta", true], 167 | ["~1.0.0alpha", "1.0.0beta", true], 168 | ["^1.0.0-alpha", "1.0.0beta", true], 169 | ["~1.0.0-alpha", "1.0.0beta", true], 170 | ["^1.0.0-alpha", "1.0.0-beta"], 171 | ["~1.0.0-alpha", "1.0.0-beta"], 172 | ["=0.1.0", "1.0.0"], 173 | ]; 174 | 175 | versions.forEach(function (tuple) { 176 | const range = tuple[0]; 177 | const version = tuple[1]; 178 | const loose = tuple[2] || false; 179 | const msg = `!ltr(${version}, ${range}, ${loose})`; 180 | assert(!semver.ltr(version, range, loose), msg); 181 | }); 182 | }); 183 | -------------------------------------------------------------------------------- /test/major.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | Deno.test("major", function (): void { 6 | // [range, version, loose] 7 | // Version should be detectable despite extra characters 8 | const versions: [string, number, boolean?][] = [ 9 | ["1.2.3", 1], 10 | [" 1.2.3 ", 1], 11 | [" 2.2.3-4 ", 2], 12 | [" 3.2.3-pre ", 3], 13 | ["v5.2.3", 5], 14 | [" v8.2.3 ", 8], 15 | ["\t13.2.3", 13], 16 | ["=21.2.3", 21, true], 17 | ["v=34.2.3", 34, true], 18 | ]; 19 | 20 | versions.forEach(function (tuple) { 21 | const range = tuple[0]; 22 | const version = tuple[1]; 23 | const loose = tuple[2] || false; 24 | const msg = `major(${range}, ${loose}) = ${version}`; 25 | assertEquals(semver.major(range, loose), version, msg); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/minVersion.test.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | Deno.test("minVersion", function (): void { 6 | // [range, version, loose] 7 | const versions: [string, string | null, boolean?][] = [ 8 | // Stars 9 | ["*", "0.0.0"], 10 | ["* || >=2", "0.0.0"], 11 | [">=2 || *", "0.0.0"], 12 | [">2 || *", "0.0.0"], 13 | 14 | // equal 15 | ["1.0.0", "1.0.0"], 16 | ["1.0", "1.0.0"], 17 | ["1.0.x", "1.0.0"], 18 | ["1.0.*", "1.0.0"], 19 | ["1", "1.0.0"], 20 | ["1.x.x", "1.0.0"], 21 | ["1.x.x", "1.0.0"], 22 | ["1.*.x", "1.0.0"], 23 | ["1.x.*", "1.0.0"], 24 | ["1.x", "1.0.0"], 25 | ["1.*", "1.0.0"], 26 | ["=1.0.0", "1.0.0"], 27 | 28 | // Tilde 29 | ["~1.1.1", "1.1.1"], 30 | ["~1.1.1-beta", "1.1.1-beta"], 31 | ["~1.1.1 || >=2", "1.1.1"], 32 | 33 | // Carot 34 | ["^1.1.1", "1.1.1"], 35 | ["^1.1.1-beta", "1.1.1-beta"], 36 | ["^1.1.1 || >=2", "1.1.1"], 37 | 38 | // '-' operator 39 | ["1.1.1 - 1.8.0", "1.1.1"], 40 | ["1.1 - 1.8.0", "1.1.0"], 41 | 42 | // Less / less or equal 43 | ["<2", "0.0.0"], 44 | ["<0.0.0-beta", "0.0.0-0"], 45 | ["<0.0.1-beta", "0.0.0"], 46 | ["<2 || >4", "0.0.0"], 47 | [">4 || <2", "0.0.0"], 48 | ["<=2 || >=4", "0.0.0"], 49 | [">=4 || <=2", "0.0.0"], 50 | ["<0.0.0-beta >0.0.0-alpha", "0.0.0-alpha.0"], 51 | [">0.0.0-alpha <0.0.0-beta", "0.0.0-alpha.0"], 52 | 53 | // Greater than or equal 54 | [">=1.1.1 <2 || >=2.2.2 <2", "1.1.1"], 55 | [">=2.2.2 <2 || >=1.1.1 <2", "1.1.1"], 56 | 57 | // Greater than but not equal 58 | [">1.0.0", "1.0.1"], 59 | [">1.0.0-0", "1.0.0-0.0"], 60 | [">1.0.0-beta", "1.0.0-beta.0"], 61 | [">2 || >1.0.0", "1.0.1"], 62 | [">2 || >1.0.0-0", "1.0.0-0.0"], 63 | [">2 || >1.0.0-beta", "1.0.0-beta.0"], 64 | 65 | // Impossible range 66 | [">4 <3", null], 67 | ]; 68 | 69 | versions.forEach(function (tuple) { 70 | const range = tuple[0]; 71 | const version = tuple[1]; 72 | const loose = tuple[2] || false; 73 | const msg = `minVersion(${range}, ${loose}) = ${version}`; 74 | const min = semver.minVersion(range, loose); 75 | assert((min as null) === version || (min && min.version === version), msg); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /test/minor.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | Deno.test("minor", function (): void { 6 | // [range, version, loose] 7 | // Version should be detectable despite extra characters 8 | const versions: [string, number, boolean?][] = [ 9 | ["1.1.3", 1], 10 | [" 1.1.3 ", 1], 11 | [" 1.2.3-4 ", 2], 12 | [" 1.3.3-pre ", 3], 13 | ["v1.5.3", 5], 14 | [" v1.8.3 ", 8], 15 | ["\t1.13.3", 13], 16 | ["=1.21.3", 21, true], 17 | ["v=1.34.3", 34, true], 18 | ]; 19 | 20 | versions.forEach(function (tuple) { 21 | const range = tuple[0]; 22 | const version = tuple[1]; 23 | const loose = tuple[2] || false; 24 | const msg = `minor(${range}, ${loose}) = ${version}`; 25 | assertEquals(semver.minor(range, loose), version, msg); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/patch.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | Deno.test("patch", function (): void { 6 | // [range, version, loose] 7 | // Version should be detectable despite extra characters 8 | const versions: [string, number, boolean?][] = [ 9 | ["1.2.1", 1], 10 | [" 1.2.1 ", 1], 11 | [" 1.2.2-4 ", 2], 12 | [" 1.2.3-pre ", 3], 13 | ["v1.2.5", 5], 14 | [" v1.2.8 ", 8], 15 | ["\t1.2.13", 13], 16 | ["=1.2.21", 21, true], 17 | ["v=1.2.34", 34, true], 18 | ]; 19 | 20 | versions.forEach(function (tuple) { 21 | const range = tuple[0]; 22 | const version = tuple[1]; 23 | const loose = tuple[2] || false; 24 | const msg = `patch(${range}, ${loose}) = ${version}`; 25 | assertEquals(semver.patch(range, loose), version, msg); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/prerelease.test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | type prereleaseParts = [string | number, (number | string)?]; 6 | 7 | Deno.test("prerelease", function (): void { 8 | // [prereleaseParts, version, loose] 9 | const versions: [prereleaseParts | null, string, boolean?][] = [ 10 | [["alpha", 1], "1.2.2-alpha.1"], 11 | [[1], "0.6.1-1"], 12 | [["beta", 2], "1.0.0-beta.2"], 13 | [["pre"], "v0.5.4-pre"], 14 | [["alpha", 1], "1.2.2-alpha.1", false], 15 | [["beta"], "0.6.1beta", true], 16 | [null, "1.0.0", true], 17 | [null, "~2.0.0-alpha.1", false], 18 | [null, "invalid version"], 19 | ]; 20 | 21 | versions.forEach(function (tuple) { 22 | const expected = tuple[0]; 23 | const version = tuple[1]; 24 | const loose = tuple[2] || false; 25 | const msg = "prerelease(" + version + ")"; 26 | assertEquals(semver.prerelease(version, loose), expected, msg); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/range.test.ts: -------------------------------------------------------------------------------- 1 | import { assert, assertEquals, assertThrows } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | type Version = string; 6 | type Options = semver.Options | boolean; 7 | 8 | Deno.test("range", function (): void { 9 | // [range, version] 10 | // version should be included by range 11 | const versions: [Version, Version, Options?][] = [ 12 | ["1.0.0 - 2.0.0", "1.2.3"], 13 | ["^1.2.3+build", "1.2.3"], 14 | ["^1.2.3+build", "1.3.0"], 15 | ["1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3"], 16 | ["1.2.3pre+asdf - 2.4.3-pre+asdf", "1.2.3", true], 17 | ["1.2.3-pre+asdf - 2.4.3pre+asdf", "1.2.3", true], 18 | ["1.2.3pre+asdf - 2.4.3pre+asdf", "1.2.3", true], 19 | ["1.2.3-pre+asdf - 2.4.3-pre+asdf", "1.2.3-pre.2"], 20 | ["1.2.3-pre+asdf - 2.4.3-pre+asdf", "2.4.3-alpha"], 21 | ["1.2.3+asdf - 2.4.3+asdf", "1.2.3"], 22 | ["1.0.0", "1.0.0"], 23 | [">=*", "0.2.4"], 24 | ["", "1.0.0"], 25 | ["*", "1.2.3", {}], 26 | ["*", "v1.2.3", { loose: 123 } as any], 27 | [">=1.0.0", "1.0.0", /asdf/], 28 | [">=1.0.0", "1.0.1", { loose: null }], 29 | [">=1.0.0", "1.1.0", { loose: 0 }], 30 | [">1.0.0", "1.0.1", { loose: undefined }], 31 | [">1.0.0", "1.1.0"], 32 | ["<=2.0.0", "2.0.0"], 33 | ["<=2.0.0", "1.9999.9999"], 34 | ["<=2.0.0", "0.2.9"], 35 | ["<2.0.0", "1.9999.9999"], 36 | ["<2.0.0", "0.2.9"], 37 | [">= 1.0.0", "1.0.0"], 38 | [">= 1.0.0", "1.0.1"], 39 | [">= 1.0.0", "1.1.0"], 40 | ["> 1.0.0", "1.0.1"], 41 | ["> 1.0.0", "1.1.0"], 42 | ["<= 2.0.0", "2.0.0"], 43 | ["<= 2.0.0", "1.9999.9999"], 44 | ["<= 2.0.0", "0.2.9"], 45 | ["< 2.0.0", "1.9999.9999"], 46 | ["<\t2.0.0", "0.2.9"], 47 | [">=0.1.97", "v0.1.97", true], 48 | [">=0.1.97", "0.1.97"], 49 | ["0.1.20 || 1.2.4", "1.2.4"], 50 | [">=0.2.3 || <0.0.1", "0.0.0"], 51 | [">=0.2.3 || <0.0.1", "0.2.3"], 52 | [">=0.2.3 || <0.0.1", "0.2.4"], 53 | ["||", "1.3.4"], 54 | ["2.x.x", "2.1.3"], 55 | ["1.2.x", "1.2.3"], 56 | ["1.2.x || 2.x", "2.1.3"], 57 | ["1.2.x || 2.x", "1.2.3"], 58 | ["x", "1.2.3"], 59 | ["2.*.*", "2.1.3"], 60 | ["1.2.*", "1.2.3"], 61 | ["1.2.* || 2.*", "2.1.3"], 62 | ["1.2.* || 2.*", "1.2.3"], 63 | ["*", "1.2.3"], 64 | ["2", "2.1.2"], 65 | ["2.3", "2.3.1"], 66 | ["~0.0.1", "0.0.1"], 67 | ["~0.0.1", "0.0.2"], 68 | ["~x", "0.0.9"], // >=2.4.0 <2.5.0 69 | ["~2", "2.0.9"], // >=2.4.0 <2.5.0 70 | ["~2.4", "2.4.0"], // >=2.4.0 <2.5.0 71 | ["~2.4", "2.4.5"], 72 | ["~>3.2.1", "3.2.2"], // >=3.2.1 <3.3.0, 73 | ["~1", "1.2.3"], // >=1.0.0 <2.0.0 74 | ["~>1", "1.2.3"], 75 | ["~> 1", "1.2.3"], 76 | ["~1.0", "1.0.2"], // >=1.0.0 <1.1.0, 77 | ["~ 1.0", "1.0.2"], 78 | ["~ 1.0.3", "1.0.12"], 79 | ["~ 1.0.3alpha", "1.0.12", { loose: true }], 80 | [">=1", "1.0.0"], 81 | [">= 1", "1.0.0"], 82 | ["<1.2", "1.1.1"], 83 | ["< 1.2", "1.1.1"], 84 | ["~v0.5.4-pre", "0.5.5"], 85 | ["~v0.5.4-pre", "0.5.4"], 86 | ["=0.7.x", "0.7.2"], 87 | ["<=0.7.x", "0.7.2"], 88 | [">=0.7.x", "0.7.2"], 89 | ["<=0.7.x", "0.6.2"], 90 | ["~1.2.1 >=1.2.3", "1.2.3"], 91 | ["~1.2.1 =1.2.3", "1.2.3"], 92 | ["~1.2.1 1.2.3", "1.2.3"], 93 | ["~1.2.1 >=1.2.3 1.2.3", "1.2.3"], 94 | ["~1.2.1 1.2.3 >=1.2.3", "1.2.3"], 95 | ["~1.2.1 1.2.3", "1.2.3"], 96 | [">=1.2.1 1.2.3", "1.2.3"], 97 | ["1.2.3 >=1.2.1", "1.2.3"], 98 | [">=1.2.3 >=1.2.1", "1.2.3"], 99 | [">=1.2.1 >=1.2.3", "1.2.3"], 100 | [">=1.2", "1.2.8"], 101 | ["^1.2.3", "1.8.1"], 102 | ["^0.1.2", "0.1.2"], 103 | ["^0.1", "0.1.2"], 104 | ["^0.0.1", "0.0.1"], 105 | ["^1.2", "1.4.2"], 106 | ["^1.2 ^1", "1.4.2"], 107 | ["^1.2.3-alpha", "1.2.3-pre"], 108 | ["^1.2.0-alpha", "1.2.0-pre"], 109 | ["^0.0.1-alpha", "0.0.1-beta"], 110 | ["^0.0.1-alpha", "0.0.1"], 111 | ["^0.1.1-alpha", "0.1.1-beta"], 112 | ["^x", "1.2.3"], 113 | ["x - 1.0.0", "0.9.7"], 114 | ["x - 1.x", "0.9.7"], 115 | ["1.0.0 - x", "1.9.7"], 116 | ["1.x - x", "1.9.7"], 117 | ["<=7.x", "7.9.9"], 118 | ]; 119 | 120 | versions.forEach(function (v) { 121 | const range = v[0]; 122 | const ver = v[1]; 123 | const loose = v[2]; 124 | assert(semver.satisfies(ver, range, loose), range + " satisfied by " + ver); 125 | }); 126 | }); 127 | 128 | Deno.test("negativeRange", function (): void { 129 | // [range, version] 130 | // version should not be included by range 131 | const versions: [Version, Version, Options?][] = [ 132 | ["1.0.0 - 2.0.0", "2.2.3"], 133 | ["1.2.3+asdf - 2.4.3+asdf", "1.2.3-pre.2"], 134 | ["1.2.3+asdf - 2.4.3+asdf", "2.4.3-alpha"], 135 | ["^1.2.3+build", "2.0.0"], 136 | ["^1.2.3+build", "1.2.0"], 137 | ["^1.2.3", "1.2.3-pre"], 138 | ["^1.2", "1.2.0-pre"], 139 | [">1.2", "1.3.0-beta"], 140 | ["<=1.2.3", "1.2.3-beta"], 141 | ["^1.2.3", "1.2.3-beta"], 142 | ["=0.7.x", "0.7.0-asdf"], 143 | [">=0.7.x", "0.7.0-asdf"], 144 | ["1", "1.0.0beta", { loose: 420 } as any], 145 | ["<1", "1.0.0beta", true], 146 | ["< 1", "1.0.0beta", true], 147 | ["1.0.0", "1.0.1"], 148 | [">=1.0.0", "0.0.0"], 149 | [">=1.0.0", "0.0.1"], 150 | [">=1.0.0", "0.1.0"], 151 | [">1.0.0", "0.0.1"], 152 | [">1.0.0", "0.1.0"], 153 | ["<=2.0.0", "3.0.0"], 154 | ["<=2.0.0", "2.9999.9999"], 155 | ["<=2.0.0", "2.2.9"], 156 | ["<2.0.0", "2.9999.9999"], 157 | ["<2.0.0", "2.2.9"], 158 | [">=0.1.97", "v0.1.93", true], 159 | [">=0.1.97", "0.1.93"], 160 | ["0.1.20 || 1.2.4", "1.2.3"], 161 | [">=0.2.3 || <0.0.1", "0.0.3"], 162 | [">=0.2.3 || <0.0.1", "0.2.2"], 163 | ["2.x.x", "1.1.3", { loose: NaN }], 164 | ["2.x.x", "3.1.3"], 165 | ["1.2.x", "1.3.3"], 166 | ["1.2.x || 2.x", "3.1.3"], 167 | ["1.2.x || 2.x", "1.1.3"], 168 | ["2.*.*", "1.1.3"], 169 | ["2.*.*", "3.1.3"], 170 | ["1.2.*", "1.3.3"], 171 | ["1.2.* || 2.*", "3.1.3"], 172 | ["1.2.* || 2.*", "1.1.3"], 173 | ["2", "1.1.2"], 174 | ["2.3", "2.4.1"], 175 | ["~0.0.1", "0.1.0-alpha"], 176 | ["~0.0.1", "0.1.0"], 177 | ["~2.4", "2.5.0"], // >=2.4.0 <2.5.0 178 | ["~2.4", "2.3.9"], 179 | ["~>3.2.1", "3.3.2"], // >=3.2.1 <3.3.0 180 | ["~>3.2.1", "3.2.0"], // >=3.2.1 <3.3.0 181 | ["~1", "0.2.3"], // >=1.0.0 <2.0.0 182 | ["~>1", "2.2.3"], 183 | ["~1.0", "1.1.0"], // >=1.0.0 <1.1.0 184 | ["<1", "1.0.0"], 185 | [">=1.2", "1.1.1"], 186 | ["1", "2.0.0beta", true], 187 | ["~v0.5.4-beta", "0.5.4-alpha"], 188 | ["=0.7.x", "0.8.2"], 189 | [">=0.7.x", "0.6.2"], 190 | ["<0.7.x", "0.7.2"], 191 | ["<1.2.3", "1.2.3-beta"], 192 | ["=1.2.3", "1.2.3-beta"], 193 | [">1.2", "1.2.8"], 194 | ["^0.0.1", "0.0.2-alpha"], 195 | ["^0.0.1", "0.0.2"], 196 | ["^1.2.3", "2.0.0-alpha"], 197 | ["^1.2.3", "1.2.2"], 198 | ["^1.2", "1.1.9"], 199 | ["*", "v1.2.3-foo", true], 200 | // invalid ranges never satisfied! 201 | ["blerg", "1.2.3"], 202 | ["git+https://user:password0123@github.com/foo", "123.0.0", true], 203 | ["^1.2.3", "2.0.0-pre"], 204 | ]; 205 | 206 | versions.forEach(function (v) { 207 | const range = v[0]; 208 | const ver = v[1]; 209 | const loose = v[2]; 210 | const found = semver.satisfies(ver, range, loose); 211 | assert(!found, ver + " not satisfied by " + range); 212 | }); 213 | }); 214 | 215 | Deno.test("unlockedPrereleaseRange", function (): void { 216 | // [range, version] 217 | // version should be included by range 218 | const versions: [Version, Version][] = [ 219 | ["*", "1.0.0-rc1"], 220 | ["^1.0.0", "2.0.0-rc1"], 221 | ["^1.0.0-0", "1.0.1-rc1"], 222 | ["^1.0.0-rc2", "1.0.1-rc1"], 223 | ["^1.0.0", "1.0.1-rc1"], 224 | ["^1.0.0", "1.1.0-rc1"], 225 | ]; 226 | 227 | versions.forEach(function (v) { 228 | const range = v[0]; 229 | const ver = v[1]; 230 | const options = { includePrerelease: true }; 231 | assert( 232 | semver.satisfies(ver, range, options), 233 | range + " satisfied by " + ver, 234 | ); 235 | }); 236 | }); 237 | 238 | Deno.test("negativeUnlockedPrereleaseRange", function (): void { 239 | // [range, version] 240 | // version should be included by range 241 | const versions: [Version, Version][] = [ 242 | ["^1.0.0", "1.0.0-rc1"], 243 | ["^1.2.3-rc2", "2.0.0"], 244 | ]; 245 | 246 | versions.forEach(function (v) { 247 | const range = v[0]; 248 | const ver = v[1]; 249 | const options = { includePrerelease: true }; 250 | const found = semver.satisfies(ver, range, options); 251 | assert(!found, ver + " not satisfied by " + range); 252 | }); 253 | }); 254 | 255 | Deno.test("validRange", function (): void { 256 | // [range, result] 257 | // validRange(range) -> result 258 | // translate ranges into their canonical form 259 | const versions: [Version | null, Version | null, Options?][] = [ 260 | ["1.0.0 - 2.0.0", ">=1.0.0 <=2.0.0"], 261 | ["1.0.0", "1.0.0"], 262 | [">=*", "*"], 263 | ["", "*"], 264 | ["*", "*"], 265 | ["*", "*"], 266 | [">=1.0.0", ">=1.0.0"], 267 | [">1.0.0", ">1.0.0"], 268 | ["<=2.0.0", "<=2.0.0"], 269 | ["1", ">=1.0.0 <2.0.0"], 270 | ["<=2.0.0", "<=2.0.0"], 271 | ["<=2.0.0", "<=2.0.0"], 272 | ["<2.0.0", "<2.0.0"], 273 | ["<2.0.0", "<2.0.0"], 274 | [">= 1.0.0", ">=1.0.0"], 275 | [">= 1.0.0", ">=1.0.0"], 276 | [">= 1.0.0", ">=1.0.0"], 277 | ["> 1.0.0", ">1.0.0"], 278 | ["> 1.0.0", ">1.0.0"], 279 | ["<= 2.0.0", "<=2.0.0"], 280 | ["<= 2.0.0", "<=2.0.0"], 281 | ["<= 2.0.0", "<=2.0.0"], 282 | ["< 2.0.0", "<2.0.0"], 283 | ["<\t2.0.0", "<2.0.0"], 284 | [">=0.1.97", ">=0.1.97"], 285 | [">=0.1.97", ">=0.1.97"], 286 | ["0.1.20 || 1.2.4", "0.1.20||1.2.4"], 287 | [">=0.2.3 || <0.0.1", ">=0.2.3||<0.0.1"], 288 | [">=0.2.3 || <0.0.1", ">=0.2.3||<0.0.1"], 289 | [">=0.2.3 || <0.0.1", ">=0.2.3||<0.0.1"], 290 | ["||", "||"], 291 | ["2.x.x", ">=2.0.0 <3.0.0"], 292 | ["1.2.x", ">=1.2.0 <1.3.0"], 293 | ["1.2.x || 2.x", ">=1.2.0 <1.3.0||>=2.0.0 <3.0.0"], 294 | ["1.2.x || 2.x", ">=1.2.0 <1.3.0||>=2.0.0 <3.0.0"], 295 | ["x", "*"], 296 | ["2.*.*", ">=2.0.0 <3.0.0"], 297 | ["1.2.*", ">=1.2.0 <1.3.0"], 298 | ["1.2.* || 2.*", ">=1.2.0 <1.3.0||>=2.0.0 <3.0.0"], 299 | ["*", "*"], 300 | ["2", ">=2.0.0 <3.0.0"], 301 | ["2.3", ">=2.3.0 <2.4.0"], 302 | ["~2.4", ">=2.4.0 <2.5.0"], 303 | ["~2.4", ">=2.4.0 <2.5.0"], 304 | ["~>3.2.1", ">=3.2.1 <3.3.0"], 305 | ["~1", ">=1.0.0 <2.0.0"], 306 | ["~>1", ">=1.0.0 <2.0.0"], 307 | ["~> 1", ">=1.0.0 <2.0.0"], 308 | ["~1.0", ">=1.0.0 <1.1.0"], 309 | ["~ 1.0", ">=1.0.0 <1.1.0"], 310 | ["^0", ">=0.0.0 <1.0.0"], 311 | ["^ 1", ">=1.0.0 <2.0.0"], 312 | ["^0.1", ">=0.1.0 <0.2.0"], 313 | ["^1.0", ">=1.0.0 <2.0.0"], 314 | ["^1.2", ">=1.2.0 <2.0.0"], 315 | ["^0.0.1", ">=0.0.1 <0.0.2"], 316 | ["^0.0.1-beta", ">=0.0.1-beta <0.0.2"], 317 | ["^0.1.2", ">=0.1.2 <0.2.0"], 318 | ["^1.2.3", ">=1.2.3 <2.0.0"], 319 | ["^1.2.3-beta.4", ">=1.2.3-beta.4 <2.0.0"], 320 | ["<1", "<1.0.0"], 321 | ["< 1", "<1.0.0"], 322 | [">=1", ">=1.0.0"], 323 | [">= 1", ">=1.0.0"], 324 | ["<1.2", "<1.2.0"], 325 | ["< 1.2", "<1.2.0"], 326 | ["1", ">=1.0.0 <2.0.0"], 327 | [">01.02.03", ">1.2.3", true], 328 | [">01.02.03", null], 329 | ["~1.2.3beta", ">=1.2.3-beta <1.3.0", true], 330 | ["~1.2.3beta", null], 331 | ["^ 1.2 ^ 1", ">=1.2.0 <2.0.0 >=1.0.0 <2.0.0"], 332 | ]; 333 | 334 | versions.forEach(function (v) { 335 | const pre = v[0]; 336 | const wanted = v[1]; 337 | const loose = v[2]; 338 | const found = semver.validRange(pre, loose); 339 | assertEquals(found, wanted, "validRange(" + pre + ") === " + wanted); 340 | }); 341 | }); 342 | 343 | Deno.test("strictVsLoose", function (): void { 344 | const versions: [Version, Version][] = [ 345 | [">=01.02.03", ">=1.2.3"], 346 | ["~1.02.03beta", ">=1.2.3-beta <1.3.0"], 347 | ]; 348 | 349 | versions.forEach(function (v) { 350 | const loose = v[0]; 351 | const comps = v[1]; 352 | assertThrows(function () { 353 | new semver.Range(loose); 354 | }); 355 | assertEquals(new semver.Range(loose, true).range, comps); 356 | }); 357 | }); 358 | 359 | Deno.test("missingRangeParameterInRangeIntersect", function (): void { 360 | assertThrows( 361 | function () { 362 | new semver.Range("1.0.0").intersects(undefined); 363 | }, 364 | TypeError, 365 | "a Range is required", 366 | ); 367 | }); 368 | 369 | Deno.test("tostrings", function (): void { 370 | assertEquals(new semver.Range(">= v1.2.3").toString(), ">=1.2.3"); 371 | }); 372 | 373 | Deno.test("rangesIntersect", function (): void { 374 | const versions: [string, string, boolean][] = [ 375 | ["1.3.0 || <1.0.0 >2.0.0", "1.3.0 || <1.0.0 >2.0.0", true], 376 | ["<1.0.0 >2.0.0", ">0.0.0", false], 377 | [">0.0.0", "<1.0.0 >2.0.0", false], 378 | ["<1.0.0 >2.0.0", ">1.4.0 <1.6.0", false], 379 | ["<1.0.0 >2.0.0", ">1.4.0 <1.6.0 || 2.0.0", false], 380 | [">1.0.0 <=2.0.0", "2.0.0", true], 381 | ["<1.0.0 >=2.0.0", "2.1.0", false], 382 | ["<1.0.0 >=2.0.0", ">1.4.0 <1.6.0 || 2.0.0", false], 383 | ["1.5.x", "<1.5.0 || >=1.6.0", false], 384 | ["<1.5.0 || >=1.6.0", "1.5.x", false], 385 | [ 386 | "<1.6.16 || >=1.7.0 <1.7.11 || >=1.8.0 <1.8.2", 387 | ">=1.6.16 <1.7.0 || >=1.7.11 <1.8.0 || >=1.8.2", 388 | false, 389 | ], 390 | [ 391 | "<=1.6.16 || >=1.7.0 <1.7.11 || >=1.8.0 <1.8.2", 392 | ">=1.6.16 <1.7.0 || >=1.7.11 <1.8.0 || >=1.8.2", 393 | true, 394 | ], 395 | [">=1.0.0", "<=1.0.0", true], 396 | [">1.0.0 <1.0.0", "<=0.0.0", false], 397 | ["*", "0.0.1", true], 398 | ["*", ">=1.0.0", true], 399 | ["*", ">1.0.0", true], 400 | ["*", "~1.0.0", true], 401 | ["*", "<1.6.0", true], 402 | ["*", "<=1.6.0", true], 403 | ["1.*", "0.0.1", false], 404 | ["1.*", "2.0.0", false], 405 | ["1.*", "1.0.0", true], 406 | ["1.*", "<2.0.0", true], 407 | ["1.*", ">1.0.0", true], 408 | ["1.*", "<=1.0.0", true], 409 | ["1.*", "^1.0.0", true], 410 | ["1.0.*", "0.0.1", false], 411 | ["1.0.*", "<0.0.1", false], 412 | ["1.0.*", ">0.0.1", true], 413 | ["*", "1.3.0 || <1.0.0 >2.0.0", true], 414 | ["1.3.0 || <1.0.0 >2.0.0", "*", true], 415 | ["1.*", "1.3.0 || <1.0.0 >2.0.0", true], 416 | ["x", "0.0.1", true], 417 | ["x", ">=1.0.0", true], 418 | ["x", ">1.0.0", true], 419 | ["x", "~1.0.0", true], 420 | ["x", "<1.6.0", true], 421 | ["x", "<=1.6.0", true], 422 | ["1.x", "0.0.1", false], 423 | ["1.x", "2.0.0", false], 424 | ["1.x", "1.0.0", true], 425 | ["1.x", "<2.0.0", true], 426 | ["1.x", ">1.0.0", true], 427 | ["1.x", "<=1.0.0", true], 428 | ["1.x", "^1.0.0", true], 429 | ["1.0.x", "0.0.1", false], 430 | ["1.0.x", "<0.0.1", false], 431 | ["1.0.x", ">0.0.1", true], 432 | ["x", "1.3.0 || <1.0.0 >2.0.0", true], 433 | ["1.3.0 || <1.0.0 >2.0.0", "x", true], 434 | ["1.x", "1.3.0 || <1.0.0 >2.0.0", true], 435 | ["*", "*", true], 436 | ["x", "", true], 437 | ]; 438 | 439 | versions.forEach(function (v) { 440 | const range1 = new semver.Range(v[0]); 441 | const range2 = new semver.Range(v[1]); 442 | const expect = v[2]; 443 | const actual1 = range1.intersects(range2); 444 | const actual2 = range2.intersects(range1); 445 | const actual3 = semver.intersects(v[1], v[0]); 446 | const actual4 = semver.intersects(v[0], v[1]); 447 | const actual5 = semver.intersects(v[1], v[0], true); 448 | const actual6 = semver.intersects(v[0], v[1], true); 449 | const actual7 = semver.intersects(range1, range2); 450 | const actual8 = semver.intersects(range2, range1); 451 | const actual9 = semver.intersects(range1, range2, true); 452 | const actual0 = semver.intersects(range2, range1, true); 453 | assertEquals(actual1, expect); 454 | assertEquals(actual2, expect); 455 | assertEquals(actual3, expect); 456 | assertEquals(actual4, expect); 457 | assertEquals(actual5, expect); 458 | assertEquals(actual6, expect); 459 | assertEquals(actual7, expect); 460 | assertEquals(actual8, expect); 461 | assertEquals(actual9, expect); 462 | assertEquals(actual0, expect); 463 | }); 464 | }); 465 | -------------------------------------------------------------------------------- /test/semver.test.ts: -------------------------------------------------------------------------------- 1 | import { assert, assertEquals, assertThrows } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | type Options = semver.Options | boolean; 6 | 7 | Deno.test("invalidVersion", function (): void { 8 | const versions = ["1.2.3.4", "NOT VALID", 1.2, null, "Infinity.NaN.Infinity"]; 9 | 10 | versions.forEach(function (v) { 11 | assertThrows( 12 | function () { 13 | new semver.SemVer(v as any); 14 | }, 15 | TypeError, 16 | `Invalid Version: ${v}`, 17 | ); 18 | }); 19 | }); 20 | 21 | Deno.test("maxSatisfying", function (): void { 22 | const versions: [string[], string, string, Options?][] = [ 23 | [["1.2.3", "1.2.4"], "1.2", "1.2.4"], 24 | [["1.2.4", "1.2.3"], "1.2", "1.2.4"], 25 | [["1.2.3", "1.2.4", "1.2.5", "1.2.6"], "~1.2.3", "1.2.6"], 26 | [ 27 | [ 28 | "1.1.0", 29 | "1.2.0", 30 | "1.2.1", 31 | "1.3.0", 32 | "2.0.0b1", 33 | "2.0.0b2", 34 | "2.0.0b3", 35 | "2.0.0", 36 | "2.1.0", 37 | ], 38 | "~2.0.0", 39 | "2.0.0", 40 | true, 41 | ], 42 | ]; 43 | 44 | versions.forEach(function (v) { 45 | const versions = v[0]; 46 | const range = v[1]; 47 | const expect = v[2]; 48 | const loose = v[3]; 49 | const actual = semver.maxSatisfying(versions, range, loose); 50 | assertEquals(actual, expect); 51 | }); 52 | }); 53 | 54 | Deno.test("minSatisfying", function (): void { 55 | const versions: [string[], string, string, Options?][] = [ 56 | [["1.2.3", "1.2.4"], "1.2", "1.2.3"], 57 | [["1.2.4", "1.2.3"], "1.2", "1.2.3"], 58 | [["1.2.3", "1.2.4", "1.2.5", "1.2.6"], "~1.2.3", "1.2.3"], 59 | [ 60 | [ 61 | "1.1.0", 62 | "1.2.0", 63 | "1.2.1", 64 | "1.3.0", 65 | "2.0.0b1", 66 | "2.0.0b2", 67 | "2.0.0b3", 68 | "2.0.0", 69 | "2.1.0", 70 | ], 71 | "~2.0.0", 72 | "2.0.0", 73 | true, 74 | ], 75 | ]; 76 | 77 | versions.forEach(function (v) { 78 | const versions = v[0]; 79 | const range = v[1]; 80 | const expect = v[2]; 81 | const loose = v[3]; 82 | const actual = semver.minSatisfying(versions, range, loose); 83 | assertEquals(actual, expect); 84 | }); 85 | }); 86 | 87 | Deno.test("outsideWithBadHiloThrows", function (): void { 88 | assertThrows( 89 | function () { 90 | semver.outside("1.2.3", ">1.5.0", "blerg" as any, true); 91 | }, 92 | TypeError, 93 | 'Must provide a hilo val of "<" or ">"', 94 | ); 95 | }); 96 | 97 | Deno.test("sorting", function (): void { 98 | const list = ["1.2.3+1", "1.2.3+0", "1.2.3", "5.9.6", "0.1.2"]; 99 | const sorted = ["0.1.2", "1.2.3", "1.2.3+0", "1.2.3+1", "5.9.6"]; 100 | const rsorted = ["5.9.6", "1.2.3+1", "1.2.3+0", "1.2.3", "0.1.2"]; 101 | assertEquals(semver.sort(list), sorted); 102 | assertEquals(semver.rsort(list), rsorted); 103 | }); 104 | 105 | Deno.test("badRangesInMaxOrMinSatisfying", function (): void { 106 | const r = "some frogs and sneks-v2.5.6"; 107 | assertEquals(semver.maxSatisfying([], r), null); 108 | assertEquals(semver.minSatisfying([], r), null); 109 | }); 110 | 111 | Deno.test("bigNumericPrerelease", function (): void { 112 | const r = new semver.SemVer("1.2.3-beta." + Number.MAX_SAFE_INTEGER + "0"); 113 | assertEquals(r.prerelease, ["beta", "90071992547409910"]); 114 | }); 115 | -------------------------------------------------------------------------------- /test/test.ts: -------------------------------------------------------------------------------- 1 | import "./semver.test.ts"; 2 | 3 | import "./major.test.ts"; 4 | import "./minor.test.ts"; 5 | import "./patch.test.ts"; 6 | import "./minVersion.test.ts"; 7 | import "./prerelease.test.ts"; 8 | import "./increment.test.ts"; 9 | import "./gtr.test.ts"; 10 | import "./equality.test.ts"; 11 | 12 | import "./comparison.test.ts"; 13 | import "./comparators.test.ts"; 14 | import "./diff.test.ts"; 15 | import "./range.test.ts"; 16 | 17 | import "./tooLong.test.ts"; 18 | import "./ltr.test.ts"; 19 | import "./clean.test.ts"; 20 | import "./coerce.test.ts"; 21 | -------------------------------------------------------------------------------- /test/tooLong.test.ts: -------------------------------------------------------------------------------- 1 | import { assert, assertEquals, assertThrows } from "./deps.ts"; 2 | 3 | import * as semver from "../mod.ts"; 4 | 5 | Deno.test("versionIsTooLong", function (): void { 6 | const v: string = "1.2." + new Array(256).join("1"); 7 | 8 | assertThrows(function () { 9 | new semver.SemVer(v); 10 | }); 11 | assertEquals(semver.valid(v, false), null); 12 | assertEquals(semver.valid(v, true), null); 13 | assertEquals(semver.inc(v, "patch"), null); 14 | }); 15 | 16 | Deno.test("tooBig", function (): void { 17 | var v = "1.2." + new Array(100).join("1"); 18 | assertThrows(function () { 19 | new semver.SemVer(v); 20 | }); 21 | assertEquals(semver.valid(v, false), null); 22 | assertEquals(semver.valid(v, true), null); 23 | assertEquals(semver.inc(v, "patch"), null); 24 | }); 25 | 26 | Deno.test("parsingNullDoesNotThrow", function (): void { 27 | assertEquals(semver.parse(null), null); 28 | assertEquals(semver.parse({} as semver.SemVer), null); 29 | assertEquals(semver.parse(new semver.SemVer("1.2.3"))!.version, "1.2.3"); 30 | }); 31 | --------------------------------------------------------------------------------