Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deprecation(semver): deprecate SemVerRange, introduce Range #4161

Merged
merged 16 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion semver/comparator_format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ import { format } from "./format.ts";
*/
export function comparatorFormat(comparator: Comparator): string {
const { semver, operator } = comparator;
return `${operator}${format(semver)}`;
return `${operator}${format(semver ?? comparator)}`;
Copy link
Contributor

@iuioiua iuioiua Jan 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Can we please have tests for added functionality? We want to ensure that both SemVer and Comparator inputs work fine on all these functions, including those in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you mean exactly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some test cases. PTAL.

}
8 changes: 4 additions & 4 deletions semver/comparator_intersects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ export function comparatorIntersects(
c0: Comparator,
c1: Comparator,
): boolean {
const l0 = comparatorMin(c0.semver, c0.operator);
const l1 = comparatorMax(c0.semver, c0.operator);
const r0 = comparatorMin(c1.semver, c1.operator);
const r1 = comparatorMax(c1.semver, c1.operator);
const l0 = comparatorMin(c0.semver ?? c0, c0.operator);
const l1 = comparatorMax(c0.semver ?? c0, c0.operator);
const r0 = comparatorMin(c1.semver ?? c1, c1.operator);
const r1 = comparatorMax(c1.semver ?? c1, c1.operator);

// We calculate the min and max ranges of both comparators.
// The minimum min is 0.0.0, the maximum max is ANY.
Expand Down
65 changes: 64 additions & 1 deletion semver/comparator_intersects_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { parseComparator } from "./parse_comparator.ts";
import { comparatorIntersects } from "./comparator_intersects.ts";
import { rangeIntersects } from "./range_intersects.ts";

Deno.test("intersect", async (t) => {
Deno.test("comparatorIntersects() handles deprecated SemVerRange.ranges property", async (t) => {
const versions: [string, string, boolean][] = [
// One is a Version
["1.3.0", ">=1.3.0", true],
Expand Down Expand Up @@ -67,3 +67,66 @@ Deno.test("intersect", async (t) => {
});
}
});

Deno.test("comparatorIntersects()", async (t) => {
const versions: [string, string, boolean][] = [
// One is a Version
["1.3.0", ">=1.3.0", true],
["1.3.0", ">1.3.0", false],
[">=1.3.0", "1.3.0", true],
[">1.3.0", "1.3.0", false],

// Same direction increasing
[">1.3.0", ">1.2.0", true],
[">1.2.0", ">1.3.0", true],
[">=1.2.0", ">1.3.0", true],
[">1.2.0", ">=1.3.0", true],

// Same direction decreasing
["<1.3.0", "<1.2.0", true],
["<1.2.0", "<1.3.0", true],
["<=1.2.0", "<1.3.0", true],
["<1.2.0", "<=1.3.0", true],

// Different directions, same semver and inclusive operator
[">=1.3.0", "<=1.3.0", true],
[">=v1.3.0", "<=1.3.0", true],
[">=1.3.0", ">=1.3.0", true],
["<=1.3.0", "<=1.3.0", true],
["<=1.3.0", "<=v1.3.0", true],
[">1.3.0", "<=1.3.0", false],
[">=1.3.0", "<1.3.0", false],

// Opposite matching directions
[">1.0.0", "<2.0.0", true],
[">=1.0.0", "<2.0.0", true],
[">=1.0.0", "<=2.0.0", true],
[">1.0.0", "<=2.0.0", true],
["<=2.0.0", ">1.0.0", true],
["<=1.0.0", ">=2.0.0", false],
];
for (const v of versions) {
const comparator1 = parseComparator(v[0]);
const comparator2 = parseComparator(v[1]);
const expect = v[2];
await t.step({
name: `${v[0]} ${expect ? "∩" : "∁"} ${v[1]}`,
fn: () => {
const actual1 = comparatorIntersects(comparator1, comparator2);
const actual2 = comparatorIntersects(comparator2, comparator1);
const actual3 = rangeIntersects(
[[comparator1]],
[[comparator2]],
);
const actual4 = rangeIntersects(
[[comparator2]],
[[comparator1]],
);
assertEquals(actual1, expect);
assertEquals(actual2, expect);
assertEquals(actual3, expect);
assertEquals(actual4, expect);
},
});
}
});
21 changes: 20 additions & 1 deletion semver/comparator_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { parse } from "./parse.ts";
import { testRange } from "./test_range.ts";
import { parseComparator } from "./parse_comparator.ts";
import { comparatorFormat } from "./comparator_format.ts";
import { Comparator } from "./types.ts";

Deno.test({
name: "comparators",
Expand Down Expand Up @@ -161,7 +162,7 @@ Deno.test({
},
});

Deno.test("tostrings", function () {
Deno.test("comparatorFormat() handles semver inheritance", function () {
assertEquals(
comparatorFormat(parseComparator(">= v1.2.3")),
">=1.2.3",
Expand All @@ -171,3 +172,21 @@ Deno.test("tostrings", function () {
">=1.2.3-pre.1+b.2",
);
});

Deno.test("comparatorFormat() handles deprecated Comparator.semver property", function () {
const c1 = parseComparator(">= v1.2.3");
assertEquals(
comparatorFormat(
{ operator: c1.operator, semver: c1.semver } as Comparator,
),
">=1.2.3",
);
const c2 = parseComparator(">= v1.2.3-pre.1+b.2");

assertEquals(
comparatorFormat(
{ operator: c2.operator, semver: c2.semver } as Comparator,
),
">=1.2.3-pre.1+b.2",
);
});
2 changes: 2 additions & 0 deletions semver/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export const ANY: SemVer = {
*/
export const ALL: Comparator = {
operator: "",
...ANY,
semver: ANY,
};

Expand All @@ -75,5 +76,6 @@ export const ALL: Comparator = {
*/
export const NONE: Comparator = {
operator: "<",
...MIN,
semver: MIN,
};
8 changes: 5 additions & 3 deletions semver/format_range.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import type { SemVerRange } from "./types.ts";
import type { Range, SemVerRange } from "./types.ts";
import { comparatorFormat } from "./comparator_format.ts";

/**
Expand All @@ -8,7 +8,9 @@ import { comparatorFormat } from "./comparator_format.ts";
* @param range The range to format
* @returns A string representation of the range
*/
export function formatRange(range: SemVerRange): string {
return range.ranges.map((c) => c.map((c) => comparatorFormat(c)).join(" "))
export function formatRange(range: SemVerRange | Range): string {
return (Array.isArray(range) ? range : range.ranges).map((c) =>
c.map((c) => comparatorFormat(c)).join(" ")
)
.join("||");
}
68 changes: 68 additions & 0 deletions semver/format_range_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,71 @@ Deno.test({
}
},
});

Deno.test({
name: "formatRange() handles deprecated SemVerRange.ranges property",
fn: async (t) => {
const versions: [string, string][] = [
["1.0.0 - 2.0.0", ">=1.0.0 <=2.0.0"],
["1.0.0", "1.0.0"],
[">=*", "*"],
["", "*"],
["*", "*"],
[">=1.0.0", ">=1.0.0"],
[">1.0.0", ">1.0.0"],
["<=2.0.0", "<=2.0.0"],
["1", ">=1.0.0 <2.0.0"],
["<=2.0.0", "<=2.0.0"],
["<=2.0.0", "<=2.0.0"],
["<2.0.0", "<2.0.0"],
["<2.0.0", "<2.0.0"],
[">=0.1.97", ">=0.1.97"],
[">=0.1.97", ">=0.1.97"],
["0.1.20 || 1.2.4", "0.1.20||1.2.4"],
[">=0.2.3 || <0.0.1", ">=0.2.3||<0.0.1"],
[">=0.2.3 || <0.0.1", ">=0.2.3||<0.0.1"],
[">=0.2.3 || <0.0.1", ">=0.2.3||<0.0.1"],
["||", "*||*"],
["2.x.x", ">=2.0.0 <3.0.0"],
["1.2.x", ">=1.2.0 <1.3.0"],
["1.2.x || 2.x", ">=1.2.0 <1.3.0||>=2.0.0 <3.0.0"],
["1.2.x || 2.x", ">=1.2.0 <1.3.0||>=2.0.0 <3.0.0"],
["x", "*"],
["2.*.*", ">=2.0.0 <3.0.0"],
["1.2.*", ">=1.2.0 <1.3.0"],
["1.2.* || 2.*", ">=1.2.0 <1.3.0||>=2.0.0 <3.0.0"],
["2", ">=2.0.0 <3.0.0"],
["2.3", ">=2.3.0 <2.4.0"],
["~2.4", ">=2.4.0 <2.5.0"],
["~2.4", ">=2.4.0 <2.5.0"],
["~>3.2.1", ">=3.2.1 <3.3.0"],
["~1", ">=1.0.0 <2.0.0"],
["~>1", ">=1.0.0 <2.0.0"],
["~1.0", ">=1.0.0 <1.1.0"],
["^0", ">=0.0.0 <1.0.0"],
["^0.1", ">=0.1.0 <0.2.0"],
["^1.0", ">=1.0.0 <2.0.0"],
["^1.2", ">=1.2.0 <2.0.0"],
["^0.0.1", ">=0.0.1 <0.0.2"],
["^0.0.1-beta", ">=0.0.1-beta <0.0.2"],
["^0.1.2", ">=0.1.2 <0.2.0"],
["^1.2.3", ">=1.2.3 <2.0.0"],
["^1.2.3-beta.4", ">=1.2.3-beta.4 <2.0.0"],
["<1", "<1.0.0"],
[">=1", ">=1.0.0"],
["<1.2", "<1.2.0"],
["1", ">=1.0.0 <2.0.0"],
];

for (const [r, expected] of versions) {
await t.step({
name: `${r} -> ${expected}`,
fn: () => {
const range = parseRange(r);
const actual = formatRange({ ranges: range.ranges });
assertEquals(actual, expected);
},
});
}
},
});
4 changes: 2 additions & 2 deletions semver/gtr.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import type { SemVer, SemVerRange } from "./types.ts";
import type { Range, SemVer, SemVerRange } from "./types.ts";
import { rangeMax } from "./range_max.ts";
import { gt } from "./gt.ts";

/** Checks to see if the version is greater than all possible versions of the range. */
export function gtr(
version: SemVer,
range: SemVerRange,
range: SemVerRange | Range,
): boolean {
return gt(version, rangeMax(range));
}
18 changes: 18 additions & 0 deletions semver/is_range.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import type { Range } from "./types.ts";
import { isComparator } from "./is_comparator.ts";

/**
* Does a deep check on the object to determine if its a valid range.
*
* Objects with extra fields are still considered valid if they have at
* least the correct fields.
*
* Adds a type assertion if true.
* @param value The value to check if its a valid Range
* @returns True if its a valid Range otherwise false.
*/
export function isRange(value: unknown): value is Range {
return Array.isArray(value) &&
value.every((r) => Array.isArray(r) && r.every((c) => isComparator(c)));
}
20 changes: 20 additions & 0 deletions semver/is_range_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { assert } from "../assert/mod.ts";
import { ALL } from "./constants.ts";
import { isRange } from "./is_range.ts";

Deno.test({
name: "isRange()",
fn: async (t) => {
let i = 0;
const ranges: unknown[] = [[
[ALL],
]];
for (const r of ranges) {
await t.step(`${(i++).toString().padStart(2, "0")}`, () => {
const actual = isRange(r);
assert(actual);
});
}
},
});
6 changes: 4 additions & 2 deletions semver/is_semver_range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import { isComparator } from "./is_comparator.ts";
* Adds a type assertion if true.
* @param value The value to check if its a valid SemVerRange
* @returns True if its a valid SemVerRange otherwise false.
*
* @deprecated (will be removed in 0.214.0) Use {@linkcode isRange} instead.
timreichen marked this conversation as resolved.
Show resolved Hide resolved
*/
export function isSemVerRange(value: unknown): value is SemVerRange {
if (value === null || value === undefined) return false;
if (Array.isArray(value)) return false;
if (typeof value !== "object") return false;
const { ranges } = value as SemVerRange;
return (
Array.isArray(ranges),
ranges.every((r) => Array.isArray(r) && r.every((c) => isComparator(c)))
Array.isArray(ranges) &&
ranges.every((r) => Array.isArray(r) && r.every((c) => isComparator(c)))
);
}
4 changes: 2 additions & 2 deletions semver/ltr.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import type { SemVer, SemVerRange } from "./types.ts";
import type { Range, SemVer, SemVerRange } from "./types.ts";
import { lt } from "./lt.ts";
import { rangeMin } from "./range_min.ts";

/** Less than range comparison */
export function ltr(
version: SemVer,
range: SemVerRange,
range: SemVerRange | Range,
): boolean {
return lt(version, rangeMin(range));
}
4 changes: 2 additions & 2 deletions semver/max_satisfying.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import type { SemVer, SemVerRange } from "./types.ts";
import type { Range, SemVer, SemVerRange } from "./types.ts";
import { testRange } from "./test_range.ts";
import { gt } from "./gt.ts";

Expand All @@ -12,7 +12,7 @@ import { gt } from "./gt.ts";
*/
export function maxSatisfying(
versions: SemVer[],
range: SemVerRange,
range: SemVerRange | Range,
): SemVer | undefined {
let max;
for (const version of versions) {
Expand Down
4 changes: 2 additions & 2 deletions semver/min_satisfying.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import type { SemVer, SemVerRange } from "./types.ts";
import type { Range, SemVer, SemVerRange } from "./types.ts";
import { testRange } from "./test_range.ts";
import { lt } from "./lt.ts";

Expand All @@ -12,7 +12,7 @@ import { lt } from "./lt.ts";
*/
export function minSatisfying(
versions: SemVer[],
range: SemVerRange,
range: SemVerRange | Range,
): SemVer | undefined {
let min;
for (const version of versions) {
Expand Down
4 changes: 2 additions & 2 deletions semver/outside.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { gt } from "./gt.ts";
import { lt } from "./lt.ts";
import type { SemVer, SemVerRange } from "./types.ts";
import type { Range, SemVer, SemVerRange } from "./types.ts";
import { rangeMax } from "./range_max.ts";
import { rangeMin } from "./range_min.ts";

Expand All @@ -16,7 +16,7 @@ import { rangeMin } from "./range_min.ts";
*/
export function outside(
version: SemVer,
range: SemVerRange,
range: SemVerRange | Range,
hilo?: ">" | "<",
): boolean {
switch (hilo) {
Expand Down
2 changes: 1 addition & 1 deletion semver/parse_comparator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,5 @@ export function parseComparator(comparator: string): Comparator {
}
: ANY;

return { operator, semver };
return { operator, ...semver, semver };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forsee the output of this object being confusing to users during the deprecation period. But I don't see another viable way of doing this. Food for thought...

}
Loading