diff --git a/src/install/lockfile/Package/WorkspaceMap.zig b/src/install/lockfile/Package/WorkspaceMap.zig index 8003cd5dfb..a802406dd5 100644 --- a/src/install/lockfile/Package/WorkspaceMap.zig +++ b/src/install/lockfile/Package/WorkspaceMap.zig @@ -226,7 +226,7 @@ pub fn processNamesArray( var walker: GlobWalker = .{}; var cwd = bun.path.dirname(source.path.text, .auto); cwd = if (bun.strings.eql(cwd, "")) bun.fs.FileSystem.instance.top_level_dir else cwd; - if ((try walker.initWithCwd(&arena, glob_pattern, cwd, false, false, false, false, true)).asErr()) |e| { + if ((try walker.initWithCwd(&arena, glob_pattern, cwd, false, false, true, false, true)).asErr()) |e| { log.addErrorFmt( source, loc, diff --git a/test/regression/issue/25801.test.ts b/test/regression/issue/25801.test.ts new file mode 100644 index 0000000000..7b85988b17 --- /dev/null +++ b/test/regression/issue/25801.test.ts @@ -0,0 +1,96 @@ +// https://github.com/oven-sh/bun/issues/25801 +// Workspace packages that are symlinks to directories outside the monorepo +// should be discovered during `bun install` when using glob patterns. + +import { expect, test } from "bun:test"; +import { symlinkSync } from "fs"; +import { bunEnv, bunExe, tempDir } from "harness"; +import { join } from "path"; + +test("workspace glob patterns should follow symlinks to external directories", async () => { + // Create a temporary directory for the external package (outside the monorepo) + using externalPkgDir = tempDir("external-pkg", { + "package.json": JSON.stringify({ + name: "backend", + version: "1.0.0", + }), + }); + + // Create the monorepo with a glob pattern in workspaces + using monorepoDir = tempDir("monorepo", { + "package.json": JSON.stringify({ + name: "monorepo-test", + workspaces: ["./*"], + dependencies: { + backend: "workspace:*", + }, + }), + }); + + // Create a symlink inside the monorepo pointing to the external package + symlinkSync(String(externalPkgDir), join(String(monorepoDir), "backend")); + + // Run bun install + await using proc = Bun.spawn({ + cmd: [bunExe(), "install"], + cwd: String(monorepoDir), + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // Should not fail with "Workspace dependency 'backend' not found" + expect(stderr).not.toContain("Workspace dependency"); + expect(stderr).not.toContain("not found"); + + // Should succeed + expect(exitCode).toBe(0); +}); + +test("workspace glob patterns should follow symlinks in packages directory", async () => { + // Create an external package to be symlinked + using externalPkgDir = tempDir("external-alias-pkg", { + "package.json": JSON.stringify({ + name: "pkg-alias", + version: "2.0.0", + }), + }); + + // Create a monorepo with packages/* glob pattern + using monorepoDir = tempDir("monorepo-internal", { + "package.json": JSON.stringify({ + name: "monorepo-internal", + workspaces: ["packages/*"], + dependencies: { + "pkg-alias": "workspace:*", + }, + }), + "packages/real-pkg/package.json": JSON.stringify({ + name: "real-pkg", + version: "1.0.0", + }), + }); + + // Create a symlink to the external package inside packages/ + symlinkSync(String(externalPkgDir), join(String(monorepoDir), "packages", "pkg-alias")); + + // Run bun install + await using proc = Bun.spawn({ + cmd: [bunExe(), "install"], + cwd: String(monorepoDir), + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // Should not fail with workspace dependency errors + expect(stderr).not.toContain("Workspace dependency"); + expect(stderr).not.toContain("not found"); + + // Should succeed + expect(exitCode).toBe(0); +});