diff --git a/semver.js b/semver.js index dd528000..639d9bc3 100644 --- a/semver.js +++ b/semver.js @@ -704,6 +704,48 @@ Comparator.prototype.test = function(version) { return cmp(version, this.operator, this.semver, this.loose); }; +Comparator.prototype.intersects = function(comp, loose, platform) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required'); + } + + var rangeTmp; + + if (this.operator === '') { + rangeTmp = new Range(comp.value, loose, platform); + return satisfies(this.value, rangeTmp, loose, platform); + } else if (comp.operator === '') { + rangeTmp = new Range(this.value, loose, platform); + return satisfies(comp.semver, rangeTmp, loose, platform); + } + + var sameDirectionIncreasing = (this.operator === '>=' || this.operator === '>') && (comp.operator === '>=' || comp.operator === '>'); + var sameDirectionDecreasing = (this.operator === '<=' || this.operator === '<') && (comp.operator === '<=' || comp.operator === '<'); + var sameSemVer = this.semver.raw === comp.semver.raw; + var differentDirectionsInclusive = (this.operator === '>=' || this.operator === '<=') && (comp.operator === '>=' || comp.operator === '<='); + var oppositeDirectionsLessThan = cmp(this.semver, '<', comp.semver, loose) && + ((this.operator === '>=' || this.operator === '>') && (comp.operator === '<=' || comp.operator === '<')); + var oppositeDirectionsGreaterThan = cmp(this.semver, '>', comp.semver, loose) && + ((this.operator === '<=' || this.operator === '<') && (comp.operator === '>=' || comp.operator === '>')); + + return sameDirectionIncreasing || sameDirectionDecreasing || (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan; +}; + +Comparator.prototype.satisfiesRange = function(range, loose, platform) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required'); + } + + var comp = this; + + return range.set.some(function(comparators) { + return comparators.every(function(comparator) { + return comp.intersects(comparator, loose, platform); + }); + }); +}; + exports.Range = Range; function Range(range, loose) { @@ -783,6 +825,18 @@ Range.prototype.parseRange = function(range) { return set; }; +Range.prototype.intersects = function(range, loose, platform) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required'); + } + + return this.set.some(function(comparators) { + return comparators.every(function(comparator) { + return comparator.satisfiesRange(range, loose, platform); + }); + }); +}; + // Mostly just for testing and legacy API reasons exports.toComparators = toComparators; function toComparators(range, loose) { @@ -793,56 +847,6 @@ function toComparators(range, loose) { }); } -exports.comparatorsIntersect = comparatorsIntersect; -function comparatorsIntersect(compA, compB, loose, platform) { - compA = new Comparator(compA, loose); - compB = new Comparator(compB, loose); - - if (compA.operator === '') { - var rangeB = new Range(compB.value, loose, platform); - return satisfies(compA.value, rangeB, loose, platform); - } else if (compB.operator === '') { - var rangeA = new Range(compA.value, loose, platform); - return satisfies(compB.semver, rangeA, loose, platform); - } - // Same direction increasing - return ((compA.operator === '>=' || compA.operator === '>') && (compB.operator === '>=' || compB.operator === '>')) || - // Same direction decreasing - ((compA.operator === '<=' || compA.operator === '<') && (compB.operator === '<=' || compB.operator === '<')) || - // Different directions, same semver and inclusive operator - (compA.semver.raw === compB.semver.raw && - (compA.operator === '>=' || compA.operator === '<=') && (compB.operator === '>=' || compB.operator === '<=')) || - // Opposite matching directions - (cmp(compA.semver, '<', compB.semver, loose) && - ((compA.operator === '>=' || compA.operator === '>') && (compB.operator === '<=' || compB.operator === '<'))) || - (cmp(compA.semver, '>', compB.semver, loose) && - ((compA.operator === '<=' || compA.operator === '<') && (compB.operator === '>=' || compB.operator === '>'))); -} - -exports.comparatorSatisfiesRange = comparatorSatisfiesRange; -function comparatorSatisfiesRange(comp, range, loose, platform) { - comp = new Comparator(comp, loose); - range = new Range(range, loose, platform); - - return range.set.some(function(comparators) { - return comparators.every(function(comparator) { - return comparatorsIntersect(comparator, comp, loose, platform); - }); - }); -} - -exports.rangesIntersect = rangesIntersect; -function rangesIntersect(rangeA, rangeB, loose, platform) { - rangeA = new Range(rangeA, loose, platform); - rangeB = new Range(rangeB, loose, platform); - - return rangeA.set.some(function(comparators) { - return comparators.every(function(comparator) { - return comparatorSatisfiesRange(comparator, rangeB, loose, platform); - }); - }); -} - // comprised of xranges, tildes, stars, and gtlt's at this point. // already replaced the hyphen ranges // turn into a set of JUST comparators. diff --git a/test/index.js b/test/index.js index c440a6df..cabfda0c 100644 --- a/test/index.js +++ b/test/index.js @@ -18,6 +18,7 @@ var replaceStars = semver.replaceStars; var toComparators = semver.toComparators; var SemVer = semver.SemVer; var Range = semver.Range; +var Comparator = semver.Comparator; test('\ncomparison tests', function(t) { // [version1, version2] @@ -744,15 +745,26 @@ test('\nintersect comparators', function(t) { ['<=2.0.0', '>1.0.0', true], ['<=1.0.0', '>=2.0.0', false] ].forEach(function(v) { - var comparator1 = v[0]; - var comparator2 = v[1]; + var comparator1 = new Comparator(v[0]); + var comparator2 = new Comparator(v[1]); var expect = v[2]; - var actual = semver.comparatorsIntersect(comparator1, comparator2); - t.equal(actual, expect); + + var actual1 = comparator1.intersects(comparator2); + var actual2 = comparator2.intersects(comparator1); + t.equal(actual1, expect); + t.equal(actual2, expect); }); t.end(); }); +test('\nmissing comparator parameter in intersect comparators', function(t) { + t.throws(function() { + new Comparator('>1.0.0').intersects(); + }, new TypeError('a Comparator is required'), + 'throws type error'); + t.end(); +}); + test('\ncomparator satisfies range', function(t) { [ ['1.3.0', '1.3.0 || <1.0.0 >2.0.0', true], @@ -760,29 +772,48 @@ test('\ncomparator satisfies range', function(t) { ['>=1.3.0', '<1.3.0', false], ['<1.3.0', '>=1.3.0', false] ].forEach(function(v) { - var comparator = v[0]; - var range = v[1]; + var comparator = new Comparator(v[0]); + var range = new Range(v[1]); var expect = v[2]; - var actual = semver.comparatorSatisfiesRange(comparator, range); + var actual = comparator.satisfiesRange(range); t.equal(actual, expect); }); t.end(); }); +test('\nmissing range parameter in comparator satisfies range', function(t) { + t.throws(function() { + new Comparator('>1.0.0').satisfiesRange(); + }, new TypeError('a Range is required'), + 'throws type error'); + t.end(); +}); + test('\nranges intersect', function(t) { [ ['1.3.0 || <1.0.0 >2.0.0', '1.3.0 || <1.0.0 >2.0.0', true], ['<1.0.0 >2.0.0', '>0.0.0', true], ['<1.0.0 >2.0.0', '>1.4.0 <1.6.0', false], ['<1.0.0 >2.0.0', '>1.4.0 <1.6.0 || 2.0.0', false], + ['>1.0.0 <=2.0.0', '2.0.0', true], ['<1.0.0 >=2.0.0', '2.1.0', false], ['<1.0.0 >=2.0.0', '>1.4.0 <1.6.0 || 2.0.0', false] ].forEach(function(v) { - var range1 = v[0]; - var range2 = v[1]; + var range1 = new Range(v[0]); + var range2 = new Range(v[1]); var expect = v[2]; - var actual = semver.rangesIntersect(range1, range2); - t.equal(actual, expect); + var actual1 = range1.intersects(range2); + var actual2 = range2.intersects(range1); + t.equal(actual1, expect); + t.equal(actual2, expect); }); t.end(); }); + +test('\nmissing range parameter in range intersect', function(t) { + t.throws(function() { + new Range('1.0.0').intersects(); + }, new TypeError('a Range is required'), + 'throws type error'); + t.end(); +});