Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
81048847c1 Fix bun link failing in workspace with sibling dependencies
When running `bun link <package>` from within a workspace subdirectory
that depends on sibling workspaces, Bun would fail with an error like:
"Workspace dependency 'bar' not found".

The issue was that the `link` subcommand was not finding the workspace
root, so it would try to resolve workspace dependencies relative to the
current working directory instead of the workspace root.

This fix:
1. Makes `shouldChdirToRoot()` return true for all subcommands,
   including `link`, so that the workspace root is properly found
2. Updates `original_package_json_path` to point to the workspace
   root package.json when a workspace is found

Fixes #24190

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 19:27:21 +00:00
2 changed files with 74 additions and 4 deletions

View File

@@ -195,7 +195,6 @@ pub const Subcommand = enum {
// TODO: make all subcommands find root and chdir
pub fn shouldChdirToRoot(this: Subcommand) bool {
return switch (this) {
.link => false,
else => true,
};
}
@@ -599,6 +598,7 @@ pub fn init(
var workspace_name_hash: ?PackageNameHash = null;
var root_package_json_name_at_time_of_init: []const u8 = "";
var found_workspace_root = false;
// Step 1. Find the nearest package.json directory
//
@@ -680,7 +680,6 @@ pub fn init(
const child_cwd = strings.withoutSuffixComptime(original_package_json_path, std.fs.path.sep_str ++ "package.json");
// Check if this is a workspace; if so, use root package
var found = false;
if (subcommand.shouldChdirToRoot()) {
if (!created_package_json) {
while (std.fs.path.dirname(this_cwd)) |parent| : (this_cwd = parent) {
@@ -696,7 +695,7 @@ pub fn init(
) catch {
continue;
};
defer if (!found) json_file.close();
defer if (!found_workspace_root) json_file.close();
const json_stat_size = try json_file.getEndPos();
const json_buf = try ctx.allocator.alloc(u8, json_stat_size + 64);
defer ctx.allocator.free(json_buf);
@@ -746,7 +745,7 @@ pub fn init(
if (strings.eqlLong(maybe_workspace_path, path, true)) {
fs.top_level_dir = try bun.default_allocator.dupeZ(u8, parent);
found = true;
found_workspace_root = true;
child_json.close();
if (comptime Environment.isWindows) {
try json_file.seekTo(0);
@@ -772,6 +771,10 @@ pub fn init(
cwd_buf[fs.top_level_dir.len] = 0;
fs.top_level_dir = cwd_buf[0..fs.top_level_dir.len :0];
root_package_json_path = try bun.getFdPathZ(.fromStdFile(root_package_json_file), &root_package_json_path_buf);
// Update original_package_json_path to point to the workspace root when found
if (found_workspace_root) {
original_package_json_path = root_package_json_path;
}
const entries_option = try fs.fs.readDirectory(fs.top_level_dir, null, 0, true);
if (entries_option.* == .err) {

View File

@@ -0,0 +1,67 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
test("bun link <package> within workspace that depends on sibling workspace", async () => {
using dir = tempDir("bun-link-workspace", {
"work/package.json": JSON.stringify({ workspaces: ["foo", "bar"] }),
"work/foo/package.json": JSON.stringify({ name: "foo", dependencies: { bar: "workspace:*" } }),
"work/bar/package.json": JSON.stringify({ name: "bar" }),
"dep/package.json": JSON.stringify({ name: "dep" }),
});
// First, run `bun install` in the workspace root to set up the workspace
await using installProc = Bun.spawn({
cmd: [bunExe(), "install"],
cwd: `${dir}/work`,
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [installStdout, installStderr, installExitCode] = await Promise.all([
installProc.stdout.text(),
installProc.stderr.text(),
installProc.exited,
]);
expect(installExitCode).toBe(0);
// Register the dep package as a linked package
await using linkProc = Bun.spawn({
cmd: [bunExe(), "link"],
cwd: `${dir}/dep`,
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [linkStdout, linkStderr, linkExitCode] = await Promise.all([
linkProc.stdout.text(),
linkProc.stderr.text(),
linkProc.exited,
]);
expect(linkStderr).not.toContain("Workspace dependency");
expect(linkStderr).not.toContain("not found");
expect(linkExitCode).toBe(0);
// Now try to link the dep package from within the foo workspace
// This should not fail with "Workspace dependency 'bar' not found"
await using linkDepProc = Bun.spawn({
cmd: [bunExe(), "link", "dep"],
cwd: `${dir}/work/foo`,
env: bunEnv,
stderr: "pipe",
stdout: "pipe",
});
const [linkDepStdout, linkDepStderr, linkDepExitCode] = await Promise.all([
linkDepProc.stdout.text(),
linkDepProc.stderr.text(),
linkDepProc.exited,
]);
expect(linkDepStderr).not.toContain("Workspace dependency");
expect(linkDepStderr).not.toContain("not found");
expect(linkDepExitCode).toBe(0);
});