aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Ai Hoshino <ambiguous404@gmail.com> 2023-08-31 09:49:09 +0800
committerGravatar GitHub <noreply@github.com> 2023-08-30 18:49:09 -0700
commit9334fbe9b4facdfa9057a25e88e998f1fd2e346a (patch)
treefb45c28d87d90898181ea511735571a693203cda
parent0a5d2a8195fbbaab7ff1f40ad54ba94726bcc104 (diff)
downloadbun-9334fbe9b4facdfa9057a25e88e998f1fd2e346a.tar.gz
bun-9334fbe9b4facdfa9057a25e88e998f1fd2e346a.tar.zst
bun-9334fbe9b4facdfa9057a25e88e998f1fd2e346a.zip
fix(install): resolve semver matching with pre-release tags. (#4412)
Close: #4398
-rw-r--r--src/install/semver.zig43
-rw-r--r--test/cli/install/bun-install.test.ts44
2 files changed, 81 insertions, 6 deletions
diff --git a/src/install/semver.zig b/src/install/semver.zig
index 04818cfd3..e773f005a 100644
--- a/src/install/semver.zig
+++ b/src/install/semver.zig
@@ -1266,7 +1266,7 @@ pub const Range = struct {
return lhs.op == rhs.op and lhs.version.eql(rhs.version);
}
- pub fn satisfies(this: Comparator, version: Version) bool {
+ pub fn satisfies(this: Comparator, version: Version, include_pre: bool) bool {
const order = version.orderWithoutTag(this.version);
return switch (order) {
@@ -1275,11 +1275,11 @@ pub const Range = struct {
else => false,
},
.gt => switch (this.op) {
- .gt, .gte => true,
+ .gt, .gte => if (!include_pre) false else true,
else => false,
},
.lt => switch (this.op) {
- .lt, .lte => true,
+ .lt, .lte => if (!include_pre) false else true,
else => false,
},
};
@@ -1287,15 +1287,46 @@ pub const Range = struct {
};
pub fn satisfies(this: Range, version: Version) bool {
- if (!this.hasLeft()) {
+ const has_left = this.hasLeft();
+ const has_right = this.hasRight();
+
+ if (!has_left) {
return true;
}
- if (!this.left.satisfies(version)) {
+ // When the boundaries of a range do not include a pre-release tag on either side,
+ // we should not consider that '7.0.0-rc2' < "7.0.0"
+ // ```
+ // > semver.satisfies("7.0.0-rc2", "<=7.0.0")
+ // false
+ // > semver.satisfies("7.0.0-rc2", ">=7.0.0")
+ // false
+ // > semver.satisfies("7.0.0-rc2", "<=7.0.0-rc2")
+ // true
+ // > semver.satisfies("7.0.0-rc2", ">=7.0.0-rc2")
+ // true
+ // ```
+ //
+ // - https://github.com/npm/node-semver#prerelease-tags
+ // - https://github.com/npm/node-semver/blob/cce61804ba6f997225a1267135c06676fe0524d2/classes/range.js#L505-L539
+ var include_pre = true;
+ if (version.tag.hasPre()) {
+ if (!has_right) {
+ if (!this.left.version.tag.hasPre()) {
+ include_pre = false;
+ }
+ } else {
+ if (!this.left.version.tag.hasPre() and !this.right.version.tag.hasPre()) {
+ include_pre = false;
+ }
+ }
+ }
+
+ if (!this.left.satisfies(version, include_pre)) {
return false;
}
- if (this.hasRight() and !this.right.satisfies(version)) {
+ if (has_right and !this.right.satisfies(version, include_pre)) {
return false;
}
diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts
index a98c22e3b..28edac092 100644
--- a/test/cli/install/bun-install.test.ts
+++ b/test/cli/install/bun-install.test.ts
@@ -1628,6 +1628,50 @@ it("should handle ^0.0.2b_4+cafe_b0ba in dependencies", async () => {
await access(join(package_dir, "bun.lockb"));
});
+it("should handle caret range in dependencies when the registry has prereleased packages, issue#4398", async () => {
+ const urls: string[] = [];
+ setHandler(dummyRegistry(urls, { "6.3.0": { as: "0.0.2" }, "7.0.0-rc2": { as: "0.0.3" } }));
+ await writeFile(
+ join(package_dir, "package.json"),
+ JSON.stringify({
+ name: "foo",
+ version: "0.0.1",
+ dependencies: {
+ bar: "^6.3.0",
+ },
+ }),
+ );
+ const { stdout, stderr, exited } = spawn({
+ cmd: [bunExe(), "install"],
+ cwd: package_dir,
+ stdout: null,
+ stdin: "pipe",
+ stderr: "pipe",
+ env,
+ });
+ expect(stderr).toBeDefined();
+ const err = await new Response(stderr).text();
+ expect(err).toContain("Saved lockfile");
+ expect(err).not.toContain("error:");
+ expect(stdout).toBeDefined();
+ const out = await new Response(stdout).text();
+ expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
+ " + bar@6.3.0",
+ "",
+ " 1 packages installed",
+ ]);
+ expect(await exited).toBe(0);
+ expect(urls.sort()).toEqual([`${root_url}/bar`, `${root_url}/bar-0.0.2.tgz`]);
+ expect(requested).toBe(2);
+ expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".cache", "bar"]);
+ expect(await readdirSorted(join(package_dir, "node_modules", "bar"))).toEqual(["package.json"]);
+ expect(await file(join(package_dir, "node_modules", "bar", "package.json")).json()).toEqual({
+ name: "bar",
+ version: "0.0.2",
+ });
+ await access(join(package_dir, "bun.lockb"));
+});
+
it("should prefer latest-tagged dependency", async () => {
const urls: string[] = [];
setHandler(