From 5cf24b53477a71c73172eb03954d5f7160e4a84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Wed, 5 Apr 2023 23:12:37 -0300 Subject: [PATCH 1/3] fix: faster parse options using cached objects --- internal/parse-options.js | 60 ++++++++++++++++++++++++++++------ test/internal/parse-options.js | 15 +++++++++ 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/internal/parse-options.js b/internal/parse-options.js index bbd9ec77..f7e6df6c 100644 --- a/internal/parse-options.js +++ b/internal/parse-options.js @@ -1,11 +1,51 @@ -// parse out just the options we care about so we always get a consistent -// obj with keys in a consistent order. -const opts = ['includePrerelease', 'loose', 'rtl'] -const parseOptions = options => - !options ? {} - : typeof options !== 'object' ? { loose: true } - : opts.filter(k => options[k]).reduce((o, k) => { - o[k] = true - return o - }, {}) +// parse out just the options we care about +const isParsedConfigSymbol = Symbol('isParsedConfig') +const var1 = Object.freeze({ includePrerelease: true, loose: true, rtl: true, [isParsedConfigSymbol]: true }) +const var2 = Object.freeze({ includePrerelease: true, loose: true, [isParsedConfigSymbol]: true }) +const var3 = Object.freeze({ includePrerelease: true, rtl: true, [isParsedConfigSymbol]: true }) +const var4 = Object.freeze({ includePrerelease: true, [isParsedConfigSymbol]: true }) +const var5 = Object.freeze({ loose: true, rtl: true, [isParsedConfigSymbol]: true }) +const var6 = Object.freeze({ loose: true, [isParsedConfigSymbol]: true }) +const var7 = Object.freeze({ rtl: true, [isParsedConfigSymbol]: true }) +const emptyOpts = Object.freeze({ [isParsedConfigSymbol]: true }) + +const parseOptions = options => { + if (!options) { + return emptyOpts + } + + if (typeof options !== 'object') { + return var6 + } + + if (options[isParsedConfigSymbol]) { + return options + } + + if (options.includePrerelease) { + if (options.loose && options.rtl) { + return var1 + } + + if (options.loose) { + return var2 + } + + if (options.rtl) { + return var3 + } + + return var4 + } else if (options.loose) { + if (options.rtl) { + return var5 + } + + return var6 + } else if (options.rtl) { + return var7 + } else { + return emptyOpts + } +} module.exports = parseOptions diff --git a/test/internal/parse-options.js b/test/internal/parse-options.js index 6213423d..e219eb71 100644 --- a/test/internal/parse-options.js +++ b/test/internal/parse-options.js @@ -27,5 +27,20 @@ t.test('objects only include truthy flags we know about, set to true', t => { rtl: true, includePrerelease: true, }) + t.strictSame(parseOptions({ loose: true }), { loose: true }) + t.strictSame(parseOptions({ rtl: true }), { rtl: true }) + t.strictSame(parseOptions({ includePrerelease: true }), { includePrerelease: true }) + t.strictSame(parseOptions({ loose: true, rtl: true }), { loose: true, rtl: true }) + t.strictSame(parseOptions({ loose: true, includePrerelease: true }), { loose: true, includePrerelease: true }) + t.strictSame(parseOptions({ rtl: true, includePrerelease: true }), { rtl: true, includePrerelease: true }) + t.end() +}) + +t.test('should skip validation when options is already parsed', t => { + const options = { loose: true, rtl: true } + const parsedOptions = parseOptions(options) + + t.equal(parseOptions(parsedOptions) === parsedOptions, true) + t.not(parseOptions(options) === parsedOptions, false) t.end() }) From 7e4e016c73499d51c9a195c348f76f5fb3f2c876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Wed, 5 Apr 2023 23:30:43 -0300 Subject: [PATCH 2/3] fix: reuse parsed options --- bin/semver.js | 3 ++- classes/comparator.js | 7 ------- functions/parse.js | 3 +-- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/bin/semver.js b/bin/semver.js index 8d1b5572..97757314 100755 --- a/bin/semver.js +++ b/bin/semver.js @@ -24,6 +24,7 @@ let rtl = false let identifier const semver = require('../') +const parseOptions = require('../internal/parse-options') let reverse = false @@ -88,7 +89,7 @@ const main = () => { } } - options = { loose: loose, includePrerelease: includePrerelease, rtl: rtl } + options = parseOptions({ loose, includePrerelease, rtl }) versions = versions.map((v) => { return coerce ? (semver.coerce(v, options) || { version: v }).version : v diff --git a/classes/comparator.js b/classes/comparator.js index 62cd204d..54ce1394 100644 --- a/classes/comparator.js +++ b/classes/comparator.js @@ -78,13 +78,6 @@ class Comparator { throw new TypeError('a Comparator is required') } - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false, - } - } - if (this.operator === '') { if (this.value === '') { return true diff --git a/functions/parse.js b/functions/parse.js index a66663aa..988f2e87 100644 --- a/functions/parse.js +++ b/functions/parse.js @@ -4,8 +4,6 @@ const SemVer = require('../classes/semver') const parseOptions = require('../internal/parse-options') const parse = (version, options) => { - options = parseOptions(options) - if (version instanceof SemVer) { return version } @@ -18,6 +16,7 @@ const parse = (version, options) => { return null } + options = parseOptions(options) const r = options.loose ? re[t.LOOSE] : re[t.FULL] if (!r.test(version)) { return null From d0c20e34836a3eceb95651688c6586d476e7638f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Thu, 6 Apr 2023 14:10:10 -0300 Subject: [PATCH 3/3] fix: simplify parse options --- internal/parse-options.js | 44 ++++------------------------------ test/internal/parse-options.js | 33 ++++++++++++------------- 2 files changed, 19 insertions(+), 58 deletions(-) diff --git a/internal/parse-options.js b/internal/parse-options.js index f7e6df6c..10d64ce0 100644 --- a/internal/parse-options.js +++ b/internal/parse-options.js @@ -1,51 +1,15 @@ // parse out just the options we care about -const isParsedConfigSymbol = Symbol('isParsedConfig') -const var1 = Object.freeze({ includePrerelease: true, loose: true, rtl: true, [isParsedConfigSymbol]: true }) -const var2 = Object.freeze({ includePrerelease: true, loose: true, [isParsedConfigSymbol]: true }) -const var3 = Object.freeze({ includePrerelease: true, rtl: true, [isParsedConfigSymbol]: true }) -const var4 = Object.freeze({ includePrerelease: true, [isParsedConfigSymbol]: true }) -const var5 = Object.freeze({ loose: true, rtl: true, [isParsedConfigSymbol]: true }) -const var6 = Object.freeze({ loose: true, [isParsedConfigSymbol]: true }) -const var7 = Object.freeze({ rtl: true, [isParsedConfigSymbol]: true }) -const emptyOpts = Object.freeze({ [isParsedConfigSymbol]: true }) - +const looseOption = Object.freeze({ loose: true }) +const emptyOpts = Object.freeze({ }) const parseOptions = options => { if (!options) { return emptyOpts } if (typeof options !== 'object') { - return var6 - } - - if (options[isParsedConfigSymbol]) { - return options + return looseOption } - if (options.includePrerelease) { - if (options.loose && options.rtl) { - return var1 - } - - if (options.loose) { - return var2 - } - - if (options.rtl) { - return var3 - } - - return var4 - } else if (options.loose) { - if (options.rtl) { - return var5 - } - - return var6 - } else if (options.rtl) { - return var7 - } else { - return emptyOpts - } + return options } module.exports = parseOptions diff --git a/test/internal/parse-options.js b/test/internal/parse-options.js index e219eb71..2400537d 100644 --- a/test/internal/parse-options.js +++ b/test/internal/parse-options.js @@ -18,29 +18,26 @@ t.test('truthy non-objects always loose mode, for backwards comp', t => { t.end() }) -t.test('objects only include truthy flags we know about, set to true', t => { - t.strictSame(parseOptions(/asdf/), {}) - t.strictSame(parseOptions(new Error('hello')), {}) - t.strictSame(parseOptions({ loose: true, a: 1, rtl: false }), { loose: true }) +t.test('any object passed is returned', t => { + t.strictSame(parseOptions(/asdf/), /asdf/) + t.strictSame(parseOptions(new Error('hello')), new Error('hello')) + t.strictSame(parseOptions({ loose: true, a: 1, rtl: false }), { loose: true, a: 1, rtl: false }) t.strictSame(parseOptions({ loose: 1, rtl: 2, includePrerelease: 10 }), { - loose: true, - rtl: true, - includePrerelease: true, + loose: 1, + rtl: 2, + includePrerelease: 10, }) t.strictSame(parseOptions({ loose: true }), { loose: true }) t.strictSame(parseOptions({ rtl: true }), { rtl: true }) t.strictSame(parseOptions({ includePrerelease: true }), { includePrerelease: true }) t.strictSame(parseOptions({ loose: true, rtl: true }), { loose: true, rtl: true }) - t.strictSame(parseOptions({ loose: true, includePrerelease: true }), { loose: true, includePrerelease: true }) - t.strictSame(parseOptions({ rtl: true, includePrerelease: true }), { rtl: true, includePrerelease: true }) - t.end() -}) - -t.test('should skip validation when options is already parsed', t => { - const options = { loose: true, rtl: true } - const parsedOptions = parseOptions(options) - - t.equal(parseOptions(parsedOptions) === parsedOptions, true) - t.not(parseOptions(options) === parsedOptions, false) + t.strictSame(parseOptions({ loose: true, includePrerelease: true }), { + loose: true, + includePrerelease: true, + }) + t.strictSame(parseOptions({ rtl: true, includePrerelease: true }), { + rtl: true, + includePrerelease: true, + }) t.end() })