mirror of
https://github.com/denoland/std.git
synced 2024-11-22 04:59:05 +00:00
302 lines
12 KiB
TypeScript
302 lines
12 KiB
TypeScript
// Copyright Isaac Z. Schlueter and Contributors. All rights reserved. ISC license.
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
// This module is browser compatible.
|
|
|
|
/**
|
|
* The Semantic Version parser.
|
|
*
|
|
* Adapted directly from {@link https://github.com/npm/node-semver | semver}.
|
|
*
|
|
* ```ts
|
|
* import {
|
|
* parse,
|
|
* parseRange,
|
|
* greaterThan,
|
|
* lessThan,
|
|
* format
|
|
* } from "@std/semver";
|
|
* import { assertEquals } from "@std/assert";
|
|
*
|
|
* const semver = parse("1.2.3");
|
|
* assertEquals(semver, {
|
|
* major: 1,
|
|
* minor: 2,
|
|
* patch: 3,
|
|
* prerelease: [],
|
|
* build: []
|
|
* });
|
|
*
|
|
* assertEquals(format(semver), "1.2.3");
|
|
*
|
|
* const range = parseRange("1.x || >=2.5.0 || 5.0.0 - 7.2.3");
|
|
*
|
|
* const s0 = parse("1.2.3");
|
|
* const s1 = parse("9.8.7");
|
|
*
|
|
* assertEquals(greaterThan(s0, s1), false);
|
|
* assertEquals(lessThan(s0, s1), true);
|
|
* ```
|
|
*
|
|
* ## Versions
|
|
*
|
|
* A "version" is described by the `v2.0.0` specification found at
|
|
* <https://semver.org>.
|
|
*
|
|
* A leading `"="` or `"v"` character is stripped off and ignored.
|
|
*
|
|
* ## Format
|
|
*
|
|
* Semantic versions can be formatted as strings, by default they
|
|
* are formatted as `full`. Below is a diagram showing the various
|
|
* formatting options.
|
|
*
|
|
* ```
|
|
* full
|
|
* ┌───┴───┐
|
|
* release │
|
|
* ┌───┴───┐ │
|
|
* primary │ │
|
|
* ┌─┴─┐ │ │
|
|
* 1.2.3-pre.1+b.1
|
|
* │ │ │ └─┬─┘ └┬┘
|
|
* │ │ │ │ └── build
|
|
* │ │ │ └─────── pre
|
|
* │ │ └─────────── patch
|
|
* │ └───────────── minor
|
|
* └─────────────── major
|
|
* ```
|
|
*
|
|
* ## Ranges
|
|
*
|
|
* A version {@linkcode Range} is a set of {@linkcode Comparator}s which specify
|
|
* versions that satisfy the range.
|
|
*
|
|
* A {@linkcode Comparator} is composed of an {@linkcode Operator} and a
|
|
* {@link SemVer}. The set of primitive `operators` is:
|
|
*
|
|
* - `<` Less than
|
|
* - `<=` Less than or equal to
|
|
* - `>` Greater than
|
|
* - `>=` Greater than or equal to
|
|
* - `=` Equal. If no operator is specified, then equality is assumed, so this
|
|
* operator is optional, but MAY be included.
|
|
*
|
|
* For example, the comparator `>=1.2.7` would match the versions `1.2.7`, `1.2.8`,
|
|
* `2.5.3`, and `1.3.9`, but not the versions `1.2.6` or `1.1.0`.
|
|
*
|
|
* Comparators can be joined by whitespace to form a `comparator set`, which is
|
|
* satisfied by the **intersection** of all of the comparators it includes.
|
|
*
|
|
* A range is composed of one or more comparator sets, joined by `||`. A version
|
|
* matches a range if and only if every comparator in at least one of the
|
|
* `||`-separated comparator sets is satisfied by the version.
|
|
*
|
|
* For example, the range `>=1.2.7 <1.3.0` would match the versions `1.2.7`,
|
|
* `1.2.8`, and `1.2.99`, but not the versions `1.2.6`, `1.3.0`, or `1.1.0`.
|
|
*
|
|
* The range `1.2.7 || >=1.2.9 <2.0.0` would match the versions `1.2.7`, `1.2.9`,
|
|
* and `1.4.6`, but not the versions `1.2.8` or `2.0.0`.
|
|
*
|
|
* ### Prerelease Tags
|
|
*
|
|
* If a version has a prerelease tag (for example, `1.2.3-alpha.3`) then it will
|
|
* only be allowed to satisfy comparator sets if at least one comparator with the
|
|
* same `[major, minor, patch]` tuple also has a prerelease tag.
|
|
*
|
|
* For example, the range `>1.2.3-alpha.3` would be allowed to match the version
|
|
* `1.2.3-alpha.7`, but it would _not_ be satisfied by `3.4.5-alpha.9`, even though
|
|
* `3.4.5-alpha.9` is technically "greater than" `1.2.3-alpha.3` according to the
|
|
* SemVer sort rules. The version range only accepts prerelease tags on the `1.2.3`
|
|
* version. The version `3.4.5` _would_ satisfy the range, because it does not have
|
|
* a prerelease flag, and `3.4.5` is greater than `1.2.3-alpha.7`.
|
|
*
|
|
* The purpose for this behavior is twofold. First, prerelease versions frequently
|
|
* are updated very quickly, and contain many breaking changes that are (by the
|
|
* author"s design) not yet fit for public consumption. Therefore, by default, they
|
|
* are excluded from range matching semantics.
|
|
*
|
|
* Second, a user who has opted into using a prerelease version has clearly
|
|
* indicated the intent to use _that specific_ set of alpha/beta/rc versions. By
|
|
* including a prerelease tag in the range, the user is indicating that they are
|
|
* aware of the risk. However, it is still not appropriate to assume that they have
|
|
* opted into taking a similar risk on the _next_ set of prerelease versions.
|
|
*
|
|
* #### Prerelease Identifiers
|
|
*
|
|
* The method {@linkcode increment} takes an additional `identifier` string
|
|
* argument that will append the value of the string as a prerelease identifier:
|
|
*
|
|
* ```ts
|
|
* import { increment, parse } from "@std/semver";
|
|
* import { assertEquals } from "@std/assert";
|
|
*
|
|
* assertEquals(increment(parse("1.2.3"), "prerelease", { prerelease: "alpha" }), parse("1.2.4-alpha.0"));
|
|
* ```
|
|
*
|
|
* ### Build Metadata
|
|
*
|
|
* Build metadata is `.` delimited alpha-numeric string.
|
|
* When parsing a version it is retained on the `build: string[]` field
|
|
* of the SemVer instance. When incrementing there is an additional parameter that
|
|
* can set the build metadata on the SemVer instance.
|
|
*
|
|
* ### Advanced Range Syntax
|
|
*
|
|
* Advanced range syntax desugars to primitive comparators in deterministic ways.
|
|
*
|
|
* Advanced ranges may be combined in the same way as primitive comparators using
|
|
* white space or `||`.
|
|
*
|
|
* #### Hyphen Ranges `X.Y.Z - A.B.C`
|
|
*
|
|
* Specifies an inclusive set.
|
|
*
|
|
* - `1.2.3 - 2.3.4` := `>=1.2.3 <=2.3.4`
|
|
*
|
|
* If a partial version is provided as the first version in the inclusive range,
|
|
* then the missing pieces are replaced with zeroes.
|
|
*
|
|
* - `1.2 - 2.3.4` := `>=1.2.0 <=2.3.4`
|
|
*
|
|
* If a partial version is provided as the second version in the inclusive range,
|
|
* then all versions that start with the supplied parts of the tuple are accepted,
|
|
* but nothing that would be greater than the provided tuple parts.
|
|
*
|
|
* - `1.2.3 - 2.3` := `>=1.2.3 <2.4.0`
|
|
* - `1.2.3 - 2` := `>=1.2.3 <3.0.0`
|
|
*
|
|
* #### X-Ranges `1.2.x` `1.X` `1.2.*` `*`
|
|
*
|
|
* Any of `X`, `x`, or `*` may be used to "stand in" for one of the numeric values
|
|
* in the `[major, minor, patch]` tuple.
|
|
*
|
|
* - `*` := `>=0.0.0` (Any version satisfies)
|
|
* - `1.x` := `>=1.0.0 <2.0.0` (Matching major version)
|
|
* - `1.2.x` := `>=1.2.0 <1.3.0` (Matching major and minor versions)
|
|
*
|
|
* A partial version range is treated as an X-Range, so the special character is in
|
|
* fact optional.
|
|
*
|
|
* - `""` (empty string) := `*` := `>=0.0.0`
|
|
* - `1` := `1.x.x` := `>=1.0.0 <2.0.0`
|
|
* - `1.2` := `1.2.x` := `>=1.2.0 <1.3.0`
|
|
*
|
|
* #### Tilde Ranges `~1.2.3` `~1.2` `~1`
|
|
*
|
|
* Allows patch-level changes if a minor version is specified on the comparator.
|
|
* Allows minor-level changes if not.
|
|
*
|
|
* - `~1.2.3` := `>=1.2.3 <1.(2+1).0` := `>=1.2.3 <1.3.0`
|
|
* - `~1.2` := `>=1.2.0 <1.(2+1).0` := `>=1.2.0 <1.3.0` (Same as `1.2.x`)
|
|
* - `~1` := `>=1.0.0 <(1+1).0.0` := `>=1.0.0 <2.0.0` (Same as `1.x`)
|
|
* - `~0.2.3` := `>=0.2.3 <0.(2+1).0` := `>=0.2.3 <0.3.0`
|
|
* - `~0.2` := `>=0.2.0 <0.(2+1).0` := `>=0.2.0 <0.3.0` (Same as `0.2.x`)
|
|
* - `~0` := `>=0.0.0 <(0+1).0.0` := `>=0.0.0 <1.0.0` (Same as `0.x`)
|
|
* - `~1.2.3-beta.2` := `>=1.2.3-beta.2 <1.3.0` Note that prereleases in the
|
|
* `1.2.3` version will be allowed, if they are greater than or equal to
|
|
* `beta.2`. So, `1.2.3-beta.4` would be allowed, but `1.2.4-beta.2` would not,
|
|
* because it is a prerelease of a different `[major, minor, patch]` tuple.
|
|
*
|
|
* #### Caret Ranges `^1.2.3` `^0.2.5` `^0.0.4`
|
|
*
|
|
* Allows changes that do not modify the left-most non-zero element in the
|
|
* `[major, minor, patch]` tuple. In other words, this allows patch and minor
|
|
* updates for versions `1.0.0` and above, patch updates for versions
|
|
* `0.X >=0.1.0`, and _no_ updates for versions `0.0.X`.
|
|
*
|
|
* Many authors treat a `0.x` version as if the `x` were the major
|
|
* "breaking-change" indicator.
|
|
*
|
|
* Caret ranges are ideal when an author may make breaking changes between `0.2.4`
|
|
* and `0.3.0` releases, which is a common practice. However, it presumes that
|
|
* there will _not_ be breaking changes between `0.2.4` and `0.2.5`. It allows for
|
|
* changes that are presumed to be additive (but non-breaking), according to
|
|
* commonly observed practices.
|
|
*
|
|
* - `^1.2.3` := `>=1.2.3 <2.0.0`
|
|
* - `^0.2.3` := `>=0.2.3 <0.3.0`
|
|
* - `^0.0.3` := `>=0.0.3 <0.0.4`
|
|
* - `^1.2.3-beta.2` := `>=1.2.3-beta.2 <2.0.0` Note that prereleases in the
|
|
* `1.2.3` version will be allowed, if they are greater than or equal to
|
|
* `beta.2`. So, `1.2.3-beta.4` would be allowed, but `1.2.4-beta.2` would not,
|
|
* because it is a prerelease of a different `[major, minor, patch]` tuple.
|
|
* - `^0.0.3-beta` := `>=0.0.3-beta <0.0.4` Note that prereleases in the `0.0.3`
|
|
* version _only_ will be allowed, if they are greater than or equal to `beta`.
|
|
* So, `0.0.3-pr.2` would be allowed.
|
|
*
|
|
* When parsing caret ranges, a missing `patch` value desugars to the number `0`,
|
|
* but will allow flexibility within that value, even if the major and minor
|
|
* versions are both `0`.
|
|
*
|
|
* - `^1.2.x` := `>=1.2.0 <2.0.0`
|
|
* - `^0.0.x` := `>=0.0.0 <0.1.0`
|
|
* - `^0.0` := `>=0.0.0 <0.1.0`
|
|
*
|
|
* A missing `minor` and `patch` values will desugar to zero, but also allow
|
|
* flexibility within those values, even if the major version is zero.
|
|
*
|
|
* - `^1.x` := `>=1.0.0 <2.0.0`
|
|
* - `^0.x` := `>=0.0.0 <1.0.0`
|
|
*
|
|
* ### Range Grammar
|
|
*
|
|
* Putting all this together, here is a Backus-Naur grammar for ranges, for the
|
|
* benefit of parser authors:
|
|
*
|
|
* ```bnf
|
|
* range-set ::= range ( logical-or range ) *
|
|
* logical-or ::= ( " " ) * "||" ( " " ) *
|
|
* range ::= hyphen | simple ( " " simple ) * | ""
|
|
* hyphen ::= partial " - " partial
|
|
* simple ::= primitive | partial | tilde | caret
|
|
* primitive ::= ( "<" | ">" | ">=" | "<=" | "=" ) partial
|
|
* partial ::= xr ( "." xr ( "." xr qualifier ? )? )?
|
|
* xr ::= "x" | "X" | "*" | nr
|
|
* nr ::= "0" | ["1"-"9"] ( ["0"-"9"] ) *
|
|
* tilde ::= "~" partial
|
|
* caret ::= "^" partial
|
|
* qualifier ::= ( "-" pre )? ( "+" build )?
|
|
* pre ::= parts
|
|
* build ::= parts
|
|
* parts ::= part ( "." part ) *
|
|
* part ::= nr | [-0-9A-Za-z]+
|
|
* ```
|
|
*
|
|
* Note that, since ranges may be non-contiguous, a version might not be greater
|
|
* than a range, less than a range, _or_ satisfy a range! For example, the range
|
|
* `1.2 <1.2.9 || >2.0.0` would have a hole from `1.2.9` until `2.0.0`, so the
|
|
* version `1.2.10` would not be greater than the range (because `2.0.1` satisfies,
|
|
* which is higher), nor less than the range (since `1.2.8` satisfies, which is
|
|
* lower), and it also does not satisfy the range.
|
|
*
|
|
* If you want to know if a version satisfies or does not satisfy a range, use the
|
|
* {@linkcode satisfies} function.
|
|
*
|
|
* @module
|
|
*/
|
|
export * from "./compare.ts";
|
|
export * from "./difference.ts";
|
|
export * from "./format.ts";
|
|
export * from "./satisfies.ts";
|
|
export * from "./increment.ts";
|
|
export * from "./is_semver.ts";
|
|
export * from "./max_satisfying.ts";
|
|
export * from "./min_satisfying.ts";
|
|
export * from "./parse_range.ts";
|
|
export * from "./parse.ts";
|
|
export * from "./range_intersects.ts";
|
|
export * from "./types.ts";
|
|
export * from "./try_parse_range.ts";
|
|
export * from "./is_range.ts";
|
|
export * from "./can_parse.ts";
|
|
export * from "./try_parse.ts";
|
|
export * from "./format_range.ts";
|
|
export * from "./equals.ts";
|
|
export * from "./not_equals.ts";
|
|
export * from "./greater_than.ts";
|
|
export * from "./greater_than_range.ts";
|
|
export * from "./greater_or_equal.ts";
|
|
export * from "./less_than.ts";
|
|
export * from "./less_than_range.ts";
|
|
export * from "./less_or_equal.ts";
|