Skip to content

Commit 6befe42

Browse files
arhxamclaude
andauthored
fix(shared): normalize a bare Windows drive root the same as C:\ / C:/ (#6189)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b30a9bc commit 6befe42

2 files changed

Lines changed: 17 additions & 1 deletion

File tree

packages/shared/src/path.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
isUncPath,
55
isWindowsAbsolutePath,
66
isWindowsDrivePath,
7+
normalizeProjectPathForComparison,
8+
normalizeProjectPathForDispatch,
79
} from "./path.ts";
810

911
describe("path helpers", () => {
@@ -31,4 +33,14 @@ describe("path helpers", () => {
3133
expect(isExplicitRelativePath("..\\repo")).toBe(true);
3234
expect(isExplicitRelativePath("~/repo")).toBe(false);
3335
});
36+
37+
it("normalizes a bare Windows drive root the same as one with a trailing separator", () => {
38+
// `C:`, `C:\` and `C:/` all refer to the drive root and must compare equal.
39+
expect(normalizeProjectPathForDispatch("C:")).toBe("C:\\");
40+
expect(normalizeProjectPathForComparison("C:")).toBe("c:\\");
41+
expect(normalizeProjectPathForComparison("C:")).toBe(normalizeProjectPathForComparison("C:\\"));
42+
expect(normalizeProjectPathForComparison("C:")).toBe(normalizeProjectPathForComparison("C:/"));
43+
// Non-root drive paths keep their trailing separator trimmed as before.
44+
expect(normalizeProjectPathForDispatch("C:\\repo\\")).toBe("C:\\repo");
45+
});
3446
});

packages/shared/src/path.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ export function isExplicitRelativePath(value: string): boolean {
2222
}
2323

2424
function isRootPath(value: string): boolean {
25-
return value === "/" || value === "\\" || /^[a-zA-Z]:[/\\]?$/.test(value);
25+
// The drive separator is required: a bare `C:` is not the drive root (it
26+
// means "current directory on C:"), and treating it as already-canonical
27+
// would leave it as `C:` while `C:\` and `C:/` normalize to the drive root,
28+
// so the same location would fail project identity/dedup comparisons.
29+
return value === "/" || value === "\\" || /^[a-zA-Z]:[/\\]$/.test(value);
2630
}
2731

2832
function trimTrailingPathSeparators(value: string): string {

0 commit comments

Comments
 (0)