diff options
author | 2023-01-24 21:52:47 +0200 | |
---|---|---|
committer | 2023-01-24 11:52:47 -0800 | |
commit | e47fe2ca00a5d3cbf9710fedc1440aa25025317d (patch) | |
tree | eec2c318509d964611a9863e1a938162c12cbb05 | |
parent | 2edbf4d0ec7340d40f5e51b3b7d262499b5bc131 (diff) | |
download | bun-e47fe2ca00a5d3cbf9710fedc1440aa25025317d.tar.gz bun-e47fe2ca00a5d3cbf9710fedc1440aa25025317d.tar.zst bun-e47fe2ca00a5d3cbf9710fedc1440aa25025317d.zip |
support `bun link` of scoped packages (#1892)
-rw-r--r-- | src/install/install.zig | 14 | ||||
-rw-r--r-- | test/bun.js/install/bun-install.test.ts | 4 | ||||
-rw-r--r-- | test/bun.js/install/bun-link.test.ts | 201 |
3 files changed, 215 insertions, 4 deletions
diff --git a/src/install/install.zig b/src/install/install.zig index aff7a3fec..7195e1ac5 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -2653,7 +2653,7 @@ pub const PackageManager = struct { }; const not_found_fmt = - \\package \"{[name]s}\" is not linked + \\package "{[name]s}" is not linked \\ \\To install a linked package: \\ <cyan>bun link my-pkg-name-from-package-json<r> @@ -4491,6 +4491,18 @@ pub const PackageManager = struct { // delete it if it exists node_modules.dir.deleteTree(name) catch {}; + // create scope if specified + if (name[0] == '@') { + if (std.mem.indexOfScalar(u8, name, '/')) |i| { + node_modules.dir.makeDir(name[0..i]) catch |err| brk: { + if (err == error.PathAlreadyExists) break :brk; + if (manager.options.log_level != .silent) + Output.prettyErrorln("<r><red>error:<r> failed to create scope in global dir due to error {s}", .{@errorName(err)}); + Global.crash(); + }; + } + } + // create the symlink node_modules.dir.symLink(Fs.FileSystem.instance.topLevelDirWithoutTrailingSlash(), name, .{ .is_directory = true }) catch |err| { if (manager.options.log_level != .silent) diff --git a/test/bun.js/install/bun-install.test.ts b/test/bun.js/install/bun-install.test.ts index d176f5dbe..de9fb5755 100644 --- a/test/bun.js/install/bun-install.test.ts +++ b/test/bun.js/install/bun-install.test.ts @@ -8,10 +8,10 @@ import { it, } from "bun:test"; import { bunExe } from "bunExe"; +import { bunEnv as env } from "bunEnv"; import { access, mkdir, mkdtemp, readdir, readlink, rm, writeFile } from "fs/promises"; import { join } from "path"; import { tmpdir } from "os"; -import { bunEnv } from "bunEnv"; let handler, package_dir, requested, server; @@ -25,8 +25,6 @@ function resetHanlder() { handler = () => new Response("Tea Break~", { status: 418 }); } -const env = bunEnv; - beforeAll(() => { server = Bun.serve({ async fetch(request) { diff --git a/test/bun.js/install/bun-link.test.ts b/test/bun.js/install/bun-link.test.ts new file mode 100644 index 000000000..32d7b91fc --- /dev/null +++ b/test/bun.js/install/bun-link.test.ts @@ -0,0 +1,201 @@ +import { file, spawn } from "bun"; +import { + afterEach, + beforeEach, + expect, + it, +} from "bun:test"; +import { bunExe } from "bunExe"; +import { bunEnv as env } from "bunEnv"; +import { access, mkdir, mkdtemp, readdir, readlink, rm, writeFile } from "fs/promises"; +import { basename, join } from "path"; +import { tmpdir } from "os"; + +let package_dir, link_dir; + +beforeEach(async () => { + link_dir = await mkdtemp(join(tmpdir(), "bun-link.test")); + package_dir = await mkdtemp(join(tmpdir(), "bun-link.pkg")); +}); +afterEach(async () => { + await rm(link_dir, { force: true, recursive: true }); + await rm(package_dir, { force: true, recursive: true }); +}); + +it("should link package", async () => { + var link_name = basename(link_dir).slice("bun-link.".length); + await writeFile(join(link_dir, "package.json"), JSON.stringify({ + name: link_name, + version: "0.0.1", + })); + await writeFile(join(package_dir, "package.json"), JSON.stringify({ + name: "foo", + version: "0.0.2", + })); + + const { stdout: stdout1, stderr: stderr1, exited: exited1 } = spawn({ + cmd: [bunExe(), "link"], + cwd: link_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + expect(stderr1).toBeDefined(); + const err1 = await new Response(stderr1).text(); + expect(err1.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual([ + "bun link", + "", + ]); + expect(stdout1).toBeDefined(); + expect(await new Response(stdout1).text()).toContain(`Success! Registered "${link_name}"`); + expect(await exited1).toBe(0); + + const { stdout: stdout2, stderr: stderr2, exited: exited2 } = spawn({ + cmd: [bunExe(), "link", link_name], + cwd: package_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + expect(stderr2).toBeDefined(); + const err2 = await new Response(stderr2).text(); + expect(err2.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual([ + "bun link", + "", + ]); + expect(stdout2).toBeDefined(); + const out2 = await new Response(stdout2).text(); + expect(out2.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + ` installed ${link_name}@link:${link_name}`, + "", + "", + " 1 packages installed", + ]); + expect(await exited2).toBe(0); + + const { stdout: stdout3, stderr: stderr3, exited: exited3 } = spawn({ + cmd: [bunExe(), "unlink"], + cwd: link_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + expect(stderr3).toBeDefined(); + const err3 = await new Response(stderr3).text(); + expect(err3.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual([ + "bun unlink", + "", + ]); + expect(stdout3).toBeDefined(); + expect(await new Response(stdout3).text()).toContain(`success: unlinked package "${link_name}"`); + expect(await exited3).toBe(0); + + const { stdout: stdout4, stderr: stderr4, exited: exited4 } = spawn({ + cmd: [bunExe(), "link", link_name], + cwd: package_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + expect(stderr4).toBeDefined(); + const err4 = await new Response(stderr4).text(); + expect(err4).toContain(`error: package "${link_name}" is not linked`); + expect(stdout4).toBeDefined(); + const out4 = await new Response(stdout4).text(); + expect(await new Response(stdout4).text()).toBe(""); + expect(await exited4).toBe(1); +}); + +it("should link scoped package", async () => { + var link_name = `@${basename(link_dir).slice("bun-link.".length)}/foo`; + await writeFile(join(link_dir, "package.json"), JSON.stringify({ + name: link_name, + version: "0.0.1", + })); + await writeFile(join(package_dir, "package.json"), JSON.stringify({ + name: "bar", + version: "0.0.2", + })); + + const { stdout: stdout1, stderr: stderr1, exited: exited1 } = spawn({ + cmd: [bunExe(), "link"], + cwd: link_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + expect(stderr1).toBeDefined(); + const err1 = await new Response(stderr1).text(); + expect(err1.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual([ + "bun link", + "", + ]); + expect(stdout1).toBeDefined(); + expect(await new Response(stdout1).text()).toContain(`Success! Registered "${link_name}"`); + expect(await exited1).toBe(0); + + const { stdout: stdout2, stderr: stderr2, exited: exited2 } = spawn({ + cmd: [bunExe(), "link", link_name], + cwd: package_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + expect(stderr2).toBeDefined(); + const err2 = await new Response(stderr2).text(); + expect(err2.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual([ + "bun link", + "", + ]); + expect(stdout2).toBeDefined(); + const out2 = await new Response(stdout2).text(); + expect(out2.replace(/\s*\[[0-9\.]+ms\]\s*$/, "").split(/\r?\n/)).toEqual([ + "", + ` installed ${link_name}@link:${link_name}`, + "", + "", + " 1 packages installed", + ]); + expect(await exited2).toBe(0); + + const { stdout: stdout3, stderr: stderr3, exited: exited3 } = spawn({ + cmd: [bunExe(), "unlink"], + cwd: link_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + expect(stderr3).toBeDefined(); + const err3 = await new Response(stderr3).text(); + expect(err3.replace(/^(.*?) v[^\n]+/, "$1").split(/\r?\n/)).toEqual([ + "bun unlink", + "", + ]); + expect(stdout3).toBeDefined(); + expect(await new Response(stdout3).text()).toContain(`success: unlinked package "${link_name}"`); + expect(await exited3).toBe(0); + + const { stdout: stdout4, stderr: stderr4, exited: exited4 } = spawn({ + cmd: [bunExe(), "link", link_name], + cwd: package_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + expect(stderr4).toBeDefined(); + const err4 = await new Response(stderr4).text(); + expect(err4).toContain(`error: package "${link_name}" is not linked`); + expect(stdout4).toBeDefined(); + const out4 = await new Response(stdout4).text(); + expect(await new Response(stdout4).text()).toBe(""); + expect(await exited4).toBe(1); +}); |