diff --git a/bin/templates/scripts/cordova/lib/versions.js b/bin/templates/scripts/cordova/lib/versions.js index d9ba5e31b..828042f5e 100755 --- a/bin/templates/scripts/cordova/lib/versions.js +++ b/bin/templates/scripts/cordova/lib/versions.js @@ -21,6 +21,7 @@ var child_process = require('child_process'); var Q = require('q'); +var semver = require('semver'); exports.get_apple_ios_version = function () { var d = Q.defer(); @@ -159,36 +160,20 @@ exports.get_tool_version = function (toolName) { }; /** - * Compares two semver-notated version strings. Returns number - * that indicates equality of provided version strings. + * Compares two version strings that can be coerced to semver. + * * @param {String} version1 Version to compare * @param {String} version2 Another version to compare * @return {Number} Negative number if first version is lower than the second, * positive otherwise and 0 if versions are equal. */ -exports.compareVersions = function (version1, version2) { - function parseVer (version) { - return version.split('.').map(function (value) { - // try to convert version segment to Number - var parsed = Number(value); - // Number constructor is strict enough and will return NaN - // if conversion fails. In this case we won't be able to compare versions properly - if (isNaN(parsed)) { - throw 'Version should contain only numbers and dots'; - } - return parsed; - }); - } - var parsedVer1 = parseVer(version1); - var parsedVer2 = parseVer(version2); +exports.compareVersions = (...args) => { + const coerceToSemverIfInvalid = v => { + const semverVersion = semver.parse(v) || semver.coerce(v); + if (!semverVersion) throw new TypeError(`Invalid Version: ${v}`); + return semverVersion; + }; - // Compare corresponding segments of each version - for (var i = 0; i < Math.max(parsedVer1.length, parsedVer2.length); i++) { - // if segment is not specified, assume that it is 0 - // E.g. 3.1 is equal to 3.1.0 - var ret = (parsedVer1[i] || 0) - (parsedVer2[i] || 0); - // if segments are not equal, we're finished - if (ret !== 0) return ret; - } - return 0; + const semverVersions = args.map(coerceToSemverIfInvalid); + return semver.compare(...semverVersions); }; diff --git a/package.json b/package.json index 596cc0c94..265c2099a 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "nopt": "^4.0.1", "plist": "^3.0.1", "q": "^1.5.1", + "semver": "^6.3.0", "shelljs": "^0.5.3", "unorm": "^1.4.1", "xcode": "^2.0.0", diff --git a/tests/spec/unit/lib/check_reqs.spec.js b/tests/spec/unit/lib/check_reqs.spec.js index d6e971e77..b1dd507f7 100644 --- a/tests/spec/unit/lib/check_reqs.spec.js +++ b/tests/spec/unit/lib/check_reqs.spec.js @@ -67,8 +67,8 @@ describe('check_reqs', function () { compareVersions: originalVersion.compareVersions }); - checkTool('node', 'v1.0.0').catch((error) => { - expect(error).toEqual('Version should contain only numbers and dots'); + checkTool('node', 'a.b.c').catch(err => { + expect(err).toEqual(new TypeError('Invalid Version: a.b.c')); done(); }); }); diff --git a/tests/spec/unit/versions.spec.js b/tests/spec/unit/versions.spec.js index c3dfc7db4..52b30d4ff 100644 --- a/tests/spec/unit/versions.spec.js +++ b/tests/spec/unit/versions.spec.js @@ -17,6 +17,7 @@ under the License. */ +var semver = require('semver'); var rewire = require('rewire'); var versions = rewire('../../../bin/templates/scripts/cordova/lib/versions'); @@ -52,3 +53,37 @@ if (process.platform === 'darwin') { }); }); } + +describe('versions', () => { + describe('compareVersions method', () => { + it('calls semver.compare, given valid semver', () => { + const testVersions = ['1.0.0', '1.1.0']; + spyOn(semver, 'compare'); + + versions.compareVersions(...testVersions); + expect(semver.compare).toHaveBeenCalledWith( + ...testVersions.map(version => + jasmine.objectContaining({ version }) + ) + ); + }); + + it('handles pre-release identifiers', () => { + expect( + versions.compareVersions('1.0.0-rc.0', '1.0.0') + ).toBe(-1); + }); + + it('handles non-semver versions', () => { + expect( + versions.compareVersions('10.1', '10') + ).toBe(1); + }); + + it('does not handle pre-release identifiers on non-semver versions', () => { + expect( + versions.compareVersions('10.1-beta.1', '10.1') + ).toBe(0); + }); + }); +});