diff --git a/classes/range.js b/classes/range.js index 766d505a..a7d6556f 100644 --- a/classes/range.js +++ b/classes/range.js @@ -299,6 +299,10 @@ const replaceTildes = (comp, options) => { const replaceTilde = (comp, options) => { const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] + // if we're including prereleases in the match, then the lower bound is + // -0, the lowest possible prerelease value, just like x-ranges and carets. + // this keeps `~1.2` equivalent to the `1.2.x` x-range it's documented as. + const z = options.includePrerelease ? '-0' : '' return comp.replace(r, (_, M, m, p, pr) => { debug('tilde', comp, _, M, m, p, pr) let ret @@ -306,10 +310,10 @@ const replaceTilde = (comp, options) => { if (isX(M)) { ret = '' } else if (isX(m)) { - ret = `>=${M}.0.0 <${+M + 1}.0.0-0` + ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0` } else if (isX(p)) { // ~1.2 == >=1.2.0 <1.3.0-0 - ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0` + ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0` } else if (pr) { debug('replaceTilde pr', pr) ret = `>=${M}.${m}.${p}-${pr diff --git a/test/fixtures/range-include.js b/test/fixtures/range-include.js index ab2b836c..3df851b8 100644 --- a/test/fixtures/range-include.js +++ b/test/fixtures/range-include.js @@ -112,6 +112,13 @@ module.exports = [ ['2.x', '2.1.0-pre.0', { includePrerelease: true }], ['1.1.x', '1.1.0-a', { includePrerelease: true }], ['1.1.x', '1.1.1-a', { includePrerelease: true }], + // tilde ranges are documented as equivalent to the matching x-range, + // so with includePrerelease they must accept the same prereleases. #512 + ['~1.1', '1.1.0-a', { includePrerelease: true }], + ['~1.1.x', '1.1.0-a', { includePrerelease: true }], + ['~1.1', '1.1.1-a', { includePrerelease: true }], + ['~2', '2.0.0-pre.0', { includePrerelease: true }], + ['~2.x', '2.0.0-pre.0', { includePrerelease: true }], ['*', '1.0.0-rc1', { includePrerelease: true }], ['^1.0.0-0', '1.0.1-rc1', { includePrerelease: true }], ['^1.0.0-rc2', '1.0.1-rc1', { includePrerelease: true }], diff --git a/test/fixtures/range-parse.js b/test/fixtures/range-parse.js index c99ca40a..1c69c96d 100644 --- a/test/fixtures/range-parse.js +++ b/test/fixtures/range-parse.js @@ -57,6 +57,18 @@ module.exports = [ ['~> 1', '>=1.0.0 <2.0.0-0'], ['~1.0', '>=1.0.0 <1.1.0-0'], ['~ 1.0', '>=1.0.0 <1.1.0-0'], + // tilde with includePrerelease must lower-bound at -0 just like the + // equivalent x-range (e.g. ~1.2 is documented as the same as 1.2.x), + // so that prereleases such as 1.2.0-rc match. See #512. + ['~1', '>=1.0.0-0 <2.0.0-0', { includePrerelease: true }], + ['~1.x', '>=1.0.0-0 <2.0.0-0', { includePrerelease: true }], + ['~1.2', '>=1.2.0-0 <1.3.0-0', { includePrerelease: true }], + ['~1.2.x', '>=1.2.0-0 <1.3.0-0', { includePrerelease: true }], + ['~0.0', '<0.1.0-0', { includePrerelease: true }], + // a fully-specified tilde keeps its exact lower bound (matches caret), + // and an explicit prerelease is preserved as-is + ['~1.2.3', '>=1.2.3 <1.3.0-0', { includePrerelease: true }], + ['~1.2.3-beta.4', '>=1.2.3-beta.4 <1.3.0-0', { includePrerelease: true }], ['^0', '<1.0.0-0'], ['^ 1', '>=1.0.0 <2.0.0-0'], ['^0.1', '>=0.1.0 <0.2.0-0'],