diff --git a/benchmarks/bench-inc.js b/benchmarks/bench-inc.js new file mode 100644 index 00000000..84a5ed5f --- /dev/null +++ b/benchmarks/bench-inc.js @@ -0,0 +1,28 @@ +'use strict' + +const Benchmark = require('benchmark') +const invalidVersions = require('../test/fixtures/invalid-versions') +const inc = require('../functions/inc') +const validVersions = require('../test/fixtures/valid-versions') +const suite = new Benchmark.Suite() + +const cases = validVersions.map(invalid => invalid[0]) +const invalidCases = invalidVersions.map(invalid => invalid[0]) + +for (const test of cases) { + suite.add(`inc(${test})`, function () { + inc(test, 'release') + }) +} + +for (const test of invalidCases) { + suite.add(`invalid inc(${test})`, function () { + inc(test, 'release') + }) +} + +suite + .on('cycle', function (event) { + console.log(String(event.target)) + }) + .run({ async: false }) diff --git a/benchmarks/bench-parse.js b/benchmarks/bench-parse.js index af10ec52..8f37fb28 100644 --- a/benchmarks/bench-parse.js +++ b/benchmarks/bench-parse.js @@ -2,11 +2,12 @@ const Benchmark = require('benchmark') const parse = require('../functions/parse') -const { MAX_SAFE_INTEGER } = require('../internal/constants') +const validVersions = require('../test/fixtures/valid-versions') +const invalidVersions = require('../test/fixtures/invalid-versions') const suite = new Benchmark.Suite() -const cases = ['1.2.1', '1.2.2-4', '1.2.3-pre'] -const invalidCases = [`${MAX_SAFE_INTEGER}0.0.0`, 'hello, world', 'xyz'] +const cases = validVersions.map(invalid => invalid[0]) +const invalidCases = invalidVersions.map(invalid => invalid[0]) for (const test of cases) { suite.add(`parse(${test})`, function () { diff --git a/benchmarks/bench-valid.js b/benchmarks/bench-valid.js new file mode 100644 index 00000000..72fd6f36 --- /dev/null +++ b/benchmarks/bench-valid.js @@ -0,0 +1,28 @@ +'use strict' + +const Benchmark = require('benchmark') +const invalidVersions = require('../test/fixtures/invalid-versions') +const valid = require('../functions/valid') +const validVersions = require('../test/fixtures/valid-versions') +const suite = new Benchmark.Suite() + +const cases = validVersions.map(invalid => invalid[0]) +const invalidCases = invalidVersions.map(invalid => invalid[0]) + +for (const test of cases) { + suite.add(`valid(${test})`, function () { + valid(test) + }) +} + +for (const test of invalidCases) { + suite.add(`invalid valid(${test})`, function () { + valid(test) + }) +} + +suite + .on('cycle', function (event) { + console.log(String(event.target)) + }) + .run({ async: false }) diff --git a/classes/semver.js b/classes/semver.js index 2efba0f4..98a46f3b 100644 --- a/classes/semver.js +++ b/classes/semver.js @@ -6,6 +6,15 @@ const { safeRe: re, t } = require('../internal/re') const parseOptions = require('../internal/parse-options') const { compareIdentifiers } = require('../internal/identifiers') + +function handleErrorOnSemver (semver, errorMessage, noThrow) { + if (!noThrow) { + throw new TypeError(errorMessage) + } else { + semver.errorMessage = errorMessage + } +} + class SemVer { constructor (version, options) { options = parseOptions(options) @@ -18,13 +27,11 @@ class SemVer { version = version.version } } else if (typeof version !== 'string') { - throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`) + return handleErrorOnSemver(this, `Invalid version. Must be a string. Got type "${typeof version}".`, options.noThrow) } if (version.length > MAX_LENGTH) { - throw new TypeError( - `version is longer than ${MAX_LENGTH} characters` - ) + return handleErrorOnSemver(this, `version is longer than ${MAX_LENGTH} characters`, options.noThrow) } debug('SemVer', version, options) @@ -37,7 +44,7 @@ class SemVer { const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) if (!m) { - throw new TypeError(`Invalid Version: ${version}`) + return handleErrorOnSemver(this, `Invalid Version: ${version}`, options.noThrow) } this.raw = version @@ -48,15 +55,15 @@ class SemVer { this.patch = +m[3] if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError('Invalid major version') + return handleErrorOnSemver(this, 'Invalid major version', options.noThrow) } if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError('Invalid minor version') + return handleErrorOnSemver(this, 'Invalid minor version', options.noThrow) } if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError('Invalid patch version') + return handleErrorOnSemver(this, 'Invalid patch version', options.noThrow) } // numberify any prerelease numeric ids diff --git a/functions/inc.js b/functions/inc.js index ff999e9d..30e3dcf8 100644 --- a/functions/inc.js +++ b/functions/inc.js @@ -1,6 +1,8 @@ 'use strict' const SemVer = require('../classes/semver') +const parseOptions = require('../internal/parse-options') +const parse = require('./parse') const inc = (version, release, options, identifier, identifierBase) => { if (typeof (options) === 'string') { @@ -9,11 +11,21 @@ const inc = (version, release, options, identifier, identifierBase) => { options = undefined } + const parsedOptions = parseOptions(options) + const parsed = parse( + version instanceof SemVer ? version.version : version, + parsedOptions.noThrow ? parsedOptions : { + ...parsedOptions, + noThrow: true, + } + ) + + if (parsed === null) { + return null + } + try { - return new SemVer( - version instanceof SemVer ? version.version : version, - options - ).inc(release, identifier, identifierBase).version + return parsed.inc(release, identifier, identifierBase).version } catch (er) { return null } diff --git a/functions/parse.js b/functions/parse.js index d544d33a..4c2f7f41 100644 --- a/functions/parse.js +++ b/functions/parse.js @@ -1,18 +1,26 @@ 'use strict' const SemVer = require('../classes/semver') +const parseOptions = require('../internal/parse-options') const parse = (version, options, throwErrors = false) => { if (version instanceof SemVer) { return version } - try { - return new SemVer(version, options) - } catch (er) { + + const parsedOptions = parseOptions(options) + const parsed = new SemVer(version, parsedOptions.noThrow ? parsedOptions : { + ...parsedOptions, + noThrow: true, + }) + + if (parsed.errorMessage) { if (!throwErrors) { return null } - throw er + throw new TypeError(parsed.errorMessage) } + + return parsed } module.exports = parse diff --git a/test/classes/semver.js b/test/classes/semver.js index 61119745..f420ef89 100644 --- a/test/classes/semver.js +++ b/test/classes/semver.js @@ -97,6 +97,19 @@ test('invalid version numbers', (t) => { t.end() }) +test('invalid version numbers without throw', (t) => { + ['1.2.3.4', 'NOT VALID', 1.2, null, 'Infinity.NaN.Infinity'].forEach((v) => { + const parsed = new SemVer(v, undefined, false) + + t.strictSame(parsed.errorMessage, typeof v === 'string' + ? `Invalid Version: ${v}` + : `Invalid version. Must be a string. Got type "${typeof v}".` + ) + }) + + t.end() +}) + test('incrementing', t => { t.plan(increments.length) increments.forEach(([ diff --git a/test/functions/inc.js b/test/functions/inc.js index 8c5f9ead..5e898b85 100644 --- a/test/functions/inc.js +++ b/test/functions/inc.js @@ -8,7 +8,7 @@ const increments = require('../fixtures/increments.js') test('increment versions test', (t) => { increments.forEach(([pre, what, wanted, options, id, base]) => { const found = inc(pre, what, options, id, base) - const cmd = `inc(${pre}, ${what}, ${id}, ${base})` + const cmd = `inc(${pre}, ${what}, ${options}, ${id}, ${base})` t.equal(found, wanted, `${cmd} === ${wanted}`) const parsed = parse(pre, options) @@ -45,3 +45,16 @@ test('increment versions test', (t) => { t.end() }) + +test('special increment version test', (t) => { + [ + ['1.2.3', 'prepatch', '1.2.4-foo.1', 'foo', '1', undefined], + ['1.2.3', 'prepatch', '1.2.4-foo.1', {}, 'foo', '1'], + ].forEach(([pre, what, wanted, options, id, base]) => { + const found = inc(pre, what, options, id, base) + const cmd = `inc(${pre}, ${what}, ${options}, ${id}, ${base})` + t.equal(found, wanted, `${cmd} === ${wanted}`) + }) + + t.end() +})