diff --git a/Makefile b/Makefile index a5ab378..97c6bed 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,7 @@ SHELL:=bash default: all - -PHONY_TARGETS:=all default +PHONY_TARGETS:=default BUILD_TARGETS:= diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ae0e7b --- /dev/null +++ b/README.md @@ -0,0 +1,705 @@ +# semver-plus +## API reference +_API generated with [dmd-readme-api](https://www.npmjs.com/package/dmd-readme-api)._ + + +- Functions: + - _Comparison operations_ + - [`cmp()`](#cmp): Returns a number indicating whether a version is greater than, equal to, or less than another version. + - [`compare()`](#compare): Returns 0 if `v1 == v2`, 1 if `v1 > v2`, and -1 if `v1 < v2`. + - [`compareBuild()`](#compareBuild): Same as [compare](#compare) except it compares build if two versions are otherwise equal. + - [`compareLoose()`](#compareLoose): Short for [compare](#compare) with `options.loose = true`. + - [`diff()`](#diff): Returns the difference between two versions. + - [`eq()`](#eq): Returns `true` if `v1` is equal to `v2`, `false` otherwise. + - [`gt()`](#gt): Returns `true` if `v1` is greater than `v2`, `false` otherwise. + - [`gte()`](#gte): Returns `true` if `v1` is greater than or equal to `v2`, `false` otherwise. + - [`lt()`](#lt): Returns `true` if `v1` is less than `v2`, `false` otherwise. + - [`lte()`](#lte): Returns `true` if `v1` is less than or equal to `v2`, `false` otherwise. + - [`neq()`](#neq): Returns `true` if `v1` is not equal to `v2`, `false` otherwise. + - [`rcompare()`](#rcompare): Reverse of [compare](#compare). + - [`rsort()`](#rsort): Sorts an array of versions in descending order using [compareBuild](#compareBuild). + - [`sort()`](#sort): Sorts an array of versions in ascending order using [compareBuild](#compareBuild). + - [`xSort()`](#xSort): Ascend sorts a mix of semver versions and x-range specified ranges (e.g., 1.2.* or 1.2.x). + - _Range operations_ + - [`gtr()`](#gtr): Returns `true` if `version` is greater than is greater than any version in `range`, `false` otherwise. + - [`intersects()`](#intersects): Returns `true` if any of the comparators in the range intersect with each other. + - [`ltr()`](#ltr): Returns `true` if `version` is less than is less than any version in `range`, `false` otherwise. + - [`maxSatisfying()`](#maxSatisfying): Returns the highest version in `versions` that satisfies the range, or null if no version satisfies the range. + - [`minSatisfying()`](#minSatisfying): Returns the lowest version in `versions` that satisfies the range, or null if no version satisfies the range. + - [`minVersion()`](#minVersion): Returns the lowest version that satisfies the range, or null if no version satisfies the range. + - [`outside()`](#outside): Returns `true` if `version` is outside of `range` in the indicated direction, `false` otherwise. + - [`satisfies()`](#satisfies): Returns `true` if the version satisfies the range, `false` otherwise. + - [`simplifyRange()`](#simplifyRange): Return a "simplified" range that matches the same items in the versions list as the range specified. + - [`subset()`](#subset): Returns `true` if `subRange` is a subset of `superRange`, `false` otherwise. + - [`upperBound()`](#upperBound): Finds the ceiling of a range. + - [`validRange()`](#validRange): Returns a parsed, normalized range string or null if the range is invalid. + - [`validVersionOrRange()`](#validVersionOrRange): Validates a string to be a valid version or range. + - _Version operations_ + - [`clean()`](#clean): Returns a cleaned version string removing unecessary comparators and, if `options.loose` is true, fixing space issues. + - [`coerce()`](#coerce): Aggressively attempts to coerce a string into a valid semver string. + - [`inc()`](#inc): Returns a new version string incremented by the specified part. + - [`major()`](#major): Returns the major version number. + - [`minor()`](#minor): Returns the minor version number. + - [`nextVersion()`](#nextVersion): Given a current version generates the next version string accourding to `increment`. + - [`parse()`](#parse): Attempts to parse and normalize a string as a semver string. + - [`patch()`](#patch): Returns the patch version number. + - [`prerelease()`](#prerelease): Returns an array of prerelease components or `null` if the version is not a prerelease. + - [`valid()`](#valid): Returns a parsed, normalized version string or null if the version is invalid. + + +### `cmp(v1, comparator, v2, options)` ⇒ `number` [source code](./src/semver-comparison-ops.mjs#L87) [global index](#global-function-index) + +Returns a number indicating whether a version is greater than, equal to, or less than another version. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `comparator` | `string` | The comparator to use. May be '<', '<=', '>', '>=', '=', '==', '!=', '===', or '!=='. An exception is thrown if an invalid comparator is provided. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.cmp function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `number` - - A number indicating whether a version is greater than, equal to, or less than another version. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `compare(v1, v2, options)` ⇒ `number` [source code](./src/semver-comparison-ops.mjs#L100) [global index](#global-function-index) + +Returns 0 if `v1 == v2`, 1 if `v1 > v2`, and -1 if `v1 < v2`. Will sort an array of versions in ascending order if +passed to `Array.sort()`. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.compare function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `number` - - 0 if `v1 == v2`, 1 if `v1 > v2`, and -1 if `v1 < v2`. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `compareBuild(v1, v2, options)` ⇒ `number` [source code](./src/semver-comparison-ops.mjs#L112) [global index](#global-function-index) + +Same as [compare](#compare) except it compares build if two versions are otherwise equal. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.compareBuild function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `number` - - 0 if `v1 == v2`, 1 if `v1 > v2`, and -1 if `v1 < v2`. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `compareLoose(v1, v2)` ⇒ `number` [source code](./src/semver-comparison-ops.mjs#L134) [global index](#global-function-index) + +Short for [compare](#compare) with `options.loose = true`. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | + +**Returns**: `number` - - 0 if `v1 == v2`, 1 if `v1 > v2`, and -1 if `v1 < v2`. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `diff(v1, v2, options)` ⇒ `string` \| `null` [source code](./src/semver-comparison-ops.mjs#L148) [global index](#global-function-index) + +Returns the difference between two versions. I.e., the most significant version component by which `v1` and `v2` +differ. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.diff function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `string` \| `null` - - `major`, 'minor', 'patch', 'prerelease', 'premajor', 'preminor', or 'prepatch' or null if +the `v1` and `v2` are identical (disregarding build metadata). + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `eq(v1, v2, options)` ⇒ `boolean` [source code](./src/semver-comparison-ops.mjs#L61) [global index](#global-function-index) + +Returns `true` if `v1` is equal to `v2`, `false` otherwise. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.eq function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if `v1` is equal to `v2`, `false` otherwise. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `gt(v1, v2, options)` ⇒ `boolean` [source code](./src/semver-comparison-ops.mjs#L13) [global index](#global-function-index) + +Returns `true` if `v1` is greater than `v2`, `false` otherwise. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.gt function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if `v1` is greater than `v2`, `false` otherwise. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `gte(v1, v2, options)` ⇒ `boolean` [source code](./src/semver-comparison-ops.mjs#L25) [global index](#global-function-index) + +Returns `true` if `v1` is greater than or equal to `v2`, `false` otherwise. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.gte function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if `v1` is greater than or equal to `v2`, `false` otherwise. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `lt(v1, v2, options)` ⇒ `boolean` [source code](./src/semver-comparison-ops.mjs#L37) [global index](#global-function-index) + +Returns `true` if `v1` is less than `v2`, `false` otherwise. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.lt function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if `v1` is less than `v2`, `false` otherwise. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `lte(v1, v2, options)` ⇒ `boolean` [source code](./src/semver-comparison-ops.mjs#L49) [global index](#global-function-index) + +Returns `true` if `v1` is less than or equal to `v2`, `false` otherwise. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.lte function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if `v1` is less than or equal to `v2`, `false` otherwise. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `neq(v1, v2, options)` ⇒ `boolean` [source code](./src/semver-comparison-ops.mjs#L73) [global index](#global-function-index) + +Returns `true` if `v1` is not equal to `v2`, `false` otherwise. + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.neq function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if `v1` is not equal to `v2`, `false` otherwise. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `rcompare(v1, v2, options)` ⇒ `number` [source code](./src/semver-comparison-ops.mjs#L124) [global index](#global-function-index) + +Reverse of [compare](#compare). + + +| Param | Type | Description | +| --- | --- | --- | +| `v1` | `string` | The first version to compare. | +| `v2` | `string` | The second version to compare. | +| `options` | `object` | The options to pass to the semver.rcompare function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `number` - - 0 if `v1 == v2`, -1 if `v1 > v2`, and 1 if `v1 < v2`. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `rsort(versions, options)` ⇒ `Array.` [source code](./src/semver-comparison-ops.mjs#L170) [global index](#global-function-index) + +Sorts an array of versions in descending order using [compareBuild](#compareBuild). + + +| Param | Type | Description | +| --- | --- | --- | +| `versions` | `Array.` | The versions to sort. | +| `options` | `object` | The options to pass to the semver.rsort function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `Array.` - - The sorted versions. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `sort(versions, options)` ⇒ `Array.` [source code](./src/semver-comparison-ops.mjs#L159) [global index](#global-function-index) + +Sorts an array of versions in ascending order using [compareBuild](#compareBuild). + + +| Param | Type | Description | +| --- | --- | --- | +| `versions` | `Array.` | The versions to sort. | +| `options` | `object` | The options to pass to the semver.sort function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `Array.` - - The sorted versions. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `xSort(versionsAndRanges)` ⇒ `Array.` [source code](./src/x-sort.mjs#L12) [global index](#global-function-index) + +Ascend sorts a mix of semver versions and x-range specified ranges (e.g., 1.2.* or 1.2.x). The ranges are sorted according to their highest version; e.g., 1.3.34 < 1.3.*. (I believe it can accept caret ranges too, but I would need to review the spec.) + + +| Param | Type | Description | +| --- | --- | --- | +| `versionsAndRanges` | `Array.` | The versions and ranges to sort. | + +**Returns**: `Array.` - - The sorted versions and ranges. + +__Category__: [Comparison operations](#global-function-Comparison-operations-index) + + +### `gtr(version, range, options)` ⇒ `boolean` [source code](./src/semver-range-ops.mjs#L52) [global index](#global-function-index) + +Returns `true` if `version` is greater than is greater than any version in `range`, `false` otherwise. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to check. | +| `range` | `string` | The range to check. | +| `options` | `object` | The options to pass to the semver.gtr function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if `version` is greater than is greater than any version in `range`, `false` otherwise. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `intersects(range, options)` ⇒ `boolean` [source code](./src/semver-range-ops.mjs#L89) [global index](#global-function-index) + +Returns `true` if any of the comparators in the range intersect with each other. + + +| Param | Type | Description | +| --- | --- | --- | +| `range` | `string` | The first version range or comparator. | +| `options` | `object` | The options to pass to the semver.intersects function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if any of the comparators in the range intersect with each other, `false` otherwise. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `ltr(version, range, options)` ⇒ `boolean` [source code](./src/semver-range-ops.mjs#L64) [global index](#global-function-index) + +Returns `true` if `version` is less than is less than any version in `range`, `false` otherwise. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to check. | +| `range` | `string` | The range to check. | +| `options` | `object` | The options to pass to the semver.ltr function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if `version` is less than is less than any version in `range`, `false` otherwise. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `maxSatisfying(versions, range, options)` ⇒ `string` \| `null` [source code](./src/max-satisfying.mjs#L26) [global index](#global-function-index) + +Returns the highest version in `versions` that satisfies the range, or null if no version satisfies the range. This +implementation differs from the base `semver.maxSatisfying` in that it correctly handles the following case: +`maxSatisfying(['1.0.0-alpha.0', '1.0.0'], '<1.0.0')` -> '1.0.0-alpha.0'. The base `semver.maxSatisfying` function +returns `null` in this case. Note, support for this syntax is not comprehensive and more complicated expressions are +likely to yield incorrect results. + + +| Param | Type | Description | +| --- | --- | --- | +| `versions` | `Array.` | The versions to check. | +| `range` | `string` | The range to check. | +| `options` | `object` | The options to pass to the semver.maxSatisfying function. | +| `options.compat` | `boolean` | If true, then uses the base `semver.maxSatisfying` function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | +| `options.includePrerelease` | `boolean` | Include prerelease versions in the result. This is effectively implied if the range contains prerelease components. | +| `options.throwIfInvalid` | `boolean` | If true, throws an exception if any version or the range is invalid. | + +**Returns**: `string` \| `null` - - The highest version that satisfies the range, or null if no version satisfies the range. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `minSatisfying(versions, range, options)` ⇒ `string` \| `null` [source code](./src/semver-range-ops.mjs#L40) [global index](#global-function-index) + +Returns the lowest version in `versions` that satisfies the range, or null if no version satisfies the range. + + +| Param | Type | Description | +| --- | --- | --- | +| `versions` | `Array.` | The versions to check. | +| `range` | `string` | The range to check. | +| `options` | `object` | The options to pass to the semver.minSatisfying function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `string` \| `null` - - The lowest version that satisfies the range, or null if no version satisfies the range. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `minVersion(range, options)` ⇒ `string` \| `null` [source code](./src/min-version.mjs#L19) [global index](#global-function-index) + +Returns the lowest version that satisfies the range, or null if no version satisfies the range. This implementation +differs from the base `semver.minVersion` in that it correctly handles simple prerelease x-ranges. E.g., +'1.0.0-alpha.x' -> '1.0.0-alpha.0'. To suppress this behavior, pass `options.compat = true`. Note, support for this +syntax is not comprehensive and more complicated expressions are likely to yield incorrect results. + + +| Param | Type | Description | +| --- | --- | --- | +| `range` | `string` | The range to check. | +| `options` | `object` | The options to pass to the semver.minVersion function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | +| `options.includePrerelease` | `boolean` | Whether to include prerelease versions. | +| `options.compat` | `boolean` | If true, prerelease ranges are treated the same as in the base semver package; e.g. `minVersion('1.0.0-alpha.x', { compat: true })` -> '1.0.0-alpha.x'. | + +**Returns**: `string` \| `null` - - The lowest version that satisfies the range, or null if no version satisfies the range. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `outside(version, range, direction, options)` ⇒ `boolean` [source code](./src/semver-range-ops.mjs#L78) [global index](#global-function-index) + +Returns `true` if `version` is outside of `range` in the indicated direction, `false` otherwise. `outside(v, r, '>)` +is equivalent to `gtr(v, r)`. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to check. | +| `range` | `string` | The range to check. | +| `direction` | `string` | The direction to check. Must be '>' or '<'. | +| `options` | `object` | The options to pass to the semver.outside function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if `version` is outside of `range` in the indicated direction, `false` otherwise. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `satisfies(version, range, options)` ⇒ `boolean` [source code](./src/semver-range-ops.mjs#L28) [global index](#global-function-index) + +Returns `true` if the version satisfies the range, `false` otherwise. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to check. | +| `range` | `string` | The range to check. | +| `options` | `object` | The options to pass to the semver.satisfies function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if the version satisfies the range, `false` otherwise. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `simplifyRange(versions, range, options)` ⇒ `string` [source code](./src/semver-range-ops.mjs#L105) [global index](#global-function-index) + +Return a "simplified" range that matches the same items in the versions list as the range specified. Note that it +does not guarantee that it would match the same versions in all cases, only for the set of versions provided. This +is useful when generating ranges by joining together multiple versions with || programmatically, to provide the user +with something a bit more ergonomic. If the provided range is shorter in string-length than the generated range, +then that is returned. + + +| Param | Type | Description | +| --- | --- | --- | +| `versions` | `Array.` | The versions to check. | +| `range` | `string` | The range to simplify. | +| `options` | `object` | The options to pass to the semver.simplifyRange function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `string` - - The simplified range. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `subset(subRange, superRange, options)` ⇒ `boolean` [source code](./src/semver-range-ops.mjs#L117) [global index](#global-function-index) + +Returns `true` if `subRange` is a subset of `superRange`, `false` otherwise. + + +| Param | Type | Description | +| --- | --- | --- | +| `subRange` | `string` | The sub-range to check. | +| `superRange` | `string` | The super-range to check. | +| `options` | `object` | The options to pass to the semver.subset function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `boolean` - - `true` if `subRange` is a subset of `superRange`, `false` otherwise. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `upperBound(range)` ⇒ `string` [source code](./src/upper-bound.mjs#L11) [global index](#global-function-index) + +Finds the ceiling of a range. For '*', the ceiling is '*'. For specific versions or any range capped by a specific +version, the ceiling is that version. For any open-ended range, the ceiling is defined by a '-0' range +function where 'verision-0' least range above the given range. + + +| Param | Type | Description | +| --- | --- | --- | +| `range` | `string` | The range to find the ceiling for. | + +**Returns**: `string` - - Either '*', a specific version, or a '-0'. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `validRange(range, options)` ⇒ `string` \| `null` [source code](./src/semver-range-ops.mjs#L16) [global index](#global-function-index) + +Returns a parsed, normalized range string or null if the range is invalid. + + +| Param | Type | Description | +| --- | --- | --- | +| `range` | `string` | The range to parse. | +| `options` | `object` | The options to pass to the semver.validRange function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `string` \| `null` - - The parsed, normalized range string or null if the range is invalid. + +__Category__: [Range operations](#global-function-Range-operations-index) + + +### `validVersionOrRange(input, options)` ⇒ `string` \| `null` [source code](./src/valid-version-or-range.mjs#L20) [global index](#global-function-index) + +Validates a string to be a valid version or range. By default, an exception is raised unless `options.disallowVersions` +is `true`, in which case it filters out invalid strings and returns a new array. + + +| Param | Type | Description | +| --- | --- | --- | +| `input` | `string` | The string to validate. | +| `options` | `object` | The options to pass to the semver.valid function. | +| `options.disallowRanges` | `boolean` | Whether to disallow ranges. | +| `options.disallowVersions` | `boolean` | Whether to disallow versions and ranges which are valid versions. | +| `options.includePrerelease` | `boolean` | Whether to include prerelease versions. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | +| `options.onlyXRange` | `boolean` | Whether to only allow x-ranges. Note, even if this is `true`, and a valid x-range is presented, the returned string will still be normalized. | +| `options.throwIfInvalid` | `boolean` | If true, throws an exception if the string is invalid. | + +**Returns**: `string` \| `null` - - A normalized version or range string or `null` if the string is invalid (if +`options.throwIfInvalid` is `true`, in which case an exception is thrown). + + +### `clean(version, options)` ⇒ `string` \| `null` [source code](./src/semver-version-ops.mjs#L109) [global index](#global-function-index) + +Returns a cleaned version string removing unecessary comparators and, if `options.loose` is true, fixing space +issues. Only works for versions, not ranges. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to clean. | +| `options` | `object` | The options to pass to the semver.clean function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `string` \| `null` - - The cleaned version string or null if the version is invalid. + +__Category__: [Version operations](#global-function-Version-operations-index) + + +### `coerce(version, options)` ⇒ `string` \| `null` [source code](./src/semver-version-ops.mjs#L97) [global index](#global-function-index) + +Aggressively attempts to coerce a string into a valid semver string. Basically, starting from the left side of the +string, it looks for a digit and then includes anything to the right of the digit that looks like part of a semver. +So, 'Number 1!' -> '1.0.0', 'Upgrade 1.2 to 1.3' -> '1.2.0', etc. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to coerce. | +| `options` | `object` | The options to pass to the semver.coerce function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | +| `options.includePrerelease` | `boolean` | Unless true, prerelease tags (and build metadata) are stripped. If | +| `options.rtl` | `boolean` | Instead of searching for a digit from the left, start searching from the right. true, then they are preserved. | + +**Returns**: `string` \| `null` - - The coerced version string or null if the version is invalid. + +__Category__: [Version operations](#global-function-Version-operations-index) + + +### `inc(version, increment, options, identifier, identifierBase)` ⇒ `string` [source code](./src/semver-version-ops.mjs#L29) [global index](#global-function-index) + +Returns a new version string incremented by the specified part. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to increment. | +| `increment` | `string` | The increment to use. | +| `options` | `object` | The options to pass to the semver.inc function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | +| `options.includePrerelease` | `boolean` | Whether to include prerelease versions. | +| `identifier` | `string` | Used to specify the prerelease name for prerelease increments. | +| `identifierBase` | `false` \| `0` \| `1` | When incrementing to a new prerelease name, specifies the base number or `false` for no number. | + +**Returns**: `string` - - The new version string. + +__Category__: [Version operations](#global-function-Version-operations-index) + + +### `major(version, options)` ⇒ `number` [source code](./src/semver-version-ops.mjs#L52) [global index](#global-function-index) + +Returns the major version number. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to parse. | +| `options` | `object` | The options to pass to the semver.major function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `number` - - The major version number. + +__Category__: [Version operations](#global-function-Version-operations-index) + + +### `minor(version, options)` ⇒ `number` [source code](./src/semver-version-ops.mjs#L63) [global index](#global-function-index) + +Returns the minor version number. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to parse. | +| `options` | `object` | The options to pass to the semver.minor function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `number` - - The minor version number. + +__Category__: [Version operations](#global-function-Version-operations-index) + + +### `nextVersion(currVer, increment)` ⇒ `string` [source code](./src/next-version.mjs#L23) [global index](#global-function-index) + +Given a current version generates the next version string accourding to `increment`. This method is similar to +[inc](#inc) from the [base semver library](https://semver.org) with two differences. +1) It supports a specific pre-release sequence prototype -> 'alpha' -> 'beta' -> 'rc' -> released and the 'pretype' + increment will advance the prerelease ID through these stages. +2) The 'prerelease' increment can only be used to advance the prerelease version number and does not function as + 'prepatch' on a non-prerelease version. + + +| Param | Type | Description | +| --- | --- | --- | +| `currVer` | `string` | The current version to increment. | +| `increment` | `string` | The increment to use. | + +**Returns**: `string` - - The next version string. + +__Category__: [Version operations](#global-function-Version-operations-index) + + +### `parse()` [source code](./src/semver-version-ops.mjs#L81) [global index](#global-function-index) + +Attempts to parse and normalize a string as a semver string. An aliase for [valid](#valid). + +__Category__: [Version operations](#global-function-Version-operations-index) + + +### `patch(version, options)` ⇒ `number` [source code](./src/semver-version-ops.mjs#L74) [global index](#global-function-index) + +Returns the patch version number. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to parse. | +| `options` | `object` | The options to pass to the semver.patch function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `number` - - The patch version number. + +__Category__: [Version operations](#global-function-Version-operations-index) + + +### `prerelease(version, options)` ⇒ `Array.` \| `null` [source code](./src/semver-version-ops.mjs#L41) [global index](#global-function-index) + +Returns an array of prerelease components or `null` if the version is not a prerelease. A 'component' is just a '.' +separated string. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to parse. | +| `options` | `object` | The options to pass to the semver.inc function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | + +**Returns**: `Array.` \| `null` - - The prerelease components or `null` if the version is not a prerelease. + +__Category__: [Version operations](#global-function-Version-operations-index) + + +### `valid(version, options)` ⇒ `string` \| `null` [source code](./src/semver-version-ops.mjs#L13) [global index](#global-function-index) + +Returns a parsed, normalized version string or null if the version is invalid. + + +| Param | Type | Description | +| --- | --- | --- | +| `version` | `string` | The version to parse. | +| `options` | `object` | The options to pass to the semver.valid function. | +| `options.loose` | `boolean` | Allow non-conforming, but recognizable semver strings. | +| `options.includePrerelease` | `boolean` | Whether to include prerelease versions. | + +**Returns**: `string` \| `null` - - The parsed, normalized version string or null if the version is invalid. + +__Category__: [Version operations](#global-function-Version-operations-index) + diff --git a/jsdoc.config.json b/jsdoc.config.json new file mode 100644 index 0000000..ade88fc --- /dev/null +++ b/jsdoc.config.json @@ -0,0 +1,17 @@ +{ + "plugins": ["dmd-readme-api"], + "recurseDepth": 10, + "source": { + "includePattern": ".+\\.(c|m)?js(doc|x)?$", + "excludePattern": "((^|\\/|\\\\)_|.+\\.test\\.(c|m)?jsx?$)" + }, + "sourceType": "module", + "tags": { + "allowUnknownTags": true, + "dictionaries": ["jsdoc","closure"] + }, + "templates": { + "cleverLinks": true, + "monospaceLinks": true + } +} \ No newline at end of file diff --git a/make/55-readme-md.mk b/make/55-readme-md.mk new file mode 100644 index 0000000..da86b5a --- /dev/null +++ b/make/55-readme-md.mk @@ -0,0 +1,15 @@ +README_MD:=README.md +README_MD_SRC:=$(shell find $(SRC)/doc -name "*.md") $(SDLC_ALL_NON_TEST_JS_FILES_SRC) +BUILD_TARGETS+=$(README_MD) + +$(README_MD): $(README_MD_SRC) + cp $(SRC)/doc/README.01.md $@ + npx jsdoc2md \ + --files 'src/**/*' \ + --plugin dmd-readme-api \ + --global-index-format grouped \ + --name-format \ + --configure jsdoc.config.json \ + --no-cache \ + >> $@ + cat $(SRC)/doc/README.02.md >> $@ \ No newline at end of file diff --git a/make/95-final-targets.mk b/make/95-final-targets.mk index 66ccb79..eb14f4f 100644 --- a/make/95-final-targets.mk +++ b/make/95-final-targets.mk @@ -2,26 +2,27 @@ # to https://npmjs.com/package/@liquid-labs/sdlc-projects-workflow-node-build for # further details +.DEFAULT_GOAL:=all + .PRECIOUS: $(PRECIOUS_TARGETS) build: $(BUILD_TARGETS) +.PHONY+= build -PHONY_TARGETS+=build +all: build doc +.PHONY+= all -all: build +doc: $(DOC_TARGETS) +.PHONY+= doc lint: $(LINT_TARGETS) - lint-fix: $(LINT_FIX_TARGETS) - -PHONY_TARGETS+=lint lint-fix +.PHONY+=lint lint-fix test: $(TEST_TARGETS) - -PHONY_TARGETS+= test +.PHONY+= test qa: test lint +.PHONY+=qa -PHONY_TARGETS+=qa - -.PHONY: $(PHONY_TARGETS) +.PHONY: $(.PHONY) diff --git a/package-lock.json b/package-lock.json index 0043893..e2b5dd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,12 +10,15 @@ "license": "UNLICENSED", "dependencies": { "http-errors": "^2.0.0", - "semver": "^7.3.8" + "semver": "^7.0.0" }, "devDependencies": { + "@liquid-labs/dmd": "^7.1.1", + "@liquid-labs/jsdoc-to-markdown": "^9.1.3", "@liquid-labs/sdlc-resource-babel-and-rollup": "^1.0.0-alpha.5", "@liquid-labs/sdlc-resource-eslint": "^1.0.0-alpha.2", - "@liquid-labs/sdlc-resource-jest": "^1.0.0-alpha.5" + "@liquid-labs/sdlc-resource-jest": "^1.0.0-alpha.5", + "dmd-readme-api": "^1.0.0-beta.8" }, "engines": { "node": ">=18.0.0" @@ -2844,6 +2847,78 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsdoc/salty": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", + "integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, + "node_modules/@liquid-labs/dmd": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@liquid-labs/dmd/-/dmd-7.1.1.tgz", + "integrity": "sha512-J8CKJBwYtGDNwmbUENCamFw+H6OAaaYHIlBS0h2OBNSgECZXU2+hIF+STE2irJTO2nzmuc+De7es7/QcWID58w==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "cache-point": "^3.0.0", + "common-sequence": "^3.0.0", + "file-set": "^5.2.2", + "handlebars": "^4.7.8", + "marked": "^4.3.0", + "regex-repo": "^5.0.1", + "walk-back": "^5.1.1" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "^0.1.10" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/@liquid-labs/jsdoc-to-markdown": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@liquid-labs/jsdoc-to-markdown/-/jsdoc-to-markdown-9.1.3.tgz", + "integrity": "sha512-NjiqezNbmDLttbUkbJyoglA/Y5ZQrs1HuVBn98UYmuQ9zosb59W71QBoVdAL3kZEy6nLbK2Y80Q1EXGdVdyJMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@liquid-labs/dmd": "^7.1.1", + "array-back": "^6.2.2", + "command-line-args": "^6.0.1", + "command-line-usage": "^7.0.3", + "config-master": "^3.1.0", + "jsdoc-api": "^9.3.5", + "jsdoc-parse": "^6.2.5", + "walk-back": "^5.1.1" + }, + "bin": { + "jsdoc2md": "bin/cli.js" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "^0.1.10" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, "node_modules/@liquid-labs/sdlc-resource-babel-and-rollup": { "version": "1.0.0-alpha.5", "resolved": "https://registry.npmjs.org/@liquid-labs/sdlc-resource-babel-and-rollup/-/sdlc-resource-babel-and-rollup-1.0.0-alpha.5.tgz", @@ -3447,6 +3522,31 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.10.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz", @@ -3579,6 +3679,16 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", @@ -3976,6 +4086,13 @@ "node": ">=8" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3987,12 +4104,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -4067,6 +4185,27 @@ "semver": "^7.0.0" } }, + "node_modules/cache-point": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-3.0.1.tgz", + "integrity": "sha512-itTIMLEKbh6Dw5DruXbxAgcyLnh/oPGVLBfTPqBOftASxHe8bAeXy7JkO4F0LvHqht7XqP5O/09h5UcHS2w0FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -4127,6 +4266,19 @@ } ] }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -4141,6 +4293,98 @@ "node": ">=4" } }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/chalk-template/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/chalk-template/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk-template/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -4244,6 +4488,46 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/command-line-args": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", + "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "find-replace": "^5.0.2", + "lodash.camelcase": "^4.3.0", + "typical": "^7.2.0" + }, + "engines": { + "node": ">=12.20" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/command-line-usage": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -4259,6 +4543,16 @@ "integrity": "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==", "dev": true }, + "node_modules/common-sequence": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-3.0.0.tgz", + "integrity": "sha512-g/CgSYk93y+a1IKm50tKl7kaT/OjjTYVQlEbUlt/49ZLV1mcKpUU7iyDiqTAeLdb4QDtQfq3ako8y8v//fzrWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -4271,6 +4565,26 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/config-master": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", + "integrity": "sha512-n7LBL1zBzYdTpF1mx5DNcZnZn05CWIdsdvtPL4MosvqbBUK3Rq6VWEtGUuF3Y0s9/CIhMejezqlSkP6TnCJ/9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "walk-back": "^2.0.1" + } + }, + "node_modules/config-master/node_modules/walk-back": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", + "integrity": "sha512-Nb6GvBR8UWX1D+Le+xUq0+Q1kFmRBIWVrfLnQAOmcpEzA9oAxwJ9gIr36t9TWYfzvWRvuMtjHiVsJYEkXWaTAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4395,6 +4709,16 @@ "node": ">= 8" } }, + "node_modules/current-module-paths": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/current-module-paths/-/current-module-paths-1.1.2.tgz", + "integrity": "sha512-H4s4arcLx/ugbu1XkkgSvcUZax0L6tXUqnppGniQb8l5VjUKGHoayXE5RiriiPhYDd+kjZnaok1Uig13PKtKYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4507,6 +4831,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dmd-readme-api": { + "version": "1.0.0-beta.8", + "resolved": "https://registry.npmjs.org/dmd-readme-api/-/dmd-readme-api-1.0.0-beta.8.tgz", + "integrity": "sha512-KocOTjjhavStJ5efGIgCmrrAMm/vuVgeJXgF9r+VAlQXQ5tQligdvcJN28RYYeR9Kk0zqBBkMk4zUiBIzPJplg==", + "dev": true, + "license": "Apache 2.0", + "dependencies": { + "extract-topic": "^1.0.0-beta.6" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -4543,6 +4877,19 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -5393,12 +5740,39 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/extract-topic": { + "version": "1.0.0-beta.6", + "resolved": "https://registry.npmjs.org/extract-topic/-/extract-topic-1.0.0-beta.6.tgz", + "integrity": "sha512-ZvOzCjllNDigyhdX9s+CsChwfyOuk2TlvFmzWWNhwK131Y4dv9A9dhu3jfRln7d+Pgnm6kEL76/iM+PLtUn4ow==", + "dev": true, + "license": "UNLICENSED", + "dependencies": { + "he": "^1.2.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -5441,11 +5815,34 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-set": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-5.3.0.tgz", + "integrity": "sha512-FKCxdjLX0J6zqTWdT0RXIxNF/n7MyXXnsSUp0syLEOCKdexvPZ02lNNv2a+gpK9E3hzUYF3+eFZe32ci7goNUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "fast-glob": "^3.3.2" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5453,6 +5850,24 @@ "node": ">=8" } }, + "node_modules/find-replace": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", + "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -5667,7 +6082,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "optional": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -5723,6 +6137,28 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -5804,6 +6240,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -6093,6 +6539,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -8004,6 +8451,111 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.5.tgz", + "integrity": "sha512-P4C6MWP9yIlMiK8nwoZvxN84vb6MsnXcHuy7XzVOvQoCizWX5JFCBsWIIWKXBltpoRZXddUOVQmCTOZt9yDj9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^14.1.1", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc-api": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-9.3.5.tgz", + "integrity": "sha512-TQwh1jA8xtCkIbVwm/XA3vDRAa5JjydyKx1cC413Sh3WohDFxcMdwKSvn4LOsq2xWyAmOU/VnSChTQf6EF0R8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "cache-point": "^3.0.1", + "current-module-paths": "^1.1.2", + "file-set": "^5.3.0", + "jsdoc": "^4.0.4", + "object-to-spawn-args": "^2.0.1", + "walk-back": "^5.1.1" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, + "node_modules/jsdoc-parse": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.2.5.tgz", + "integrity": "sha512-8JaSNjPLr2IuEY4Das1KM6Z4oLHZYUnjRrr27hKSa78Cj0i5Lur3DzNnCkz+DfrKBDoljGMoWOiBVQbtUZJBPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "find-replace": "^5.0.1", + "sort-array": "^5.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jsdoc/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -8046,6 +8598,16 @@ "node": ">=6" } }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -8083,6 +8645,16 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -8101,6 +8673,13 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -8165,19 +8744,86 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "license": "Unlicense", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -8262,6 +8908,13 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -8313,6 +8966,16 @@ "node": ">= 0.4" } }, + "node_modules/object-to-spawn-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.1.tgz", + "integrity": "sha512-6FuKFQ39cOID+BMZ3QaphcC8Y4cw6LXBLyIgPU+OhIYwviJamPAn+4mITapnSBQrejB+NNp+FMskhD8Cq+Ys3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/object.assign": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", @@ -8634,6 +9297,16 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", @@ -8731,6 +9404,16 @@ "@babel/runtime": "^7.8.4" } }, + "node_modules/regex-repo": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/regex-repo/-/regex-repo-5.2.0.tgz", + "integrity": "sha512-uctdIywmzsW9Rl64m53qI+UCzaQXQ7boasV/dJgIIdP8mkiS2MOMpfpViCHMZTIFeJohN0p5nYIAzJAeI0Bt5A==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "14.x || 16.x || >=18.0.0" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", @@ -8807,6 +9490,16 @@ "node": ">=0.10.0" } }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, "node_modules/resolve": { "version": "1.22.6", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", @@ -9046,12 +9739,10 @@ } }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -9059,22 +9750,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/serialize-javascript": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", @@ -9165,6 +9840,28 @@ "integrity": "sha512-9LK+E7Hv5R9u4g4C3p+jjLstaLe11MDsL21UpYaCNmapvMkYhqCV4A/f/3gyH8QjMyh6l68q9xC85vihY9ahMQ==", "dev": true }, + "node_modules/sort-array": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-5.1.1.tgz", + "integrity": "sha512-EltS7AIsNlAFIM9cayrgKrM6XP94ATWwXP4LCL4IQbvbYhELSt2hZTrixg+AaQwnWFs/JGJgqU3rxMcNNWxGAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "typical": "^7.1.1" + }, + "engines": { + "node": ">=12.17" + }, + "peerDependencies": { + "@75lb/nature": "^0.1.1" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9416,6 +10113,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/table-layout": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" + }, + "engines": { + "node": ">=12.17" + } + }, "node_modules/terser": { "version": "5.21.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.21.0.tgz", @@ -9492,6 +10203,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -9638,6 +10350,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typical": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -9653,6 +10396,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true, + "license": "MIT" + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -9752,6 +10502,16 @@ "node": ">=10.12.0" } }, + "node_modules/walk-back": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.1.tgz", + "integrity": "sha512-e/FRLDVdZQWFrAzU6Hdvpm7D7m2ina833gIKLptQykRK49mmCYHLHq7UqjPDbxbKLZkTkW1rFqbengdE3sLfdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -9811,6 +10571,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wordwrapjs": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.1.tgz", + "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -9880,6 +10657,13 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 80fe9ca..bd99db6 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,11 @@ "main": "dist/semver-plus.js", "scripts": { "build": "make build", + "doc": "make doc", "lint": "make lint", "lint:fix": "make lint-fix", "test": "make test", - "preversion": "npm run qa", + "preversion": "npm run qa && npm run doc", "prepack": "npm run build", "qa": "make qa" }, @@ -27,16 +28,19 @@ }, "homepage": "https://github.com/liquid-labs/semver-plus#readme", "devDependencies": { + "@liquid-labs/dmd": "^7.1.1", + "@liquid-labs/jsdoc-to-markdown": "^9.1.3", "@liquid-labs/sdlc-resource-babel-and-rollup": "^1.0.0-alpha.5", "@liquid-labs/sdlc-resource-eslint": "^1.0.0-alpha.2", - "@liquid-labs/sdlc-resource-jest": "^1.0.0-alpha.5" + "@liquid-labs/sdlc-resource-jest": "^1.0.0-alpha.5", + "dmd-readme-api": "^1.0.0-beta.8" }, "liq": { "packageType": "node|lib" }, "dependencies": { "http-errors": "^2.0.0", - "semver": "^7.3.8" + "semver": "^7.0.0" }, "_npm-check-plus": { "depcheck": { diff --git a/src/constants.mjs b/src/constants.mjs index 94ff706..7b65167 100644 --- a/src/constants.mjs +++ b/src/constants.mjs @@ -1,5 +1,5 @@ export const xRangeRE = // v single tuple v doublpe tuple v triple tuple v quad/pre-release tuple // v if there's more than a single digit, it can't lead with a zero - /^(?:[x*]|[1-9]?[0-9]+\.[x*]|(?:[1-9]?[0-9]+\.){2}[x*]|(?:[1-9]?[0-9]+\.){2}[1-9]?[0-9]+-(?:alpha|beta|rc)\.[x*])$/ -export const prereleaseXRangeRE = /^(?:[1-9]?[0-9]+\.){2}[1-9]?[0-9]+-(?:alpha|beta|rc)\.[x*]$/ + /^(?:[Xx*]|[1-9]?[0-9]+\.[Xx*]|(?:[1-9]?[0-9]+\.){2}[Xx*]|(?:[1-9]?[0-9]+\.){2}[1-9]?[0-9]+-(?:alpha|beta|rc)\.[Xx*])$/ +export const prereleaseXRangeRE = /^(?:[1-9]?[0-9]+\.){2}[1-9]?[0-9]+-(?:alpha|beta|rc)\.[Xx*]$/ diff --git a/src/doc/README.01.md b/src/doc/README.01.md new file mode 100644 index 0000000..3ac5dff --- /dev/null +++ b/src/doc/README.01.md @@ -0,0 +1 @@ +# semver-plus diff --git a/src/doc/README.02.md b/src/doc/README.02.md new file mode 100644 index 0000000..e69de29 diff --git a/src/filter-valid-version-or-range.mjs b/src/filter-valid-version-or-range.mjs deleted file mode 100644 index 6d31fa1..0000000 --- a/src/filter-valid-version-or-range.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { validVersionOrRange } from './valid-version-or-range' - -const filterValidVersionOrRange = (input = throw new Error("'input' is required."), options) => - input.filter((i) => validVersionOrRange(i, options)) - -export { filterValidVersionOrRange } diff --git a/src/index.js b/src/index.js index 6468376..913a643 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,11 @@ export * from './ext-constants' -export * from './filter-valid-version-or-range' +export * from './min-version' +export * from './max-satisfying' export * from './min-version' export * from './next-version' -export * from './prerelease' +export * from './semver-comparison-ops' +export * from './semver-range-ops' +export * from './semver-version-ops' export * from './upper-bound' export * from './valid-version-or-range' -export * from './version-compare' export * from './x-sort' diff --git a/src/lib/compare-helper.mjs b/src/lib/compare-helper.mjs new file mode 100644 index 0000000..1300fe6 --- /dev/null +++ b/src/lib/compare-helper.mjs @@ -0,0 +1,16 @@ +const compareHelper = (versions, semverTest) => { + let currLead + for (let i = 0; i < versions.length; i += 1) { + const testVer = versions[i] + if (currLead === undefined) { + currLead = testVer + } + else if (semverTest(currLead, testVer)) { + currLead = testVer + } + } + + return currLead +} + +export { compareHelper } diff --git a/src/lib/set-default-options.mjs b/src/lib/set-default-options.mjs new file mode 100644 index 0000000..39804be --- /dev/null +++ b/src/lib/set-default-options.mjs @@ -0,0 +1,12 @@ +const setDefaultOptions = (options = {}) => { + if (!('includePrerelease' in options) + && (process.env.SEMVER_PLUS_COMPAT === undefined + || process.env.SEMVER_PLUS_COMPAT.toLocaleLowerCase() === 'false' + || process.env.SEMVER_PLUS_COMPAT === '0')) { + options.includePrerelease = true + } + + return options +} + +export { setDefaultOptions } diff --git a/src/max-satisfying.mjs b/src/max-satisfying.mjs new file mode 100644 index 0000000..f1aa27a --- /dev/null +++ b/src/max-satisfying.mjs @@ -0,0 +1,44 @@ +import semver from 'semver' + +import { rsort } from './semver-comparison-ops' +import { satisfies } from './semver-range-ops' +import { validVersionOrRange } from './valid-version-or-range' + +/** + * Returns the highest version in `versions` that satisfies the range, or null if no version satisfies the range. This + * implementation differs from the base `semver.maxSatisfying` in that it correctly handles the following case: + * `maxSatisfying(['1.0.0-alpha.0', '1.0.0'], '<1.0.0')` -> '1.0.0-alpha.0'. The base `semver.maxSatisfying` function + * returns `null` in this case. Note, support for this syntax is not comprehensive and more complicated expressions are + * likely to yield incorrect results. + * @param {string[]} versions - The versions to check. + * @param {string} range - The range to check. + * @param {object} options - The options to pass to the semver.maxSatisfying function. + * @param {boolean} options.compat - If true, then uses the base `semver.maxSatisfying` function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @param {boolean} options.includePrerelease - Include prerelease versions in the result. This is effectively implied + * if the range contains prerelease components. + * @param {boolean} options.throwIfInvalid - If true, throws an exception if any version or the range is invalid. + * @returns {string|null} - The highest version that satisfies the range, or null if no version satisfies the range. + * @category Range operations + * @function + */ +// export const maxSatisfying = semver.maxSatisfying +export const maxSatisfying = (versions, range, options) => { + if (options?.throwIfInvalid === true) { + validVersionOrRange(versions, { ...options, disallowRanges : true }) + validVersionOrRange([range], { ...options, disallowVersions : true }) + } + + if (options?.compat === true) { + return semver.maxSatisfying(versions, range, options) + } + + versions = rsort(versions, options) + + for (const version of versions) { + if (satisfies(version, range, options)) { + return version + } + } + return null +} diff --git a/src/min-version.mjs b/src/min-version.mjs index 801f87d..fe576b4 100644 --- a/src/min-version.mjs +++ b/src/min-version.mjs @@ -2,20 +2,27 @@ import semver from 'semver' import { prereleaseXRangeRE } from './constants' -const minVersion = (range) => { - // this has to come first because weirds, 'semver.validRange' recognizes 1.0.0-alpha.x (but not '.*'?!), but - // 'semver.minVersion' does not return correct results - if (range.match(prereleaseXRangeRE)) { +/** + * Returns the lowest version that satisfies the range, or null if no version satisfies the range. This implementation + * differs from the base `semver.minVersion` in that it correctly handles simple prerelease x-ranges. E.g., + * '1.0.0-alpha.x' -> '1.0.0-alpha.0'. To suppress this behavior, pass `options.compat = true`. Note, support for this + * syntax is not comprehensive and more complicated expressions are likely to yield incorrect results. + * @param {string} range - The range to check. + * @param {object} options - The options to pass to the semver.minVersion function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @param {boolean} options.includePrerelease - Whether to include prerelease versions. + * @param {boolean} options.compat - If true, prerelease ranges are treated the same as in the base semver package; + * e.g. `minVersion('1.0.0-alpha.x', { compat: true })` -> '1.0.0-alpha.x'. + * @returns {string|null} - The lowest version that satisfies the range, or null if no version satisfies the range. + * @category Range operations + */ +const minVersion = (range, options) => { + // we have to do this first, because semver does not recognize prerelease X-ranges ending with '*' (even though it + // does recognize these as valid ranges) + if (options?.compat !== true && range.match(prereleaseXRangeRE)) { return range.slice(0, -1) + '0' } - else { - const semverRange = semver.validRange(range) - if (semverRange !== null) { - return semver.minVersion(semverRange).version - } - } - // else - throw new Error(`Invaid range '${range}'.`) + return semver.minVersion(range, options) } export { minVersion } diff --git a/src/next-version.mjs b/src/next-version.mjs index f4db279..d579234 100644 --- a/src/next-version.mjs +++ b/src/next-version.mjs @@ -9,17 +9,16 @@ import { } from './ext-constants' /** - * Given a current version, increment generates the next version string. This method follows the - * [semver spec](https://semver.org) except that: - * - `increment` 'prerelease' cannot be used with non-prerelease versions (results in execption) - * - supports 'pretype' and specific pre-release sequence 'alpha' -> 'beta' -> 'rc' -> released - * - * #### Parameters - * - `currVer`: the current version to increment. - * - `increment`: what to inrement; may be: 'major', 'minor', 'patch', 'premajor', 'preminor', 'prepatch', - * 'prerelease', or 'pretype'. The first seven are defined by the [semver spec](https://semver.org) and 'pretype' - * means to increment the pre-release ID from 'alpha' -> 'beta' -> 'rc' -> none. Passing in a `currVer` with any - * other prerelease ID with a 'pretype' increment will result in undefined results + * Given a current version generates the next version string accourding to `increment`. This method is similar to + * {@link inc} from the [base semver library](https://semver.org) with two differences. + * 1) It supports a specific pre-release sequence prototype -> 'alpha' -> 'beta' -> 'rc' -> released and the 'pretype' + * increment will advance the prerelease ID through these stages. + * 2) The 'prerelease' increment can only be used to advance the prerelease version number and does not function as + * 'prepatch' on a non-prerelease version. + * @param {string} currVer - The current version to increment. + * @param {string} increment - The increment to use. + * @returns {string} - The next version string. + * @category Version operations */ const nextVersion = (currVer, increment) => { if (!semver.valid(currVer)) { diff --git a/src/prerelease.mjs b/src/prerelease.mjs deleted file mode 100644 index 2f8c86d..0000000 --- a/src/prerelease.mjs +++ /dev/null @@ -1,7 +0,0 @@ -import semver from 'semver' - -const prerelease = (version) => { - return semver.prerelease(version) -} - -export { prerelease } diff --git a/src/semver-comparison-ops.mjs b/src/semver-comparison-ops.mjs new file mode 100644 index 0000000..88244d4 --- /dev/null +++ b/src/semver-comparison-ops.mjs @@ -0,0 +1,170 @@ +import semver from 'semver' + +/** + * Returns `true` if `v1` is greater than `v2`, `false` otherwise. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.gt function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if `v1` is greater than `v2`, `false` otherwise. + * @category Comparison operations + * @function + */ +export const gt = semver.gt + +/** + * Returns `true` if `v1` is greater than or equal to `v2`, `false` otherwise. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.gte function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if `v1` is greater than or equal to `v2`, `false` otherwise. + * @category Comparison operations + * @function + */ +export const gte = semver.gte + +/** + * Returns `true` if `v1` is less than `v2`, `false` otherwise. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.lt function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if `v1` is less than `v2`, `false` otherwise. + * @category Comparison operations + * @function + */ +export const lt = semver.lt + +/** + * Returns `true` if `v1` is less than or equal to `v2`, `false` otherwise. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.lte function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if `v1` is less than or equal to `v2`, `false` otherwise. + * @category Comparison operations + * @function + */ +export const lte = semver.lte + +/** + * Returns `true` if `v1` is equal to `v2`, `false` otherwise. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.eq function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if `v1` is equal to `v2`, `false` otherwise. + * @category Comparison operations + * @function + */ +export const eq = semver.eq + +/** + * Returns `true` if `v1` is not equal to `v2`, `false` otherwise. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.neq function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if `v1` is not equal to `v2`, `false` otherwise. + * @category Comparison operations + * @function + */ +export const neq = semver.neq + +/** + * Returns a number indicating whether a version is greater than, equal to, or less than another version. + * @param {string} v1 - The first version to compare. + * @param {string} comparator - The comparator to use. May be '<', '<=', '>', '>=', '=', '==', '!=', '===', or '!=='. + * An exception is thrown if an invalid comparator is provided. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.cmp function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {number} - A number indicating whether a version is greater than, equal to, or less than another version. + * @category Comparison operations + * @function + */ +export const cmp = semver.cmp + +/** + * Returns 0 if `v1 == v2`, 1 if `v1 > v2`, and -1 if `v1 < v2`. Will sort an array of versions in ascending order if + * passed to `Array.sort()`. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.compare function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {number} - 0 if `v1 == v2`, 1 if `v1 > v2`, and -1 if `v1 < v2`. + * @category Comparison operations + * @function + */ +export const compare = semver.compare + +/** + * Same as {@link compare} except it compares build if two versions are otherwise equal. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.compareBuild function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {number} - 0 if `v1 == v2`, 1 if `v1 > v2`, and -1 if `v1 < v2`. + * @category Comparison operations + * @function + */ +export const compareBuild = semver.compareBuild + +/** + * Reverse of {@link compare}. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.rcompare function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {number} - 0 if `v1 == v2`, -1 if `v1 > v2`, and 1 if `v1 < v2`. + * @category Comparison operations + * @function + */ +export const rcompare = semver.rcompare + +/** + * Short for {@link compare} with `options.loose = true`. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @returns {number} - 0 if `v1 == v2`, 1 if `v1 > v2`, and -1 if `v1 < v2`. + * @category Comparison operations + * @function + */ +export const compareLoose = semver.compareLoose + +/** + * Returns the difference between two versions. I.e., the most significant version component by which `v1` and `v2` + * differ. + * @param {string} v1 - The first version to compare. + * @param {string} v2 - The second version to compare. + * @param {object} options - The options to pass to the semver.diff function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {string|null} - `major`, 'minor', 'patch', 'prerelease', 'premajor', 'preminor', or 'prepatch' or null if + * the `v1` and `v2` are identical (disregarding build metadata). + * @category Comparison operations + * @function + */ +export const diff = semver.diff + +/** + * Sorts an array of versions in ascending order using {@link compareBuild}. + * @param {string[]} versions - The versions to sort. + * @param {object} options - The options to pass to the semver.sort function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {string[]} - The sorted versions. + * @category Comparison operations + * @function + */ +export const sort = semver.sort + +/** + * Sorts an array of versions in descending order using {@link compareBuild}. + * @param {string[]} versions - The versions to sort. + * @param {object} options - The options to pass to the semver.rsort function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {string[]} - The sorted versions. + * @category Comparison operations + * @function + */ +export const rsort = semver.rsort diff --git a/src/semver-range-ops.mjs b/src/semver-range-ops.mjs new file mode 100644 index 0000000..a55a9cd --- /dev/null +++ b/src/semver-range-ops.mjs @@ -0,0 +1,117 @@ +import semver from 'semver' + +// note: +// - `maxSatisfying` is overriden and defined in `max-satisfying.mjs` +// - `minVersion` is overriden and defined in `min-version.mjs` + +/** + * Returns a parsed, normalized range string or null if the range is invalid. + * @param {string} range - The range to parse. + * @param {object} options - The options to pass to the semver.validRange function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {string|null} - The parsed, normalized range string or null if the range is invalid. + * @category Range operations + * @function + */ +export const validRange = semver.validRange + +/** + * Returns `true` if the version satisfies the range, `false` otherwise. + * @param {string} version - The version to check. + * @param {string} range - The range to check. + * @param {object} options - The options to pass to the semver.satisfies function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if the version satisfies the range, `false` otherwise. + * @category Range operations + * @function + */ +export const satisfies = semver.satisfies + +/** + * Returns the lowest version in `versions` that satisfies the range, or null if no version satisfies the range. + * @param {string[]} versions - The versions to check. + * @param {string} range - The range to check. + * @param {object} options - The options to pass to the semver.minSatisfying function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {string|null} - The lowest version that satisfies the range, or null if no version satisfies the range. + * @category Range operations + * @function + */ +export const minSatisfying = semver.minSatisfying + +/** + * Returns `true` if `version` is greater than is greater than any version in `range`, `false` otherwise. + * @param {string} version - The version to check. + * @param {string} range - The range to check. + * @param {object} options - The options to pass to the semver.gtr function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if `version` is greater than is greater than any version in `range`, `false` otherwise. + * @category Range operations + * @function + */ +export const gtr = semver.gtr + +/** + * Returns `true` if `version` is less than is less than any version in `range`, `false` otherwise. + * @param {string} version - The version to check. + * @param {string} range - The range to check. + * @param {object} options - The options to pass to the semver.ltr function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if `version` is less than is less than any version in `range`, `false` otherwise. + * @category Range operations + * @function + */ +export const ltr = semver.ltr + +/** + * Returns `true` if `version` is outside of `range` in the indicated direction, `false` otherwise. `outside(v, r, '>)` + * is equivalent to `gtr(v, r)`. + * @param {string} version - The version to check. + * @param {string} range - The range to check. + * @param {string} direction - The direction to check. Must be '>' or '<'. + * @param {object} options - The options to pass to the semver.outside function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if `version` is outside of `range` in the indicated direction, `false` otherwise. + * @category Range operations + * @function + */ +export const outside = semver.outside + +/** + * Returns `true` if any of the comparators in the range intersect with each other. + * @param {string} range - The first version range or comparator. + * @param {object} options - The options to pass to the semver.intersects function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if any of the comparators in the range intersect with each other, `false` otherwise. + * @category Range operations + * @function + */ +export const intersects = semver.intersects + +/** + * Return a "simplified" range that matches the same items in the versions list as the range specified. Note that it + * does not guarantee that it would match the same versions in all cases, only for the set of versions provided. This + * is useful when generating ranges by joining together multiple versions with || programmatically, to provide the user + * with something a bit more ergonomic. If the provided range is shorter in string-length than the generated range, + * then that is returned. + * @param {string[]} versions - The versions to check. + * @param {string} range - The range to simplify. + * @param {object} options - The options to pass to the semver.simplifyRange function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {string} - The simplified range. + * @category Range operations + * @function + */ +export const simplifyRange = semver.simplifyRange + +/** + * Returns `true` if `subRange` is a subset of `superRange`, `false` otherwise. + * @param {string} subRange - The sub-range to check. + * @param {string} superRange - The super-range to check. + * @param {object} options - The options to pass to the semver.subset function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {boolean} - `true` if `subRange` is a subset of `superRange`, `false` otherwise. + * @category Range operations + * @function + */ +export const subset = semver.subset diff --git a/src/semver-version-ops.mjs b/src/semver-version-ops.mjs new file mode 100644 index 0000000..3d862a2 --- /dev/null +++ b/src/semver-version-ops.mjs @@ -0,0 +1,109 @@ +import semver from 'semver' + +/** + * Returns a parsed, normalized version string or null if the version is invalid. + * @param {string} version - The version to parse. + * @param {object} options - The options to pass to the semver.valid function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @param {boolean} options.includePrerelease - Whether to include prerelease versions. + * @returns {string|null} - The parsed, normalized version string or null if the version is invalid. + * @category Version operations + * @function + */ +export const valid = semver.valid + +/** + * Returns a new version string incremented by the specified part. + * @param {string} version - The version to increment. + * @param {string} increment - The increment to use. + * @param {object} options - The options to pass to the semver.inc function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @param {boolean} options.includePrerelease - Whether to include prerelease versions. + * @param {string} identifier - Used to specify the prerelease name for prerelease increments. + * @param {false|0|1} identifierBase - When incrementing to a new prerelease name, specifies the base number or + * `false` for no number. + * @returns {string} - The new version string. + * @category Version operations + * @function + */ +export const inc = semver.inc + +/** + * Returns an array of prerelease components or `null` if the version is not a prerelease. A 'component' is just a '.' + * separated string. + * @param {string} version - The version to parse. + * @param {object} options - The options to pass to the semver.inc function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {string[]|null} - The prerelease components or `null` if the version is not a prerelease. + * @category Version operations + * @function + */ +export const prerelease = semver.prerelease + +/** + * Returns the major version number. + * @param {string} version - The version to parse. + * @param {object} options - The options to pass to the semver.major function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {number} - The major version number. + * @category Version operations + * @function + */ +export const major = semver.major + +/** + * Returns the minor version number. + * @param {string} version - The version to parse. + * @param {object} options - The options to pass to the semver.minor function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {number} - The minor version number. + * @category Version operations + * @function + */ +export const minor = semver.minor + +/** + * Returns the patch version number. + * @param {string} version - The version to parse. + * @param {object} options - The options to pass to the semver.patch function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {number} - The patch version number. + * @category Version operations + * @function + */ +export const patch = semver.patch + +/** + * Attempts to parse and normalize a string as a semver string. An aliase for {@link valid}. + * @category Version operations + * @function + */ +export const parse = semver.parse + +/** + * Aggressively attempts to coerce a string into a valid semver string. Basically, starting from the left side of the + * string, it looks for a digit and then includes anything to the right of the digit that looks like part of a semver. + * So, 'Number 1!' -> '1.0.0', 'Upgrade 1.2 to 1.3' -> '1.2.0', etc. + * @param {string} version - The version to coerce. + * @param {object} options - The options to pass to the semver.coerce function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @param {boolean} options.includePrerelease - Unless true, prerelease tags (and build metadata) are stripped. If + * @param {boolean} options.rtl - Instead of searching for a digit from the left, start searching from the right. + * true, then they are preserved. + * @returns {string|null} - The coerced version string or null if the version is invalid. + * @category Version operations + * @function + */ +export const coerce = semver.coerce + +/** + * Returns a cleaned version string removing unecessary comparators and, if `options.loose` is true, fixing space + * issues. Only works for versions, not ranges. + * @param {string} version - The version to clean. + * @param {object} options - The options to pass to the semver.clean function. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @returns {string|null} - The cleaned version string or null if the version is invalid. + * @category Version operations + * @function + */ +export const clean = semver.clean diff --git a/src/test/filter-valid-version-or-range.test.mjs b/src/test/filter-valid-version-or-range.test.mjs deleted file mode 100644 index 80a5c4f..0000000 --- a/src/test/filter-valid-version-or-range.test.mjs +++ /dev/null @@ -1,11 +0,0 @@ -/* global describe expect test */ -import { filterValidVersionOrRange } from '../filter-valid-version-or-range' - -describe('filterValidVersionOrRange', () => { - test.each([ - [ - ['1.0', '1.2.3', '2.3.9-alpha.12', '1.1.1-alpha.x', 'backlog', '1.2.x', '8.*', '*', '1.2.4.*', '^1.2.3'], - ['1.2.3', '2.3.9-alpha.12', '1.1.1-alpha.x', '1.2.x', '8.*', '*'] - ] - ])('%p => %p', (input, expected) => expect(filterValidVersionOrRange(input, { onlyXRange : true })).toEqual(expected)) -}) diff --git a/src/test/max-satisfying.test.mjs b/src/test/max-satisfying.test.mjs new file mode 100644 index 0000000..b564266 --- /dev/null +++ b/src/test/max-satisfying.test.mjs @@ -0,0 +1,34 @@ +/* global describe expect test */ +import { maxSatisfying } from '../max-satisfying' + +describe('maxSatisfying', () => { + test.each([ + [['1.0.0-alpha.0', '1.0.0-beta.0'], '<1.0.0-beta.0', undefined, '1.0.0-alpha.0'], + [['1.0.0-beta.0', '1.0.0-rc.0'], '>1.0.0-beta.0', undefined, '1.0.0-rc.0'], + [['1.0.0-alpha.0', '1.0.0'], '*', undefined, '1.0.0'], + [['1.0.0-beta.0', '1.0.0-rc.0', '1.0.0'], '<1.0.0', undefined, null], + [['1.0.0-alpha.2', '1.0.0-alpha.13'], '>1.0.0-alpha.0', undefined, '1.0.0-alpha.13'], + [['2.0.0-alpha.0', '1.0.0'], '*', undefined, '1.0.0'], + [[], '*', undefined, null], + // includePrerelease = true + [['1.0.0-alpha.0', '1.0.0-beta.0'], '<1.0.0-beta.0', { includePrerelease : true }, '1.0.0-alpha.0'], + [['1.0.0-beta.0', '1.0.0-rc.0'], '>1.0.0-beta.0', { includePrerelease : true }, '1.0.0-rc.0'], + [['1.0.0-alpha.0', '1.0.0'], '*', { includePrerelease : true }, '1.0.0'], + [['1.0.0-beta.0', '1.0.0-rc.0', '1.0.0'], '<1.0.0', { includePrerelease : true }, '1.0.0-rc.0'], + [['1.0.0-alpha.2', '1.0.0-alpha.13'], '>1.0.0-alpha.0', { includePrerelease : true }, '1.0.0-alpha.13'], + [['2.0.0-alpha.0', '1.0.0'], '*', { includePrerelease : true }, '2.0.0-alpha.0'], + [[], '*', { includePrerelease : true }, null], + // compat = true + [['1.0.0-alpha.0', '1.0.0-beta.0', '1.0.0-rc.0', '1.0.0'], '<1.0.0', { compat : true, includePrerelease : true }, '1.0.0-rc.0'] + ])('versions %p, range %s, options %v -> %s', + (versions, range, options, expected) => + expect(maxSatisfying(versions, range, options)?.toString() || null).toBe(expected)) + + test('invalid version raises exception', + () => expect(() => maxSatisfying(['1.0.0', 'not-a-valid-vesion'], '*', { throwIfInvalid : true })).toThrow()) + + /* test.each([ + [['1.0.0', 'not-a-valid-vesion', 'abc'], '1.0.0'] + ])("'ignoreNonVersions' filters non-version strings from the version list", + (versions, expected) => expect(maxSatisfying(versions, '*', { ignoreNonVersions : true })).toBe(expected)) */ +}) diff --git a/src/test/min-version.test.mjs b/src/test/min-version.test.mjs index e993c56..5ba0455 100644 --- a/src/test/min-version.test.mjs +++ b/src/test/min-version.test.mjs @@ -1,20 +1,41 @@ /* global describe expect test */ +import semver from 'semver' + import { minVersion } from '../min-version' -describe('minVersion', () => { +describe('minVersionString', () => { + test('verify semver prerelease bug', () => { + // we want to make sure we have the actual semver and not our local, which may have wrapper logic to fix this + expect(semver.minVersion('1.0.0-alpha.x').toString()).toBe('1.0.0-alpha.x') // correct + expect(semver.minVersion('1.0.0-alpha.x', { includePrerelease : true }).toString()).toBe('1.0.0-alpha.x') // incorrect + }) + test.each([ + // X-ranges ['*', '0.0.0'], ['1.*', '1.0.0'], ['1.0.*', '1.0.0'], - ['1.0.0-alpha.*', '1.0.0-alpha.0'], - ['1.0.0-beta.*', '1.0.0-beta.0'], - ['1.0.0-rc.*', '1.0.0-rc.0'], ['x', '0.0.0'], ['1.x', '1.0.0'], ['1.0.x', '1.0.0'], + ['X', '0.0.0'], + // prerelease X-ranges ['1.0.0-alpha.x', '1.0.0-alpha.0'], ['1.0.0-beta.x', '1.0.0-beta.0'], ['1.0.0-rc.x', '1.0.0-rc.0'], - ['1.0.0 - 2', '1.0.0'] - ])('%s => %s', (input, expected) => expect(minVersion(input)).toBe(expected)) + ['1.0.0-alpha.X', '1.0.0-alpha.0'], + ['1.0.0-alpha.*', '1.0.0-alpha.0'], + // hyphen ranges + ['1.0.0 - 2', '1.0.0'], + // tilde ranges + ['~1.2.3', '1.2.3'], + ['~1.2', '1.2.0'], + ['~1', '1.0.0'], + ['~1.2.3-alpha.2', '1.2.3-alpha.2'], + // caret ranges + ['^1.2.3', '1.2.3'], + ['^1.2', '1.2.0'], + ['^1', '1.0.0'], + ['^1.2.3-alpha.2', '1.2.3-alpha.2'] + ])('%s => %s', (input, expected) => expect(minVersion(input)?.toString()).toBe(expected)) }) diff --git a/src/test/prerelease.test.mjs b/src/test/prerelease.test.mjs index c1c99cc..235f32c 100644 --- a/src/test/prerelease.test.mjs +++ b/src/test/prerelease.test.mjs @@ -1,6 +1,6 @@ /* global describe expect test */ -import { prerelease } from '../prerelease' +import { prerelease } from '../semver-version-ops' describe('prerelease', () => { test.each([ diff --git a/src/test/valid-version-or-range.test.mjs b/src/test/valid-version-or-range.test.mjs index a2e254b..7c31537 100644 --- a/src/test/valid-version-or-range.test.mjs +++ b/src/test/valid-version-or-range.test.mjs @@ -3,11 +3,25 @@ import { validVersionOrRange } from '../valid-version-or-range' describe('validVersionOrRange', () => { test.each([ - ['1.0.0', undefined, true], - ['1.0.0-alpha.0', undefined, true], - ['1.0.0-alpha.1', undefined, true], - ['1.0.0 - 2', undefined, true], - ['1.0.x', { onlyXRange : true }, true], - ['1.0.0-alpha.1', undefined, true] + ['1.0.0', undefined, '1.0.0'], + ['1.0.0-alpha.0', undefined, '1.0.0-alpha.0'], + ['1.0.0-alpha.1', undefined, '1.0.0-alpha.1'], + ['1.0.0 - 2', undefined, '>=1.0.0 <3.0.0-0'], + ['1.0.x', { onlyXRange : true }, '>=1.0.0 <1.1.0-0'], + ['not-a-valid-version', undefined, null], + ['not-a-valid-range', undefined, null], + // invalid versions and ranges + ['1.0.0', { disallowVersions : true }, null], + ['1.0.x', { disallowRanges : true }, null] ])('%s => %s', (input, options, expected) => expect(validVersionOrRange(input, options)).toBe(expected)) + + test.each([ + ['not-a-valid-version', { throwIfInvalid : true }], + ['not-a-valid-range', { throwIfInvalid : true }], + // invalid versions and ranges + ['1.0.0', { disallowVersions : true, throwIfInvalid : true }], + ['1.0.x', { disallowRanges : true, throwIfInvalid : true }], + ['1.0.0', { disallowVersions : true, disallowRanges : true }] + ])('invalid %s (%o) raises exception', (input, options) => + expect(() => validVersionOrRange(input, options)).toThrow()) }) diff --git a/src/test/version-compare.test.mjs b/src/test/version-compare.test.mjs deleted file mode 100644 index d910663..0000000 --- a/src/test/version-compare.test.mjs +++ /dev/null @@ -1,45 +0,0 @@ -/* global describe expect test */ - -import { maxVersion, minVersion } from '../version-compare' - -describe('maxVersion', () => { - test.each([ - [['1.0.0-alpha.0', '1.0.0-beta.0'], '1.0.0-beta.0'], - [['1.0.0-beta.0', '1.0.0-rc.0'], '1.0.0-rc.0'], - [['1.0.0-alpha.0', '1.0.0'], '1.0.0'], - [['1.0.0-alpha.0', '1.0.0-beta.0', '1.0.0-rc.0', '1.0.0'], '1.0.0'], - [['1.0.0-alpha.2', '1.0.0-alpha.13'], '1.0.0-alpha.13'], - [['2.0.0-alpha.0', '1.0.0'], '2.0.0-alpha.0'] - ])('versions %p -> %s', (versions, expected) => expect(maxVersion({ versions })).toBe(expected)) - - test('[] => null', () => expect(maxVersion({ versions : [] })).toBe(null)) - - test('mixed version types raises exception', - () => expect(() => maxVersion({ versions : ['1.0.0', 'not-a-valid-vesion'] })).toThrow()) - - test.each([ - [['1.0.0', 'not-a-valid-vesion', 'abc'], '1.0.0'] - ])("'ignoreNonVersions' filters non-version strings from the version list", - (versions, expected) => expect(maxVersion({ ignoreNonVersions : true, versions })).toBe(expected)) -}) - -describe('minVersion', () => { - test.each([ - [['1.0.0-alpha.0', '1.0.0-beta.0'], '1.0.0-alpha.0'], - [['1.0.0-beta.0', '1.0.0-rc.0'], '1.0.0-beta.0'], - [['1.0.0-alpha.0', '1.0.0'], '1.0.0-alpha.0'], - [['1.0.0-alpha.0', '1.0.0-beta.0', '1.0.0-rc.0', '1.0.0'], '1.0.0-alpha.0'], - [['2.0.0-alpha.0', '1.0.0'], '1.0.0'], - [['1.0.0-alpha.2', '1.0.0-alpha.13'], '1.0.0-alpha.2'] - ])('versions %p -> %s', (versions, expected) => expect(minVersion({ versions })).toBe(expected)) - - test('[] => null', () => expect(minVersion({ versions : [] })).toBe(null)) - - test('mixed version types raises exception', - () => expect(() => minVersion({ versions : ['1.0.0', 'not-a-valid-vesion'] })).toThrow()) - - test.each([ - [['1.0.0', 'not-a-valid-vesion', 'abc'], '1.0.0'] - ])("'ignoreNonVersions' filters non-version strings from the version list", - (versions, expected) => expect(minVersion({ ignoreNonVersions : true, versions })).toBe(expected)) -}) diff --git a/src/upper-bound.mjs b/src/upper-bound.mjs index 9b40d34..d7d7db9 100644 --- a/src/upper-bound.mjs +++ b/src/upper-bound.mjs @@ -6,6 +6,7 @@ import semver from 'semver' * function where 'verision-0' least range above the given range. * @param {string} range - The range to find the ceiling for. * @returns {string} - Either '*', a specific version, or a '-0'. + * @category Range operations */ const upperBound = (range, { stripOperators = false } = {}) => { const normalizedRange = semver.validRange(range) diff --git a/src/valid-version-or-range.mjs b/src/valid-version-or-range.mjs index 44b5a8b..563cbae 100644 --- a/src/valid-version-or-range.mjs +++ b/src/valid-version-or-range.mjs @@ -2,13 +2,60 @@ import semver from 'semver' import { xRangeRE } from './constants' +/** + * Validates a string to be a valid version or range. By default, an exception is raised unless `options.disallowVersions` + * is `true`, in which case it filters out invalid strings and returns a new array. + * @param {string} input - The string to validate. + * @param {object} options - The options to pass to the semver.valid function. + * @param {boolean} options.disallowRanges - Whether to disallow ranges. + * @param {boolean} options.disallowVersions - Whether to disallow versions and ranges which are valid versions. + * @param {boolean} options.includePrerelease - Whether to include prerelease versions. + * @param {boolean} options.loose - Allow non-conforming, but recognizable semver strings. + * @param {boolean} options.onlyXRange - Whether to only allow x-ranges. Note, even if this is `true`, and a valid + * x-range is presented, the returned string will still be normalized. + * @param {boolean} options.throwIfInvalid - If true, throws an exception if the string is invalid. + * @returns {string|null} - A normalized version or range string or `null` if the string is invalid (if + * `options.throwIfInvalid` is `true`, in which case an exception is thrown). + */ const validVersionOrRange = (input = throw new Error("'input' is required."), { - dissallowRanges = false, - dissallowVersions = false, - onlyXRange = false -} = {}) => - !!((dissallowVersions !== true && semver.valid(input)) - || (onlyXRange !== true && dissallowRanges !== true && semver.validRange(input)) - || (onlyXRange === true && input.match(xRangeRE))) + disallowRanges = false, + disallowVersions = false, + onlyXRange = false, + throwIfInvalid = false, + ...options +} = {}) => { + if (disallowVersions === true && disallowRanges === true) { + throw new Error("'disallowVersions' and 'disallowRanges' cannot both be true.") + } + if (disallowRanges === true && onlyXRange === true) { + throw new Error("'disallowRanges' and 'onlyXRange' cannot both be true.") + } + + let normalized = disallowRanges === true ? semver.valid(input, options) : semver.validRange(input, options) + // test for x-range specifically + if (normalized !== null && onlyXRange === true && input.match(xRangeRE) === null) { + if (throwIfInvalid === true) { + throw new Error(`'${input}' is a valid range, but an x-range is specifically expected.`) + } + normalized = null + } + // test for range exclusive of versions + if (disallowVersions === true && semver.valid(input, options) !== null) { + if (throwIfInvalid === true) { + throw new Error(`'${input}' is a valid version, and a non-version range is expected.`) + } + normalized = null + } + // throw an exception if the string is invalid and `options.throwIfInvalid` is `true` + if (normalized === null && throwIfInvalid === true) { + const msg = `'${input}' is not a valid ` + + `${disallowVersions === true + ? 'range exclusive of versions' + : (disallowRanges === true ? 'version' : 'version or range')}.` + throw new Error(msg) + } + + return normalized +} export { validVersionOrRange } diff --git a/src/version-compare.mjs b/src/version-compare.mjs deleted file mode 100644 index f60b6f8..0000000 --- a/src/version-compare.mjs +++ /dev/null @@ -1,57 +0,0 @@ -import semver from 'semver' - -const compareHelper = ({ semverTest, timeverTest, versions }) => { - let currLead - for (let i = 0; i < versions.length; i += 1) { - const testVer = versions[i] - if (currLead === undefined) { - currLead = testVer - } - else if (semverTest(currLead, testVer)) { - currLead = testVer - } - } - - return currLead -} - -const maxVersion = ({ ignoreNonVersions, versions }) => { - if (!versions || versions.length === 0) { - return null - } - - versions = filterValidVersions(versions, { ignoreNonVersions }) - - return compareHelper({ semverTest : semver.lt, timeverTest : (a, b) => a.localeCompare(b) < 0, versions }) -} - -const minVersion = ({ ignoreNonVersions, versions }) => { - if (!versions || versions.length === 0) { - return null - } - - versions = filterValidVersions(versions, { ignoreNonVersions }) - - return compareHelper({ semverTest : semver.gt, timeverTest : (a, b) => a.localeCompare(b) > 0, versions }) -} - -const filterValidVersions = (versions, { ignoreNonVersions }) => { - versions = versions.filter((version) => { - const valid = semver.valid(version) !== null - - if (valid === false) { - if (ignoreNonVersions === true) { - return false - } - else { - throw new Error(`'${version}' is not a valid semver.`) - } - } - - return true - }) - - return versions -} - -export { maxVersion, minVersion } diff --git a/src/x-sort.mjs b/src/x-sort.mjs index 42a2989..8e71d8c 100644 --- a/src/x-sort.mjs +++ b/src/x-sort.mjs @@ -5,6 +5,9 @@ import { upperBound } from './upper-bound' /** * Ascend sorts a mix of semver versions and x-range specified ranges (e.g., 1.2.* or 1.2.x). The ranges are sorted according to their highest version; e.g., 1.3.34 < 1.3.*. (I believe it can accept caret ranges too, but I would need to review the spec.) + * @param {string[]} versionsAndRanges - The versions and ranges to sort. + * @returns {string[]} - The sorted versions and ranges. + * @category Comparison operations */ const xSort = (versionsAndRanges) => { // TODO: to sort any range type, develop 'minOutOfRange' and use those to sort ranges