From b7b22dcef6e0f38dcb2655d2b157cdd1af2601b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Bj=C3=B8rn=20Michelsen?= Date: Thu, 7 Apr 2022 08:52:51 -0700 Subject: [PATCH] feat: rewrite in TypeScript BREAKING CHANGE: main export is now named Allows for a single source of truth and multiple bundle targets. Support both ESM and CJS (through UMD). --- .gitignore | 1 + README.md | 21 +- bower.json | 11 - index.d.ts | 72 ----- index.js | 165 ------------ index.mjs | 124 --------- package-lock.json | 437 ++++++++++++++++++++++++++++--- package.json | 23 +- src/index.ts | 180 +++++++++++++ test/{compare.mjs => compare.ts} | 60 +++-- test/satisfies.mjs | 59 ----- test/satisfies.ts | 62 +++++ test/{sort.mjs => sort.ts} | 2 +- test/validate.mjs | 32 --- test/validate.ts | 34 +++ tsconfig.json | 20 ++ 16 files changed, 755 insertions(+), 548 deletions(-) delete mode 100644 bower.json delete mode 100644 index.d.ts delete mode 100644 index.js delete mode 100644 index.mjs create mode 100644 src/index.ts rename test/{compare.mjs => compare.ts} (84%) delete mode 100644 test/satisfies.mjs create mode 100644 test/satisfies.ts rename test/{sort.mjs => sort.ts} (95%) delete mode 100644 test/validate.mjs create mode 100644 test/validate.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 76ebb04..81def47 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .nyc_output/ .vscode/ coverage/ +lib/ node_modules/ \ No newline at end of file diff --git a/README.md b/README.md index 7260a5a..5b0e3f3 100644 --- a/README.md +++ b/README.md @@ -19,14 +19,16 @@ Supports the full semver specification including versions with different number $ npm install compare-versions ``` -Note: Starting from v4 this library includes a ESM version which will automatically be selected by your bundler (webpack, parcel etc). The old CJS version is `index.js` and the new ESM version is `index.mjs`. +__Note__: Starting from v5 the main export is now _named_ like so: `import { compareVersions } from 'compare-versions'`. + +__Note__: Starting from v4 this library includes a ESM version which will automatically be selected by your bundler (webpack, parcel etc). The CJS/UMD version is `lib/umd/index.js` and the new ESM version is `lib/esm/index.js`. ## Usage Will return `1` if first version is greater, `0` if versions are equal, and `-1` if the second version is greater: ```js -import compareVersions from 'compare-versions'; +import { compareVersions } from 'compare-versions'; compareVersions('11.1.1', '10.0.0'); // 1 compareVersions('10.0.0', '10.0.0'); // 0 @@ -85,7 +87,7 @@ satisfies('10.1.1', '>=10.2.2'); // false ### Validate version numbers -Applies the same ruleset used comparing version numbers and returns a boolean: +Applies the same rules used comparing version numbers and returns a boolean: ```js import { validate } from 'compare-versions'; @@ -97,14 +99,15 @@ validate('foo'); // false ### Browser -If included directly in the browser, `compareVersions()` is available on the global window: +If included directly in the browser, the functions above are available on the global window under the `compareVersions` object: ```html - + ``` diff --git a/bower.json b/bower.json deleted file mode 100644 index d1c472a..0000000 --- a/bower.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "compare-versions", - "version": "4.1.4", - "description": "Compare semver version strings to find greater, equal or lesser.", - "main": "index.js", - "authors": ["Ole Bjørn Michelsen "], - "moduleType": ["amd", "globals", "node"], - "keywords": ["semver", "version", "compare", "browser", "node"], - "license": "MIT", - "ignore": ["**/.*", "node_modules", "bower_components", "test", "tests"] -} diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index eb84c01..0000000 --- a/index.d.ts +++ /dev/null @@ -1,72 +0,0 @@ -declare namespace compareVersions { - /** - * Allowed arithmetic operators - */ - type CompareOperator = '>' | '>=' | '=' | '<' | '<='; -} - -declare const compareVersions: { - /** - * Compare [semver](https://semver.org/) version strings to find greater, equal or lesser. - * This library supports the full semver specification, including comparing versions with different number of digits like `1.0.0`, `1.0`, `1`, and pre-release versions like `1.0.0-alpha`. - * @param firstVersion - First version to compare - * @param secondVersion - Second version to compare - * @returns Numeric value compatible with the [Array.sort(fn) interface](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters). - */ - (firstVersion: string, secondVersion: string): 1 | 0 | -1; - - /** - * Compare [semver](https://semver.org/) version strings using the specified operator. - * - * @param firstVersion First version to compare - * @param secondVersion Second version to compare - * @param operator Allowed arithmetic operator to use - * @returns `true` if the comparison between the firstVersion and the secondVersion satisfies the operator, `false` otherwise. - * - * @example - * ``` - * compareVersions.compare('10.1.8', '10.0.4', '>'); // return true - * compareVersions.compare('10.0.1', '10.0.1', '='); // return true - * compareVersions.compare('10.1.1', '10.2.2', '<'); // return true - * compareVersions.compare('10.1.1', '10.2.2', '<='); // return true - * compareVersions.compare('10.1.1', '10.2.2', '>='); // return false - * ``` - */ - compare( - firstVersion: string, - secondVersion: string, - operator: compareVersions.CompareOperator - ): boolean; - - /** - * Validate [semver](https://semver.org/) version strings. - * - * @param version Version number to validate - * @returns `true` if the version number is a valid semver version number, `false` otherwise. - * - * @example - * ``` - * compareVersions.validate('1.0.0-rc.1'); // return true - * compareVersions.validate('1.0-rc.1'); // return false - * compareVersions.validate('foo'); // return false - * ``` - */ - validate(version: string): boolean; - - /** - * Match [npm semver](https://docs.npmjs.com/cli/v6/using-npm/semver) version range. - * - * @param version Version number to match - * @param range Range pattern for version - * @returns `true` if the version number is within the range, `false` otherwise. - * - * @example - * ``` - * satisfies('1.1.0', '^1.0.0'); // return true - * satisfies('1.1.0', '~1.0.0'); // return false - * ``` - */ - satisfies(version: string, range: string): boolean; -}; - -export = compareVersions; diff --git a/index.js b/index.js deleted file mode 100644 index f12d87b..0000000 --- a/index.js +++ /dev/null @@ -1,165 +0,0 @@ -/* global define */ -(function (root, factory) { - /* istanbul ignore next */ - if (typeof define === 'function' && define.amd) { - define([], factory); - } else if (typeof exports === 'object') { - module.exports = factory(); - } else { - root.compareVersions = factory(); - } -})(this, function () { - var semver = - /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i; - - function indexOrEnd(str, q) { - return str.indexOf(q) === -1 ? str.length : str.indexOf(q); - } - - function split(v) { - var c = v.replace(/^v/, '').replace(/\+.*$/, ''); - var patchIndex = indexOrEnd(c, '-'); - var arr = c.substring(0, patchIndex).split('.'); - arr.push(c.substring(patchIndex + 1)); - return arr; - } - - function tryParse(v) { - var n = parseInt(v, 10); - return isNaN(n) ? v : n; - } - - function validateAndParse(v) { - if (typeof v !== 'string') { - throw new TypeError('Invalid argument expected string'); - } - var match = v.match(semver); - if (!match) { - throw new Error( - "Invalid argument not valid semver ('" + v + "' received)" - ); - } - match.shift(); - return match; - } - - function forceType(a, b) { - return typeof a !== typeof b ? [String(a), String(b)] : [a, b]; - } - - function compareStrings(a, b) { - var [ap, bp] = forceType(tryParse(a), tryParse(b)); - if (ap > bp) return 1; - if (ap < bp) return -1; - return 0; - } - - function compareSegments(a, b) { - for (var i = 0; i < Math.max(a.length, b.length); i++) { - var r = compareStrings(a[i] || 0, b[i] || 0); - if (r !== 0) return r; - } - return 0; - } - - function compareVersions(v1, v2) { - [v1, v2].forEach(validateAndParse); - - var s1 = split(v1); - var s2 = split(v2); - - for (var i = 0; i < Math.max(s1.length - 1, s2.length - 1); i++) { - var n1 = parseInt(s1[i] || 0, 10); - var n2 = parseInt(s2[i] || 0, 10); - - if (n1 > n2) return 1; - if (n2 > n1) return -1; - } - - var sp1 = s1[s1.length - 1]; - var sp2 = s2[s2.length - 1]; - - if (sp1 && sp2) { - var p1 = sp1.split('.').map(tryParse); - var p2 = sp2.split('.').map(tryParse); - - for (i = 0; i < Math.max(p1.length, p2.length); i++) { - if ( - p1[i] === undefined || - (typeof p2[i] === 'string' && typeof p1[i] === 'number') - ) - return -1; - if ( - p2[i] === undefined || - (typeof p1[i] === 'string' && typeof p2[i] === 'number') - ) - return 1; - - if (p1[i] > p2[i]) return 1; - if (p2[i] > p1[i]) return -1; - } - } else if (sp1 || sp2) { - return sp1 ? -1 : 1; - } - - return 0; - } - - var allowedOperators = ['>', '>=', '=', '<', '<=']; - - var operatorResMap = { - '>': [1], - '>=': [0, 1], - '=': [0], - '<=': [-1, 0], - '<': [-1], - }; - - function validateOperator(op) { - if (typeof op !== 'string') { - throw new TypeError( - 'Invalid operator type, expected string but got ' + typeof op - ); - } - if (allowedOperators.indexOf(op) === -1) { - throw new TypeError( - 'Invalid operator, expected one of ' + allowedOperators.join('|') - ); - } - } - - compareVersions.validate = function (version) { - return typeof version === 'string' && semver.test(version); - }; - - compareVersions.compare = function (v1, v2, operator) { - // Validate operator - validateOperator(operator); - - // since result of compareVersions can only be -1 or 0 or 1 - // a simple map can be used to replace switch - var res = compareVersions(v1, v2); - return operatorResMap[operator].indexOf(res) > -1; - }; - - compareVersions.satisfies = function (v, r) { - // if no range operator then "=" - var match = r.match(/^([<>=~^]+)/); - var op = match ? match[1] : '='; - - // if gt/lt/eq then operator compare - if (op !== '^' && op !== '~') return compareVersions.compare(v, r, op); - - // else range of either "~" or "^" is assumed - var [v1, v2, v3] = validateAndParse(v); - var [m1, m2, m3] = validateAndParse(r); - if (compareStrings(v1, m1) !== 0) return false; - if (op === '^') { - return compareSegments([v2, v3], [m2, m3]) >= 0; - } - if (compareStrings(v2, m2) !== 0) return false; - return compareStrings(v3, m3) >= 0; - }; - - return compareVersions; -}); diff --git a/index.mjs b/index.mjs deleted file mode 100644 index c63432b..0000000 --- a/index.mjs +++ /dev/null @@ -1,124 +0,0 @@ -export default function compareVersions(v1, v2) { - // validate input and split into segments - const n1 = validateAndParse(v1); - const n2 = validateAndParse(v2); - - // pop off the patch - const p1 = n1.pop(); - const p2 = n2.pop(); - - // validate numbers - const r = compareSegments(n1, n2); - if (r !== 0) return r; - - // validate pre-release - if (p1 && p2) { - return compareSegments(p1.split('.'), p2.split('.')); - } else if (p1 || p2) { - return p1 ? -1 : 1; - } - - return 0; -} - -export const validate = (v) => - typeof v === 'string' && /^[v\d]/.test(v) && semver.test(v); - -export const compare = (v1, v2, operator) => { - // validate input operator - assertValidOperator(operator); - - // since result of compareVersions can only be -1 or 0 or 1 - // a simple map can be used to replace switch - const res = compareVersions(v1, v2); - - return operatorResMap[operator].includes(res); -}; - -export const satisfies = (v, r) => { - // if no range operator then "=" - const m = r.match(/^([<>=~^]+)/); - const op = m ? m[1] : '='; - - // if gt/lt/eq then operator compare - if (op !== '^' && op !== '~') return compare(v, r, op); - - // else range of either "~" or "^" is assumed - const [v1, v2, v3] = validateAndParse(v); - const [r1, r2, r3] = validateAndParse(r); - if (compareStrings(v1, r1) !== 0) return false; - if (op === '^') { - return compareSegments([v2, v3], [r2, r3]) >= 0; - } - if (compareStrings(v2, r2) !== 0) return false; - return compareStrings(v3, r3) >= 0; -}; - -// export CJS style for parity -compareVersions.validate = validate; -compareVersions.compare = compare; -compareVersions.satisfies = satisfies; - -const semver = - /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i; - -const validateAndParse = (v) => { - if (typeof v !== 'string') { - throw new TypeError('Invalid argument expected string'); - } - const match = v.match(semver); - if (!match) { - throw new Error(`Invalid argument not valid semver ('${v}' received)`); - } - match.shift(); - return match; -}; - -const isWildcard = (s) => s === '*' || s === 'x' || s === 'X'; - -const tryParse = (v) => { - const n = parseInt(v, 10); - return isNaN(n) ? v : n; -}; - -const forceType = (a, b) => - typeof a !== typeof b ? [String(a), String(b)] : [a, b]; - -const compareStrings = (a, b) => { - if (isWildcard(a) || isWildcard(b)) return 0; - const [ap, bp] = forceType(tryParse(a), tryParse(b)); - if (ap > bp) return 1; - if (ap < bp) return -1; - return 0; -}; - -const compareSegments = (a, b) => { - for (let i = 0; i < Math.max(a.length, b.length); i++) { - const r = compareStrings(a[i] || 0, b[i] || 0); - if (r !== 0) return r; - } - return 0; -}; - -const operatorResMap = { - '>': [1], - '>=': [0, 1], - '=': [0], - '<=': [-1, 0], - '<': [-1], -}; - -const allowedOperators = Object.keys(operatorResMap); - -const assertValidOperator = (op) => { - if (typeof op !== 'string') { - throw new TypeError( - `Invalid operator type, expected string but got ${typeof op}` - ); - } - if (allowedOperators.indexOf(op) === -1) { - throw new Error( - `Invalid operator, expected one of ${allowedOperators.join('|')}` - ); - } -}; diff --git a/package-lock.json b/package-lock.json index 4148b02..990e97a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,20 @@ { "name": "compare-versions", - "version": "4.1.4", + "version": "4.1.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "compare-versions", - "version": "4.1.4", + "version": "4.1.3", "license": "MIT", "devDependencies": { + "@types/mocha": "^9.1.0", "c8": "^7.10.0", - "mocha": "^10.0.0" + "mocha": "^10.0.0", + "rollup": "^2.78.1", + "ts-node": "^10.7.0", + "typescript": "^4.6.3" } }, "node_modules/@bcoe/v8-coverage": { @@ -19,6 +23,18 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -44,27 +60,85 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", "dev": true }, + "node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.7.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.12.tgz", + "integrity": "sha512-caqFX7GwvZ4KLnhpI9CfiMkgHKp6kvFAIgpkha0cjO7bAQvB6dWe+q3fTHmm7fQvv59pd4tPj77nriq2M6U2dw==", + "dev": true, + "peer": true + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -111,6 +185,12 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -133,13 +213,12 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -291,6 +370,12 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -487,6 +572,16 @@ "node": ">= 6" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -711,6 +806,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", @@ -723,15 +824,6 @@ "node": ">=10" } }, - "node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/mocha": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", @@ -947,6 +1039,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "2.78.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz", + "integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1076,6 +1183,16 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/test-exclude/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1100,6 +1217,77 @@ "node": ">=8.0" } }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -1114,6 +1302,16 @@ "node": ">=10.12.0" } }, + "node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1209,6 +1407,15 @@ "node": ">=10" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -1229,6 +1436,15 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, "@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1248,27 +1464,76 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.15", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", - "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, "@types/istanbul-lib-coverage": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", "dev": true }, + "@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true + }, + "@types/node": { + "version": "18.7.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.12.tgz", + "integrity": "sha512-caqFX7GwvZ4KLnhpI9CfiMkgHKp6kvFAIgpkha0cjO7bAQvB6dWe+q3fTHmm7fQvv59pd4tPj77nriq2M6U2dw==", + "dev": true, + "peer": true + }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -1300,6 +1565,12 @@ "picomatch": "^2.0.4" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1319,13 +1590,12 @@ "dev": true }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "braces": { @@ -1444,6 +1714,12 @@ } } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1570,6 +1846,16 @@ "path-is-absolute": "^1.0.0" }, "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1742,6 +2028,12 @@ "semver": "^6.0.0" } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "minimatch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", @@ -1749,17 +2041,6 @@ "dev": true, "requires": { "brace-expansion": "^2.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - } } }, "mocha": { @@ -1911,6 +2192,15 @@ "glob": "^7.1.3" } }, + "rollup": { + "version": "2.78.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz", + "integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -1999,6 +2289,16 @@ "minimatch": "^3.0.4" }, "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2019,6 +2319,47 @@ "is-number": "^7.0.0" } }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, + "typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -2028,6 +2369,18 @@ "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^1.6.0" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } } }, "which": { @@ -2101,6 +2454,12 @@ "is-plain-obj": "^2.1.0" } }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index e5b5362..2e77216 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "compare-versions", - "version": "4.1.4", + "version": "4.1.3", "description": "Compare semver version strings to find greater, equal or lesser.", "repository": { "type": "git", @@ -20,19 +20,24 @@ "node" ], "scripts": { - "test": "c8 --reporter=lcov mocha" + "build": "npm run build:esm && npm run build:umd", + "build:esm": "tsc --module esnext --target es2017 --outDir lib/esm", + "build:umd": "rollup lib/esm/index.js --format umd --name compareVersions --sourcemap -o lib/umd/index.js", + "test": "c8 --reporter=lcov mocha -r ts-node/register test/**/*.ts" }, - "main": "index.js", - "module": "index.mjs", - "types": "index.d.ts", + "main": "./lib/umd/index.js", + "module": "./lib/esm/index.js", + "types": "./lib/esm/index.d.ts", "sideEffects": false, "files": [ - "index.js", - "index.mjs", - "index.d.ts" + "lib" ], "devDependencies": { + "@types/mocha": "^9.1.0", "c8": "^7.10.0", - "mocha": "^10.0.0" + "mocha": "^10.0.0", + "rollup": "^2.78.1", + "ts-node": "^10.7.0", + "typescript": "^4.6.3" } } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..48d5156 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,180 @@ +/** + * Compare [semver](https://semver.org/) version strings to find greater, equal or lesser. + * This library supports the full semver specification, including comparing versions with different number of digits like `1.0.0`, `1.0`, `1`, and pre-release versions like `1.0.0-alpha`. + * @param v1 - First version to compare + * @param v2 - Second version to compare + * @returns Numeric value compatible with the [Array.sort(fn) interface](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters). + */ +export const compareVersions = (v1: string, v2: string) => { + // validate input and split into segments + const n1 = validateAndParse(v1); + const n2 = validateAndParse(v2); + + // pop off the patch + const p1 = n1.pop(); + const p2 = n2.pop(); + + // validate numbers + const r = compareSegments(n1, n2); + if (r !== 0) return r; + + // validate pre-release + if (p1 && p2) { + return compareSegments(p1.split('.'), p2.split('.')); + } else if (p1 || p2) { + return p1 ? -1 : 1; + } + + return 0; +}; + +/** + * Validate [semver](https://semver.org/) version strings. + * + * @param version Version number to validate + * @returns `true` if the version number is a valid semver version number, `false` otherwise. + * + * @example + * ``` + * validate('1.0.0-rc.1'); // return true + * validate('1.0-rc.1'); // return false + * validate('foo'); // return false + * ``` + */ +export const validate = (version: string) => + typeof version === 'string' && /^[v\d]/.test(version) && semver.test(version); + +/** + * Allowed arithmetic operators + */ +export type CompareOperator = '>' | '>=' | '=' | '<' | '<='; + +/** + * Compare [semver](https://semver.org/) version strings using the specified operator. + * + * @param v1 First version to compare + * @param v2 Second version to compare + * @param operator Allowed arithmetic operator to use + * @returns `true` if the comparison between the firstVersion and the secondVersion satisfies the operator, `false` otherwise. + * + * @example + * ``` + * compare('10.1.8', '10.0.4', '>'); // return true + * compare('10.0.1', '10.0.1', '='); // return true + * compare('10.1.1', '10.2.2', '<'); // return true + * compare('10.1.1', '10.2.2', '<='); // return true + * compare('10.1.1', '10.2.2', '>='); // return false + * ``` + */ +export const compare = (v1: string, v2: string, operator: CompareOperator) => { + // validate input operator + assertValidOperator(operator); + + // since result of compareVersions can only be -1 or 0 or 1 + // a simple map can be used to replace switch + const res = compareVersions(v1, v2); + + return operatorResMap[operator].includes(res); +}; + +/** + * Match [npm semver](https://docs.npmjs.com/cli/v6/using-npm/semver) version range. + * + * @param version Version number to match + * @param range Range pattern for version + * @returns `true` if the version number is within the range, `false` otherwise. + * + * @example + * ``` + * satisfies('1.1.0', '^1.0.0'); // return true + * satisfies('1.1.0', '~1.0.0'); // return false + * ``` + */ +export const satisfies = (version: string, range: string) => { + // if no range operator then "=" + const m = range.match(/^([<>=~^]+)/); + const op = m ? m[1] : '='; + + // if gt/lt/eq then operator compare + if (op !== '^' && op !== '~') + return compare(version, range, op as CompareOperator); + + // else range of either "~" or "^" is assumed + const [v1, v2, v3] = validateAndParse(version); + const [r1, r2, r3] = validateAndParse(range); + if (compareStrings(v1, r1) !== 0) return false; + if (op === '^') { + return compareSegments([v2, v3], [r2, r3]) >= 0; + } + if (compareStrings(v2, r2) !== 0) return false; + return compareStrings(v3, r3) >= 0; +}; + +const semver = + /^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i; + +const validateAndParse = (version: string) => { + if (typeof version !== 'string') { + throw new TypeError('Invalid argument expected string'); + } + const match = version.match(semver); + if (!match) { + throw new Error( + `Invalid argument not valid semver ('${version}' received)` + ); + } + match.shift(); + return match; +}; + +const isWildcard = (s: string) => s === '*' || s === 'x' || s === 'X'; + +const tryParse = (v: string) => { + const n = parseInt(v, 10); + return isNaN(n) ? v : n; +}; + +const forceType = (a: string | number, b: string | number) => + typeof a !== typeof b ? [String(a), String(b)] : [a, b]; + +const compareStrings = (a: string, b: string) => { + if (isWildcard(a) || isWildcard(b)) return 0; + const [ap, bp] = forceType(tryParse(a), tryParse(b)); + if (ap > bp) return 1; + if (ap < bp) return -1; + return 0; +}; + +const compareSegments = ( + a: string | RegExpMatchArray, + b: string | RegExpMatchArray +) => { + for (let i = 0; i < Math.max(a.length, b.length); i++) { + const r = compareStrings(a[i] || '0', b[i] || '0'); + if (r !== 0) return r; + } + return 0; +}; + +const operatorResMap = { + '>': [1], + '>=': [0, 1], + '=': [0], + '<=': [-1, 0], + '<': [-1], +}; + +const allowedOperators = Object.keys(operatorResMap); + +const assertValidOperator = (op: string) => { + if (typeof op !== 'string') { + throw new TypeError( + `Invalid operator type, expected string but got ${typeof op}` + ); + } + if (allowedOperators.indexOf(op) === -1) { + throw new Error( + `Invalid operator, expected one of ${allowedOperators.join('|')}` + ); + } +}; diff --git a/test/compare.mjs b/test/compare.ts similarity index 84% rename from test/compare.mjs rename to test/compare.ts index 8d53bc0..27ed7ce 100644 --- a/test/compare.mjs +++ b/test/compare.ts @@ -1,14 +1,14 @@ import assert from 'assert'; -import compareVersions, { compare } from '../index.mjs'; +import { compareVersions, compare, CompareOperator } from '../src/index'; describe('compare versions', () => { const cmp = { - 1: '>', - 0: '=', + '1': '>', + '0': '=', '-1': '<', - }; + } as { [key: string]: string }; - const runTests = (dataSet) => { + const runTests = (dataSet: Array<[string, string, number]>) => { dataSet.forEach(([v1, v2, expected]) => { it(`${v1} ${cmp[expected]} ${v2}`, () => assert.strictEqual( @@ -152,18 +152,20 @@ describe('compare versions', () => { }); describe('invalid input', () => { - [ - [42, /Invalid argument expected string/], - [{}, /Invalid argument expected string/], - [[], /Invalid argument expected string/], - [() => undefined, /Invalid argument expected string/], - ['6.3.', /Invalid argument not valid semver/], - ['1.2.3a', /Invalid argument not valid semver/], - ['1.2.-3a', /Invalid argument not valid semver/], - ].forEach(([v1, exception]) => { + ( + [ + [42, /Invalid argument expected string/], + [{}, /Invalid argument expected string/], + [[], /Invalid argument expected string/], + [() => undefined, /Invalid argument expected string/], + ['6.3.', /Invalid argument not valid semver/], + ['1.2.3a', /Invalid argument not valid semver/], + ['1.2.-3a', /Invalid argument not valid semver/], + ] as const + ).forEach(([v1, exception]) => { it(`should throw on ${v1}`, () => { assert.throws(() => { - compareVersions(v1, v1); + compareVersions(v1 as any, v1 as any); }, exception); }); }); @@ -204,7 +206,9 @@ describe('compare versions', () => { }); describe('human readable compare versions', () => { - const runTests = (dataSet) => { + const runTests = ( + dataSet: Array<[string, string, CompareOperator, boolean]> + ) => { dataSet.forEach(([v1, v2, operator, expected]) => { it(`${v1} ${operator} ${v2}`, () => assert.strictEqual( @@ -215,7 +219,7 @@ describe('human readable compare versions', () => { }); }; - const runThrows = (dataSet) => { + const runThrows = (dataSet: Array<[string, string, any, RegExp]>) => { dataSet.forEach(([v1, v2, operator, expected]) => { it(`${v1} ${operator} ${v2}`, () => { assert.throws(() => { @@ -263,17 +267,19 @@ describe('human readable compare versions', () => { ]); it('should throw the same Errors thrown by the main function', () => { - [ - [42, /Invalid argument expected string/], - [{}, /Invalid argument expected string/], - [[], /Invalid argument expected string/], - [() => undefined, /Invalid argument expected string/], - ['6.3.', /Invalid argument not valid semver/], - ['1.2.3a', /Invalid argument not valid semver/], - ['1.2.-3a', /Invalid argument not valid semver/], - ].forEach(([v1, exception]) => { + ( + [ + [42, /Invalid argument expected string/], + [{}, /Invalid argument expected string/], + [[], /Invalid argument expected string/], + [() => undefined, /Invalid argument expected string/], + ['6.3.', /Invalid argument not valid semver/], + ['1.2.3a', /Invalid argument not valid semver/], + ['1.2.-3a', /Invalid argument not valid semver/], + ] as const + ).forEach(([v1, exception]) => { assert.throws(() => { - compare(v1, v1, '>'); + compare(v1 as any, v1 as any, '>'); }, exception); }); }); diff --git a/test/satisfies.mjs b/test/satisfies.mjs deleted file mode 100644 index 78d1ffa..0000000 --- a/test/satisfies.mjs +++ /dev/null @@ -1,59 +0,0 @@ -import assert from 'assert'; -import { satisfies } from '../index.mjs'; - -describe('validate versions', () => { - [ - ['1.0.0', '^1', true], - ['1.0.0', '^1.0', true], - ['1.0.0', '^1.0.0', true], - ['1.2.0', '^1.0.0', true], - ['v1.2.0', '^1.0.0', true], - ['2.0.0', '^1.0.0', false], - ['1.0.0', '^1.2.0', false], - ['1.0.1', '^1.2.0', false], - ['1.3.4', '^1.2.5', true], - ['1.0.0', '~1.2.0', false], - ['1.0.0', '~1.0.1', false], - ['1.0.1', '~1.0.0', true], - ['1.3.4', '~1.2.5', false], - ['1.0.0', '~1.0.0', true], - ['1.0.0-alpha.1', '^1.0.0', true], - ['1.1.0-alpha.1', '^1.0.0', true], - ['1.2.0', '>1.0.0', true], - ['1.2.0', '<1.0.0', false], - ['1.0.0', '<=1.0.0', true], - ['1.0.0', '<=2.0.0', true], - ['1.0.1', '1.0.0', false], - ['1.0.0', '1.0.0', true], - ['10.1.8', '>10.0.4', true], - ['10.1.8', '>=10.0.4', true], - ['10.0.1', '=10.0.1', true], - ['10.0.1', '=10.1.*', false], - ['10.1.1', '<10.2.2', true], - ['10.1.1', '<10.0.2', false], - ['10.1.1', '<=10.2.2', true], - ['10.1.1', '<=10.1.1', true], - ['10.1.1', '<=10.0.2', false], - ['10.1.1', '>=10.0.2', true], - ['10.1.1', '>=10.1.1', true], - ['10.1.1', '>=10.2.2', false], - ['3', '3.x.x', true], - ['3.3', '3.x.x', true], - ['3.3.3', '3.x.x', true], - ['3.x.x', '3.3.3', true], - ['3.3.3', '3.X.X', true], - ['3.3.3', '3.3.x', true], - ['3.3.3', '3.*.*', true], - ['3.3.3', '3.3.*', true], - ['3.0.3', '3.0.*', true], - ['1.1.0', '1.2.x', false], - ['1.1.0', '2.x.x', false], - ['2.0.0', '<2.x.x', false], - ['2.0.0', '<=2.x.x', true], - ['2.0.0', '>2.x.x', false], - ].forEach(([v, m, expected]) => { - it(`${v} satisfies ${m}`, () => { - assert.equal(satisfies(v, m), expected); - }); - }); -}); diff --git a/test/satisfies.ts b/test/satisfies.ts new file mode 100644 index 0000000..93ffc46 --- /dev/null +++ b/test/satisfies.ts @@ -0,0 +1,62 @@ +import assert from 'assert'; +import { satisfies } from '../src/index'; + +describe('validate versions', () => { + ( + [ + ['1.0.0', '^1', true], + ['1.0.0', '^1.0', true], + ['1.0.0', '^1.0.0', true], + ['1.2.0', '^1.0.0', true], + ['v1.2.0', '^1.0.0', true], + ['2.0.0', '^1.0.0', false], + ['1.0.0', '^1.2.0', false], + ['1.0.1', '^1.2.0', false], + ['1.3.4', '^1.2.5', true], + ['1.0.0', '~1.2.0', false], + ['1.0.0', '~1.0.1', false], + ['1.0.1', '~1.0.0', true], + ['1.3.4', '~1.2.5', false], + ['1.0.0', '~1.0.0', true], + ['1.0.0-alpha.1', '^1.0.0', true], + ['1.1.0-alpha.1', '^1.0.0', true], + ['1.2.0', '>1.0.0', true], + ['1.2.0', '<1.0.0', false], + ['1.0.0', '<=1.0.0', true], + ['1.0.0', '<=2.0.0', true], + ['1.0.1', '1.0.0', false], + ['1.0.0', '1.0.0', true], + ['10.1.8', '>10.0.4', true], + ['10.1.8', '>=10.0.4', true], + ['10.0.1', '=10.0.1', true], + ['10.0.1', '=10.1.*', false], + ['10.1.1', '<10.2.2', true], + ['10.1.1', '<10.0.2', false], + ['10.1.1', '<=10.2.2', true], + ['10.1.1', '<=10.1.1', true], + ['10.1.1', '<=10.0.2', false], + ['10.1.1', '>=10.0.2', true], + ['10.1.1', '>=10.1.1', true], + ['10.1.1', '>=10.2.2', false], + ['11.0.0', '>=10.1.1', true], + ['3', '3.x.x', true], + ['3.3', '3.x.x', true], + ['3.3.3', '3.x.x', true], + ['3.x.x', '3.3.3', true], + ['3.3.3', '3.X.X', true], + ['3.3.3', '3.3.x', true], + ['3.3.3', '3.*.*', true], + ['3.3.3', '3.3.*', true], + ['3.0.3', '3.0.*', true], + ['1.1.0', '1.2.x', false], + ['1.1.0', '2.x.x', false], + ['2.0.0', '<2.x.x', false], + ['2.0.0', '<=2.x.x', true], + ['2.0.0', '>2.x.x', false], + ] as const + ).forEach(([v, m, expected]) => { + it(`${v} satisfies ${m}`, () => { + assert.equal(satisfies(v, m), expected); + }); + }); +}); diff --git a/test/sort.mjs b/test/sort.ts similarity index 95% rename from test/sort.mjs rename to test/sort.ts index f72b575..d0a85cd 100644 --- a/test/sort.mjs +++ b/test/sort.ts @@ -1,5 +1,5 @@ import assert from 'assert'; -import compareVersions from '../index.mjs'; +import { compareVersions } from '../src/index'; describe('sort versions', function () { it('should sort versions', function () { diff --git a/test/validate.mjs b/test/validate.mjs deleted file mode 100644 index d11aef4..0000000 --- a/test/validate.mjs +++ /dev/null @@ -1,32 +0,0 @@ -import assert from 'assert'; -import { validate } from '../index.mjs'; - -describe('validate versions', () => { - [ - [undefined, false], - [null, false], - [42, false], - [{}, false], - [[], false], - [() => undefined, false], - ['foo', false], - ['6.3.', false], - ['1.2.3a', false], - ['1.2.-3a', false], - ['v1.0.0', true], - ['01.0.0', true], - ['1.0.x', true], - ['1.0.0-rc.1', true], - ['1.0.0-alpha', true], - ['1.0.0-build.3928', true], - ['1.0.0+20130313144700', true], - ['1.2.3.100', true], - ['2020', true], - ['=1.0', false], - ['>1.0.0', false], - ].forEach(([v, expected]) => { - it(`${v}`, () => { - assert.equal(validate(v), expected); - }); - }); -}); diff --git a/test/validate.ts b/test/validate.ts new file mode 100644 index 0000000..7771f29 --- /dev/null +++ b/test/validate.ts @@ -0,0 +1,34 @@ +import assert from 'assert'; +import { validate } from '../src/index'; + +describe('validate versions', () => { + ( + [ + [undefined, false], + [null, false], + [42, false], + [{}, false], + [[], false], + [() => undefined, false], + ['foo', false], + ['6.3.', false], + ['1.2.3a', false], + ['1.2.-3a', false], + ['v1.0.0', true], + ['01.0.0', true], + ['1.0.x', true], + ['1.0.0-rc.1', true], + ['1.0.0-alpha', true], + ['1.0.0-build.3928', true], + ['1.0.0+20130313144700', true], + ['1.2.3.100', true], + ['2020', true], + ['=1.0', false], + ['>1.0.0', false], + ] as const + ).forEach(([v, expected]) => { + it(`${v}`, () => { + assert.equal(validate(v as any), expected); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b9cfe1b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "declaration": true, + "esModuleInterop": true, + "module": "UMD", + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": ".", + "sourceMap": true, + "strict": true, + "target": "ES5" + }, + "include": [ + "src/index.ts" + ], + "exclude": [ + "./src/test/**/*", + "node_modules" + ] +}