diff options
-rw-r--r-- | src/install/lockfile.zig | 22 | ||||
-rw-r--r-- | test/cli/install/bun-install.test.ts | 120 |
2 files changed, 141 insertions, 1 deletions
diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index 73347871a..c224cc765 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -2515,8 +2515,20 @@ pub const Package = extern struct { &sliced, log, ) orelse Dependency.Version{}; + var workspace_range: ?Semver.Query.Group = null; const name_hash = switch (dependency_version.tag) { .npm => String.Builder.stringHash(dependency_version.value.npm.name.slice(buf)), + .workspace => if (strings.hasPrefixComptime(sliced.slice, "workspace:")) brk: { + const input = sliced.slice["workspace:".len..]; + const at = strings.lastIndexOfChar(input, '@') orelse 0; + if (at > 0) { + workspace_range = Semver.Query.parse(allocator, input[at + 1 ..], sliced) catch return error.InstallFailed; + break :brk String.Builder.stringHash(input[0..at]); + } else { + workspace_range = Semver.Query.parse(allocator, input, sliced) catch null; + break :brk external_alias.hash; + } + } else external_alias.hash, else => external_alias.hash, }; const workspace_path = if (comptime tag == null) lockfile.workspace_paths.get(@truncate(name_hash)) else null; @@ -2564,7 +2576,15 @@ pub const Package = extern struct { } }, .workspace => if (workspace_path) |path| { - dependency_version.value.workspace = path; + if (workspace_range) |range| { + if (workspace_version) |ver| { + if (range.satisfies(ver)) { + dependency_version.value.workspace = path; + } + } + } else { + dependency_version.value.workspace = path; + } } else { { const workspace = dependency_version.value.workspace.slice(buf); diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts index b1f453f86..bdd098126 100644 --- a/test/cli/install/bun-install.test.ts +++ b/test/cli/install/bun-install.test.ts @@ -5188,3 +5188,123 @@ it("should override aliased child npm dependency by matching workspace", async ( expect(await readlink(join(package_dir, "packages", "baz", "node_modules", "bar"))).toBe(join("..", "..", "bar")); await access(join(package_dir, "bun.lockb")); }); + +it("should handle `workspace:` with semver range", async () => { + const urls: string[] = []; + setHandler(dummyRegistry(urls)); + await writeFile( + join(package_dir, "package.json"), + JSON.stringify({ + name: "foo", + workspaces: ["bar", "baz"], + }), + ); + await mkdir(join(package_dir, "bar")); + const bar_package = JSON.stringify({ + name: "bar", + version: "0.0.1", + }); + await writeFile(join(package_dir, "bar", "package.json"), bar_package); + await mkdir(join(package_dir, "baz")); + await writeFile( + join(package_dir, "baz", "package.json"), + JSON.stringify({ + name: "baz", + version: "0.1.0", + dependencies: { + bar: "workspace:~0.0.1", + }, + }), + ); + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: package_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + expect(stderr).toBeDefined(); + const err = await new Response(stderr).text(); + expect(err).toContain("Saved lockfile"); + expect(stdout).toBeDefined(); + const out = await new Response(stdout).text(); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + " + bar@workspace:bar", + " + baz@workspace:baz", + "", + " 2 packages installed", + ]); + expect(await exited).toBe(0); + expect(urls.sort()).toBeEmpty(); + expect(requested).toBe(0); + expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".cache", "bar", "baz"]); + expect(await readlink(join(package_dir, "node_modules", "bar"))).toBe(join("..", "bar")); + expect(await file(join(package_dir, "node_modules", "bar", "package.json")).text()).toEqual(bar_package); + expect(await readlink(join(package_dir, "node_modules", "baz"))).toBe(join("..", "baz")); + expect(await readdirSorted(join(package_dir, "node_modules", "baz"))).toEqual(["package.json"]); + await access(join(package_dir, "bun.lockb")); +}); + +it("should handle `workspace:` with alias & @scope", async () => { + const urls: string[] = []; + setHandler(dummyRegistry(urls)); + await writeFile( + join(package_dir, "package.json"), + JSON.stringify({ + name: "foo", + workspaces: ["packages/*"], + }), + ); + await mkdir(join(package_dir, "packages", "bar"), { recursive: true }); + const bar_package = JSON.stringify({ + name: "@moo/bar", + version: "0.1.2", + }); + await writeFile(join(package_dir, "packages", "bar", "package.json"), bar_package); + await mkdir(join(package_dir, "packages", "baz"), { recursive: true }); + await writeFile( + join(package_dir, "packages", "baz", "package.json"), + JSON.stringify({ + name: "@moz/baz", + dependencies: { + "@moz/bar": "workspace:@moo/bar@>=0.1", + }, + }), + ); + const { stdout, stderr, exited } = spawn({ + cmd: [bunExe(), "install"], + cwd: package_dir, + stdout: null, + stdin: "pipe", + stderr: "pipe", + env, + }); + expect(stderr).toBeDefined(); + const err = await new Response(stderr).text(); + expect(err).toContain("Saved lockfile"); + expect(stdout).toBeDefined(); + const out = await new Response(stdout).text(); + expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([ + " + @moo/bar@workspace:packages/bar", + " + @moz/baz@workspace:packages/baz", + "", + " 2 packages installed", + ]); + expect(await exited).toBe(0); + expect(urls.sort()).toBeEmpty(); + expect(requested).toBe(0); + expect(await readdirSorted(join(package_dir, "node_modules"))).toEqual([".cache", "@moo", "@moz"]); + expect(await readdirSorted(join(package_dir, "node_modules", "@moo"))).toEqual(["bar"]); + expect(await readlink(join(package_dir, "node_modules", "@moo", "bar"))).toBe(join("..", "..", "packages", "bar")); + expect(await file(join(package_dir, "node_modules", "@moo", "bar", "package.json")).text()).toEqual(bar_package); + expect(await readdirSorted(join(package_dir, "node_modules", "@moz"))).toEqual(["baz"]); + expect(await readlink(join(package_dir, "node_modules", "@moz", "baz"))).toBe(join("..", "..", "packages", "baz")); + expect(await readdirSorted(join(package_dir, "packages", "baz"))).toEqual(["node_modules", "package.json"]); + expect(await readdirSorted(join(package_dir, "packages", "baz", "node_modules"))).toEqual(["@moz"]); + expect(await readdirSorted(join(package_dir, "packages", "baz", "node_modules", "@moz"))).toEqual(["bar"]); + expect(await readlink(join(package_dir, "packages", "baz", "node_modules", "@moz", "bar"))).toBe( + join("..", "..", "..", "bar"), + ); + await access(join(package_dir, "bun.lockb")); +}); |