In the previous tutorial we walked you through how npm handles the script field. In this tutorial we will take a look at the semantic versioner for npm.
Npm install -save semver
Usage as a node module:
const semver = require('semver') semver.valid('1.2.4') // '1.2.4' semver.valid('a.b.c') // null semver.clean(' =v1.2.4 ') // '1.2.4' semver.satisfies('1.2.4', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true semver.gt('1.2.4', '9.8.7') // false semver.lt('1.2.4', '9.8.7') // true semver.valid(semver.coerce('v2')) // '2.0.0' semver.valid(semver.coerce('22.214.171.124.3-alpha')) // '42.6.7'
usage as a command-line utility:
Copyright Isaac Z. Schlueter
Usage: semver [options] <version> [<version> [...]]
This will print valid versions sorted by SemVer precedence
-r --range <range>
This will print versions that match the specified range.
-i --increment [<level>]
This will increment a version by the specified level. The levels can be either of: major, minor, patch, premajor, preminor, prepatch, or prerelease. The default level is 'patch'. Only one version can be specified.
This is the identifier to be used to prefix premajor, preminor,
prepatch or prerelease version increments.
This will interpret versions and ranges loosely
-c --coerce This will coerce a string into SemVer if possible (does not imply --loose)
Program will exit successfully if any valid version satisfies all supplied ranges, and will print all satisfying versions.
If there is no satisfying version found, then exits failure.
Versions will be printed in ascending order, so supplying multiple versions to the utility will sort them.
A "version" is described by the v2.0.0 specification that is found at http://semver.org/.
A leading "=" or "v" character will be stripped off and ignored.
The version range is a set of comparators that specifies versions that satisfy the range.
Every comparator is composed of an operator and a version. The of primitive operator is given below:
- < Less than
- <= Less than or equal to
- > Greater than
- >= Greater than or equal to
- = Equal.
If you do not specify any operator, then equality will be assumed, so these operators are optional, but MAY be included.
You can join comparators by whitespace to form a comparator set, which will be satisfied by the intersection of all the comparators it includes.
A range is usually composed of one or more comparator sets, that is joined by ||. A version will match a range if and only if every comparator in at least one of the ||-separated comparator sets has been satisfied by the version.
For instance, the range >=1.2.7 <1.3.0 matches the versions 1.2.7, 1.2.8, and 1.2.99, but do not match the versions 1.2.6, 1.3.0, or 1.1.0.
The range 1.2.7 || >=1.2.9 <2.0.0 will match the versions 1.2.7, 1.2.9, and 1.4.6, but will not match the versions 1.2.8 or 2.0.0.
If a version contains 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 equally contains a prerelease tag.
For instance, the range >1.2.3-alpha.3 can match the version 1.2.3-alpha.7, but it will not be satisfied by 3.4.5-alpha.9, even though technically 3.4.5-alpha.9 is "greater than" 1.2.3-alpha.3 according to the SemVer sort rules. The version range will only accept prerelease tags on the 1.2.3 version. The version 3.4.5 will satisfy the range, since it does not have a prerelease flag, and 3.4.5 is greater than 1.2.3-alpha.7.
There are two reasons for this behavior. First is that most often, prerelease versions are updated very quickly, and they contain many breaking changes that are (by the author's design) not yet fit for public consumption. Hence, they are excluded from range matching semantics by default.
Second, a user that 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 will be indicating that they are aware of the risk. However, it is still inappropriate to assume that they have opted into taking a similar risk on the next set of prerelease versions.
The method .inc will take an additional identifier string argument that appends the value of the string as a prerelease identifier:
semver.inc('1.2.3', 'prerelease', 'beta') // '1.2.4-beta.0'
$ semver 1.2.3 -i prerelease --preid beta 1.2.4-beta.0
Which can then be used to increment further:
$ semver 1.2.4-beta.0 -i prerelease 1.2.4-beta.1
Advanced Range Syntax
The advanced range syntax desugars to primitive comparators in deterministic ways.
Advanced ranges can be combined in the same way as primitive comparators using white space or ||.
Hyphen Ranges X.Y.Z - A.B.C
This specifies an inclusive set.
- 1.2.3 - 2.3.4 := >=1.2.3 <=2.3.4
If you provide a partial version as the first version in the inclusive range, then the missing pieces will be replaced with zeroes.
- 1.2 - 2.3.4 := >=1.2.0 <=2.3.4
If you provide a partial version as the second version in the inclusive range, then all versions that start with the supplied parts of the tuple will be accepted, but nothing that will 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 * can 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 will be treated as an X-Range, so the special character will be 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
This will allow patch-level changes if a minor version is specified on the comparator. It will allow 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 are allowed, if those prereleases are greater than or equal to beta.2. So, 1.2.3-beta.4 will be allowed, but 1.2.4-beta.2 will not be, this is because, it is a prerelease of a different [major, minor, patch] tuple.
Caret Ranges ^1.2.3 ^0.2.5 ^0.0.4
This will allow changes that does not modify the left-most non-zero digit in the [major, minor, patch] tuple. In other words, this will allow patch and minor updates for versions 1.0.0 and above, it will patch updates for versions 0.X >=0.1.0, and there will be no updates for versions 0.0.X.
Many authors treat the 0.x version as if the x were the major "breaking-change" indicator.
The caret ranges are ideal when an author may make breaking changes between 0.2.4 and 0.3.0 releases, this is a common practice. However, it presumes that there will be no breaking changes between 0.2.4 and 0.2.5. It will allow changes that are presumed to be additive (but non-breaking), according to the 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 are allowed, if they are greater than or equal to beta.2. So, 1.2.3-beta.4 will be allowed, but 1.2.4-beta.2 will not be, this is 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 are only allowed, when they are greater than or equal to beta. So, 0.0.3-pr.2 will be allowed.
When you are parsing caret ranges, a missing patch value will desugar to the number 0, but allows flexibility within that value, even when 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 desugars to zero, but will also allow flexibility within those values, even when the major version is zero.
- ^1.x := >=1.0.0 <2.0.0
- ^0.x := >=0.0.0 <1.0.0
When we put all this together, we get a Backus-Naur grammar for ranges, which is for the benefit of parser authors:
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]+
All the methods and classes take a final loose boolean argument that, if true, are more forgiving about not-quite-valid semver strings. The resulting output is always 100% strict, of course.
Strict-mode Comparators and Ranges are strict about the SemVer strings that they parse.
- valid(v): This will return the parsed version, or null if it is not valid.
- inc(v, release): This will return the version incremented by the release type (major, premajor, minor, preminor, patch, prepatch, or prerelease), or null if it is not valid
- premajor in one call bumps the version up to the next major version and then down to a prerelease of that major version. preminor, and prepatch both work the same way.
- When called from a non-prerelease version, the prerelease works the same as prepatch. It will increment the patch version, then it will make a prerelease. In the case where the input version is already a prerelease it will simply increment it.
- prerelease(v): This will return an array of prerelease components, or null where none exist. Example: prerelease('1.2.3-alpha.1') -> ['alpha', 1]
- major(v): This will return the major version number.
- minor(v): This will return the minor version number.
- patch(v): This will return the patch version number.
- intersects(r1, r2, loose): This will return true if the two supplied ranges or comparators intersect.
- gt(v1, v2): v1 > v2
- gte(v1, v2): v1 >= v2
- lt(v1, v2): v1 < v2
- lte(v1, v2): v1 <= v2
- eq(v1, v2): v1 == v2 This will be true if they're logically equivalent, even if they are not the exact same string. You know how to compare strings already.
- neq(v1, v2): v1 != v2 This is the opposite of eq.
- cmp(v1, comparator, v2): When you pass in a comparison string, and it will call the corresponding function above. "===" and "!==" do simple string comparison, but will be included for completeness. It will be thrown if an invalid comparison string is provided.
- compare(v1, v2): This will return 0 if v1 == v2, or 1 where v1 is greater, or -1 where v2 is greater. It will sort in ascending order when passed to Array.sort().
- rcompare(v1, v2): This is the reverse of compare. It will sort an array of versions in descending order when passed to Array.sort().
- diff(v1, v2): This will return difference between two versions by the release type (major, premajor, minor, preminor, patch, prepatch, or prerelease), or null when the versions are the same.
intersects(comparator): This will return true if the comparators intersect
validRange(range): This will return the valid range or null if it is not valid
satisfies(version, range): This will return true if the version satisfies the range.
maxSatisfying(versions, range): This will return the highest version in the list that satisfies the range, or null if none of them do satisfies the range.
minSatisfying(versions, range): This will return the lowest version in the list that satisfies the range, or null when none of them do.
gtr(version, range): This will return true if version is greater than all the versions possible in the range.
ltr(version, range): This will return true if version is less than all the versions possible in the range.
outside(version, range, hilo): This will return true if the version is outside the bounds of the range in either the high or low direction. The hilo argument has to be either the string '>' or '<'. (This is the function that is called by gtr and ltr.)
intersects(range): This will return true if any of the ranges comparators intersect
It should be noted that, since ranges can be non-contiguous, a version may not be greater than a range, less than a range, or satisfy a range! For instance, the range 1.2 <1.2.9 || >2.0.0 will have a hole from 1.2.9 until 2.0.0, so the version 1.2.10 will not be greater than the range (because 2.0.1 satisfies, which is higher), nor will it be less than the range (since 1.2.8 satisfies, which is lower), and it will also not satisfy the range.
If you want to know whether a version satisfies a range or not, you need to use the satisfies(version, range) function.
- coerce(version): This coerces a string to semver if possible
This aims at providing a very forgiving translation of a non-semver string to semver. It will look for the first digit in a string, and then consumes all remaining characters which satisfy at least a partial semver (e.g., 1, 1.2, 1.2.3) up to the max permitted length (256 characters). Longer versions will simply be truncated (126.96.36.199.2-alpha2 becomes 4.6.3). All surrounding text will simply be ignored (v3.4 replaces v3.3.1 becomes 3.4.0). It is only text which lacks digits that will fail coercion (version one is not valid). The maximum length for any semver component considered for coercion will be 16 characters; longer components are ignored (10000000000000000.4.7.4 becomes 4.7.4). The maximum value for any semver component is usually Integer.MAX_SAFE_INTEGER || (2**53 - 1); All higher value components is invalid (99999999999999188.8.131.52 is likely invalid).
- Weekly Trends
- Python Interview Questions and Answers: Comprehensive Guide
- Scala Exercises, Practice, Solution
- Kotlin Exercises practice with solution
- MongoDB Exercises, Practice, Solution
- SQL Exercises, Practice, Solution - JOINS
- Java Basic Programming Exercises
- SQL Subqueries
- Adventureworks Database Exercises
- C# Sharp Basic Exercises
- SQL COUNT() with distinct
- Java Collection Exercises
- SQL COUNT() function
- SQL Inner Join