Skip to content

Commit

Permalink
fix some cases in the dirname function.
Browse files Browse the repository at this point in the history
  • Loading branch information
Hanaasagi committed Jul 24, 2023
1 parent 967ccb5 commit 8aab32c
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 21 deletions.
85 changes: 83 additions & 2 deletions src/bun.js/node/types.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1682,6 +1682,87 @@ pub const Path = struct {

return JSC.ZigString.init(out).withEncoding().toValueGC(globalThis);
}

fn dirnameWindows(path: []const u8) []const u8 {
if (path.len == 0)
return ".";

const root_slice = std.fs.path.diskDesignatorWindows(path);
if (path.len == root_slice.len)
return root_slice;

const have_root_slash = path.len > root_slice.len and (path[root_slice.len] == '/' or path[root_slice.len] == '\\');

var end_index: usize = path.len - 1;

while (path[end_index] == '/' or path[end_index] == '\\') {
// e.g. '\\' => "\\"
if (end_index == 0) {
return path[0..1];
}
end_index -= 1;
}

while (path[end_index] != '/' and path[end_index] != '\\') {
if (end_index == 0) {
if (root_slice.len == 0) {
return ".";
}
if (have_root_slash) {
// e.g. "c:\\" => "c:\\"
return path[0 .. root_slice.len + 1];
} else {
// e.g. "c:foo" => "c:"
return root_slice;
}
}
end_index -= 1;
}

if (have_root_slash and end_index == root_slice.len) {
end_index += 1;
}

return path[0..end_index];
}

fn dirnamePosix(path: []const u8) []const u8 {
if (path.len == 0)
return ".";

var end_index: usize = path.len - 1;

while (path[end_index] == '/') {
// e.g. "////" => "/"
if (end_index == 0) {
return "/";
}
end_index -= 1;
}

while (path[end_index] != '/') {
if (end_index == 0) {
// e.g. "a/", "a"
return ".";
}
end_index -= 1;
}

// e.g. "/a/" => "/"
if (end_index == 0 and path[0] == '/') {
return "/";
}

// "a/b" => "a" or "//b" => "//"
if (end_index <= 1) {
if (path[0] == '/' and path[1] == '/') {
end_index += 1;
}
}

return path[0..end_index];
}

pub fn dirname(globalThis: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue {
if (comptime is_bindgen) return JSC.JSValue.jsUndefined();
if (args_len == 0) {
Expand All @@ -1697,9 +1778,9 @@ pub const Path = struct {
const base_slice = path.slice();

const out = if (isWindows)
std.fs.path.dirnameWindows(base_slice) orelse "."
@This().dirnameWindows(base_slice)
else
std.fs.path.dirnamePosix(base_slice) orelse ".";
@This().dirnamePosix(base_slice);

return JSC.ZigString.init(out).toValueGC(globalThis);
}
Expand Down
105 changes: 86 additions & 19 deletions test/js/node/path/path.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,93 @@ it("should not inherit Object.prototype", () => {
expect(path).not.toHaveProperty("toString");
});

it("path.dirname", () => {
const fixtures = [
["yo", "."],
["/yo", "/"],
["/yo/", "/"],
["/yo/123", "/yo"],
[".", "."],
["../", "."],
["../../", ".."],
["../../foo", "../.."],
["../../foo/../", "../../foo"],
["/foo/../", "/foo"],
["../../foo/../bar", "../../foo/.."],
];
for (const [input, expected] of fixtures) {
expect(path.posix.dirname(input)).toBe(expected);
if (process.platform !== "win32") {
expect(path.dirname(input)).toBe(expected);
describe("dirname", () => {
it("path.dirname", () => {
const fixtures = [
["yo", "."],
["/yo", "/"],
["/yo/", "/"],
["/yo/123", "/yo"],
[".", "."],
["../", "."],
["../../", ".."],
["../../foo", "../.."],
["../../foo/../", "../../foo"],
["/foo/../", "/foo"],
["../../foo/../bar", "../../foo/.."],
];
for (const [input, expected] of fixtures) {
expect(path.posix.dirname(input)).toBe(expected);
if (process.platform !== "win32") {
expect(path.dirname(input)).toBe(expected);
}
}
}
});
it("path.posix.dirname", () => {
expect(path.posix.dirname("/a/b/")).toBe("/a");
expect(path.posix.dirname("/a/b")).toBe("/a");
expect(path.posix.dirname("/a")).toBe("/");
expect(path.posix.dirname("/a/")).toBe("/");
expect(path.posix.dirname("")).toBe(".");
expect(path.posix.dirname("/")).toBe("/");
expect(path.posix.dirname("//")).toBe("/");
expect(path.posix.dirname("///")).toBe("/");
expect(path.posix.dirname("////")).toBe("/");
expect(path.posix.dirname("//a")).toBe("//");
expect(path.posix.dirname("//ab")).toBe("//");
expect(path.posix.dirname("///a")).toBe("//");
expect(path.posix.dirname("////a")).toBe("///");
expect(path.posix.dirname("/////a")).toBe("////");
expect(path.posix.dirname("foo")).toBe(".");
expect(path.posix.dirname("foo/")).toBe(".");
expect(path.posix.dirname("a/b")).toBe("a");
expect(path.posix.dirname("a/")).toBe(".");
expect(path.posix.dirname("a///b")).toBe("a//");
expect(path.posix.dirname("a//b")).toBe("a/");
expect(path.posix.dirname("\\")).toBe(".");
expect(path.posix.dirname("\\a")).toBe(".");
expect(path.posix.dirname("a")).toBe(".");
expect(path.posix.dirname("/a/b//c")).toBe("/a/b/");
});
it("path.win32.dirname", () => {
expect(path.win32.dirname("c:\\")).toBe("c:\\");
expect(path.win32.dirname("c:\\foo")).toBe("c:\\");
expect(path.win32.dirname("c:\\foo\\")).toBe("c:\\");
expect(path.win32.dirname("c:\\foo\\bar")).toBe("c:\\foo");
expect(path.win32.dirname("c:\\foo\\bar\\")).toBe("c:\\foo");
expect(path.win32.dirname("c:\\foo\\bar\\baz")).toBe("c:\\foo\\bar");
expect(path.win32.dirname("c:\\foo bar\\baz")).toBe("c:\\foo bar");
expect(path.win32.dirname("c:\\\\foo")).toBe("c:\\");
expect(path.win32.dirname("\\")).toBe("\\");
expect(path.win32.dirname("\\foo")).toBe("\\");
expect(path.win32.dirname("\\foo\\")).toBe("\\");
expect(path.win32.dirname("\\foo\\bar")).toBe("\\foo");
expect(path.win32.dirname("\\foo\\bar\\")).toBe("\\foo");
expect(path.win32.dirname("\\foo\\bar\\baz")).toBe("\\foo\\bar");
expect(path.win32.dirname("\\foo bar\\baz")).toBe("\\foo bar");
expect(path.win32.dirname("c:")).toBe("c:");
expect(path.win32.dirname("c:foo")).toBe("c:");
expect(path.win32.dirname("c:foo\\")).toBe("c:");
expect(path.win32.dirname("c:foo\\bar")).toBe("c:foo");
expect(path.win32.dirname("c:foo\\bar\\")).toBe("c:foo");
expect(path.win32.dirname("c:foo\\bar\\baz")).toBe("c:foo\\bar");
expect(path.win32.dirname("c:foo bar\\baz")).toBe("c:foo bar");
expect(path.win32.dirname("file:stream")).toBe(".");
expect(path.win32.dirname("dir\\file:stream")).toBe("dir");
expect(path.win32.dirname("\\\\unc\\share")).toBe("\\\\unc\\share");
expect(path.win32.dirname("\\\\unc\\share\\foo")).toBe("\\\\unc\\share\\");
expect(path.win32.dirname("\\\\unc\\share\\foo\\")).toBe("\\\\unc\\share\\");
expect(path.win32.dirname("\\\\unc\\share\\foo\\bar")).toBe("\\\\unc\\share\\foo");
expect(path.win32.dirname("\\\\unc\\share\\foo\\bar\\")).toBe("\\\\unc\\share\\foo");
expect(path.win32.dirname("\\\\unc\\share\\foo\\bar\\baz")).toBe("\\\\unc\\share\\foo\\bar");
expect(path.win32.dirname("/a/b/")).toBe("/a");
expect(path.win32.dirname("/a/b")).toBe("/a");
expect(path.win32.dirname("/a")).toBe("/");
expect(path.win32.dirname("")).toBe(".");
expect(path.win32.dirname("/")).toBe("/");
expect(path.win32.dirname("////")).toBe("/");
expect(path.win32.dirname("foo")).toBe(".");
});
});

it("path.parse().name", () => {
Expand Down

0 comments on commit 8aab32c

Please sign in to comment.