diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index eb399fee0567bc..d745ec86c9502e 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -543,7 +543,7 @@ pub const Tree = struct { for (this_dependencies) |dep_id| { const dep = builder.dependencies[dep_id]; if (dep.name_hash != dependency.name_hash) continue; - if (builder.resolutions[dep_id] != package_id) { + if (builder.resolutions[dep_id] != package_id and !dependency.behavior.isPeer()) { if (as_defined and !dep.behavior.isPeer()) { builder.maybeReportError("Package \"{}@{}\" has a dependency loop\n Resolution: \"{}@{}\"\n Dependency: \"{}@{}\"", .{ builder.packageName(package_id), diff --git a/test/cli/install/registry/bun-install-registry.test.ts b/test/cli/install/registry/bun-install-registry.test.ts index 932e0ae6f7713c..c7c575dd0d8343 100644 --- a/test/cli/install/registry/bun-install-registry.test.ts +++ b/test/cli/install/registry/bun-install-registry.test.ts @@ -180,6 +180,87 @@ test("dependency from root satisfies range from dependency", async () => { expect(await exited).toBe(0); }); +test("peerDependency in child npm dependency should not maintain old version when package is upgraded", async () => { + await writeFile( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + version: "1.0.0", + dependencies: { + "peer-deps-fixed": "1.0.0", + "no-deps": "1.0.0", + }, + }), + ); + + var { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + + expect(stderr).toBeDefined(); + var err = await new Response(stderr).text(); + expect(stdout).toBeDefined(); + var out = await new Response(stdout).text(); + expect(err).toContain("Saved lockfile"); + expect(err).not.toContain("not found"); + expect(err).not.toContain("error:"); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + " + no-deps@1.0.0", + " + peer-deps-fixed@1.0.0", + "", + " 2 packages installed", + ]); + expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ + name: "no-deps", + version: "1.0.0", + } as any); + expect(await exited).toBe(0); + + await writeFile( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + version: "1.0.0", + dependencies: { + "peer-deps-fixed": "1.0.0", + "no-deps": "1.0.1", // upgrade the package + }, + }), + ); + + ({ stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + })); + + err = await new Response(stderr).text(); + out = await new Response(stdout).text(); + expect(err).not.toContain("not found"); + expect(err).not.toContain("error:"); + expect(await file(join(packageDir, "node_modules", "no-deps", "package.json")).json()).toEqual({ + name: "no-deps", + version: "1.0.1", + } as any); + expect(await exists(join(packageDir, "node_modules", "peer-deps-fixed", "node_modules"))).toBeFalse(); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + " + no-deps@1.0.1", + "", + " 1 package installed", + ]); + expect(await exited).toBe(0); +}); + test("package added after install", async () => { await writeFile( join(packageDir, "package.json"), @@ -3997,48 +4078,46 @@ describe("yarn tests", () => { expect(await exited).toBe(0); }); - test.todo( - "it should install in such a way that two identical packages with different peer dependencies are different instances", - async () => { - await writeFile( - join(packageDir, "package.json"), - JSON.stringify({ - name: "foo", - version: "1.0.0", - dependencies: { - "provides-peer-deps-1-0-0": "1.0.0", - "provides-peer-deps-2-0-0": "1.0.0", - }, - }), - ); + test("it should install in such a way that two identical packages with different peer dependencies are different instances", async () => { + await writeFile( + join(packageDir, "package.json"), + JSON.stringify({ + name: "foo", + version: "1.0.0", + dependencies: { + "provides-peer-deps-1-0-0": "1.0.0", + "provides-peer-deps-2-0-0": "1.0.0", + }, + }), + ); - var { stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "install"], - cwd: packageDir, - stdout: null, - stdin: "pipe", - stderr: "pipe", - env, - }); + var { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: packageDir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); - var err = await new Response(stderr).text(); - var out = await new Response(stdout).text(); - expect(err).toContain("Saved lockfile"); - expect(err).not.toContain("error:"); - expect(err).not.toContain("not found"); - expect(err).not.toContain("incorrect peer dependency"); - expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ - "", - " + provides-peer-deps-1-0-0@1.0.0", - " + provides-peer-deps-2-0-0@1.0.0", - "", - " 5 packages installed", - ]); - expect(await exited).toBe(0); + var err = await new Response(stderr).text(); + var out = await new Response(stdout).text(); + expect(err).toContain("Saved lockfile"); + expect(err).not.toContain("error:"); + expect(err).not.toContain("not found"); + expect(err).not.toContain("incorrect peer dependency"); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + " + provides-peer-deps-1-0-0@1.0.0", + " + provides-peer-deps-2-0-0@1.0.0", + "", + " 5 packages installed", + ]); + expect(await exited).toBe(0); - await writeFile( - join(packageDir, "test.js"), - `console.log( + await writeFile( + join(packageDir, "test.js"), + `console.log( require("provides-peer-deps-1-0-0").dependencies["peer-deps"] === require("provides-peer-deps-2-0-0").dependencies["peer-deps"] ); @@ -4086,24 +4165,23 @@ describe("yarn tests", () => { }, }) );`, - ); + ); - ({ stdout, stderr, exited } = spawn({ - cmd: [bunExe(), "test.js"], - cwd: packageDir, - stdout: null, - stdin: "pipe", - stderr: "pipe", - env, - })); + ({ stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "test.js"], + cwd: packageDir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + })); - err = await new Response(stderr).text(); - out = await new Response(stdout).text(); - expect(out).toBe("true\ntrue\ntrue"); - expect(err).toBeEmpty(); - expect(await exited).toBe(0); - }, - ); + err = await new Response(stderr).text(); + out = await new Response(stdout).text(); + expect(out).toBe("true\ntrue\nfalse\n"); + expect(err).toBeEmpty(); + expect(await exited).toBe(0); + }); test("it should install in such a way that two identical packages with the same peer dependencies are the same instances (simple)", async () => { await writeFile(