mirror of
https://github.com/oven-sh/bun
synced 2026-02-12 20:09:04 +00:00
fix bun link in workspace package (#6631)
* link workspace package * add test * more complete test
This commit is contained in:
committed by
Jarred Sumner
parent
c900d08bea
commit
7dbe01b822
@@ -5512,63 +5512,65 @@ pub const PackageManager = struct {
|
||||
const child_cwd = this_cwd;
|
||||
// Check if this is a workspace; if so, use root package
|
||||
var found = false;
|
||||
if (!created_package_json) {
|
||||
while (std.fs.path.dirname(this_cwd)) |parent| : (this_cwd = parent) {
|
||||
const parent_without_trailing_slash = strings.withoutTrailingSlash(parent);
|
||||
var buf2: [bun.MAX_PATH_BYTES + 1]u8 = undefined;
|
||||
@memcpy(buf2[0..parent_without_trailing_slash.len], parent_without_trailing_slash);
|
||||
buf2[parent_without_trailing_slash.len..buf2.len][0.."/package.json".len].* = "/package.json".*;
|
||||
buf2[parent_without_trailing_slash.len + "/package.json".len] = 0;
|
||||
if (comptime subcommand != .link) {
|
||||
if (!created_package_json) {
|
||||
while (std.fs.path.dirname(this_cwd)) |parent| : (this_cwd = parent) {
|
||||
const parent_without_trailing_slash = strings.withoutTrailingSlash(parent);
|
||||
var buf2: [bun.MAX_PATH_BYTES + 1]u8 = undefined;
|
||||
@memcpy(buf2[0..parent_without_trailing_slash.len], parent_without_trailing_slash);
|
||||
buf2[parent_without_trailing_slash.len..buf2.len][0.."/package.json".len].* = "/package.json".*;
|
||||
buf2[parent_without_trailing_slash.len + "/package.json".len] = 0;
|
||||
|
||||
const json_file = std.fs.cwd().openFileZ(
|
||||
buf2[0 .. parent_without_trailing_slash.len + "/package.json".len :0].ptr,
|
||||
.{ .mode = .read_write },
|
||||
) catch {
|
||||
continue;
|
||||
};
|
||||
defer if (!found) 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);
|
||||
const json_len = try json_file.preadAll(json_buf, 0);
|
||||
const json_path = try bun.getFdPath(json_file.handle, &package_json_cwd_buf);
|
||||
const json_source = logger.Source.initPathString(json_path, json_buf[0..json_len]);
|
||||
initializeStore();
|
||||
const json = try json_parser.ParseJSONUTF8(&json_source, ctx.log, ctx.allocator);
|
||||
if (json.asProperty("workspaces")) |prop| {
|
||||
const json_array = switch (prop.expr.data) {
|
||||
.e_array => |arr| arr,
|
||||
.e_object => |obj| if (obj.get("packages")) |packages| switch (packages.data) {
|
||||
.e_array => |arr| arr,
|
||||
else => break,
|
||||
} else break,
|
||||
else => break,
|
||||
const json_file = std.fs.cwd().openFileZ(
|
||||
buf2[0 .. parent_without_trailing_slash.len + "/package.json".len :0].ptr,
|
||||
.{ .mode = .read_write },
|
||||
) catch {
|
||||
continue;
|
||||
};
|
||||
var log = logger.Log.init(ctx.allocator);
|
||||
defer log.deinit();
|
||||
const workspace_packages_count = Package.processWorkspaceNamesArray(
|
||||
&workspace_names,
|
||||
ctx.allocator,
|
||||
&log,
|
||||
json_array,
|
||||
&json_source,
|
||||
prop.loc,
|
||||
null,
|
||||
) catch break;
|
||||
_ = workspace_packages_count;
|
||||
for (workspace_names.keys()) |path| {
|
||||
if (strings.eql(child_cwd, path)) {
|
||||
fs.top_level_dir = parent;
|
||||
if (comptime subcommand == .install) {
|
||||
found = true;
|
||||
child_json.close();
|
||||
break :brk json_file;
|
||||
} else {
|
||||
break :brk child_json;
|
||||
defer if (!found) 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);
|
||||
const json_len = try json_file.preadAll(json_buf, 0);
|
||||
const json_path = try bun.getFdPath(json_file.handle, &package_json_cwd_buf);
|
||||
const json_source = logger.Source.initPathString(json_path, json_buf[0..json_len]);
|
||||
initializeStore();
|
||||
const json = try json_parser.ParseJSONUTF8(&json_source, ctx.log, ctx.allocator);
|
||||
if (json.asProperty("workspaces")) |prop| {
|
||||
const json_array = switch (prop.expr.data) {
|
||||
.e_array => |arr| arr,
|
||||
.e_object => |obj| if (obj.get("packages")) |packages| switch (packages.data) {
|
||||
.e_array => |arr| arr,
|
||||
else => break,
|
||||
} else break,
|
||||
else => break,
|
||||
};
|
||||
var log = logger.Log.init(ctx.allocator);
|
||||
defer log.deinit();
|
||||
const workspace_packages_count = Package.processWorkspaceNamesArray(
|
||||
&workspace_names,
|
||||
ctx.allocator,
|
||||
&log,
|
||||
json_array,
|
||||
&json_source,
|
||||
prop.loc,
|
||||
null,
|
||||
) catch break;
|
||||
_ = workspace_packages_count;
|
||||
for (workspace_names.keys()) |path| {
|
||||
if (strings.eql(child_cwd, path)) {
|
||||
fs.top_level_dir = parent;
|
||||
if (comptime subcommand == .install) {
|
||||
found = true;
|
||||
child_json.close();
|
||||
break :brk json_file;
|
||||
} else {
|
||||
break :brk child_json;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { spawn } from "bun";
|
||||
import { spawn, file } from "bun";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, expect, it } from "bun:test";
|
||||
import { bunExe, bunEnv as env } from "harness";
|
||||
import { access, mkdtemp, readlink, realpath, rm, writeFile } from "fs/promises";
|
||||
import { access, mkdtemp, readlink, realpath, rm, writeFile, mkdir } from "fs/promises";
|
||||
import { basename, join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
import {
|
||||
@@ -27,6 +27,171 @@ afterEach(async () => {
|
||||
await dummyAfterEach();
|
||||
});
|
||||
|
||||
it("should link and unlink workspace package", async () => {
|
||||
await writeFile(
|
||||
join(link_dir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "foo",
|
||||
version: "1.0.0",
|
||||
workspaces: ["packages/*"],
|
||||
}),
|
||||
);
|
||||
await mkdir(join(link_dir, "packages", "moo"), { recursive: true });
|
||||
await mkdir(join(link_dir, "packages", "boba"), { recursive: true });
|
||||
await writeFile(
|
||||
join(link_dir, "packages", "moo", "package.json"),
|
||||
JSON.stringify({
|
||||
name: "moo",
|
||||
version: "0.0.1",
|
||||
}),
|
||||
);
|
||||
await writeFile(
|
||||
join(link_dir, "packages", "boba", "package.json"),
|
||||
JSON.stringify({
|
||||
name: "boba",
|
||||
version: "0.0.1",
|
||||
}),
|
||||
);
|
||||
var { stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: link_dir,
|
||||
stdout: null,
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env,
|
||||
});
|
||||
expect(stderr).toBeDefined();
|
||||
var err = await new Response(stderr).text();
|
||||
expect(err.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual(["bun install", " Saved lockfile", ""]);
|
||||
expect(stdout).toBeDefined();
|
||||
var out = await new Response(stdout).text();
|
||||
expect(out.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
|
||||
" + boba@workspace:packages/boba",
|
||||
" + moo@workspace:packages/moo",
|
||||
"",
|
||||
" 2 packages installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
|
||||
({ stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "link"],
|
||||
cwd: join(link_dir, "packages", "moo"),
|
||||
stdout: null,
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env,
|
||||
}));
|
||||
|
||||
expect(stderr).toBeDefined();
|
||||
err = await new Response(stderr).text();
|
||||
expect(err.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual(["bun link", ""]);
|
||||
expect(stdout).toBeDefined();
|
||||
expect(await new Response(stdout).text()).toContain(`Success! Registered "moo"`);
|
||||
expect(await exited).toBe(0);
|
||||
|
||||
({ stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "link", "moo"],
|
||||
cwd: join(link_dir, "packages", "boba"),
|
||||
stdout: null,
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env,
|
||||
}));
|
||||
|
||||
expect(stderr).toBeDefined();
|
||||
err = await new Response(stderr).text();
|
||||
expect(err.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual(["bun link", ""]);
|
||||
expect(stdout).toBeDefined();
|
||||
expect((await new Response(stdout).text()).replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
|
||||
"",
|
||||
` installed moo@link:moo`,
|
||||
"",
|
||||
"",
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await file(join(link_dir, "packages", "boba", "node_modules", "moo", "package.json")).json()).toEqual({
|
||||
name: "moo",
|
||||
version: "0.0.1",
|
||||
});
|
||||
|
||||
({ stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "unlink"],
|
||||
cwd: join(link_dir, "packages", "moo"),
|
||||
stdout: null,
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env,
|
||||
}));
|
||||
|
||||
expect(stderr).toBeDefined();
|
||||
err = await new Response(stderr).text();
|
||||
expect(err.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual(["bun unlink", ""]);
|
||||
expect(stdout).toBeDefined();
|
||||
expect(await new Response(stdout).text()).toContain(`success: unlinked package "moo"`);
|
||||
expect(await exited).toBe(0);
|
||||
|
||||
// link the workspace root package to a workspace package
|
||||
({ stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "link"],
|
||||
cwd: link_dir,
|
||||
stdout: null,
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env,
|
||||
}));
|
||||
|
||||
expect(stderr).toBeDefined();
|
||||
err = await new Response(stderr).text();
|
||||
expect(err.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual(["bun link", ""]);
|
||||
expect(stdout).toBeDefined();
|
||||
expect(await new Response(stdout).text()).toContain(`Success! Registered "foo"`);
|
||||
expect(await exited).toBe(0);
|
||||
|
||||
({ stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "link", "foo"],
|
||||
cwd: join(link_dir, "packages", "boba"),
|
||||
stdout: null,
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env,
|
||||
}));
|
||||
|
||||
expect(stderr).toBeDefined();
|
||||
err = await new Response(stderr).text();
|
||||
expect(err.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual(["bun link", ""]);
|
||||
expect(stdout).toBeDefined();
|
||||
expect((await new Response(stdout).text()).replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([
|
||||
"",
|
||||
` installed foo@link:foo`,
|
||||
"",
|
||||
"",
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await file(join(link_dir, "packages", "boba", "node_modules", "foo", "package.json")).json()).toEqual({
|
||||
name: "foo",
|
||||
version: "1.0.0",
|
||||
workspaces: ["packages/*"],
|
||||
});
|
||||
expect(await exited).toBe(0);
|
||||
|
||||
({ stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "unlink"],
|
||||
cwd: link_dir,
|
||||
stdout: null,
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env,
|
||||
}));
|
||||
|
||||
expect(stderr).toBeDefined();
|
||||
err = await new Response(stderr).text();
|
||||
expect(err.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual(["bun unlink", ""]);
|
||||
expect(stdout).toBeDefined();
|
||||
expect(await new Response(stdout).text()).toContain(`success: unlinked package "foo"`);
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
|
||||
it("should link package", async () => {
|
||||
const link_name = basename(link_dir).slice("bun-link.".length);
|
||||
await writeFile(
|
||||
|
||||
Reference in New Issue
Block a user