diff options
author | 2023-07-25 15:41:22 +0800 | |
---|---|---|
committer | 2023-07-25 00:41:22 -0700 | |
commit | 130079b558c87519b60109028e98feffdf984933 (patch) | |
tree | fd6c06575a930f9776a30d7c639d9c001949c0e2 | |
parent | 83568307274d515aca645b638879118bae2d15a9 (diff) | |
download | bun-130079b558c87519b60109028e98feffdf984933.tar.gz bun-130079b558c87519b60109028e98feffdf984933.tar.zst bun-130079b558c87519b60109028e98feffdf984933.zip |
fix some cases in the `dirname` function. (#3785)
Close: #3782
-rw-r--r-- | src/bun.js/node/types.zig | 85 | ||||
-rw-r--r-- | test/js/node/path/path.test.js | 105 |
2 files changed, 169 insertions, 21 deletions
diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index d1c92e970..bc2144095 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -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) { @@ -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); } diff --git a/test/js/node/path/path.test.js b/test/js/node/path/path.test.js index 03037c2e9..deab41796 100644 --- a/test/js/node/path/path.test.js +++ b/test/js/node/path/path.test.js @@ -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", () => { |