From 429920883c967bf0cb0a94761a2cb883b0fcbbda Mon Sep 17 00:00:00 2001 From: i529015 Date: Wed, 24 Sep 2025 13:21:26 +0530 Subject: [PATCH 1/3] x-range partial version build metadata support --- classes/range.js | 7 +++++- test/classes/range.js | 50 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/classes/range.js b/classes/range.js index f80c2359..9455781b 100644 --- a/classes/range.js +++ b/classes/range.js @@ -33,7 +33,7 @@ class Range { // First reduce all whitespace as much as possible so we do not have to rely // on potentially slow regexes like \s*. This is then stored and used for // future error messages as well. - this.raw = range.trim().replace(SPACE_CHARACTERS, ' ') + this.raw = range.trim().replace(SPACE_CHARACTERS, ' ').replace(/\+[^ ]*/g, '') // First, split on || this.set = this.raw @@ -399,6 +399,11 @@ const replaceXRange = (comp, options) => { const xp = xm || isX(p) const anyX = xp + // Disallow prerelease with any X-range or partial version + if ((xM || xm || xp) && pr) { + throw new TypeError('Prerelease not allowed with X-ranges or partial versions') + } + if (gtlt === '=' && anyX) { gtlt = '' } diff --git a/test/classes/range.js b/test/classes/range.js index d012e50c..98e48863 100644 --- a/test/classes/range.js +++ b/test/classes/range.js @@ -126,3 +126,53 @@ test('cache', (t) => { t.equal(r2.set[0][cached], true) // Will be true, showing it's cached. t.end() }) + +test('Build metadata is allowed and ignored for X-ranges and partials', t => { + const buildCases = [ + '1.x.x+build >2.x.x+build', + '>=1.x.x+build <2.x.x+build', + '1.x.x+build || 2.x.x+build', + '1.x.x+build.123', + '1.x.x+meta-data', + '1.x.x+build.123 >2.x.x+meta-data', + '1.x.x+build <2.x.x+meta', + '>1.x.x+build <=2.x.x+meta', + ' 1.x.x+build >2.x.x+build ', + ] + t.plan(buildCases.length) + buildCases.forEach(range => { + t.doesNotThrow(() => new Range(range), `${range} should not throw`) + }) +}) + +test('Build metadata with prerelease in X-ranges/partials throws', t => { + const cases = [ + '1.x.x-alpha+build', + '1.x-alpha+build', + '1-alpha+build', + '>1.x.x-alpha+build', + '>=1.x.x-alpha+build <2.x.x+build', + '1.x.x-alpha+build || 2.x.x+build', + ] + t.plan(cases.length) + cases.forEach(range => { + t.throws(() => new Range(range), TypeError, `${range} should throw TypeError`) + }) +}) + +test('Prerelease is NOT allowed with X-ranges or partials', t => { + const prereleaseCases = [ + '1.x-alpha', + '1-alpha', + '1.x.x-alpha', + '>1.x-alpha', + '>1-alpha', + '>1.x.x-alpha', + '1.x.x-alpha <2.x.x-alpha', + '>1.x.x-alpha <=2.x-alpha', + ] + t.plan(prereleaseCases.length) + prereleaseCases.forEach(range => { + t.throws(() => new Range(range), TypeError, `${range} should throw TypeError`) + }) +}) From 36439e4545fdeba679d4274abf0792c85c0e4331 Mon Sep 17 00:00:00 2001 From: i529015 Date: Fri, 26 Sep 2025 10:23:38 +0530 Subject: [PATCH 2/3] using re[t.BUILD] in parseComparator --- classes/range.js | 8 ++------ test/classes/range.js | 46 ++++++++++++++++++++----------------------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/classes/range.js b/classes/range.js index 9455781b..94629ce6 100644 --- a/classes/range.js +++ b/classes/range.js @@ -33,7 +33,7 @@ class Range { // First reduce all whitespace as much as possible so we do not have to rely // on potentially slow regexes like \s*. This is then stored and used for // future error messages as well. - this.raw = range.trim().replace(SPACE_CHARACTERS, ' ').replace(/\+[^ ]*/g, '') + this.raw = range.trim().replace(SPACE_CHARACTERS, ' ') // First, split on || this.set = this.raw @@ -255,6 +255,7 @@ const isSatisfiable = (comparators, options) => { // already replaced the hyphen ranges // turn into a set of JUST comparators. const parseComparator = (comp, options) => { + comp = comp.replace(re[t.BUILD], '') debug('comp', comp, options) comp = replaceCarets(comp, options) debug('caret', comp) @@ -399,11 +400,6 @@ const replaceXRange = (comp, options) => { const xp = xm || isX(p) const anyX = xp - // Disallow prerelease with any X-range or partial version - if ((xM || xm || xp) && pr) { - throw new TypeError('Prerelease not allowed with X-ranges or partial versions') - } - if (gtlt === '=' && anyX) { gtlt = '' } diff --git a/test/classes/range.js b/test/classes/range.js index 98e48863..62655b9d 100644 --- a/test/classes/range.js +++ b/test/classes/range.js @@ -129,50 +129,46 @@ test('cache', (t) => { test('Build metadata is allowed and ignored for X-ranges and partials', t => { const buildCases = [ - '1.x.x+build >2.x.x+build', - '>=1.x.x+build <2.x.x+build', + '1.x.x+build >2.x+build', + '>=1.x+build <2.x.x+build', '1.x.x+build || 2.x.x+build', - '1.x.x+build.123', + '1.x+build.123', '1.x.x+meta-data', '1.x.x+build.123 >2.x.x+meta-data', '1.x.x+build <2.x.x+meta', - '>1.x.x+build <=2.x.x+meta', + '>1.x+build <=2.x.x+meta', ' 1.x.x+build >2.x.x+build ', + '^1.x+build', + '^1.x.x+build', + '^1.2.x+build', + '^1.x+meta-data', + '^1.x.x+build.123', + '~1.x+build', + '~1.x.x+build', + '~1.2.x+build', + '~1.x+meta-data', + '~1.x.x+build.123', + '^1.x.x+build || ~2.x.x+meta', + '~1.x.x+build >2.x+meta', + '^1.x+build.123 <2.x.x+meta-data', ] t.plan(buildCases.length) buildCases.forEach(range => { t.doesNotThrow(() => new Range(range), `${range} should not throw`) }) + t.end() }) -test('Build metadata with prerelease in X-ranges/partials throws', t => { +test('Build metadata with prerelease in X-ranges/partials', t => { const cases = [ '1.x.x-alpha+build', - '1.x-alpha+build', - '1-alpha+build', '>1.x.x-alpha+build', '>=1.x.x-alpha+build <2.x.x+build', '1.x.x-alpha+build || 2.x.x+build', ] t.plan(cases.length) cases.forEach(range => { - t.throws(() => new Range(range), TypeError, `${range} should throw TypeError`) - }) -}) - -test('Prerelease is NOT allowed with X-ranges or partials', t => { - const prereleaseCases = [ - '1.x-alpha', - '1-alpha', - '1.x.x-alpha', - '>1.x-alpha', - '>1-alpha', - '>1.x.x-alpha', - '1.x.x-alpha <2.x.x-alpha', - '>1.x.x-alpha <=2.x-alpha', - ] - t.plan(prereleaseCases.length) - prereleaseCases.forEach(range => { - t.throws(() => new Range(range), TypeError, `${range} should throw TypeError`) + t.doesNotThrow(() => new Range(range), TypeError, `${range} should not throw`) }) + t.end() }) From 8848854e33cb75067416174cca277f595ab235c5 Mon Sep 17 00:00:00 2001 From: i529015 Date: Sat, 27 Sep 2025 12:03:58 +0530 Subject: [PATCH 3/3] moved test cases to fixture(range-build.js) --- test/classes/range.js | 46 --------------------------------- test/fixtures/range-build.js | 50 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 46 deletions(-) create mode 100644 test/fixtures/range-build.js diff --git a/test/classes/range.js b/test/classes/range.js index 62655b9d..d012e50c 100644 --- a/test/classes/range.js +++ b/test/classes/range.js @@ -126,49 +126,3 @@ test('cache', (t) => { t.equal(r2.set[0][cached], true) // Will be true, showing it's cached. t.end() }) - -test('Build metadata is allowed and ignored for X-ranges and partials', t => { - const buildCases = [ - '1.x.x+build >2.x+build', - '>=1.x+build <2.x.x+build', - '1.x.x+build || 2.x.x+build', - '1.x+build.123', - '1.x.x+meta-data', - '1.x.x+build.123 >2.x.x+meta-data', - '1.x.x+build <2.x.x+meta', - '>1.x+build <=2.x.x+meta', - ' 1.x.x+build >2.x.x+build ', - '^1.x+build', - '^1.x.x+build', - '^1.2.x+build', - '^1.x+meta-data', - '^1.x.x+build.123', - '~1.x+build', - '~1.x.x+build', - '~1.2.x+build', - '~1.x+meta-data', - '~1.x.x+build.123', - '^1.x.x+build || ~2.x.x+meta', - '~1.x.x+build >2.x+meta', - '^1.x+build.123 <2.x.x+meta-data', - ] - t.plan(buildCases.length) - buildCases.forEach(range => { - t.doesNotThrow(() => new Range(range), `${range} should not throw`) - }) - t.end() -}) - -test('Build metadata with prerelease in X-ranges/partials', t => { - const cases = [ - '1.x.x-alpha+build', - '>1.x.x-alpha+build', - '>=1.x.x-alpha+build <2.x.x+build', - '1.x.x-alpha+build || 2.x.x+build', - ] - t.plan(cases.length) - cases.forEach(range => { - t.doesNotThrow(() => new Range(range), TypeError, `${range} should not throw`) - }) - t.end() -}) diff --git a/test/fixtures/range-build.js b/test/fixtures/range-build.js new file mode 100644 index 00000000..b761b84c --- /dev/null +++ b/test/fixtures/range-build.js @@ -0,0 +1,50 @@ +'use strict' + +const { test } = require('tap') +const Range = require('../../classes/range') + +test('Build metadata is allowed and ignored for X-ranges and partials', t => { + const buildCases = [ + '1.x.x+build >2.x+build', + '>=1.x+build <2.x.x+build', + '1.x.x+build || 2.x.x+build', + '1.x+build.123', + '1.x.x+meta-data', + '1.x.x+build.123 >2.x.x+meta-data', + '1.x.x+build <2.x.x+meta', + '>1.x+build <=2.x.x+meta', + ' 1.x.x+build >2.x.x+build ', + '^1.x+build', + '^1.x.x+build', + '^1.2.x+build', + '^1.x+meta-data', + '^1.x.x+build.123', + '~1.x+build', + '~1.x.x+build', + '~1.2.x+build', + '~1.x+meta-data', + '~1.x.x+build.123', + '^1.x.x+build || ~2.x.x+meta', + '~1.x.x+build >2.x+meta', + '^1.x+build.123 <2.x.x+meta-data', + ] + t.plan(buildCases.length) + buildCases.forEach(range => { + t.doesNotThrow(() => new Range(range), `${range} should not throw`) + }) + t.end() +}) + +test('Build metadata with prerelease in X-ranges/partials', t => { + const cases = [ + '1.x.x-alpha+build', + '>1.x.x-alpha+build', + '>=1.x.x-alpha+build <2.x.x+build', + '1.x.x-alpha+build || 2.x.x+build', + ] + t.plan(cases.length) + cases.forEach(range => { + t.doesNotThrow(() => new Range(range), TypeError, `${range} should not throw`) + }) + t.end() +})