From 3df3fff865df1886a716d3afefdbbd063922b4c7 Mon Sep 17 00:00:00 2001 From: Matthieu Mordrel Date: Mon, 23 Feb 2026 10:27:56 +0100 Subject: [PATCH 1/4] fix(tools): support object-form workspaces in BunTool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bun supports an object-form `workspaces` field in package.json for features like workspace catalogs: "workspaces": { "packages": ["apps/*", "packages/*"], "catalog": { "some-dep": "^1.0.0" } } BunTool only checked for `Array.isArray(workspaces)`, causing it to miss monorepos using this format. The fix mirrors YarnTool's existing approach: accept both `string[]` and `{ packages: string[] }`. Affected methods: - `isMonorepoRoot` / `isMonorepoRootSync` — now recognize object form - `getPackages` / `getPackagesSync` — extract globs from either form --- .../basic-bun-object-workspaces/bun.lock | 4 ++++ .../basic-bun-object-workspaces/package.json | 13 ++++++++++++ .../packages/package-one/package.json | 4 ++++ .../packages/package-one/src/index.js | 1 + .../bun-object-workspace-base/bun.lock | 0 .../bun-object-workspace-base/package.json | 13 ++++++++++++ .../packages/pkg-a/package.json | 5 +++++ .../packages/pkg-b/package.json | 5 +++++ packages/find-root/src/index.test.ts | 11 ++++++++++ packages/get-packages/src/index.test.ts | 21 +++++++++++++++++++ packages/tools/src/BunTool.ts | 20 +++++++++++++----- 11 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 __fixtures__/basic-bun-object-workspaces/bun.lock create mode 100644 __fixtures__/basic-bun-object-workspaces/package.json create mode 100644 __fixtures__/basic-bun-object-workspaces/packages/package-one/package.json create mode 100644 __fixtures__/basic-bun-object-workspaces/packages/package-one/src/index.js create mode 100644 __fixtures__/bun-object-workspace-base/bun.lock create mode 100644 __fixtures__/bun-object-workspace-base/package.json create mode 100644 __fixtures__/bun-object-workspace-base/packages/pkg-a/package.json create mode 100644 __fixtures__/bun-object-workspace-base/packages/pkg-b/package.json diff --git a/__fixtures__/basic-bun-object-workspaces/bun.lock b/__fixtures__/basic-bun-object-workspaces/bun.lock new file mode 100644 index 00000000..669d511a --- /dev/null +++ b/__fixtures__/basic-bun-object-workspaces/bun.lock @@ -0,0 +1,4 @@ +{ + "version": "lockfile/5", + "lockfile": {} +} \ No newline at end of file diff --git a/__fixtures__/basic-bun-object-workspaces/package.json b/__fixtures__/basic-bun-object-workspaces/package.json new file mode 100644 index 00000000..ac66bee0 --- /dev/null +++ b/__fixtures__/basic-bun-object-workspaces/package.json @@ -0,0 +1,13 @@ +{ + "name": "basic-bun-object-workspaces", + "version": "1.0.0", + "private": true, + "workspaces": { + "packages": [ + "packages/*" + ], + "catalog": { + "some-dep": "^1.0.0" + } + } +} diff --git a/__fixtures__/basic-bun-object-workspaces/packages/package-one/package.json b/__fixtures__/basic-bun-object-workspaces/packages/package-one/package.json new file mode 100644 index 00000000..80acbd05 --- /dev/null +++ b/__fixtures__/basic-bun-object-workspaces/packages/package-one/package.json @@ -0,0 +1,4 @@ +{ + "name": "package-one", + "version": "1.0.0" +} diff --git a/__fixtures__/basic-bun-object-workspaces/packages/package-one/src/index.js b/__fixtures__/basic-bun-object-workspaces/packages/package-one/src/index.js new file mode 100644 index 00000000..8b1a3937 --- /dev/null +++ b/__fixtures__/basic-bun-object-workspaces/packages/package-one/src/index.js @@ -0,0 +1 @@ +// empty diff --git a/__fixtures__/bun-object-workspace-base/bun.lock b/__fixtures__/bun-object-workspace-base/bun.lock new file mode 100644 index 00000000..e69de29b diff --git a/__fixtures__/bun-object-workspace-base/package.json b/__fixtures__/bun-object-workspace-base/package.json new file mode 100644 index 00000000..3447a9ed --- /dev/null +++ b/__fixtures__/bun-object-workspace-base/package.json @@ -0,0 +1,13 @@ +{ + "name": "bun-object-workspace-base", + "version": "1.0.0", + "private": true, + "workspaces": { + "packages": [ + "packages/*" + ], + "catalog": { + "some-dep": "^1.0.0" + } + } +} diff --git a/__fixtures__/bun-object-workspace-base/packages/pkg-a/package.json b/__fixtures__/bun-object-workspace-base/packages/pkg-a/package.json new file mode 100644 index 00000000..68c0c900 --- /dev/null +++ b/__fixtures__/bun-object-workspace-base/packages/pkg-a/package.json @@ -0,0 +1,5 @@ +{ + "name": "bun-object-workspace-base-pkg-a", + "version": "1.0.0", + "main": "src/index.js" +} diff --git a/__fixtures__/bun-object-workspace-base/packages/pkg-b/package.json b/__fixtures__/bun-object-workspace-base/packages/pkg-b/package.json new file mode 100644 index 00000000..5f68c4b7 --- /dev/null +++ b/__fixtures__/bun-object-workspace-base/packages/pkg-b/package.json @@ -0,0 +1,5 @@ +{ + "name": "bun-object-workspace-base-pkg-b", + "version": "1.0.0", + "main": "src/index.js" +} diff --git a/packages/find-root/src/index.test.ts b/packages/find-root/src/index.test.ts index 0b599809..8a9b77e6 100644 --- a/packages/find-root/src/index.test.ts +++ b/packages/find-root/src/index.test.ts @@ -97,6 +97,17 @@ const runTests = (findRoot: FindRoot) => { }); }); + test("it returns the root of a bun monorepo with object-form workspaces", async () => { + let tmpPath = f.copy("basic-bun-object-workspaces"); + let monorepoRoot = await findRoot( + path.join(tmpPath, "packages", "package-one", "src") + ); + expect(monorepoRoot).toEqual({ + tool: BunTool.type, + rootDir: tmpPath, + }); + }); + test("it does not detect bun monorepo without lock file", async () => { let tmpPath = f.copy("bun-no-lock"); let monorepoRoot = await findRoot( diff --git a/packages/get-packages/src/index.test.ts b/packages/get-packages/src/index.test.ts index 53b89fab..0f24bb6c 100644 --- a/packages/get-packages/src/index.test.ts +++ b/packages/get-packages/src/index.test.ts @@ -141,6 +141,27 @@ let runTests = (getPackages: GetPackages) => { } }); + it("should resolve workspaces for bun with object-form workspaces", async () => { + const dir = f.copy("bun-object-workspace-base"); + + // Test for both root and subdirectories + for (const location of [".", "packages", "packages/pkg-a"]) { + const allPackages = await getPackages(path.join(dir, location)); + + if (allPackages.packages === null) { + return expect(allPackages.packages).not.toBeNull(); + } + + expect(allPackages.packages[0].packageJson.name).toEqual( + "bun-object-workspace-base-pkg-a" + ); + expect(allPackages.packages[1].packageJson.name).toEqual( + "bun-object-workspace-base-pkg-b" + ); + expect(allPackages.tool.type).toEqual("bun"); + } + }); + it("should resolve workspaces for lerna", async () => { const dir = f.copy("lerna-workspace-base"); diff --git a/packages/tools/src/BunTool.ts b/packages/tools/src/BunTool.ts index 7981ec4b..35579c08 100644 --- a/packages/tools/src/BunTool.ts +++ b/packages/tools/src/BunTool.ts @@ -16,7 +16,7 @@ import { import { readJson, readJsonSync } from "./utils.ts"; interface BunPackageJSON extends PackageJSON { - workspaces?: string[]; + workspaces?: string[] | { packages: string[] }; } async function hasBunLockFile(directory: string): Promise { @@ -55,7 +55,10 @@ export const BunTool: Tool = { hasBunLockFile(directory), ]); if (pkgJson.workspaces && hasLockFile) { - if (Array.isArray(pkgJson.workspaces)) { + if ( + Array.isArray(pkgJson.workspaces) || + Array.isArray(pkgJson.workspaces.packages) + ) { return true; } } @@ -76,7 +79,10 @@ export const BunTool: Tool = { } const pkgJson = readJsonSync(directory, "package.json") as BunPackageJSON; if (pkgJson.workspaces) { - if (Array.isArray(pkgJson.workspaces)) { + if ( + Array.isArray(pkgJson.workspaces) || + Array.isArray(pkgJson.workspaces.packages) + ) { return true; } } @@ -97,7 +103,9 @@ export const BunTool: Tool = { rootDir, "package.json" )) as BunPackageJSON; - const packageGlobs: string[] = pkgJson.workspaces || []; + const packageGlobs: string[] = Array.isArray(pkgJson.workspaces) + ? pkgJson.workspaces + : pkgJson.workspaces?.packages || []; return { tool: BunTool, @@ -124,7 +132,9 @@ export const BunTool: Tool = { try { const pkgJson = readJsonSync(rootDir, "package.json") as BunPackageJSON; - const packageGlobs: string[] = pkgJson.workspaces || []; + const packageGlobs: string[] = Array.isArray(pkgJson.workspaces) + ? pkgJson.workspaces + : pkgJson.workspaces?.packages || []; return { tool: BunTool, From fda6aa2e3617cbe74adbc6ae44640a1e65c6d31e Mon Sep 17 00:00:00 2001 From: Matthieu Mordrel Date: Mon, 23 Feb 2026 14:37:06 +0100 Subject: [PATCH 2/4] fix(tools): add catalog type to BunPackageJSON and changeset Co-Authored-By: Claude Opus 4.6 --- .changeset/fix-bun-object-workspaces.md | 5 +++++ packages/tools/src/BunTool.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-bun-object-workspaces.md diff --git a/.changeset/fix-bun-object-workspaces.md b/.changeset/fix-bun-object-workspaces.md new file mode 100644 index 00000000..9921a7ff --- /dev/null +++ b/.changeset/fix-bun-object-workspaces.md @@ -0,0 +1,5 @@ +--- +"@manypkg/tools": patch +--- + +fix(tools): support object-form workspaces in BunTool to handle catalog usage diff --git a/packages/tools/src/BunTool.ts b/packages/tools/src/BunTool.ts index 35579c08..4fbf2398 100644 --- a/packages/tools/src/BunTool.ts +++ b/packages/tools/src/BunTool.ts @@ -16,7 +16,7 @@ import { import { readJson, readJsonSync } from "./utils.ts"; interface BunPackageJSON extends PackageJSON { - workspaces?: string[] | { packages: string[] }; + workspaces?: string[] | { packages: string[]; catalog?: Record }; } async function hasBunLockFile(directory: string): Promise { From aab5c404802bc521d1d1e2335f31c213972d3543 Mon Sep 17 00:00:00 2001 From: Matthieu Mordrel Date: Mon, 23 Feb 2026 14:38:50 +0100 Subject: [PATCH 3/4] style: fix prettier formatting Co-Authored-By: Claude Opus 4.6 --- packages/tools/src/BunTool.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/tools/src/BunTool.ts b/packages/tools/src/BunTool.ts index 4fbf2398..542e4b1a 100644 --- a/packages/tools/src/BunTool.ts +++ b/packages/tools/src/BunTool.ts @@ -16,7 +16,9 @@ import { import { readJson, readJsonSync } from "./utils.ts"; interface BunPackageJSON extends PackageJSON { - workspaces?: string[] | { packages: string[]; catalog?: Record }; + workspaces?: + | string[] + | { packages: string[]; catalog?: Record }; } async function hasBunLockFile(directory: string): Promise { From 45b60acc90e1b349fa04356cc01f937c7f4b76a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 23 Feb 2026 15:01:06 +0100 Subject: [PATCH 4/4] Apply suggestion from @Andarist --- .changeset/fix-bun-object-workspaces.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fix-bun-object-workspaces.md b/.changeset/fix-bun-object-workspaces.md index 9921a7ff..de9c5ee9 100644 --- a/.changeset/fix-bun-object-workspaces.md +++ b/.changeset/fix-bun-object-workspaces.md @@ -2,4 +2,4 @@ "@manypkg/tools": patch --- -fix(tools): support object-form workspaces in BunTool to handle catalog usage +Support object-form workspaces in `BunTool` to handle `catalog` usage