All files / semver / range_intersects.ts

100.00% Branches 54/54
100.00% Functions 4/4
100.00% Lines 63/63
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
 
 
x25
x25
x25
 
 
x338
x338
x338
 
x338
x338
 
x338
 
x52
x30
x30
x338
x34
x22
x22
 
x252
 
x338
x338
x338
x338
x338
x338
x338
x338
x338
x338
x338
x338
x338
 
x338
x338
x338
x338
x338
x338
 
x171
x171
 
x336
x171
x171
 
x336
 
x336
x83
x83
x83
x12
x12
x83
x71
x324
x336
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x25
x171
x171
x165
x179
x212
x212
 
x179
x165
x171
x171



































































































// Copyright 2018-2026 the Deno authors. MIT license.
// This module is browser compatible.
import { isWildcardComparator } from "./_shared.ts";
import { compare } from "./compare.ts";
import { satisfies } from "./satisfies.ts";
import type { Comparator, Range } from "./types.ts";

function comparatorIntersects(
  comparator1: Comparator,
  comparator2: Comparator,
): boolean {
  const op0 = comparator1.operator;
  const op1 = comparator2.operator;

  if (op0 === undefined) {
    // if comparator1 is empty comparator, then returns true
    if (isWildcardComparator(comparator1)) return true;
    return satisfies(comparator1, [[comparator2]]);
  }
  if (op1 === undefined) {
    if (isWildcardComparator(comparator2)) return true;
    return satisfies(comparator2, [[comparator1]]);
  }

  const cmp = compare(comparator1, comparator2);

  const sameDirectionIncreasing = (op0 === ">=" || op0 === ">") &&
    (op1 === ">=" || op1 === ">");
  const sameDirectionDecreasing = (op0 === "<=" || op0 === "<") &&
    (op1 === "<=" || op1 === "<");
  const sameSemVer = cmp === 0;
  const differentDirectionsInclusive = (op0 === ">=" || op0 === "<=") &&
    (op1 === ">=" || op1 === "<=");
  const oppositeDirectionsLessThan = cmp === -1 &&
    (op0 === ">=" || op0 === ">") &&
    (op1 === "<=" || op1 === "<");
  const oppositeDirectionsGreaterThan = cmp === 1 &&
    (op0 === "<=" || op0 === "<") &&
    (op1 === ">=" || op1 === ">");

  return sameDirectionIncreasing ||
    sameDirectionDecreasing ||
    (sameSemVer && differentDirectionsInclusive) ||
    oppositeDirectionsLessThan ||
    oppositeDirectionsGreaterThan;
}

function rangesSatisfiable(ranges: Range[]): boolean {
  return ranges.every((r) => {
    // For each OR at least one AND must be satisfiable
    return r.some((comparators) => comparatorsSatisfiable(comparators));
  });
}

function comparatorsSatisfiable(comparators: Comparator[]): boolean {
  // Comparators are satisfiable if they all intersect with each other
  for (let i = 0; i < comparators.length - 1; i++) {
    const comparator1 = comparators[i]!;
    for (const comparator2 of comparators.slice(i + 1)) {
      if (!comparatorIntersects(comparator1, comparator2)) {
        return false;
      }
    }
  }
  return true;
}

/**
 * The ranges intersect every range of AND comparators intersects with a least
 * one range of OR ranges.
 *
 * @example Usage
 * ```ts
 * import { parseRange, rangeIntersects } from "@std/semver";
 * import { assert } from "@std/assert";
 *
 * const range1 = parseRange(">=1.0.0 <2.0.0");
 * const range2 = parseRange(">=1.0.0 <1.2.3");
 * const range3 = parseRange(">=1.2.3 <2.0.0");
 *
 * assert(rangeIntersects(range1, range2));
 * assert(rangeIntersects(range1, range3));
 * assert(!rangeIntersects(range2, range3));
 * ```
 *
 * @param range1 range 0
 * @param range2 range 1
 * @returns returns true if the given ranges intersect, false otherwise
 */
export function rangeIntersects(range1: Range, range2: Range): boolean {
  return rangesSatisfiable([range1, range2]) &&
    range1.some((range10) => {
      return range2.some((r11) => {
        return range10.every((comparator1) => {
          return r11.every((comparator2) =>
            comparatorIntersects(comparator1, comparator2)
          );
        });
      });
    });
}