Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
10 changes: 8 additions & 2 deletions src/install/lockfile/Package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -789,9 +789,11 @@ impl Package<u64> {

let mut behavior = group.behavior;
if is_peer {
// a peer dependency on `bun` is satisfied by the running runtime (#39755)
behavior.set(
Behavior::OPTIONAL,
(i as u32) < package_version.non_optional_peer_dependencies_start,
(i as u32) < package_version.non_optional_peer_dependencies_start
|| key.slice(&manifest.string_buf) == b"bun",
);
}
if package_version_ptr.all_dependencies_bundled() {
Expand Down Expand Up @@ -2937,7 +2939,11 @@ impl Package<u64> {
)? {
let mut dep = dep_;
if group.behavior.is_peer()
&& optional_peer_dependencies.swap_remove(&external_name.hash)
&& (optional_peer_dependencies.swap_remove(&external_name.hash)
// a peer dependency on `bun` is satisfied by the running runtime (#39755)
|| (!FEATURES.is_main
&& !FEATURES.is_workspace
&& key == b"bun"))
{
dep.behavior.insert(Behavior::OPTIONAL);
}
Expand Down
123 changes: 123 additions & 0 deletions test/cli/install/bun-install-registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,129 @@ describe("text lockfile", () => {
});
});

// #39755: a transitive peer dependency on `bun` is satisfied by the running
// runtime and must not auto-install the npm `bun` package.
describe("peer dependency on bun", () => {
async function run(...args: string[]) {
await using proc = spawn({
cmd: [bunExe(), ...args],
cwd: packageDir,
stdout: "pipe",
stderr: "pipe",
env,
});
const [out, err, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(err).not.toContain("error:");
expect(exitCode).toBe(0);
return out;
}

test("transitive peer is not auto-installed", async () => {
await write(
packageJson,
JSON.stringify({
name: "foo",
dependencies: {
"peer-on-bun": "1.0.0",
},
}),
);

await run("install", "--save-text-lockfile");

expect(await exists(join(packageDir, "node_modules", "peer-on-bun"))).toBeTrue();
expect(await exists(join(packageDir, "node_modules", "bun"))).toBeFalse();

const firstLockfile = (await file(join(packageDir, "bun.lock")).text()).replaceAll(
/localhost:\d+/g,
"localhost:1234",
);

await rm(join(packageDir, "node_modules"), { recursive: true, force: true });

// an install from the saved lockfile keeps the peer unbound
await run("install");

expect(await exists(join(packageDir, "node_modules", "bun"))).toBeFalse();
expect((await file(join(packageDir, "bun.lock")).text()).replaceAll(/localhost:\d+/g, "localhost:1234")).toBe(
firstLockfile,
);
});

test("transitive peer of a folder dependency is not auto-installed", async () => {
await Promise.all([
write(
packageJson,
JSON.stringify({
name: "foo",
dependencies: {
"peer-on-bun-folder": "file:./peer-on-bun-folder",
},
}),
),
write(
join(packageDir, "peer-on-bun-folder", "package.json"),
JSON.stringify({
name: "peer-on-bun-folder",
version: "1.0.0",
peerDependencies: {
bun: ">=1.0.0",
},
}),
),
]);

await run("install");

expect(await exists(join(packageDir, "node_modules", "peer-on-bun-folder"))).toBeTrue();
expect(await exists(join(packageDir, "node_modules", "bun"))).toBeFalse();
});

test("explicit root dependency on bun still installs and binds the peer", async () => {
await write(
packageJson,
JSON.stringify({
name: "foo",
dependencies: {
"peer-on-bun": "1.0.0",
bun: "1.0.0",
},
}),
);

await run("install", "--save-text-lockfile");

expect(await file(join(packageDir, "node_modules", "bun", "package.json")).json()).toMatchObject({
name: "bun",
version: "1.0.0",
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// the optional peer binds to the explicitly installed bun@1.0.0
const why = await run("why", "bun");
expect(why).toContain("bun@1.0.0");
expect(why).toContain("peer-on-bun@1.0.0");
});

test("root peerDependencies on bun is still auto-installed", async () => {
await write(
packageJson,
JSON.stringify({
name: "foo",
peerDependencies: {
bun: ">=1.0.0",
},
}),
);

await run("install");

expect(await file(join(packageDir, "node_modules", "bun", "package.json")).json()).toMatchObject({
name: "bun",
version: "1.1.0",
});
});
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

test("--lockfile-only", async () => {
await Promise.all([
write(
Expand Down
Binary file added test/cli/install/registry/packages/bun/bun-1.0.0.tgz
Binary file not shown.
Binary file added test/cli/install/registry/packages/bun/bun-1.1.0.tgz
Binary file not shown.
29 changes: 29 additions & 0 deletions test/cli/install/registry/packages/bun/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"_id": "bun",
"name": "bun",
"dist-tags": {
"latest": "1.1.0"
},
"versions": {
"1.0.0": {
"name": "bun",
"version": "1.0.0",
"_id": "bun@1.0.0",
"dist": {
"integrity": "sha512-3VNhhAGhbsTKgZPaVDiegkf9P4SxsiVBiv2Z7IY5boYZh0l7J1u8ry+O5LcyYwy+djBTf34pbAHrGYSPOQ9mYA==",
"shasum": "8d3144fc1fa9f9f73b586e9b24e955e651cf5a4d",
"tarball": "http://localhost:4873/bun/-/bun-1.0.0.tgz"
}
},
"1.1.0": {
"name": "bun",
"version": "1.1.0",
"_id": "bun@1.1.0",
"dist": {
"integrity": "sha512-/iVApoHt3UStLynyDTB8jZhItmHeFfWSGqPJurllfrdxAZvL/RbXgPMUrpaiZFidQ+/77u61zSgzqVWSN8ghGw==",
"shasum": "60db82c6fbc62da41963bf18562053bde4168d43",
"tarball": "http://localhost:4873/bun/-/bun-1.1.0.tgz"
}
}
}
}
57 changes: 57 additions & 0 deletions test/cli/install/registry/packages/create-bun-peer-packages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env bun
/**
* Generates the fixtures used by the "peer dependency on bun" tests in
* bun-install-registry.test.ts.
*
* - bun@1.0.0 / bun@1.1.0 stand-ins for the npm `bun` package
* - peer-on-bun@1.0.0 non-optional peer on bun (">=1.0.0")
*/

import { mkdir, writeFile } from "fs/promises";
import { join } from "path";

const packagesDir = import.meta.dir;

type Manifest = {
version: string;
dependencies?: Record<string, string>;
peerDependencies?: Record<string, string>;
};

const packages: Record<string, Manifest[]> = {
bun: [{ version: "1.0.0" }, { version: "1.1.0" }],
"peer-on-bun": [{ version: "1.0.0", peerDependencies: { bun: ">=1.0.0" } }],
};

for (const [name, manifests] of Object.entries(packages)) {
const dir = join(packagesDir, name);
await mkdir(dir, { recursive: true });

const versions: Record<string, object> = {};
let latest = "";
for (const manifest of manifests) {
const pkgJson = { name, ...manifest };
const files: Record<string, string> = { "package/package.json": JSON.stringify(pkgJson, null, 2) };
const tarball = join(dir, `${name}-${manifest.version}.tgz`);
await Bun.Archive.write(tarball, files, { compress: "gzip" });

const bytes = await Bun.file(tarball).bytes();
versions[manifest.version] = {
...pkgJson,
_id: `${name}@${manifest.version}`,
dist: {
integrity: `sha512-${Buffer.from(new Bun.CryptoHasher("sha512").update(bytes).digest()).toString("base64")}`,
shasum: new Bun.CryptoHasher("sha1").update(bytes).digest("hex"),
tarball: `http://localhost:4873/${name}/-/${name}-${manifest.version}.tgz`,
},
};
latest = manifest.version;
}

await writeFile(
join(dir, "package.json"),
JSON.stringify({ _id: name, name, "dist-tags": { latest }, versions }, null, 2),
);
}

console.log("Created bun-peer test packages");
22 changes: 22 additions & 0 deletions test/cli/install/registry/packages/peer-on-bun/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"_id": "peer-on-bun",
"name": "peer-on-bun",
"dist-tags": {
"latest": "1.0.0"
},
"versions": {
"1.0.0": {
"name": "peer-on-bun",
"version": "1.0.0",
"peerDependencies": {
"bun": ">=1.0.0"
},
"_id": "peer-on-bun@1.0.0",
"dist": {
"integrity": "sha512-r+jJvV2iX8gP4+nhiuMh4U6VI0m0q0ZmAYvx4dS2+2WyLWDGAfru4oj/UF80NfMgw4pqN+GKOK+K2JDZG9zQ4w==",
"shasum": "7dc24a1eff8fc4102c96d01f9dc34def606a77e0",
"tarball": "http://localhost:4873/peer-on-bun/-/peer-on-bun-1.0.0.tgz"
}
}
}
}
Binary file not shown.
Loading