@release-change/semver
v0.1.0
Published
The semantic versioning parser used by release-change
Maintainers
Readme
@release-change/semver
The semantic versioning parser used by release-change
Installation
Install package for Node.js:
pnpm add --save-dev @release-change/semverYou can also install it using npm:
npm install --save-dev @release-change/semverUsage
import { coerce, gt, lt, satisfies, validate } from "@release-change/semver";
validate("1.2.3"); // "1.2.3"
validate("a.b.c"); // null
satisfies("1.2.3", "1.x || >=2.5.0 || 5.0.0 - 7.2.3"); // true
gt("1.2.3", "9.8.7"); // false
lt("1.2.3", "9.8.7"); // true
validate(coerce("version2")); // "2.0.0"
validate(coerce("42.6.7.9.3-alpha")); // "42.6.7"Documentation
Versions
A version is described by the Semantic Versioning 2.0.0 specification.
Ranges
A version range is a set of comparators which specify versions satisfying the range.
A comparator is composed of an operator and a version. 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, equality is assumed, so this operator is optional).
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. The comparator >1 is equivalent to >=2.0.0 and would match the versions 2.0.0 and 3.1.0, but not the versions 1.0.1 or 1.1.0.
Whitespace can join comparators to form a comparator set, which is satisfied by the *intersection- of all the comparators it includes.
A range is composed of one or more comparator sets, joined by || (logical or). A version matches a range if and only if the version satisfies every comparator in at least one of the logical-or-separated comparator sets.
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.
Pre-release tags
If a version has a pre-release 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 pre-release 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 Semantic Versioning sort rules. The version range only accepts pre-release tags on the 1.2.3 version. Version 3.4.5 would satisfy the range because it does not have a pre-release flag and 3.4.5 is greater than 1.2.3-alpha.7.
The purpose of this behaviour is twofold.
First, pre-release versions frequently are updated very quickly and contain many breaking changes that are (by the author’s design) not fit for public consumption yet. Therefore, by default, they are excluded from range-matching semantics.
Second, a user who has opted into using a pre-release version has indicated the intent to use that specific set of alpha/beta/rc versions. By including a pre-release tag in the range, the user is indicating being 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 pre-release versions.
Note that this behaviour can be suppressed (treating all pre-release versions as though they were normal versions, for range matching) by setting the includePrerelease flag on the options object to any functions doing range matching.
Pre-release identifiers
The function increase takes a prereleaseIdentifiers parameter, which takes an optional property prefix, which will append the value of the string as a pre-release identifier:
increase("1.2.3", "prerelease", { prefix: "beta" });
// "1.2.4-beta.0"Pre-release identifier base
The function increase takes a prereleaseIdentifiers parameter, which takes an optional property identifierBase, which will let you let your pre-release number as zero-based or one-based. Set to false to omit the pre-release number altogether. If you do not specify this parameter, it will default to zero-based.
increase("1.2.3", "prerelease", { prefix: "beta", identifierBase: "1" });
// "1.2.4-beta.1"increase("1.2.3", "prerelease", { prefix: "beta", identifierBase: false });
// "1.2.4-beta"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. Example: 1.2.3 - 2.3.4 (is equivalent to >=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 is equivalent to >=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.3is equivalent to>=1.2.3 <2.4.0-0,1.2.3 - 2is equivalent to>=1.2.3 <3.0.0-0.
X-ranges (1.2.x, 1.X, 1.2.* and *)
Any of X, x or * may be used to “stand in” for one of the numeric values in the [major, minor, patch] tuple:
*(is equivalent to>=0.0.0: any non-pre-release version satisfies, unlessincludePrereleaseis specified, in which case any version at all satisfies),1.x(is equivalent to>=1.0.0 <2.0.0-0: matching a major version),1.2.x(is equivalent to>=1.2.0 <1.3.0-0: matching major and minor versions).
A partial version range is treated as an X-range, so the special character is actually optional:
""(empty string, which is equivalent to*, which is equivalent to>=0.0.0),1(is equivalent to1.x.x, which is equivalent to>=1.0.0 <2.0.0-0),1.2(is equivalent to1.2.x, which is equivalent to>=1.2.0 <1.3.0-0).
Tilde ranges (~1.2.3, ~1.2 and ~1)
Allows patch-level changes if a minor version is specified on the comparator, minor-level changes otherwise.
Examples:
~1.2.3(is equivalent to>=1.2.3 <1.(2+1).0, which is equivalent to>=1.2.3 <1.3.0-0),~1.2(is equivalent to>=1.2.0 <1.(2+1).0, which is equivalent to>=1.2.0 <1.3.0-0, which is the same as1.2.x),~1(is equivalent to>=1.0.0 <(1+1).0.0, which is equivalent to>=1.0.0 <2.0.0-0, which is the same as1.x),~0.2.3(is equivalent to>=0.2.3 <0.(2+1).0, which is equivalent to>=0.2.3 <0.3.0-0),~0.2(is equivalent to>=0.2.0 <0.(2+1).0, which is equivalent to>=0.2.0 <0.3.0-0, which is the same as0.2.x),~0(is equivalent to>=0.0.0 <(0+1).0.0, which is equivalent to>=0.0.0 <1.0.0-0, which is the same as0.x),~1.2.3-beta.2(is equivalent to>=1.2.3-beta.2 <1.3.0-0: note that pre-releases in the1.2.3version will be allowed if they are greater than or equal tobeta.2; so,1.2.3-beta.4would be allowed, but1.2.4-beta.2would not because it is a pre-release of a different[major, minor, patch]tuple).
Caret ranges (^1.2.3, ^0.2.5 and ^0.0.4)
Allows changes which 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.
Examples:
^1.2.3(is equivalent to>=1.2.3 <2.0.0-0),^0.2.3(is equivalent to>=0.2.3 <0.3.0-0),^0.0.3(is equivalent to>=0.0.3 <0.0.4-0),^1.2.3-beta.2(is equivalent to>=1.2.3-beta.2 <2.0.0-0: note that pre-releases in the1.2.3version will be allowed if they are greater than or equal tobeta.2; so,1.2.3-beta.4would be allowed, but1.2.4-beta.2would not because it is a pre-release of a different[major, minor, patch]tuple),^0.0.3-beta(is equivalent to>=0.0.3-beta <0.0.4-0: note that pre-releases in the0.0.3version only will be allowed if they are greater than or equal tobeta; so,0.0.3-pr.2would be allowed).
When parsing caret ranges, a missing patch value desugars to the number 0, but will allow flexibility within that value, even though the major and minor versions are both 0:
^1.2.x(is equivalent to>=1.2.0 <2.0.0-0),^0.0.x(is equivalent to>=0.0.0 <0.1.0-0),^0.0(is equivalent to>=0.0.0 <0.1.0-0).
A missing minor and patch values will desugar to zero, but also allow flexibility within those values, even though the major version is zero:
^1.x(is equivalent to>=1.0.0 <2.0.0-0),^0.x(is equivalent to>=0.0.0 <1.0.0-0).
Functions
All functions take a final options object argument. All options in this object are false by default. The options supported are:
loose: be more forgiving about not-quite-valid semantic versioning strings (any resulting output will always be 100% strictly compliant, of course);includePrerelease: set to suppress the default behaviour of excluding pre-release tagged versions from ranges (unless they are explicitly opted into).
Strict-mode comparators and ranges will be strict about the semantic versioning strings they parse.
Basic functions
validate(version): returns the parsed version, or null if it is not valid;increase(version, releaseType, prereleaseIdentifiers): returns the version increased by the release type (major,premajor,minor,preminor,patch,prepatch,prereleaseorrelease) ornullif it is not valid:premajorin one call will bump the version up to the next major version and down to a pre-release of that major version (preminor, andprepatchwork the same way);- if called from a non-pre-release version,
prereleasewill work the same asprepatch, increasing the patch version and then making a pre-release (if the input version is already a pre-release, it simply increments it); releasewill remove any pre-release part of the version;identifiercan be used to prefixpremajor,preminor,prepatchorprereleaseversion increments (identifierBaseis the base to be used for theprereleaseidentifier).
getPrerelease(version): returns an array of pre-release components, ornullif none exist (for example:prerelease('1.2.3-alpha.1')returns['alpha', 1]);getMajor(version): returns the major version number;getMinor(version): returns the minor version number;getPatch(version): returns the patch version number;parse(version): attempts to parse a string as a semantic version, returning either aSemverobject ornull.
Sorting
sort(versions): returns an array of versions sorted in ascending order, considering the builds;reverseSort(versions): returns an array of versions sorted in descending order, considering the builds.
Comparisons
gt(version1, version2):version1 > version2;gte(version1, version2):version1 >= version2;lt(version1, version2):version1 < version2;lte(version1, version2):version1 <= version2;eq(version1, version2):version1 == version2(this is true if they are logically equivalent, even though they are not completely the same string);neq(version1, version2):version1 != version2(the opposite ofeq);compareWithOperator(version1, operator, version2): passes in a comparison string and calls the corresponding function above ("==="and"!=="do simple string comparison), throwing if an invalid comparison string is provided;compare(version1, version2): returns0ifversion1 == version2,1ifversion1is greater or-1ifversion2is greater;reverseCompare(version1, version2): returns0ifversion1 == version2,1ifversion2is greater or-1ifversion1is greater (the reverse ofcompare);compareBuild(version1, version2)(the same ascompare, but considers the builds when two versions are equal);compareLoose(version1, version2)(short forcompare(version1, version2, { loose: true }));getDifference(version1, version2): returns the difference between two versions by the release type (major,minor,patch,premajor,preminor,prepatchorprerelease) ornullif both versions are the same.
Ranges
validateRange(range): returns the range if it is valid,nullotherwise;satisfies(version, range): returnstrueif the version satisfies the range,falseotherwise;getMaxSatisfyingVersion(versions, range): returns the highest version in the list if there is one satisfying the range,nullotherwise;getMinSatisfyingVersion(versions, range): returns the lowest version in the list if there is one satisfying the range,nullotherwise;getMinVersion(range): returns the lowest version which can match the given range if found, ornullotherwise;gtRange(version, range): returnstrueif the version is greater than all the versions within the range,falseotherwise;ltRange(version, range): returnstrueif the version is less than all the versions within the range,falseotherwise;isVersionOutside(version, range, highLowDirection): returnstrueif the version is outside the bounds of the range in either the high or low direction,falseotherwise;intersects(range1, range2): returnstrueif any of the range comparators intersect,falseotherwise;simplifyRange(versions, range): returns a simplified range matching the same items in theversionslist as the range specified, which is useful when generating ranges by joining together multiple versions with||programmatically (note that it does not guarantee that it would match the same versions in all cases, only for the set of versions provided);isRangeSubset(subRange, superRange): returnstrueif thesubRangerange is entirely contained by thesuperRangerange,falseotherwise.
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 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 whether a version satisfies a range or not, use the satisfies function.
Coercion
coerce(version, options): coerces a string to a semantic version if possible.
This aims to provide a very forgiving translation of a non-semver string to semantic versioning. It looks for the first digit in a string and consumes all remaining characters which satisfy at least a partial semver (e.g.: 1, 1.2, 1.2.3). Longer versions are simply truncated (4.6.3.9.2-alpha2 becomes 4.6.3). All surrounding text is simply ignored (v3.4 replaces v3.3.1 becomes 3.4.0). Only text which lacks digits will fail coercion (version one is not valid). The maximum value for any semantic versioning component considered for coercion is Number.MAX_SAFE_INTEGER (or 2 ** 53 - 1), components with higher values will be ignored (10000000000000000.4.7.4 becomes 4.7.4).
If the options.rtl flag is set, then coerce will return the right-most coercible tuple not sharing an ending index with a longer coercible tuple: for example, 1.2.3.4 will return 2.3.4 in rtl mode, not 4.0.0, 1.2.3/4 will return 4.0.0 because the 4 is not a part of any other overlapping semantic versioning tuple.
If the options.includePrerelease flag is set, then the coerce result will contain pre-release and build parts of a version: for example, 1.2.3.4-rc.1+rev.2 will preserve pre-release rc.1 and build rev.2 in the result.
Cleaning
clean(version): cleans a string to be a valid semantic version, if possible.
This will return a cleaned and trimmed semantic version. If the provided version is not valid, null will be returned. This does not work for ranges.
Examples:
clean(" = v 2.1.5foo"); // null,clean(" = v 2.1.5foo', { loose: true }); // '2.1.5-foo',clean(" = v 2.1.5-foo"); // null,clean(" = v 2.1.5-foo", { loose: true }); // '2.1.5-foo',clean("=v2.1.5"); // '2.1.5',clean(" =v2.1.5"); // '2.1.5',clean(" 2.1.5 "); // '2.1.5',clean("~1.0.0"); // null.
Get help
Copyright & licence
© 2025-present Victor Brito — Released under the MIT licence.
