Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
3f3aa2a2c2 fix(glob): preserve directory name case in glob walker
Fixes #11295

The glob walker's DirEntryAccessor was using the lowercase map key
instead of the case-preserved filename stored in the Entry. This
caused ENOENT errors when trying to open directories with mixed-case
names like "BuildIconList" because it would try to open "buildiconlist".

The fix uses entry.base() to get the original case-preserved name
instead of the lowercase key used for case-insensitive lookups.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-24 01:06:46 +00:00
2 changed files with 106 additions and 2 deletions

View File

@@ -211,8 +211,10 @@ pub const DirEntryAccessor = struct {
pub inline fn next(self: *DirIter) Maybe(?IterResult) {
if (self.value) |*value| {
const nextval = value.next() orelse return .{ .result = null };
const name = nextval.key_ptr.*;
const kind = nextval.value_ptr.*.kind(&FS.instance.fs, true);
const entry = nextval.value_ptr.*;
// Use the original case-preserved name, not the lowercase key
const name = entry.base();
const kind = entry.kind(&FS.instance.fs, true);
const fskind = switch (kind) {
.file => std.fs.File.Kind.file,
.dir => std.fs.File.Kind.directory,

View File

@@ -0,0 +1,102 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
test("issue #11295 - bun run --filter preserves directory case", async () => {
// Regression test: the glob walker was using lowercase keys from the FS cache
// instead of the case-preserved names, causing ENOENT when trying to open
// directories with mixed-case names like "BuildIconList"
using dir = tempDir("ws-case-sensitive", {
"tools/BuildIconList/package.json": JSON.stringify({
name: "@test/build-icon-list",
version: "1.0.0",
scripts: {
build: "echo building BuildIconList",
},
}),
"tools/lowercase/package.json": JSON.stringify({
name: "@test/lowercase",
version: "1.0.0",
scripts: {
build: "echo building lowercase",
},
}),
"package.json": JSON.stringify({
name: "case-test",
private: true,
workspaces: ["tools/*"],
}),
});
await using proc = Bun.spawn({
cmd: [bunExe(), "run", "--filter", "*", "build"],
cwd: String(dir),
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]);
expect(stderr).not.toContain("ENOENT");
expect(stdout).toContain("building BuildIconList");
expect(stdout).toContain("building lowercase");
expect(exitCode).toBe(0);
});
test("issue #11295 - bun run --filter with path filter preserves case", async () => {
using dir = tempDir("ws-path-filter", {
"packages/MyPackage/package.json": JSON.stringify({
name: "my-package",
version: "1.0.0",
scripts: {
test: "echo testing MyPackage",
},
}),
"package.json": JSON.stringify({
name: "path-filter-test",
private: true,
workspaces: ["packages/*"],
}),
});
await using proc = Bun.spawn({
cmd: [bunExe(), "run", "--filter", "./packages/MyPackage", "test"],
cwd: String(dir),
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]);
expect(stderr).not.toContain("ENOENT");
expect(stdout).toContain("testing MyPackage");
expect(exitCode).toBe(0);
});
test("Bun.Glob preserves directory case (coverage test)", async () => {
// Bun.Glob uses SyscallAccessor (direct filesystem access) which was not
// affected by the case sensitivity bug, but adding coverage for completeness
using dir = tempDir("glob-case", {
"MixedCase/file.txt": "content",
"lowercase/file.txt": "content",
"UPPERCASE/file.txt": "content",
});
const glob = new Bun.Glob("*/file.txt");
const results: string[] = [];
for await (const entry of glob.scan({ cwd: String(dir) })) {
results.push(entry);
}
results.sort();
expect(results).toEqual(["MixedCase/file.txt", "UPPERCASE/file.txt", "lowercase/file.txt"]);
});