From ddefa110702e4f219886eab2b10980d1315b7b1b Mon Sep 17 00:00:00 2001 From: robobun Date: Sun, 1 Feb 2026 00:33:59 -0800 Subject: [PATCH] fix(fs): handle '.' path normalization on Windows (#26634) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Fix path normalization for "." on Windows where `normalizeStringBuf` was incorrectly stripping it to an empty string - This caused `existsSync('.')`, `statSync('.')`, and other fs operations to fail on Windows ## Test plan - Added regression test `test/regression/issue/26631.test.ts` that tests `existsSync`, `exists`, `statSync`, and `stat` for both `.` and `..` paths - All tests pass locally with `bun bd test test/regression/issue/26631.test.ts` - Verified code compiles on all platforms with `bun run zig:check-all` Fixes #26631 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Bot Co-authored-by: Claude Opus 4.5 --- src/bun.js/node/types.zig | 4 +++ test/regression/issue/26631.test.ts | 42 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 test/regression/issue/26631.test.ts diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 4e5fc66cac..e0bb397537 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -649,6 +649,10 @@ pub const PathLike = union(enum) { const normal = path_handler.normalizeBuf(resolve, b, .windows); return strings.toKernel32Path(@alignCast(std.mem.bytesAsSlice(u16, buf)), normal); } + // Handle "." specially since normalizeStringBuf strips it to an empty string + if (s.len == 1 and s[0] == '.') { + return strings.toKernel32Path(@alignCast(std.mem.bytesAsSlice(u16, buf)), "."); + } const normal = path_handler.normalizeStringBuf(s, b, true, .windows, false); return strings.toKernel32Path(@alignCast(std.mem.bytesAsSlice(u16, buf)), normal); } diff --git a/test/regression/issue/26631.test.ts b/test/regression/issue/26631.test.ts new file mode 100644 index 0000000000..6598f4b706 --- /dev/null +++ b/test/regression/issue/26631.test.ts @@ -0,0 +1,42 @@ +import { expect, test } from "bun:test"; +import { existsSync, statSync } from "node:fs"; +import { exists, stat } from "node:fs/promises"; + +// https://github.com/oven-sh/bun/issues/26631 +// Path resolution fails for current directory '.' on Windows + +test("existsSync('.') should return true", () => { + expect(existsSync(".")).toBe(true); +}); + +test("exists('.') should return true", async () => { + expect(await exists(".")).toBe(true); +}); + +test("statSync('.') should return directory stats", () => { + const stats = statSync("."); + expect(stats.isDirectory()).toBe(true); +}); + +test("stat('.') should return directory stats", async () => { + const stats = await stat("."); + expect(stats.isDirectory()).toBe(true); +}); + +test("existsSync('..') should return true", () => { + expect(existsSync("..")).toBe(true); +}); + +test("exists('..') should return true", async () => { + expect(await exists("..")).toBe(true); +}); + +test("statSync('..') should return directory stats", () => { + const stats = statSync(".."); + expect(stats.isDirectory()).toBe(true); +}); + +test("stat('..') should return directory stats", async () => { + const stats = await stat(".."); + expect(stats.isDirectory()).toBe(true); +});