mirror of
https://github.com/oven-sh/bun
synced 2026-02-14 12:51:54 +00:00
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>
This commit is contained in:
@@ -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) {
|
||||
|
||||
67
test/regression/issue/24190.test.ts
Normal file
67
test/regression/issue/24190.test.ts
Normal 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);
|
||||
});
|
||||
Reference in New Issue
Block a user