aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-02-02 23:33:37 -0800
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-02-02 23:33:37 -0800
commita52a948a706724895979604e780a6a5d5db92f95 (patch)
treec72e88af48b3cb78f542f1ed9ccb412e72cc9a04
parent94cbfa45792342105fb1a5b69cafc3a8f4a1ba7a (diff)
downloadbun-a52a948a706724895979604e780a6a5d5db92f95.tar.gz
bun-a52a948a706724895979604e780a6a5d5db92f95.tar.zst
bun-a52a948a706724895979604e780a6a5d5db92f95.zip
`path.relative` passes Node's tests (which also fixed bugs)
-rw-r--r--integration/bunjs-only-snippets/path.test.js455
-rw-r--r--src/javascript/jsc/node/types.zig7
-rw-r--r--src/javascript/jsc/path-posix.exports.js35
-rw-r--r--src/javascript/jsc/path-win32.exports.js35
-rw-r--r--src/javascript/jsc/path.exports.js18
-rw-r--r--src/resolver/resolve_path.zig288
-rw-r--r--src/string_immutable.zig2
-rw-r--r--src/test/tester.zig14
8 files changed, 571 insertions, 283 deletions
diff --git a/integration/bunjs-only-snippets/path.test.js b/integration/bunjs-only-snippets/path.test.js
index 7f4708b31..292a79f5e 100644
--- a/integration/bunjs-only-snippets/path.test.js
+++ b/integration/bunjs-only-snippets/path.test.js
@@ -1,184 +1,307 @@
+const { file } = import.meta;
+
import { describe, it, expect } from "bun:test";
import * as path from "node:path";
+import assert from "assert";
-const __filename = import.meta.file;
+const strictEqual = (...args) => {
+ assert.strictEqual(...args);
+ expect(true).toBe(true);
+};
-describe("path.basename", () => {
- it("basics", () => {
- expect(path.basename(__filename)).toBe("path.test.js");
- expect(path.basename(__filename, ".js")).toBe("path.test");
- expect(path.basename(".js", ".js")).toBe("");
- expect(path.basename("")).toBe("");
- expect(path.basename("/dir/basename.ext")).toBe("basename.ext");
- expect(path.basename("/basename.ext")).toBe("basename.ext");
- expect(path.basename("basename.ext")).toBe("basename.ext");
- expect(path.basename("basename.ext/")).toBe("basename.ext");
- expect(path.basename("basename.ext//")).toBe("basename.ext");
- expect(path.basename("aaa/bbb", "/bbb")).toBe("bbb");
- expect(path.basename("aaa/bbb", "a/bbb")).toBe("bbb");
- expect(path.basename("aaa/bbb", "bbb")).toBe("bbb");
- expect(path.basename("aaa/bbb//", "bbb")).toBe("bbb");
- expect(path.basename("aaa/bbb", "bb")).toBe("b");
- expect(path.basename("aaa/bbb", "b")).toBe("bb");
- expect(path.basename("/aaa/bbb", "/bbb")).toBe("bbb");
- expect(path.basename("/aaa/bbb", "a/bbb")).toBe("bbb");
- expect(path.basename("/aaa/bbb", "bbb")).toBe("bbb");
- expect(path.basename("/aaa/bbb//", "bbb")).toBe("bbb");
- expect(path.basename("/aaa/bbb", "bb")).toBe("b");
- expect(path.basename("/aaa/bbb", "b")).toBe("bb");
- expect(path.basename("/aaa/bbb")).toBe("bbb");
- expect(path.basename("/aaa/")).toBe("aaa");
- expect(path.basename("/aaa/b")).toBe("b");
- expect(path.basename("/a/b")).toBe("b");
- expect(path.basename("//a")).toBe("a");
- });
+it("path.basename", () => {
+ strictEqual(path.basename(file), "path.test.js");
+ strictEqual(path.basename(file, ".js"), "path.test");
+ strictEqual(path.basename(".js", ".js"), "");
+ strictEqual(path.basename(""), "");
+ strictEqual(path.basename("/dir/basename.ext"), "basename.ext");
+ strictEqual(path.basename("/basename.ext"), "basename.ext");
+ strictEqual(path.basename("basename.ext"), "basename.ext");
+ strictEqual(path.basename("basename.ext/"), "basename.ext");
+ strictEqual(path.basename("basename.ext//"), "basename.ext");
+ strictEqual(path.basename("aaa/bbb", "/bbb"), "bbb");
+ strictEqual(path.basename("aaa/bbb", "a/bbb"), "bbb");
+ strictEqual(path.basename("aaa/bbb", "bbb"), "bbb");
+ strictEqual(path.basename("aaa/bbb//", "bbb"), "bbb");
+ strictEqual(path.basename("aaa/bbb", "bb"), "b");
+ strictEqual(path.basename("aaa/bbb", "b"), "bb");
+ strictEqual(path.basename("/aaa/bbb", "/bbb"), "bbb");
+ strictEqual(path.basename("/aaa/bbb", "a/bbb"), "bbb");
+ strictEqual(path.basename("/aaa/bbb", "bbb"), "bbb");
+ strictEqual(path.basename("/aaa/bbb//", "bbb"), "bbb");
+ strictEqual(path.basename("/aaa/bbb", "bb"), "b");
+ strictEqual(path.basename("/aaa/bbb", "b"), "bb");
+ strictEqual(path.basename("/aaa/bbb"), "bbb");
+ strictEqual(path.basename("/aaa/"), "aaa");
+ strictEqual(path.basename("/aaa/b"), "b");
+ strictEqual(path.basename("/a/b"), "b");
+ strictEqual(path.basename("//a"), "a");
+ strictEqual(path.basename("a", "a"), "");
- it("On unix a backslash is just treated as any other character.", () => {
- expect(path.posix.basename("\\dir\\basename.ext")).toBe(
- "\\dir\\basename.ext"
- );
- expect(path.posix.basename("\\basename.ext")).toBe("\\basename.ext");
- expect(path.posix.basename("basename.ext")).toBe("basename.ext");
- expect(path.posix.basename("basename.ext\\")).toBe("basename.ext\\");
- expect(path.posix.basename("basename.ext\\\\")).toBe("basename.ext\\\\");
- expect(path.posix.basename("foo")).toBe("foo");
- });
+ // On Windows a backslash acts as a path separator.
+ strictEqual(path.win32.basename("\\dir\\basename.ext"), "basename.ext");
+ strictEqual(path.win32.basename("\\basename.ext"), "basename.ext");
+ strictEqual(path.win32.basename("basename.ext"), "basename.ext");
+ strictEqual(path.win32.basename("basename.ext\\"), "basename.ext");
+ strictEqual(path.win32.basename("basename.ext\\\\"), "basename.ext");
+ strictEqual(path.win32.basename("foo"), "foo");
+ strictEqual(path.win32.basename("aaa\\bbb", "\\bbb"), "bbb");
+ strictEqual(path.win32.basename("aaa\\bbb", "a\\bbb"), "bbb");
+ strictEqual(path.win32.basename("aaa\\bbb", "bbb"), "bbb");
+ strictEqual(path.win32.basename("aaa\\bbb\\\\\\\\", "bbb"), "bbb");
+ strictEqual(path.win32.basename("aaa\\bbb", "bb"), "b");
+ strictEqual(path.win32.basename("aaa\\bbb", "b"), "bb");
+ strictEqual(path.win32.basename("C:"), "");
+ strictEqual(path.win32.basename("C:."), ".");
+ strictEqual(path.win32.basename("C:\\"), "");
+ strictEqual(path.win32.basename("C:\\dir\\base.ext"), "base.ext");
+ strictEqual(path.win32.basename("C:\\basename.ext"), "basename.ext");
+ strictEqual(path.win32.basename("C:basename.ext"), "basename.ext");
+ strictEqual(path.win32.basename("C:basename.ext\\"), "basename.ext");
+ strictEqual(path.win32.basename("C:basename.ext\\\\"), "basename.ext");
+ strictEqual(path.win32.basename("C:foo"), "foo");
+ strictEqual(path.win32.basename("file:stream"), "file:stream");
+ strictEqual(path.win32.basename("a", "a"), "");
- it("POSIX filenames may include control characters", () => {
- // c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html
- var controlCharFilename = "Icon" + String.fromCharCode(13);
- expect(path.posix.basename("/a/b/" + controlCharFilename)).toBe(
- controlCharFilename
- );
- });
+ // On unix a backslash is just treated as any other character.
+ strictEqual(
+ path.posix.basename("\\dir\\basename.ext"),
+ "\\dir\\basename.ext"
+ );
+ strictEqual(path.posix.basename("\\basename.ext"), "\\basename.ext");
+ strictEqual(path.posix.basename("basename.ext"), "basename.ext");
+ strictEqual(path.posix.basename("basename.ext\\"), "basename.ext\\");
+ strictEqual(path.posix.basename("basename.ext\\\\"), "basename.ext\\\\");
+ strictEqual(path.posix.basename("foo"), "foo");
+
+ // POSIX filenames may include control characters
+ // c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html
+ const controlCharFilename = `Icon${String.fromCharCode(13)}`;
+ strictEqual(
+ path.posix.basename(`/a/b/${controlCharFilename}`),
+ controlCharFilename
+ );
});
-it("path.posix.extname", () => {
- var pairs = [
- [__filename, ".js"],
- ["", ""],
- ["/path/to/file", ""],
- ["/path/to/file.ext", ".ext"],
- ["/path.to/file.ext", ".ext"],
- ["/path.to/file", ""],
- ["/path.to/.file", ""],
- ["/path.to/.file.ext", ".ext"],
- ["/path/to/f.ext", ".ext"],
- ["/path/to/..ext", ".ext"],
- ["/path/to/..", ""],
- ["file", ""],
- ["file.ext", ".ext"],
- [".file", ""],
- [".file.ext", ".ext"],
- ["/file", ""],
- ["/file.ext", ".ext"],
- ["/.file", ""],
- ["/.file.ext", ".ext"],
- [".path/file.ext", ".ext"],
- ["file.ext.ext", ".ext"],
- ["file.", "."],
- [".", ""],
- ["./", ""],
- [".file.ext", ".ext"],
- [".file", ""],
- [".file.", "."],
- [".file..", "."],
- ["..", ""],
- ["../", ""],
- ["..file.ext", ".ext"],
- ["..file", ".file"],
- ["..file.", "."],
- ["..file..", "."],
- ["...", "."],
- ["...ext", ".ext"],
- ["....", "."],
- ["file.ext/", ".ext"],
- ["file.ext//", ".ext"],
- ["file/", ""],
- ["file//", ""],
- ["file./", "."],
- ["file.//", "."],
+it("path.join", () => {
+ const failures = [];
+ const backslashRE = /\\/g;
+
+ const joinTests = [
+ [
+ [path.posix.join],
+ // Arguments result
+ [
+ [[".", "x/b", "..", "/b/c.js"], "x/b/c.js"],
+ // [[], '.'],
+ [["/.", "x/b", "..", "/b/c.js"], "/x/b/c.js"],
+ [["/foo", "../../../bar"], "/bar"],
+ [["foo", "../../../bar"], "../../bar"],
+ [["foo/", "../../../bar"], "../../bar"],
+ [["foo/x", "../../../bar"], "../bar"],
+ [["foo/x", "./bar"], "foo/x/bar"],
+ [["foo/x/", "./bar"], "foo/x/bar"],
+ [["foo/x/", ".", "bar"], "foo/x/bar"],
+ [["./"], "./"],
+ [[".", "./"], "./"],
+ [[".", ".", "."], "."],
+ [[".", "./", "."], "."],
+ [[".", "/./", "."], "."],
+ [[".", "/////./", "."], "."],
+ [["."], "."],
+ [["", "."], "."],
+ [["", "foo"], "foo"],
+ [["foo", "/bar"], "foo/bar"],
+ [["", "/foo"], "/foo"],
+ [["", "", "/foo"], "/foo"],
+ [["", "", "foo"], "foo"],
+ [["foo", ""], "foo"],
+ [["foo/", ""], "foo/"],
+ [["foo", "", "/bar"], "foo/bar"],
+ [["./", "..", "/foo"], "../foo"],
+ [["./", "..", "..", "/foo"], "../../foo"],
+ [[".", "..", "..", "/foo"], "../../foo"],
+ [["", "..", "..", "/foo"], "../../foo"],
+ [["/"], "/"],
+ [["/", "."], "/"],
+ [["/", ".."], "/"],
+ [["/", "..", ".."], "/"],
+ [[""], "."],
+ [["", ""], "."],
+ [[" /foo"], " /foo"],
+ [[" ", "foo"], " /foo"],
+ [[" ", "."], " "],
+ [[" ", "/"], " /"],
+ [[" ", ""], " "],
+ [["/", "foo"], "/foo"],
+ [["/", "/foo"], "/foo"],
+ [["/", "//foo"], "/foo"],
+ [["/", "", "/foo"], "/foo"],
+ [["", "/", "foo"], "/foo"],
+ [["", "/", "/foo"], "/foo"],
+ ],
+ ],
];
- pairs.forEach(function (p) {
- var input = p[0];
- var expected = p[1];
- expect(expected).toBe(path.posix.extname(input));
+ // Windows-specific join tests
+ // joinTests.push([
+ // path.win32.join,
+ // joinTests[0][1].slice(0).concat([
+ // // Arguments result
+ // // UNC path expected
+ // [['//foo/bar'], '\\\\foo\\bar\\'],
+ // [['\\/foo/bar'], '\\\\foo\\bar\\'],
+ // [['\\\\foo/bar'], '\\\\foo\\bar\\'],
+ // // UNC path expected - server and share separate
+ // [['//foo', 'bar'], '\\\\foo\\bar\\'],
+ // [['//foo/', 'bar'], '\\\\foo\\bar\\'],
+ // [['//foo', '/bar'], '\\\\foo\\bar\\'],
+ // // UNC path expected - questionable
+ // [['//foo', '', 'bar'], '\\\\foo\\bar\\'],
+ // [['//foo/', '', 'bar'], '\\\\foo\\bar\\'],
+ // [['//foo/', '', '/bar'], '\\\\foo\\bar\\'],
+ // // UNC path expected - even more questionable
+ // [['', '//foo', 'bar'], '\\\\foo\\bar\\'],
+ // [['', '//foo/', 'bar'], '\\\\foo\\bar\\'],
+ // [['', '//foo/', '/bar'], '\\\\foo\\bar\\'],
+ // // No UNC path expected (no double slash in first component)
+ // [['\\', 'foo/bar'], '\\foo\\bar'],
+ // [['\\', '/foo/bar'], '\\foo\\bar'],
+ // [['', '/', '/foo/bar'], '\\foo\\bar'],
+ // // No UNC path expected (no non-slashes in first component -
+ // // questionable)
+ // [['//', 'foo/bar'], '\\foo\\bar'],
+ // [['//', '/foo/bar'], '\\foo\\bar'],
+ // [['\\\\', '/', '/foo/bar'], '\\foo\\bar'],
+ // [['//'], '\\'],
+ // // No UNC path expected (share name missing - questionable).
+ // [['//foo'], '\\foo'],
+ // [['//foo/'], '\\foo\\'],
+ // [['//foo', '/'], '\\foo\\'],
+ // [['//foo', '', '/'], '\\foo\\'],
+ // // No UNC path expected (too many leading slashes - questionable)
+ // [['///foo/bar'], '\\foo\\bar'],
+ // [['////foo', 'bar'], '\\foo\\bar'],
+ // [['\\\\\\/foo/bar'], '\\foo\\bar'],
+ // // Drive-relative vs drive-absolute paths. This merely describes the
+ // // status quo, rather than being obviously right
+ // [['c:'], 'c:.'],
+ // [['c:.'], 'c:.'],
+ // [['c:', ''], 'c:.'],
+ // [['', 'c:'], 'c:.'],
+ // [['c:.', '/'], 'c:.\\'],
+ // [['c:.', 'file'], 'c:file'],
+ // [['c:', '/'], 'c:\\'],
+ // [['c:', 'file'], 'c:\\file'],
+ // ]),
+ // ]);
+ joinTests.forEach((test) => {
+ if (!Array.isArray(test[0])) test[0] = [test[0]];
+ test[0].forEach((join) => {
+ test[1].forEach((test) => {
+ const actual = join.apply(null, test[0]);
+ const expected = test[1];
+ // For non-Windows specific tests with the Windows join(), we need to try
+ // replacing the slashes since the non-Windows specific tests' `expected`
+ // use forward slashes
+ let actualAlt;
+ let os;
+ if (join === path.win32.join) {
+ actualAlt = actual.replace(backslashRE, "/");
+ os = "win32";
+ } else {
+ os = "posix";
+ }
+ if (actual !== expected && actualAlt !== expected) {
+ const delimiter = test[0].map(JSON.stringify).join(",");
+ const message = `path.${os}.join(${delimiter})\n expect=${JSON.stringify(
+ expected
+ )}\n actual=${JSON.stringify(actual)}`;
+ failures.push(`\n${message}`);
+ }
+ });
+ });
});
+ strictEqual(failures.length, 0, failures.join(""));
});
-it("path.posix.dirname", function (t) {
- 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("")).toBe(".");
- expect(path.posix.dirname("/")).toBe("/");
- expect(path.posix.dirname("//a")).toBe("//");
- expect(path.posix.dirname("foo")).toBe(".");
-});
-
-it("path.posix.isAbsolute", () => {
- expect(path.posix.isAbsolute("/home/foo")).toBe(true);
- expect(path.posix.isAbsolute("/home/foo/..")).toBe(true);
- expect(path.posix.isAbsolute("bar/")).toBe(false);
- expect(path.posix.isAbsolute("./baz")).toBe(false);
-});
+it("path.relative", () => {
+ const failures = [];
-tape("path.posix.join", () => {
- var joinTests =
- // arguments result
+ const relativeTests = [
+ // [
+ // path.win32.relative,
+ // // Arguments result
+ // [
+ // ["c:/blah\\blah", "d:/games", "d:\\games"],
+ // ["c:/aaaa/bbbb", "c:/aaaa", ".."],
+ // ["c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"],
+ // ["c:/aaaa/bbbb", "c:/aaaa/bbbb", ""],
+ // ["c:/aaaa/bbbb", "c:/aaaa/cccc", "..\\cccc"],
+ // ["c:/aaaa/", "c:/aaaa/cccc", "cccc"],
+ // ["c:/", "c:\\aaaa\\bbbb", "aaaa\\bbbb"],
+ // ["c:/aaaa/bbbb", "d:\\", "d:\\"],
+ // ["c:/AaAa/bbbb", "c:/aaaa/bbbb", ""],
+ // ["c:/aaaaa/", "c:/aaaa/cccc", "..\\aaaa\\cccc"],
+ // ["C:\\foo\\bar\\baz\\quux", "C:\\", "..\\..\\..\\.."],
+ // [
+ // "C:\\foo\\test",
+ // "C:\\foo\\test\\bar\\package.json",
+ // "bar\\package.json",
+ // ],
+ // ["C:\\foo\\bar\\baz-quux", "C:\\foo\\bar\\baz", "..\\baz"],
+ // ["C:\\foo\\bar\\baz", "C:\\foo\\bar\\baz-quux", "..\\baz-quux"],
+ // ["\\\\foo\\bar", "\\\\foo\\bar\\baz", "baz"],
+ // ["\\\\foo\\bar\\baz", "\\\\foo\\bar", ".."],
+ // ["\\\\foo\\bar\\baz-quux", "\\\\foo\\bar\\baz", "..\\baz"],
+ // ["\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz-quux", "..\\baz-quux"],
+ // ["C:\\baz-quux", "C:\\baz", "..\\baz"],
+ // ["C:\\baz", "C:\\baz-quux", "..\\baz-quux"],
+ // ["\\\\foo\\baz-quux", "\\\\foo\\baz", "..\\baz"],
+ // ["\\\\foo\\baz", "\\\\foo\\baz-quux", "..\\baz-quux"],
+ // ["C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"],
+ // ["\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"],
+ // ],
+ // ],
[
- [[".", "x/b", "..", "/b/c.js"], "x/b/c.js"],
- [[], "."],
- [["/.", "x/b", "..", "/b/c.js"], "/x/b/c.js"],
- [["/foo", "../../../bar"], "/bar"],
- [["foo", "../../../bar"], "../../bar"],
- [["foo/", "../../../bar"], "../../bar"],
- [["foo/x", "../../../bar"], "../bar"],
- [["foo/x", "./bar"], "foo/x/bar"],
- [["foo/x/", "./bar"], "foo/x/bar"],
- [["foo/x/", ".", "bar"], "foo/x/bar"],
- [["./"], "./"],
- [[".", "./"], "./"],
- [[".", ".", "."], "."],
- [[".", "./", "."], "."],
- [[".", "/./", "."], "."],
- [[".", "/////./", "."], "."],
- [["."], "."],
- [["", "."], "."],
- [["", "foo"], "foo"],
- [["foo", "/bar"], "foo/bar"],
- [["", "/foo"], "/foo"],
- [["", "", "/foo"], "/foo"],
- [["", "", "foo"], "foo"],
- [["foo", ""], "foo"],
- [["foo/", ""], "foo/"],
- [["foo", "", "/bar"], "foo/bar"],
- [["./", "..", "/foo"], "../foo"],
- [["./", "..", "..", "/foo"], "../../foo"],
- [[".", "..", "..", "/foo"], "../../foo"],
- [["", "..", "..", "/foo"], "../../foo"],
- [["/"], "/"],
- [["/", "."], "/"],
- [["/", ".."], "/"],
- [["/", "..", ".."], "/"],
- [[""], "."],
- [["", ""], "."],
- [[" /foo"], " /foo"],
- [[" ", "foo"], " /foo"],
- [[" ", "."], " "],
- [[" ", "/"], " /"],
- [[" ", ""], " "],
- [["/", "foo"], "/foo"],
- [["/", "/foo"], "/foo"],
- [["/", "//foo"], "/foo"],
- [["/", "", "/foo"], "/foo"],
- [["", "/", "foo"], "/foo"],
- [["", "/", "/foo"], "/foo"],
- ];
+ path.posix.relative,
+ // Arguments result
+ [
+ ["/var/lib", "/var", ".."],
+ ["/var/lib", "/bin", "../../bin"],
+ ["/var/lib", "/var/lib", ""],
+ ["/var/lib", "/var/apache", "../apache"],
+ ["/var/", "/var/lib", "lib"],
+ ["/", "/var/lib", "var/lib"],
+ ["/foo/test", "/foo/test/bar/package.json", "bar/package.json"],
+ ["/Users/a/web/b/test/mails", "/Users/a/web/b", "../.."],
+ ["/foo/bar/baz-quux", "/foo/bar/baz", "../baz"],
+ ["/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux"],
+ ["/baz-quux", "/baz", "../baz"],
+ ["/baz", "/baz-quux", "../baz-quux"],
+ ["/page1/page2/foo", "/", "../../.."],
+ ],
+ ],
+ ];
- joinTests.forEach(() => {
- var actual = path.posix.join.apply(null, p[0]);
- expect(actual).toBe(p[1]);
+ relativeTests.forEach((test) => {
+ const relative = test[0];
+ test[1].forEach((test) => {
+ const actual = relative(test[0], test[1]);
+ const expected = test[2];
+ if (actual !== expected) {
+ const os = relative === path.win32.relative ? "win32" : "posix";
+ const message = `path.${os}.relative(${test
+ .slice(0, 2)
+ .map(JSON.stringify)
+ .join(",")})\n expect=${JSON.stringify(
+ expected
+ )}\n actual=${JSON.stringify(actual)}`;
+ failures.push(`\n${message}`);
+ }
+ });
});
+
+ assert.strictEqual(failures.length, 0, failures.join(""));
+ expect(true).toBe(true);
});
diff --git a/src/javascript/jsc/node/types.zig b/src/javascript/jsc/node/types.zig
index 9238c3970..bbdbba30c 100644
--- a/src/javascript/jsc/node/types.zig
+++ b/src/javascript/jsc/node/types.zig
@@ -2168,6 +2168,7 @@ pub const Path = struct {
}
pub fn join(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) return JSC.ZigString.init("").toValue(globalThis);
var stack_fallback_allocator = std.heap.stackFallback(
(32 * @sizeOf(string)),
@@ -2263,7 +2264,7 @@ pub const Path = struct {
var arguments = args_ptr[0..args_len];
if (args_len > 1 and JSC.JSValue.eqlValue(args_ptr[0], args_ptr[1]))
- return JSC.ZigString.init(".").toValue(globalThis);
+ return JSC.ZigString.init("").toValue(globalThis);
var from_slice: JSC.ZigString.Slice = if (args_len > 0) arguments[0].toSlice(globalThis, heap_allocator) else JSC.ZigString.Slice.empty;
defer from_slice.deinit();
@@ -2274,9 +2275,9 @@ pub const Path = struct {
var to = to_slice.slice();
var out = if (!isWindows)
- PathHandler.relativeNormalized(from, to, .posix, false)
+ PathHandler.relativePlatform(from, to, .posix, true)
else
- PathHandler.relativeNormalized(from, to, .windows, false);
+ PathHandler.relativePlatform(from, to, .windows, true);
var out_str = JSC.ZigString.init(out);
out_str.detectEncoding();
diff --git a/src/javascript/jsc/path-posix.exports.js b/src/javascript/jsc/path-posix.exports.js
new file mode 100644
index 000000000..b3f61c1a2
--- /dev/null
+++ b/src/javascript/jsc/path-posix.exports.js
@@ -0,0 +1,35 @@
+function bound(obj) {
+ return {
+ basename: obj.basename.bind(obj),
+ dirname: obj.dirname.bind(obj),
+ extname: obj.extname.bind(obj),
+ format: obj.format.bind(obj),
+ isAbsolute: obj.isAbsolute.bind(obj),
+ join: obj.join.bind(obj),
+ normalize: obj.normalize.bind(obj),
+ parse: obj.parse.bind(obj),
+ relative: obj.relative.bind(obj),
+ resolve: obj.resolve.bind(obj),
+ toNamespacedPath: obj.toNamespacedPath.bind(obj),
+ sep: obj.sep,
+ delimiter: obj.delimiter,
+ };
+}
+var path = bound(Bun._Path(false));
+
+export var {
+ basename,
+ dirname,
+ extname,
+ format,
+ isAbsolute,
+ join,
+ normalize,
+ parse,
+ relative,
+ resolve,
+ toNamespacedPath,
+ sep,
+ delimiter,
+} = path;
+export default path;
diff --git a/src/javascript/jsc/path-win32.exports.js b/src/javascript/jsc/path-win32.exports.js
new file mode 100644
index 000000000..932cc8960
--- /dev/null
+++ b/src/javascript/jsc/path-win32.exports.js
@@ -0,0 +1,35 @@
+function bound(obj) {
+ return {
+ basename: obj.basename.bind(obj),
+ dirname: obj.dirname.bind(obj),
+ extname: obj.extname.bind(obj),
+ format: obj.format.bind(obj),
+ isAbsolute: obj.isAbsolute.bind(obj),
+ join: obj.join.bind(obj),
+ normalize: obj.normalize.bind(obj),
+ parse: obj.parse.bind(obj),
+ relative: obj.relative.bind(obj),
+ resolve: obj.resolve.bind(obj),
+ toNamespacedPath: obj.toNamespacedPath.bind(obj),
+ sep: obj.sep,
+ delimiter: obj.delimiter,
+ };
+}
+var path = bound(Bun._Path(true));
+
+export var {
+ basename,
+ dirname,
+ extname,
+ format,
+ isAbsolute,
+ join,
+ normalize,
+ parse,
+ relative,
+ resolve,
+ toNamespacedPath,
+ sep,
+ delimiter,
+} = path;
+export default path;
diff --git a/src/javascript/jsc/path.exports.js b/src/javascript/jsc/path.exports.js
index 6fa7af88d..98f685fc1 100644
--- a/src/javascript/jsc/path.exports.js
+++ b/src/javascript/jsc/path.exports.js
@@ -21,7 +21,7 @@ path.posix = posix;
export var posix = bound(Bun._Path(false));
export var win32 = bound(Bun._Path(true));
-var {
+export var {
basename,
dirname,
extname,
@@ -37,20 +37,4 @@ var {
delimiter,
} = path;
-export {
- basename,
- dirname,
- extname,
- format,
- isAbsolute,
- join,
- normalize,
- parse,
- relative,
- resolve,
- toNamespacedPath,
- sep,
- delimiter,
-};
-
export default path;
diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig
index d77825211..30da31018 100644
--- a/src/resolver/resolve_path.zig
+++ b/src/resolver/resolve_path.zig
@@ -1,16 +1,17 @@
const tester = @import("../test/tester.zig");
const std = @import("std");
+const strings = @import("../string_immutable.zig");
const FeatureFlags = @import("../feature_flags.zig");
const default_allocator = @import("../memory_allocator.zig").c_allocator;
-threadlocal var parser_join_input_buffer: [1024]u8 = undefined;
+threadlocal var parser_join_input_buffer: [4096]u8 = undefined;
threadlocal var parser_buffer: [1024]u8 = undefined;
-inline fn nqlAtIndex(comptime string_count: comptime_int, index: usize, strings: []const []const u8) bool {
+inline fn nqlAtIndex(comptime string_count: comptime_int, index: usize, input: []const []const u8) bool {
comptime var string_index = 1;
inline while (string_index < string_count) : (string_index += 1) {
- if (strings[0][index] != strings[string_index][index]) {
+ if (input[0][index] != input[string_index][index]) {
return true;
}
}
@@ -19,13 +20,14 @@ inline fn nqlAtIndex(comptime string_count: comptime_int, index: usize, strings:
}
const IsSeparatorFunc = fn (char: u8) bool;
+const LastSeparatorFunction = fn (slice: []const u8) ?usize;
// TODO: is it faster to determine longest_common_separator in the while loop
// or as an extra step at the end?
// only boether to check if this function appears in benchmarking
-pub fn longestCommonPathGeneric(strings: []const []const u8, comptime separator: u8, comptime isPathSeparator: IsSeparatorFunc) []const u8 {
+pub fn longestCommonPathGeneric(input: []const []const u8, comptime separator: u8, comptime isPathSeparator: IsSeparatorFunc) []const u8 {
var min_length: usize = std.math.maxInt(usize);
- for (strings) |str| {
+ for (input) |str| {
min_length = @minimum(str.len, min_length);
}
@@ -33,79 +35,79 @@ pub fn longestCommonPathGeneric(strings: []const []const u8, comptime separator:
var last_common_separator: usize = 0;
// try to use an unrolled version of this loop
- switch (strings.len) {
+ switch (input.len) {
0 => {
return "";
},
1 => {
- return strings[0];
+ return input[0];
},
2 => {
while (index < min_length) : (index += 1) {
- if (strings[0][index] != strings[1][index]) {
+ if (input[0][index] != input[1][index]) {
break;
}
- if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{strings[0][index]})) {
+ if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{input[0][index]})) {
last_common_separator = index;
}
}
},
3 => {
while (index < min_length) : (index += 1) {
- if (nqlAtIndex(3, index, strings)) {
+ if (nqlAtIndex(3, index, input)) {
break;
}
- if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{strings[0][index]})) {
+ if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{input[0][index]})) {
last_common_separator = index;
}
}
},
4 => {
while (index < min_length) : (index += 1) {
- if (nqlAtIndex(4, index, strings)) {
+ if (nqlAtIndex(4, index, input)) {
break;
}
- if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{strings[0][index]})) {
+ if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{input[0][index]})) {
last_common_separator = index;
}
}
},
5 => {
while (index < min_length) : (index += 1) {
- if (nqlAtIndex(5, index, strings)) {
+ if (nqlAtIndex(5, index, input)) {
break;
}
- if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{strings[0][index]})) {
+ if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{input[0][index]})) {
last_common_separator = index;
}
}
},
6 => {
while (index < min_length) : (index += 1) {
- if (nqlAtIndex(6, index, strings)) {
+ if (nqlAtIndex(6, index, input)) {
break;
}
- if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{strings[0][index]})) {
+ if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{input[0][index]})) {
last_common_separator = index;
}
}
},
7 => {
while (index < min_length) : (index += 1) {
- if (nqlAtIndex(7, index, strings)) {
+ if (nqlAtIndex(7, index, input)) {
break;
}
- if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{strings[0][index]})) {
+ if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{input[0][index]})) {
last_common_separator = index;
}
}
},
8 => {
while (index < min_length) : (index += 1) {
- if (nqlAtIndex(8, index, strings)) {
+ if (nqlAtIndex(8, index, input)) {
break;
}
- if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{strings[0][index]})) {
+ if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{input[0][index]})) {
last_common_separator = index;
}
}
@@ -113,12 +115,12 @@ pub fn longestCommonPathGeneric(strings: []const []const u8, comptime separator:
else => {
var string_index: usize = 1;
while (index < min_length) : (index += 1) {
- while (string_index < strings.len) : (string_index += 1) {
- if (strings[0][index] != strings[index][string_index]) {
+ while (string_index < input.len) : (string_index += 1) {
+ if (input[0][index] != input[index][string_index]) {
break;
}
}
- if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{strings[0][index]})) {
+ if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{input[0][index]})) {
last_common_separator = index;
}
}
@@ -138,7 +140,7 @@ pub fn longestCommonPathGeneric(strings: []const []const u8, comptime separator:
// /app/public/
// To detect /app/public is actually a folder, we do one more loop through the strings
// and say, "do one of you have a path separator after what we thought was the end?"
- for (strings) |str| {
+ for (input) |str| {
if (str.len > index + 1) {
if (@call(.{ .modifier = .always_inline }, isPathSeparator, .{str[index]})) {
return str[0 .. index + 2];
@@ -146,19 +148,19 @@ pub fn longestCommonPathGeneric(strings: []const []const u8, comptime separator:
}
}
- return strings[0][0 .. last_common_separator + 1];
+ return input[0][0 .. last_common_separator + 1];
}
-pub fn longestCommonPath(strings: []const []const u8) []const u8 {
- return longestCommonPathGeneric(strings, '/', isSepAny);
+pub fn longestCommonPath(input: []const []const u8) []const u8 {
+ return longestCommonPathGeneric(input, '/', isSepAny);
}
-pub fn longestCommonPathWindows(strings: []const []const u8) []const u8 {
- return longestCommonPathGeneric(strings, std.fs.path.sep_windows, isSepWin32);
+pub fn longestCommonPathWindows(input: []const []const u8) []const u8 {
+ return longestCommonPathGeneric(input, std.fs.path.sep_windows, isSepWin32);
}
-pub fn longestCommonPathPosix(strings: []const []const u8) []const u8 {
- return longestCommonPathGeneric(strings, std.fs.path.sep_posix, isSepPosix);
+pub fn longestCommonPathPosix(input: []const []const u8) []const u8 {
+ return longestCommonPathGeneric(input, std.fs.path.sep_posix, isSepPosix);
}
threadlocal var relative_to_common_path_buf: [4096]u8 = undefined;
@@ -178,9 +180,9 @@ pub fn relativeToCommonPath(
const common_path = if (has_leading_separator) _common_path[1..] else _common_path;
- var shortest = @minimum(normalized_from.len, normalized_to.len);
+ const shortest = @minimum(normalized_from.len, normalized_to.len);
- var last_common_separator = @maximum(common_path.len, 1) - 1;
+ var last_common_separator = std.mem.lastIndexOfScalar(u8, _common_path, separator) orelse 0;
if (shortest == common_path.len) {
if (normalized_to.len > normalized_from.len) {
@@ -209,20 +211,6 @@ pub fn relativeToCommonPath(
}
}
}
-
- if (normalized_from.len > normalized_to.len) {
- // We get here if `to` is the exact base path for `from`.
- // For example: from='/foo/bar/baz'; to='/foo/bar'
- if (normalized_from[common_path.len - 1] == separator) {
- last_common_separator = common_path.len - 1;
- } else if (normalized_from[common_path.len] == separator) {
- last_common_separator = common_path.len;
- } else if (common_path.len == 0) {
- // We get here if `to` is the root.
- // For example: from='/foo/bar'; to='/'
- last_common_separator = 0;
- }
- }
}
// Generate the relative path based on the path difference between `to`
@@ -237,25 +225,27 @@ pub fn relativeToCommonPath(
if (i == normalized_from.len or (normalized_from[i] == separator and i + 1 < normalized_from.len)) {
if (out_slice.len == 0) {
out_slice = buf[0 .. out_slice.len + 2];
- out_slice[0] = '.';
- out_slice[1] = '.';
+ out_slice[0..2].* = "..".*;
} else {
- var old_len = out_slice.len;
- out_slice = buf[0 .. out_slice.len + 3];
- out_slice[old_len] = separator;
- old_len += 1;
- out_slice[old_len] = '.';
- old_len += 1;
- out_slice[old_len] = '.';
+ const old_len = out_slice.len;
+ out_slice.len += 3;
+ out_slice[old_len..][0..3].* = "/..".*;
}
}
}
}
if (normalized_to.len > last_common_separator + 1) {
- const tail = normalized_to[last_common_separator..];
- const insert_leading_slash = last_common_separator > 0 and normalized_to[last_common_separator - 1] != separator and tail[0] != separator;
+ var tail = normalized_to[last_common_separator..];
+ if (normalized_from.len > 0 and (last_common_separator == normalized_from.len or (last_common_separator == normalized_from.len - 1))) {
+ if (tail[0] == separator) {
+ tail = tail[1..];
+ }
+ }
+ const insert_leading_slash = last_common_separator > 0 and normalized_to[last_common_separator] != separator and tail[0] != separator;
+
+ // avoid making non-absolute paths absolute
if (insert_leading_slash) {
buf[out_slice.len] = separator;
out_slice = buf[0 .. out_slice.len + 1];
@@ -273,8 +263,13 @@ pub fn relativeToCommonPath(
}
pub fn relativeNormalized(from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 {
+ if (from.len == to.len and strings.eqlLong(from, to, true)) {
+ return "";
+ }
+
const two = [_][]const u8{ from, to };
const common_path = longestCommonPathGeneric(&two, comptime platform.separator(), comptime platform.getSeparatorFunc());
+
return relativeToCommonPath(common_path, from, to, &relative_to_common_path_buf, comptime platform.separator(), always_copy);
}
@@ -308,8 +303,27 @@ pub fn relative(from: []const u8, to: []const u8) []const u8 {
}
pub fn relativePlatform(from: []const u8, to: []const u8, comptime platform: Platform, comptime always_copy: bool) []const u8 {
- const normalized_from = normalizeStringBuf(from, &relative_from_buf, true, platform, true);
- const normalized_to = normalizeStringBuf(to, &relative_to_buf, true, platform, true);
+ const from_allow_above_root = (from.len > 1 and from[0] == platform.separator());
+ const to_allow_above_root = (to.len > 1 and to[0] == platform.separator());
+ var normalized_from =
+ if (!from_allow_above_root)
+ normalizeStringBuf(from, relative_from_buf[1..], false, platform, true)
+ else
+ normalizeStringBuf(from, relative_from_buf[1..], true, platform, true);
+ var normalized_to =
+ if (!to_allow_above_root)
+ normalizeStringBuf(to, relative_to_buf[1..], false, platform, true)
+ else
+ normalizeStringBuf(to, relative_to_buf[1..], true, platform, true);
+
+ if (from_allow_above_root == to_allow_above_root and from_allow_above_root) {
+ relative_from_buf[0] = platform.separator();
+ normalized_from = relative_from_buf[0 .. normalized_from.len + 1];
+
+ relative_to_buf[0] = platform.separator();
+ normalized_to = relative_to_buf[0 .. normalized_to.len + 1];
+ }
+ //
return relativeNormalized(normalized_from, normalized_to, platform, always_copy);
}
@@ -348,7 +362,7 @@ pub fn normalizeStringGeneric(str: []const u8, buf: []u8, comptime allow_above_r
if (last_slash == @intCast(i32, i) - 1 or dots == 1) {
// NOOP
} else if (dots == 2) {
- if (written_len < 2 or last_segment_length != 2 or buf[written_len - 1] != '.' or buf[written_len - 2] != '.') {
+ if (written_len < 2 or last_segment_length != 2 or @bitCast(u16, buf[written_len - 2 ..][0..2].*) != std.mem.readIntNative(u16, "..")) {
if (written_len > 2) {
if (lastIndexOfSeparator(buf[0..written_len])) |last_slash_index| {
written_len = last_slash_index;
@@ -373,10 +387,8 @@ pub fn normalizeStringGeneric(str: []const u8, buf: []u8, comptime allow_above_r
written_len += 1;
}
- buf[written_len] = '.';
- written_len += 1;
- buf[written_len] = '.';
- written_len += 1;
+ buf[written_len .. written_len + 2][0..2].* = "..".*;
+ written_len += 2;
last_segment_length = 2;
}
@@ -428,6 +440,14 @@ pub const Platform = enum {
};
}
+ pub fn separatorString(comptime platform: Platform) []const u8 {
+ return comptime switch (platform) {
+ .auto => platform.resolve().separatorString(),
+ .loose, .posix => std.fs.path.sep_str_posix,
+ .windows => std.fs.path.sep_str_windows,
+ };
+ }
+
pub const current: Platform = switch (@import("builtin").target.os.tag) {
.windows => Platform.windows,
else => Platform.posix,
@@ -448,6 +468,21 @@ pub const Platform = enum {
}
}
+ pub fn getLastSeparatorFunc(comptime _platform: Platform) LastSeparatorFunction {
+ switch (comptime _platform.resolve()) {
+ .auto => unreachable,
+ .loose => {
+ return lastIndexOfSeparatorLoose;
+ },
+ .windows => {
+ return lastIndexOfSeparatorWindows;
+ },
+ .posix => {
+ return lastIndexOfSeparatorPosix;
+ },
+ }
+ }
+
pub inline fn isSeparator(comptime _platform: Platform, char: u8) bool {
switch (comptime _platform.resolve()) {
.auto => unreachable,
@@ -630,12 +665,14 @@ pub fn joinStringBuf(buf: []u8, _parts: anytype, comptime _platform: Platform) [
var written: usize = 0;
const platform = comptime _platform.resolve();
+ parser_join_input_buffer[0] = 0;
+
for (_parts) |part| {
- if (part.len == 0 or (part.len == 1 and part[0] == '.')) {
+ if (part.len == 0) {
continue;
}
- if (!platform.isSeparator(part[part.len - 1])) {
+ if (written > 0) {
parser_join_input_buffer[written] = platform.separator();
written += 1;
}
@@ -648,22 +685,12 @@ pub fn joinStringBuf(buf: []u8, _parts: anytype, comptime _platform: Platform) [
written += part.len;
}
- // Preserve leading separator
- if (_parts[0].len > 0 and _parts[0][0] == _platform.separator()) {
- const out = switch (comptime platform) {
- // .loose =>
- // .windows => @compileError("Not implemented yet"),
- else => normalizeStringLooseBuf(parser_join_input_buffer[0..written], buf[1..], false, false),
- };
- buf[0] = _platform.separator();
-
- return buf[0 .. out.len + 1];
- } else {
- return switch (platform) {
- else => normalizeStringLooseBuf(parser_join_input_buffer[0..written], buf[0..], false, false),
- // .windows => @compileError("Not implemented yet"),
- };
+ if (written == 0) {
+ buf[0] = '.';
+ return buf[0..1];
}
+
+ return normalizeStringNode(parser_join_input_buffer[0..written], buf, platform);
}
pub fn joinAbsStringBuf(_cwd: []const u8, buf: []u8, _parts: anytype, comptime _platform: Platform) []const u8 {
@@ -791,7 +818,12 @@ pub fn lastIndexOfSeparatorLoose(slice: []const u8) ?usize {
return std.mem.lastIndexOfAny(u8, slice, "/\\");
}
-pub fn normalizeStringLooseBuf(str: []const u8, buf: []u8, comptime allow_above_root: bool, comptime preserve_trailing_slash: bool) []u8 {
+pub fn normalizeStringLooseBuf(
+ str: []const u8,
+ buf: []u8,
+ comptime allow_above_root: bool,
+ comptime preserve_trailing_slash: bool,
+) []u8 {
return normalizeStringGeneric(
str,
buf,
@@ -803,6 +835,70 @@ pub fn normalizeStringLooseBuf(str: []const u8, buf: []u8, comptime allow_above_
);
}
+pub fn normalizeStringNode(
+ str: []const u8,
+ buf: []u8,
+ comptime platform: Platform,
+) []u8 {
+ if (str.len == 0) {
+ buf[0] = '.';
+ return buf[0..1];
+ }
+
+ const is_absolute = platform.isSeparator(str[0]);
+ const trailing_separator = platform.isSeparator(str[str.len - 1]);
+ var buf_ = buf[1..];
+
+ var out = if (!is_absolute) normalizeStringGeneric(
+ str,
+ buf_,
+ true,
+ comptime platform.resolve().separator(),
+ comptime platform.getSeparatorFunc(),
+ comptime platform.getLastSeparatorFunc(),
+ false,
+ ) else normalizeStringGeneric(
+ str,
+ buf_,
+ false,
+ comptime platform.resolve().separator(),
+ comptime platform.getSeparatorFunc(),
+ comptime platform.getLastSeparatorFunc(),
+ false,
+ );
+
+ if (out.len == 0) {
+ if (is_absolute) {
+ buf[0] = '/';
+ return buf[0..1];
+ }
+
+ if (trailing_separator) {
+ buf[0..2].* = "./".*;
+ return buf[0..2];
+ }
+
+ buf[0] = '.';
+ return buf[0..1];
+ }
+
+ if (trailing_separator) {
+ if (!platform.isSeparator(out[out.len - 1])) {
+ buf_[out.len] = platform.separator();
+ out = buf_[0 .. out.len + 1];
+ }
+ }
+
+ if (is_absolute) {
+ std.debug.assert(!platform.isSeparator(out[0]));
+
+ buf[0] = platform.separator();
+ out = buf[0 .. out.len + 1];
+ }
+
+ return out;
+}
+
test "joinAbsStringPosix" {
var t = tester.Tester.t(default_allocator);
defer t.report(@src());
@@ -984,7 +1080,29 @@ test "relative" {
var t = tester.Tester.t(default_allocator);
defer t.report(@src());
- _ = t.expect("var/foo", try relativeAlloc(default_allocator, "/", "/var/foo/"), @src());
+ const fixtures = .{
+ .{ "/var/lib", "/var", ".." },
+ .{ "/var/lib", "/bin", "../../bin" },
+ .{ "/var/lib", "/var/lib", "" },
+ .{ "/var/lib", "/var/apache", "../apache" },
+ .{ "/var/", "/var/lib", "lib" },
+ .{ "/", "/var/lib", "var/lib" },
+ .{ "/foo/test", "/foo/test/bar/package.json", "bar/package.json" },
+ .{ "/Users/a/web/b/test/mails", "/Users/a/web/b", "../.." },
+ .{ "/foo/bar/baz-quux", "/foo/bar/baz", "../baz" },
+ .{ "/foo/bar/baz", "/foo/bar/baz-quux", "../baz-quux" },
+ .{ "/baz-quux", "/baz", "../baz" },
+ .{ "/baz", "/baz-quux", "../baz-quux" },
+ .{ "/page1/page2/foo", "/", "../../.." },
+ };
+
+ inline for (fixtures) |fixture| {
+ const from = fixture[0];
+ const to = fixture[1];
+ const expected = fixture[2];
+ _ = t.expect(expected, try relativeAlloc(default_allocator, from, to), @src());
+ }
+
_ = t.expect("index.js", try relativeAlloc(default_allocator, "/app/public/", "/app/public/index.js"), @src());
_ = t.expect("..", try relativeAlloc(default_allocator, "/app/public/index.js", "/app/public/"), @src());
_ = t.expect("../../src/bacon.ts", try relativeAlloc(default_allocator, "/app/public/index.html", "/app/src/bacon.ts"), @src());
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index 160242fbb..207b6634d 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -1,8 +1,6 @@
const std = @import("std");
const expect = std.testing.expect;
-const JavascriptString = @import("ast/base.zig").JavascriptString;
-
const string = @import("string_types.zig").string;
const stringZ = @import("string_types.zig").stringZ;
const CodePoint = @import("string_types.zig").CodePoint;
diff --git a/src/test/tester.zig b/src/test/tester.zig
index 84be7ddbb..2c53acc81 100644
--- a/src/test/tester.zig
+++ b/src/test/tester.zig
@@ -64,19 +64,13 @@ pub const Tester = struct {
stderr.writeAll(RESET) catch unreachable;
stderr.writeAll("\n") catch unreachable;
}
-
+ const strings = @import("../string_immutable.zig");
pub fn evaluate_outcome(self: *const @This()) Outcome {
- if (self.expected.len > self.result.len) {
+ if (strings.eql(self.expected, self.result)) {
+ return .pass;
+ } else {
return .fail;
}
-
- for (self.expected) |char, i| {
- if (char != self.result[i]) {
- return Outcome.fail;
- }
- }
-
- return Outcome.pass;
}
};