Skip to content

Commit dfe9150

Browse files
dylan-conwayclaude
andcommitted
fix(bundler): normalize Windows paths in FileMap for consistent lookup
- Use .loose platform for path joining to preserve Windows drive letters - Normalize backslashes to forward slashes in get/contains/resolve - Convert joined paths in-place on Windows using platformToPosixInPlace 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 284087c commit dfe9150

File tree

1 file changed

+29
-12
lines changed

1 file changed

+29
-12
lines changed

src/bun.js/api/JSBundler.zig

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,21 @@ pub const JSBundler = struct {
2323
/// directory, matches a key in the map.
2424
pub fn get(self: *const FileMap, specifier: []const u8) ?[]const u8 {
2525
if (self.map.count() == 0) return null;
26-
const entry = self.map.get(specifier) orelse return null;
26+
// Normalize backslashes to forward slashes for consistent lookup
27+
// Map keys are stored with forward slashes (normalized in fromJS)
28+
var buf: bun.PathBuffer = undefined;
29+
const normalized = bun.path.pathToPosixBuf(u8, specifier, &buf);
30+
const entry = self.map.get(normalized) orelse return null;
2731
return entry.slice();
2832
}
2933

3034
/// Check if the file map contains a given specifier.
3135
pub fn contains(self: *const FileMap, specifier: []const u8) bool {
3236
if (self.map.count() == 0) return false;
33-
return self.map.contains(specifier);
37+
// Normalize backslashes to forward slashes for consistent lookup
38+
var buf: bun.PathBuffer = undefined;
39+
const normalized = bun.path.pathToPosixBuf(u8, specifier, &buf);
40+
return self.map.contains(normalized);
3441
}
3542

3643
/// Returns a resolver Result for a file in the map, or null if not found.
@@ -44,15 +51,20 @@ pub const JSBundler = struct {
4451
// Fast path: if the map is empty, return immediately
4552
if (self.map.count() == 0) return null;
4653

47-
// Check if the specifier is directly in the map
48-
// Must use getKey to return the map's owned key, not the parameter
49-
if (self.map.getKey(specifier)) |key| {
50-
return _resolver.Result{
51-
.path_pair = .{
52-
.primary = Fs.Path.initWithNamespace(key, "memory"),
53-
},
54-
.module_type = .unknown,
55-
};
54+
{
55+
var buf: bun.PathBuffer = undefined;
56+
const normalized_specifier = bun.path.pathToPosixBuf(u8, specifier, &buf);
57+
58+
// Check if the specifier is directly in the map
59+
// Must use getKey to return the map's owned key, not the parameter
60+
if (self.map.getKey(normalized_specifier)) |key| {
61+
return _resolver.Result{
62+
.path_pair = .{
63+
.primary = Fs.Path.initWithNamespace(key, "memory"),
64+
},
65+
.module_type = .unknown,
66+
};
67+
}
5668
}
5769

5870
// Also try with source directory joined for relative specifiers
@@ -89,7 +101,12 @@ pub const JSBundler = struct {
89101
Fs.FileSystem.instance.top_level_dir)
90102
else
91103
source_dir;
92-
const joined = bun.path.joinAbsStringBuf(effective_source_dir, &buf, &.{specifier}, .posix);
104+
// Use .loose to preserve Windows drive letters, then normalize in-place on Windows
105+
const joined_len = bun.path.joinAbsStringBuf(effective_source_dir, &buf, &.{specifier}, .loose).len;
106+
if (bun.Environment.isWindows) {
107+
bun.path.platformToPosixInPlace(u8, buf[0..joined_len]);
108+
}
109+
const joined = buf[0..joined_len];
93110
// Must use getKey to return the map's owned key, not the temporary buffer
94111
if (self.map.getKey(joined)) |key| {
95112
return _resolver.Result{

0 commit comments

Comments
 (0)