From eeb10f3f7df6621eba32bd1d9477cb39637b6e9c Mon Sep 17 00:00:00 2001 From: dave caruso Date: Wed, 24 Jan 2024 01:19:47 -0800 Subject: [PATCH] fix(windows): fix node:url pathFromFileURL and fileURLFromPath and file url tests (#8442) * yay for file urls * swag * oops * merge conf * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- docs/project/building-windows.md | 15 +------ src/bun.js/api/BunObject.zig | 6 +-- src/bun.js/api/JSBundler.zig | 14 +++--- src/bun.js/api/JSTranspiler.zig | 2 +- src/bun.js/api/bun/socket.zig | 8 ++-- src/bun.js/api/ffi.zig | 6 +-- src/bun.js/api/html_rewriter.zig | 2 +- src/bun.js/api/server.zig | 16 +++---- src/bun.js/base.zig | 2 +- src/bun.js/bindings/BunObject.cpp | 39 +++++++++++------ src/bun.js/bindings/ImportMetaObject.cpp | 42 +----------------- src/bun.js/bindings/PathInlines.h | 55 ++++++++++++++++++++++++ src/bun.js/javascript.zig | 4 +- src/bun.js/module_loader.zig | 28 ++++++------ src/bun.js/node/node_fs.zig | 18 ++++---- src/bun.js/node/types.zig | 6 +-- src/bun.js/test/expect.zig | 2 +- src/bun.js/web_worker.zig | 2 +- src/bun.js/webcore/blob.zig | 4 +- src/bun.js/webcore/blob/ReadFile.zig | 6 +-- src/bun.js/webcore/request.zig | 6 +-- src/bun.js/webcore/response.zig | 6 +-- src/deps/lol-html.zig | 2 +- src/http/websocket_http_client.zig | 2 +- src/napi/napi.zig | 2 +- src/resolver/resolve_path.zig | 19 ++++++++ src/resolver/resolver.zig | 6 +-- src/sourcemap/CodeCoverage.zig | 2 +- src/string.zig | 12 +++--- src/sys.zig | 2 +- test/harness.ts | 7 +++ test/js/bun/resolve/import-meta.test.js | 22 ++++++++-- test/js/bun/resolve/resolve-test.js | 7 +-- test/js/bun/resolve/resolve.test.ts | 44 +++++++++++-------- test/js/bun/util/fileUrl.test.js | 20 +++++++-- 35 files changed, 257 insertions(+), 179 deletions(-) create mode 100644 src/bun.js/bindings/PathInlines.h diff --git a/docs/project/building-windows.md b/docs/project/building-windows.md index cc0cf9f696..0e954fcfec 100644 --- a/docs/project/building-windows.md +++ b/docs/project/building-windows.md @@ -6,8 +6,7 @@ The following document is not yet complete, please join the [#windows channel on Here are the extra steps I ran on my fresh windows machine (some of these are a little opiniated) -- Change user to a local account (set username to `window` and empty password) - - (Empty password will disable the password and auto-login on boot) +- Change user to a local account (set username to `window` and 'bun!') - Set Windows Terminal as default terminal - Install latest version of Powershell - Display scale to 100% @@ -86,18 +85,6 @@ To verify, you can check for an MSVC-only command line such as `mt.exe` Get-Command mt ``` -### Zig - -Bun pins a version of Zig. As the compiler is still in development, breaking changes happen often that will break the build. It is recommended to use [Zigup](https://github.com/marler8997/zigup/releases) as it can quickly switch to any version by name, but you can also [manually download Zig](https://ziglang.org/download/). - -```bash -$ zigup 0.12.0-dev.1604+caae40c21 -``` - -{% callout %} -We last updated Zig on **October 26th, 2023** -{% /callout %} - ### Codegen On Unix platforms, we depend on an existing build of Bun to generate code for itself. Since the Windows build is not stable enough for this to run the code generators, you currently need to use another computer or WSL to generate this: diff --git a/src/bun.js/api/BunObject.zig b/src/bun.js/api/BunObject.zig index d07823a6d7..2107cae783 100644 --- a/src/bun.js/api/BunObject.zig +++ b/src/bun.js/api/BunObject.zig @@ -549,7 +549,7 @@ pub fn shellEscape( globalThis.throwOutOfMemory(); return .undefined; }; - return bun.String.create(outbuf.items[0..]).toJS(globalThis); + return bun.String.createUTF8(outbuf.items[0..]).toJS(globalThis); } return jsval; } @@ -559,7 +559,7 @@ pub fn shellEscape( globalThis.throwOutOfMemory(); return .undefined; }; - return bun.String.create(outbuf.items[0..]).toJS(globalThis); + return bun.String.createUTF8(outbuf.items[0..]).toJS(globalThis); } return jsval; @@ -5364,7 +5364,7 @@ const InternalTestingAPIs = struct { return .zero; }; - var str = bun.String.create(buffer.list.items); + var str = bun.String.createUTF8(buffer.list.items); defer str.deref(); return str.toJS(globalThis); } diff --git a/src/bun.js/api/JSBundler.zig b/src/bun.js/api/JSBundler.zig index f4a5dc2474..570b72a4f3 100644 --- a/src/bun.js/api/JSBundler.zig +++ b/src/bun.js/api/JSBundler.zig @@ -899,8 +899,8 @@ pub const JSBundler = struct { const namespace_string = if (path.isFile()) bun.String.empty else - bun.String.create(path.namespace); - const path_string = bun.String.create(path.text); + bun.String.createUTF8(path.namespace); + const path_string = bun.String.createUTF8(path.text); return JSBundlerPlugin__anyMatches(this, &namespace_string, &path_string, is_onLoad); } @@ -918,8 +918,8 @@ pub const JSBundler = struct { const namespace_string = if (namespace.len == 0) bun.String.static("file") else - bun.String.create(namespace); - const path_string = bun.String.create(path); + bun.String.createUTF8(namespace); + const path_string = bun.String.createUTF8(path); defer namespace_string.deref(); defer path_string.deref(); JSBundlerPlugin__matchOnLoad(globalThis, this, &namespace_string, &path_string, context, @intFromEnum(default_loader)); @@ -940,9 +940,9 @@ pub const JSBundler = struct { const namespace_string = if (strings.eqlComptime(namespace, "file")) bun.String.empty else - bun.String.create(namespace); - const path_string = bun.String.create(path); - const importer_string = bun.String.create(importer); + bun.String.createUTF8(namespace); + const path_string = bun.String.createUTF8(path); + const importer_string = bun.String.createUTF8(importer); defer namespace_string.deref(); defer path_string.deref(); defer importer_string.deref(); diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig index 17ba0ffdf3..54f091c2f3 100644 --- a/src/bun.js/api/JSTranspiler.zig +++ b/src/bun.js/api/JSTranspiler.zig @@ -1196,7 +1196,7 @@ fn namedExportsToJS(global: *JSGlobalObject, named_exports: *JSAst.Ast.NamedExpo }); var i: usize = 0; while (named_exports_iter.next()) |entry| { - names[i] = bun.String.create(entry.key_ptr.*); + names[i] = bun.String.createUTF8(entry.key_ptr.*); i += 1; } return bun.String.toJSArray(global, names); diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index 9369f07802..9224801c92 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -1495,8 +1495,8 @@ fn NewSocket(comptime ssl: bool) type { const reason = if (ssl_error.reason == null) "" else ssl_error.reason[0..bun.len(ssl_error.reason)]; const fallback = JSC.SystemError{ - .code = bun.String.create(code), - .message = bun.String.create(reason), + .code = bun.String.createUTF8(code), + .message = bun.String.createUTF8(reason), }; authorization_error = fallback.toErrorInstance(globalObject); @@ -1673,8 +1673,8 @@ fn NewSocket(comptime ssl: bool) type { const reason = if (ssl_error.reason == null) "" else ssl_error.reason[0..bun.len(ssl_error.reason)]; const fallback = JSC.SystemError{ - .code = bun.String.create(code), - .message = bun.String.create(reason), + .code = bun.String.createUTF8(code), + .message = bun.String.createUTF8(reason), }; return fallback.toErrorInstance(globalObject); diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index 9b4196bf14..aa7d3efc3a 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -307,9 +307,9 @@ pub const FFI = struct { break :brk std.DynLib.open(backup_name) catch { // Then, if that fails, report an error. const system_error = JSC.SystemError{ - .code = bun.String.create(@tagName(JSC.Node.ErrorCode.ERR_DLOPEN_FAILED)), - .message = bun.String.create("Failed to open library. This is usually caused by a missing library or an invalid library path."), - .syscall = bun.String.create("dlopen"), + .code = bun.String.createUTF8(@tagName(JSC.Node.ErrorCode.ERR_DLOPEN_FAILED)), + .message = bun.String.createUTF8("Failed to open library. This is usually caused by a missing library or an invalid library path."), + .syscall = bun.String.createUTF8("dlopen"), }; return system_error.toErrorInstance(global); }; diff --git a/src/bun.js/api/html_rewriter.zig b/src/bun.js/api/html_rewriter.zig index b17cc86829..1a143461b8 100644 --- a/src/bun.js/api/html_rewriter.zig +++ b/src/bun.js/api/html_rewriter.zig @@ -1752,7 +1752,7 @@ pub const Element = struct { ) callconv(.C) JSValue { if (this.element == null) return JSValue.jsUndefined(); - var str = bun.String.create(std.mem.span(this.element.?.namespaceURI())); + var str = bun.String.createUTF8(std.mem.span(this.element.?.namespaceURI())); defer str.deref(); return str.toJS(globalObject); } diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 3647a0432d..8d4654776c 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -5301,7 +5301,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp } existing_request = Request{ - .url = bun.String.create(url.href), + .url = bun.String.createUTF8(url.href), .headers = headers, .body = JSC.WebCore.InitRequestBodyValue(body) catch unreachable, .method = method, @@ -5383,7 +5383,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp this: *ThisServer, globalThis: *JSC.JSGlobalObject, ) callconv(.C) JSC.JSValue { - var str = bun.String.create(this.config.id); + var str = bun.String.createUTF8(this.config.id); defer str.deref(); return str.toJS(globalThis); } @@ -5405,7 +5405,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp pub fn getAddress(this: *ThisServer, globalThis: *JSGlobalObject) callconv(.C) JSC.JSValue { switch (this.config.address) { .unix => |unix| { - var value = bun.String.create(bun.sliceTo(@constCast(unix), 0)); + var value = bun.String.createUTF8(bun.sliceTo(@constCast(unix), 0)); defer value.deref(); return value.toJS(globalThis); }, @@ -5419,7 +5419,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp var is_ipv6: bool = false; if (listener.socket().localAddressText(&buf, &is_ipv6)) |slice| { - var ip = bun.String.create(slice); + var ip = bun.String.createUTF8(slice); return JSSocketAddress__create( this.globalThis, ip.toJS(this.globalThis), @@ -5455,7 +5455,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp const buf = std.fmt.allocPrint(default_allocator, "{any}", .{fmt}) catch @panic("Out of memory"); defer default_allocator.free(buf); - var value = bun.String.create(buf); + var value = bun.String.createUTF8(buf); return value.toJSDOMURL(globalThis); } @@ -5471,7 +5471,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp var len: i32 = 1024; listener.socket().remoteAddress(&buf, &len); if (len > 0) { - this.cached_hostname = bun.String.create(buf[0..@as(usize, @intCast(len))]); + this.cached_hostname = bun.String.createUTF8(buf[0..@as(usize, @intCast(len))]); } } @@ -5479,7 +5479,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp switch (this.config.address) { .tcp => |tcp| { if (tcp.hostname) |hostname| { - this.cached_hostname = bun.String.create(bun.sliceTo(hostname, 0)); + this.cached_hostname = bun.String.createUTF8(bun.sliceTo(hostname, 0)); } else { this.cached_hostname = bun.String.createAtomASCII("localhost"); } @@ -5494,7 +5494,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp pub fn getProtocol(this: *ThisServer, globalThis: *JSGlobalObject) callconv(.C) JSC.JSValue { if (this.cached_protocol.isEmpty()) { - this.cached_protocol = bun.String.create(if (ssl_enabled) "https" else "http"); + this.cached_protocol = bun.String.createUTF8(if (ssl_enabled) "https" else "http"); } return this.cached_protocol.toJS(globalThis); diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index 2116339c12..ee8cff61ca 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -52,7 +52,7 @@ pub fn toJS(globalObject: *JSC.JSGlobalObject, comptime ValueType: type, value: bool => return JSC.JSValue.jsBoolean(if (comptime Type != ValueType) value.* else value), *JSC.JSGlobalObject => return value.toJSValue(), []const u8, [:0]const u8, [*:0]const u8, []u8, [:0]u8, [*:0]u8 => { - const str = bun.String.create(value); + const str = bun.String.createUTF8(value); defer str.deref(); return str.toJS(globalObject); }, diff --git a/src/bun.js/bindings/BunObject.cpp b/src/bun.js/bindings/BunObject.cpp index 5a8958925f..e94a00bbbd 100644 --- a/src/bun.js/bindings/BunObject.cpp +++ b/src/bun.js/bindings/BunObject.cpp @@ -30,6 +30,7 @@ #include "JSDOMException.h" #include "JSDOMConvert.h" #include "wtf/Compiler.h" +#include "PathInlines.h" namespace Bun { @@ -485,12 +486,14 @@ JSC_DEFINE_HOST_FUNCTION(functionPathToFileURL, (JSC::JSGlobalObject * lexicalGl auto& globalObject = *reinterpret_cast(lexicalGlobalObject); auto& vm = globalObject.vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); - auto path = JSC::JSValue::encode(callFrame->argument(0)); + auto pathValue = callFrame->argument(0); - JSC::JSString* pathString = JSC::JSValue::decode(path).toString(lexicalGlobalObject); - RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); + WTF::String pathString = pathValue.toWTFString(lexicalGlobalObject); + RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode({})); - auto fileURL = WTF::URL::fileURLWithFileSystemPath(pathString->value(lexicalGlobalObject)); + pathString = pathResolveWTFString(lexicalGlobalObject, pathString); + + auto fileURL = WTF::URL::fileURLWithFileSystemPath(pathString); auto object = WebCore::DOMURL::create(fileURL.string(), String()); auto jsValue = WebCore::toJSNewlyCreated>(*lexicalGlobalObject, globalObject, throwScope, WTFMove(object)); RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(jsValue)); @@ -501,29 +504,37 @@ JSC_DEFINE_HOST_FUNCTION(functionFileURLToPath, (JSC::JSGlobalObject * globalObj auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSValue arg0 = callFrame->argument(0); + WTF::URL url; + auto path = JSC::JSValue::encode(arg0); auto* domURL = WebCoreCast(path); if (!domURL) { if (arg0.isString()) { - auto url = WTF::URL(arg0.toWTFString(globalObject)); - if (UNLIKELY(!url.protocolIsFile())) { - throwTypeError(globalObject, scope, "Argument must be a file URL"_s); - return JSC::JSValue::encode(JSC::JSValue {}); - } + url = WTF::URL(arg0.toWTFString(globalObject)); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::jsUndefined())); - RELEASE_AND_RETURN(scope, JSValue::encode(jsString(vm, url.fileSystemPath()))); + } else { + throwTypeError(globalObject, scope, "Argument must be a URL"_s); + return JSC::JSValue::encode(JSC::JSValue {}); } - throwTypeError(globalObject, scope, "Argument must be a URL"_s); - return JSC::JSValue::encode(JSC::JSValue {}); + } else { + url = domURL->href(); } - auto& url = domURL->href(); if (UNLIKELY(!url.protocolIsFile())) { throwTypeError(globalObject, scope, "Argument must be a file URL"_s); return JSC::JSValue::encode(JSC::JSValue {}); } - return JSC::JSValue::encode(JSC::jsString(vm, url.fileSystemPath())); + auto fileSystemPath = url.fileSystemPath(); + +#if OS(WINDOWS) + if (!isAbsolutePath(fileSystemPath)) { + throwTypeError(globalObject, scope, "File URL path must be absolute"_s); + return JSC::JSValue::encode(JSC::JSValue {}); + } +#endif + + return JSC::JSValue::encode(JSC::jsString(vm, fileSystemPath)); } JSC_DEFINE_HOST_FUNCTION(functionHashCode, diff --git a/src/bun.js/bindings/ImportMetaObject.cpp b/src/bun.js/bindings/ImportMetaObject.cpp index de472a66d4..424e1d027f 100644 --- a/src/bun.js/bindings/ImportMetaObject.cpp +++ b/src/bun.js/bindings/ImportMetaObject.cpp @@ -45,47 +45,7 @@ #include #include "CommonJSModuleRecord.h" #include - -#if OS(WINDOWS) -#define PLATFORM_SEP_s "\\"_s -#define PLATFORM_SEP '\\' -#else -#define PLATFORM_SEP_s "/"_s -#define PLATFORM_SEP '/' -#endif - -ALWAYS_INLINE bool isAbsolutePath(WTF::String input) -{ -#if OS(WINDOWS) - if (input.is8Bit()) { - auto len = input.length(); - if (len < 1) - return false; - auto bytes = input.characters8(); - if (bytes[0] == '/' || bytes[0] == '\\') - return true; - if (len < 2) - return false; - if (bytes[1] == ':' && (bytes[2] == '/' || bytes[2] == '\\')) - return true; - return false; - } else { - auto len = input.length(); - if (len < 1) - return false; - auto bytes = input.characters16(); - if (bytes[0] == '/' || bytes[0] == '\\') - return true; - if (len < 2) - return false; - if (bytes[1] == ':' && (bytes[2] == '/' || bytes[2] == '\\')) - return true; - return false; - } -#else // OS(WINDOWS) - return input.startsWith('/'); -#endif -} +#include "PathInlines.h" namespace Zig { using namespace JSC; diff --git a/src/bun.js/bindings/PathInlines.h b/src/bun.js/bindings/PathInlines.h new file mode 100644 index 0000000000..eb8495563d --- /dev/null +++ b/src/bun.js/bindings/PathInlines.h @@ -0,0 +1,55 @@ +#pragma once +#include "root.h" + +#if OS(WINDOWS) +#define PLATFORM_SEP_s "\\"_s +#define PLATFORM_SEP '\\' +#else +#define PLATFORM_SEP_s "/"_s +#define PLATFORM_SEP '/' +#endif + +ALWAYS_INLINE bool isAbsolutePath(WTF::String input) +{ +#if OS(WINDOWS) + if (input.is8Bit()) { + auto len = input.length(); + if (len < 1) + return false; + auto bytes = input.characters8(); + if (bytes[0] == '/' || bytes[0] == '\\') + return true; + if (len < 2) + return false; + if (bytes[1] == ':' && (bytes[2] == '/' || bytes[2] == '\\')) + return true; + return false; + } else { + auto len = input.length(); + if (len < 1) + return false; + auto bytes = input.characters16(); + if (bytes[0] == '/' || bytes[0] == '\\') + return true; + if (len < 2) + return false; + if (bytes[1] == ':' && (bytes[2] == '/' || bytes[2] == '\\')) + return true; + return false; + } +#else // OS(WINDOWS) + return input.startsWith('/'); +#endif +} + +extern "C" BunString ResolvePath__joinAbsStringBufCurrentPlatformBunString(JSC::JSGlobalObject*, BunString); + +/// CWD is determined by the global object's current cwd. +ALWAYS_INLINE WTF::String pathResolveWTFString(JSC::JSGlobalObject* globalToGetCwdFrom, WTF::String input) +{ + if (isAbsolutePath(input)) + return input; + BunString in = Bun::toString(input); + BunString out = ResolvePath__joinAbsStringBufCurrentPlatformBunString(globalToGetCwdFrom, in); + return out.toWTFString(); +} diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index f6f02d6762..d06d8b0701 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -1019,12 +1019,12 @@ pub const VirtualMachine = struct { const debugger = other_vm.debugger.?; if (debugger.unix.len > 0) { - var url = bun.String.create(debugger.unix); + var url = bun.String.createUTF8(debugger.unix); Bun__startJSDebuggerThread(this.global, debugger.script_execution_context_id, &url); } if (debugger.path_or_port) |path_or_port| { - var url = bun.String.create(path_or_port); + var url = bun.String.createUTF8(path_or_port); Bun__startJSDebuggerThread(this.global, debugger.script_execution_context_id, &url); } diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 18aacfd740..961ed96073 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -312,8 +312,8 @@ pub const RuntimeTranspilerStore = struct { const promise = this.promise.swap(); const globalThis = this.globalThis; this.poll_ref.unref(vm); - var specifier = if (this.parse_error == null) this.resolved_source.specifier else bun.String.create(this.path.text); - const referrer = bun.String.create(this.referrer); + var specifier = if (this.parse_error == null) this.resolved_source.specifier else bun.String.createUTF8(this.path.text); + const referrer = bun.String.createUTF8(this.referrer); var log = this.log; this.log = logger.Log.init(bun.default_allocator); var resolved_source = this.resolved_source; @@ -516,7 +516,7 @@ pub const RuntimeTranspilerStore = struct { } if (cache.entry) |*entry| { - const duped = String.create(specifier); + const duped = String.createUTF8(specifier); vm.source_mappings.putMappings(parse_result.source, .{ .list = .{ .items = @constCast(entry.sourcemap), .capacity = entry.sourcemap.len }, .allocator = bun.default_allocator, @@ -531,7 +531,7 @@ pub const RuntimeTranspilerStore = struct { .source_code = switch (entry.output_code) { .string => entry.output_code.string, .utf8 => brk: { - const result = bun.String.create(entry.output_code.utf8); + const result = bun.String.createUTF8(entry.output_code.utf8); cache.output_code_allocator.free(entry.output_code.utf8); entry.output_code.utf8 = ""; break :brk result; @@ -547,7 +547,7 @@ pub const RuntimeTranspilerStore = struct { } if (parse_result.already_bundled) { - const duped = String.create(specifier); + const duped = String.createUTF8(specifier); this.resolved_source = ResolvedSource{ .allocator = null, .source_code = bun.String.createLatin1(parse_result.source.contents), @@ -624,7 +624,7 @@ pub const RuntimeTranspilerStore = struct { dumpSource(specifier, &printer); } - const duped = String.create(specifier); + const duped = String.createUTF8(specifier); this.resolved_source = ResolvedSource{ .allocator = null, .source_code = brk: { @@ -640,7 +640,7 @@ pub const RuntimeTranspilerStore = struct { break :brk result; }, .specifier = duped, - .source_url = if (duped.eqlUTF8(path.text)) duped.dupeRef() else String.create(path.text), + .source_url = if (duped.eqlUTF8(path.text)) duped.dupeRef() else String.createUTF8(path.text), .commonjs_exports = null, .commonjs_exports_len = if (parse_result.ast.exports_kind == .cjs) std.math.maxInt(u32) @@ -1651,7 +1651,7 @@ pub const ModuleLoader = struct { if (loader == .json and !path.isJSONCFile()) { return ResolvedSource{ .allocator = null, - .source_code = bun.String.create(parse_result.source.contents), + .source_code = bun.String.createUTF8(parse_result.source.contents), .specifier = input_specifier, .source_url = if (input_specifier.eqlUTF8(path.text)) input_specifier.dupeRef() else String.init(path.text), @@ -1700,7 +1700,7 @@ pub const ModuleLoader = struct { .source_code = switch (entry.output_code) { .string => entry.output_code.string, .utf8 => brk: { - const result = bun.String.create(entry.output_code.utf8); + const result = bun.String.createUTF8(entry.output_code.utf8); cache.output_code_allocator.free(entry.output_code.utf8); entry.output_code.utf8 = ""; break :brk result; @@ -1994,7 +1994,7 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, - .source_code = bun.String.create(sqlite_module_source_code_string), + .source_code = bun.String.createUTF8(sqlite_module_source_code_string), .specifier = input_specifier, .source_url = if (input_specifier.eqlUTF8(path.text)) input_specifier.dupeRef() else String.init(path.text), .tag = .esm, @@ -2020,7 +2020,7 @@ pub const ModuleLoader = struct { writer.writeAll(";\n") catch unreachable; } - const public_url = bun.String.create(buf.toOwnedSliceLeaky()); + const public_url = bun.String.createUTF8(buf.toOwnedSliceLeaky()); return ResolvedSource{ .allocator = &jsc_vm.allocator, .source_code = public_url, @@ -2259,7 +2259,7 @@ pub const ModuleLoader = struct { .@"bun:main" => { return ResolvedSource{ .allocator = null, - .source_code = bun.String.create(jsc_vm.entry_point.source.contents), + .source_code = bun.String.createUTF8(jsc_vm.entry_point.source.contents), .specifier = specifier, .source_url = specifier, .hash = 0, @@ -2342,7 +2342,7 @@ pub const ModuleLoader = struct { if (jsc_vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(spec.slice()))) |entry| { return ResolvedSource{ .allocator = null, - .source_code = bun.String.create(entry.source.contents), + .source_code = bun.String.createUTF8(entry.source.contents), .specifier = specifier, .source_url = specifier.dupeRef(), .hash = 0, @@ -2862,6 +2862,6 @@ export fn Bun__resolveEmbeddedNodeFile(vm: *JSC.VirtualMachine, in_out_str: *bun else => {}, } - in_out_str.* = bun.String.create(bun.path.joinAbs(bun.fs.FileSystem.instance.fs.tmpdirPath(), .auto, tmpfilename)); + in_out_str.* = bun.String.createUTF8(bun.path.joinAbs(bun.fs.FileSystem.instance.fs.tmpdirPath(), .auto, tmpfilename)); return true; } diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index 582c42051e..0b25295a86 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -4717,7 +4717,7 @@ pub const NodeFS = struct { switch (ExpectedType) { Dirent => { entries.append(.{ - .name = bun.String.create(utf8_name), + .name = bun.String.createUTF8(utf8_name), .kind = current.kind, }) catch bun.outOfMemory(); }, @@ -4725,7 +4725,7 @@ pub const NodeFS = struct { entries.append(Buffer.fromString(utf8_name, bun.default_allocator) catch bun.outOfMemory()) catch bun.outOfMemory(); }, bun.String => { - entries.append(bun.String.create(utf8_name)) catch bun.outOfMemory(); + entries.append(bun.String.createUTF8(utf8_name)) catch bun.outOfMemory(); }, else => @compileError("unreachable"), } @@ -4851,7 +4851,7 @@ pub const NodeFS = struct { switch (comptime ExpectedType) { Dirent => { entries.append(.{ - .name = bun.String.create(name_to_copy), + .name = bun.String.createUTF8(name_to_copy), .kind = current.kind, }) catch bun.outOfMemory(); }, @@ -4859,7 +4859,7 @@ pub const NodeFS = struct { entries.append(Buffer.fromString(name_to_copy, bun.default_allocator) catch bun.outOfMemory()) catch bun.outOfMemory(); }, bun.String => { - entries.append(bun.String.create(name_to_copy)) catch bun.outOfMemory(); + entries.append(bun.String.createUTF8(name_to_copy)) catch bun.outOfMemory(); }, else => bun.outOfMemory(), } @@ -4980,7 +4980,7 @@ pub const NodeFS = struct { switch (comptime ExpectedType) { Dirent => { entries.append(.{ - .name = bun.String.create(name_to_copy), + .name = bun.String.createUTF8(name_to_copy), .kind = current.kind, }) catch bun.outOfMemory(); }, @@ -4988,7 +4988,7 @@ pub const NodeFS = struct { entries.append(Buffer.fromString(name_to_copy, bun.default_allocator) catch bun.outOfMemory()) catch bun.outOfMemory(); }, bun.String => { - entries.append(bun.String.create(name_to_copy)) catch bun.outOfMemory(); + entries.append(bun.String.createUTF8(name_to_copy)) catch bun.outOfMemory(); }, else => @compileError("Impossible"), } @@ -5397,7 +5397,7 @@ pub const NodeFS = struct { } else .{ - .string = .{ .utf8 = .{}, .underlying = bun.String.create(outbuf[0..len]) }, + .string = .{ .utf8 = .{}, .underlying = bun.String.createUTF8(outbuf[0..len]) }, }, }, }; @@ -5436,7 +5436,7 @@ pub const NodeFS = struct { } else .{ - .string = .{ .utf8 = .{}, .underlying = bun.String.create(buf) }, + .string = .{ .utf8 = .{}, .underlying = bun.String.createUTF8(buf) }, }, }, }; @@ -5485,7 +5485,7 @@ pub const NodeFS = struct { } else .{ - .string = .{ .utf8 = .{}, .underlying = bun.String.create(buf) }, + .string = .{ .utf8 = .{}, .underlying = bun.String.createUTF8(buf) }, }, }, }; diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index b5ca019676..60c9653257 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -292,7 +292,7 @@ pub const StringOrBuffer = union(enum) { this.encoded_slice = .{}; } - const str = bun.String.create(this.encoded_slice.slice()); + const str = bun.String.createUTF8(this.encoded_slice.slice()); defer str.deref(); return str.toJS(ctx); }, @@ -685,7 +685,7 @@ pub const PathLike = union(enum) { if (allocator.vtable == bun.default_allocator.vtable) {} } - const str = bun.String.create(encoded.slice()); + const str = bun.String.createUTF8(encoded.slice()); defer str.deref(); return str.toJS(globalObject); }, @@ -2172,7 +2172,7 @@ pub const Path = struct { else PathHandler.joinStringBuf(buf_to_use, to_join[0..i], .windows); - var str = bun.String.create(out); + var str = bun.String.createUTF8(out); defer str.deref(); return str.toJS(globalThis); } diff --git a/src/bun.js/test/expect.zig b/src/bun.js/test/expect.zig index 225ff6582f..fcdf4e44bc 100644 --- a/src/bun.js/test/expect.zig +++ b/src/bun.js/test/expect.zig @@ -5124,7 +5124,7 @@ pub const ExpectMatcherUtils = struct { try buffered_writer.flush(); - return bun.String.create(mutable_string.toOwnedSlice()).toJS(globalObject); + return bun.String.createUTF8(mutable_string.toOwnedSlice()).toJS(globalObject); } inline fn printValueCatched(globalObject: *JSC.JSGlobalObject, value: JSValue, comptime color_or_null: ?[]const u8) JSValue { diff --git a/src/bun.js/web_worker.zig b/src/bun.js/web_worker.zig index 13e1ede9ca..e2e3908d4c 100644 --- a/src/bun.js/web_worker.zig +++ b/src/bun.js/web_worker.zig @@ -240,7 +240,7 @@ pub const WebWorker = struct { @panic("OOM"); }; JSC.markBinding(@src()); - WebWorker__dispatchError(globalObject, worker.cpp_worker, bun.String.create(array.toOwnedSliceLeaky()), error_instance); + WebWorker__dispatchError(globalObject, worker.cpp_worker, bun.String.createUTF8(array.toOwnedSliceLeaky()), error_instance); if (vm.worker) |worker_| { worker.requested_terminate = true; worker.parent_poll_ref.unrefConcurrently(worker.parent); diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index 0e4c47df93..85a7dc2c87 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -2214,7 +2214,7 @@ pub const Blob = struct { const globalThis = this.globalThis; var system_error: SystemError = this.system_error orelse SystemError{}; if (this.source_file_store.pathlike == .path and system_error.path.isEmpty()) { - system_error.path = bun.String.create(this.source_file_store.pathlike.path.slice()); + system_error.path = bun.String.createUTF8(this.source_file_store.pathlike.path.slice()); } if (system_error.message.isEmpty()) { @@ -3187,7 +3187,7 @@ pub const Blob = struct { globalThis: *JSC.JSGlobalObject, ) callconv(.C) JSValue { if (this.getFileName()) |path| { - var str = bun.String.create(path); + var str = bun.String.createUTF8(path); return str.toJS(globalThis); } diff --git a/src/bun.js/webcore/blob/ReadFile.zig b/src/bun.js/webcore/blob/ReadFile.zig index 035e087d30..eb838bb11d 100644 --- a/src/bun.js/webcore/blob/ReadFile.zig +++ b/src/bun.js/webcore/blob/ReadFile.zig @@ -242,7 +242,7 @@ pub const ReadFile = struct { this.system_error = err.toSystemError(); if (this.system_error.?.path.isEmpty()) { this.system_error.?.path = if (this.file_store.pathlike == .path) - bun.String.create(this.file_store.pathlike.path.slice()) + bun.String.createUTF8(this.file_store.pathlike.path.slice()) else bun.String.empty; } @@ -356,7 +356,7 @@ pub const ReadFile = struct { this.system_error = JSC.SystemError{ .code = bun.String.static("EISDIR"), .path = if (this.file_store.pathlike == .path) - bun.String.create(this.file_store.pathlike.path.slice()) + bun.String.createUTF8(this.file_store.pathlike.path.slice()) else bun.String.empty, .message = bun.String.static("Directories cannot be read like files"), @@ -661,7 +661,7 @@ pub const ReadFileUV = struct { this.system_error = JSC.SystemError{ .code = bun.String.static("EISDIR"), .path = if (this.file_store.pathlike == .path) - bun.String.create(this.file_store.pathlike.path.slice()) + bun.String.createUTF8(this.file_store.pathlike.path.slice()) else bun.String.empty, .message = bun.String.static("Directories cannot be read like files"), diff --git a/src/bun.js/webcore/request.zig b/src/bun.js/webcore/request.zig index 3638191f6b..3650c1cd35 100644 --- a/src/bun.js/webcore/request.zig +++ b/src/bun.js/webcore/request.zig @@ -391,7 +391,7 @@ pub const Request = struct { } } else { // TODO: what is the right thing to do for invalid URLS? - this.url = bun.String.create(url); + this.url = bun.String.createUTF8(url); } return; @@ -414,7 +414,7 @@ pub const Request = struct { req_url, }) catch bun.outOfMemory(); defer bun.default_allocator.free(temp_url); - this.url = bun.String.create(temp_url); + this.url = bun.String.createUTF8(temp_url); } const href = bun.JSC.URL.hrefFromString(this.url); @@ -430,7 +430,7 @@ pub const Request = struct { if (comptime Environment.allow_assert) { std.debug.assert(this.sizeOfURL() == req_url.len); } - this.url = bun.String.create(req_url); + this.url = bun.String.createUTF8(req_url); } } diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index a47ee9eeb5..1ca91a7b89 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -1136,7 +1136,7 @@ pub const Fetch = struct { defer BoringSSL.X509_free(x509); const globalObject = this.global_this; const js_cert = X509.toJS(x509, globalObject); - var hostname: bun.String = bun.String.create(certificate_info.hostname); + var hostname: bun.String = bun.String.createUTF8(certificate_info.hostname); const js_hostname = hostname.toJS(globalObject); js_hostname.ensureStillAlive(); js_cert.ensureStillAlive(); @@ -1205,9 +1205,9 @@ pub const Fetch = struct { // some times we don't have metadata so we also check http.url if (this.metadata) |metadata| { - path = bun.String.create(metadata.url); + path = bun.String.createUTF8(metadata.url); } else if (this.http) |http_| { - path = bun.String.create(http_.url.href); + path = bun.String.createUTF8(http_.url.href); } else { path = bun.String.empty; } diff --git a/src/deps/lol-html.zig b/src/deps/lol-html.zig index ddb315990c..8ad1a727f2 100644 --- a/src/deps/lol-html.zig +++ b/src/deps/lol-html.zig @@ -591,7 +591,7 @@ pub const HTMLString = extern struct { return bun.String.createExternal(bytes, true, @constCast(bytes.ptr), &deinit_external); } defer this.deinit(); - return bun.String.create(bytes); + return bun.String.createUTF8(bytes); } pub fn toJS(this: HTMLString, globalThis: *bun.JSC.JSGlobalObject) bun.JSC.JSValue { diff --git a/src/http/websocket_http_client.zig b/src/http/websocket_http_client.zig index d19bebc64e..bcd57a34f2 100644 --- a/src/http/websocket_http_client.zig +++ b/src/http/websocket_http_client.zig @@ -1586,7 +1586,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { this.terminate(ErrorCode.invalid_utf8); return; } - reason = bun.String.create(body_slice); + reason = bun.String.createUTF8(body_slice); @memcpy(final_body_bytes[8..][0..body_len], body_slice); } } diff --git a/src/napi/napi.zig b/src/napi/napi.zig index 796a3b0636..135cb9314f 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -319,7 +319,7 @@ pub export fn napi_create_string_utf8(env: napi_env, str: ?[*]const u8, length: log("napi_create_string_utf8: {s}", .{slice}); - var string = bun.String.create(slice); + var string = bun.String.createUTF8(slice); if (string.tag == .Dead) { return .generic_failure; } diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig index 7e494cdfc3..f8859c3172 100644 --- a/src/resolver/resolve_path.zig +++ b/src/resolver/resolve_path.zig @@ -1912,3 +1912,22 @@ pub const PosixToWinNormalizer = struct { return buf[0..maybe_posix_path.len :0]; } }; + +/// Used in PathInlines.h +/// gets cwd off of the global object +export fn ResolvePath__joinAbsStringBufCurrentPlatformBunString( + globalObject: *bun.JSC.JSGlobalObject, + in: bun.String, +) bun.String { + const str = in.toUTF8WithoutRef(bun.default_allocator); + defer str.deinit(); + + const out_slice = joinAbsStringBuf( + globalObject.bunVM().bundler.fs.top_level_dir, + &join_buf, + &.{str.slice()}, + comptime Platform.auto.resolve(), + ); + + return bun.String.createUTF8(out_slice); +} diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index 6bd32a33f1..eaa045de77 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -3260,7 +3260,7 @@ pub const Resolver = struct { pub export fn Resolver__propForRequireMainPaths(globalThis: *bun.JSC.JSGlobalObject) callconv(.C) bun.JSC.JSValue { bun.JSC.markBinding(@src()); - const in_str = bun.String.create("."); + const in_str = bun.String.createUTF8("."); const r = &globalThis.bunVM().bundler.resolver; return nodeModulePathsJSValue(r, in_str, globalThis); } @@ -3298,7 +3298,7 @@ pub const Resolver = struct { break :brk [2]string{ path_without_trailing_slash, "/node_modules" }; }; list.append( - bun.String.create( + bun.String.createUTF8( bun.strings.concat(stack_fallback_allocator.get(), &path_parts) catch unreachable, ), ) catch unreachable; @@ -3312,7 +3312,7 @@ pub const Resolver = struct { const path_without_trailing_slash = strings.withoutTrailingSlash(path); list.append( - bun.String.create( + bun.String.createUTF8( bun.strings.concat( stack_fallback_allocator.get(), &[_]string{ diff --git a/src/sourcemap/CodeCoverage.zig b/src/sourcemap/CodeCoverage.zig index 4636c02d6b..03c2e0363d 100644 --- a/src/sourcemap/CodeCoverage.zig +++ b/src/sourcemap/CodeCoverage.zig @@ -617,7 +617,7 @@ pub const ByteRangeMapping = struct { return .zero; }; - var str = bun.String.create(mutable_str.toOwnedSliceLeaky()); + var str = bun.String.createUTF8(mutable_str.toOwnedSliceLeaky()); defer str.deref(); return str.toJS(globalThis); } diff --git a/src/string.zig b/src/string.zig index 9addb67072..c70920715e 100644 --- a/src/string.zig +++ b/src/string.zig @@ -362,7 +362,7 @@ pub const String = extern struct { return BunString__fromLatin1(bytes.ptr, bytes.len); } - pub fn create(bytes: []const u8) String { + pub fn createUTF8(bytes: []const u8) String { JSC.markBinding(@src()); if (bytes.len == 0) return String.empty; return BunString__fromBytes(bytes.ptr, bytes.len); @@ -378,7 +378,7 @@ pub const String = extern struct { pub fn createFromOSPath(os_path: bun.OSPathSlice) String { return switch (@TypeOf(os_path)) { - []const u8 => create(os_path), + []const u8 => createUTF8(os_path), []const u16 => createUTF16(os_path), else => comptime unreachable, }; @@ -408,7 +408,7 @@ pub const String = extern struct { return new; } - return create(this.byteSlice()); + return createUTF8(this.byteSlice()); } extern fn BunString__createAtom(bytes: [*]const u8, len: usize) String; @@ -435,7 +435,7 @@ pub const String = extern struct { } } - return create(bytes); + return createUTF8(bytes); } pub fn utf8ByteLength(this: String) usize { @@ -1141,7 +1141,7 @@ pub const String = extern struct { inline for (0..n) |i| { slices_holded[i].deinit(); } - return create(result); + return createUTF8(result); } } @@ -1283,7 +1283,7 @@ pub const SliceWithUnderlyingString = struct { } } - const out = bun.String.create(this.utf8.slice()); + const out = bun.String.createUTF8(this.utf8.slice()); defer out.deref(); return out.toJS(globalObject); } diff --git a/src/sys.zig b/src/sys.zig index 6491db3290..c16bfd9aab 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -1494,7 +1494,7 @@ pub const Error = struct { } if (this.path.len > 0) { - err.path = bun.String.create(this.path); + err.path = bun.String.createUTF8(this.path); } if (this.fd != bun.invalid_fd) { diff --git a/test/harness.ts b/test/harness.ts index 1b98e33cb9..6c218aa461 100644 --- a/test/harness.ts +++ b/test/harness.ts @@ -250,3 +250,10 @@ expect.extend({ }; }, }); + +export function ospath(path: string) { + if (process.platform === "win32") { + return path.replace(/\//g, "\\"); + } + return path; +} diff --git a/test/js/bun/resolve/import-meta.test.js b/test/js/bun/resolve/import-meta.test.js index 90f8c86917..30dea3f6ad 100644 --- a/test/js/bun/resolve/import-meta.test.js +++ b/test/js/bun/resolve/import-meta.test.js @@ -1,7 +1,7 @@ // @known-failing-on-windows: 1 failing import { spawnSync } from "bun"; import { expect, it } from "bun:test"; -import { bunEnv, bunExe } from "harness"; +import { bunEnv, bunExe, ospath } from "harness"; import { mkdirSync, rmSync, writeFileSync } from "node:fs"; import * as Module from "node:module"; import { join, sep } from "node:path"; @@ -191,7 +191,7 @@ it("import.meta.require (javascript, live bindings)", () => { }); it("import.meta.dir", () => { - expect(dir.replaceAll("\\", "/").endsWith("/bun/test/js/bun/resolve")).toBe(true); + expect(dir).toEndWith(ospath("/bun/test/js/bun/resolve")); }); it("import.meta.dirname", () => { @@ -203,7 +203,7 @@ it("import.meta.filename", () => { }); it("import.meta.path", () => { - expect(path.replaceAll("\\", "/").endsWith("/bun/test/js/bun/resolve/import-meta.test.js")).toBe(true); + expect(path).toEndWith(ospath("/bun/test/js/bun/resolve/import-meta.test.js")); }); it('require("bun") works', () => { @@ -239,3 +239,19 @@ it("import non exist error code", async () => { expect(e.code).toBe("ERR_MODULE_NOT_FOUND"); } }); + +it("import.meta paths have the correct slash", () => { + const correct_sep = sep; + const wrong_sep = correct_sep === "/" ? "\\" : "/"; + + expect(import.meta.path).toInclude(correct_sep); + expect(import.meta.path).not.toInclude(wrong_sep); + expect(import.meta.dir).toInclude(correct_sep); + expect(import.meta.dir).not.toInclude(wrong_sep); + + expect(import.meta.file).not.toInclude(sep); + expect(import.meta.file).not.toInclude(sep); + + expect(import.meta.url).toStartWith("file:///"); + expect(import.meta.url).not.toInclude("\\"); +}); diff --git a/test/js/bun/resolve/resolve-test.js b/test/js/bun/resolve/resolve-test.js index 81a2249d9a..242834b6fe 100644 --- a/test/js/bun/resolve/resolve-test.js +++ b/test/js/bun/resolve/resolve-test.js @@ -1,4 +1,5 @@ import { it, expect } from "bun:test"; +import { ospath } from "harness"; import { join, resolve } from "path"; function resolveFrom(from) { @@ -7,19 +8,19 @@ function resolveFrom(from) { it("#imports", async () => { const baz = await import.meta.resolve("#foo", join(await import.meta.resolve("package-json-imports/baz"), "../")); - expect(baz.endsWith("foo/private-foo.js")).toBe(true); + expect(baz).toBe(resolve(import.meta.dir, "node_modules/package-json-imports/foo/private-foo.js")); const subpath = await import.meta.resolve( "#foo/bar", join(await import.meta.resolve("package-json-imports/baz"), "../"), ); - expect(subpath.endsWith("foo/private-foo.js")).toBe(true); + expect(subpath).toBe(resolve(import.meta.dir, "node_modules/package-json-imports/foo/private-foo.js")); const react = await import.meta.resolve( "#internal-react", join(await import.meta.resolve("package-json-imports/baz"), "../"), ); - expect(react.endsWith("/react/index.js")).toBe(true); + expect(react).toBe(resolve(import.meta.dir, "../../../../node_modules/react/index.js")); // Check that #foo is not resolved to the package.json file. try { diff --git a/test/js/bun/resolve/resolve.test.ts b/test/js/bun/resolve/resolve.test.ts index 3b0d4e23f0..36e3eefedc 100644 --- a/test/js/bun/resolve/resolve.test.ts +++ b/test/js/bun/resolve/resolve.test.ts @@ -1,8 +1,10 @@ // @known-failing-on-windows: 1 failing import { it, expect } from "bun:test"; -import { mkdirSync, writeFileSync, existsSync, rmSync, copyFileSync } from "fs"; +import { mkdirSync, writeFileSync } from "fs"; import { join } from "path"; import { bunExe, bunEnv, tempDirWithFiles } from "harness"; +import { pathToFileURL } from "bun"; +import { sep } from "path"; it("spawn test file", () => { writePackageJSONImportsFixture(); @@ -89,14 +91,20 @@ it("file url in import resolves", async () => { const dir = tempDirWithFiles("fileurl", { "index.js": "export const foo = 1;", }); - writeFileSync(`${dir}/test.js`, `import {foo} from 'file://${dir}/index.js';\nconsole.log(foo);`); + writeFileSync(`${dir}/test.js`, `import {foo} from '${pathToFileURL(dir)}/index.js';\nconsole.log(foo);`); + console.log(dir); const { exitCode, stdout } = Bun.spawnSync({ cmd: [bunExe(), `${dir}/test.js`], env: bunEnv, cwd: import.meta.dir, }); - expect(exitCode).toBe(0); + try { + expect(exitCode).toBe(0); + } catch (e) { + console.log(stdout.toString("utf8")); + throw e; + } expect(stdout.toString("utf8")).toBe("1\n"); }); @@ -117,7 +125,7 @@ it("file url in await import resolves", async () => { const dir = tempDirWithFiles("fileurl", { "index.js": "export const foo = 1;", }); - writeFileSync(`${dir}/test.js`, `const {foo} = await import('file://${dir}/index.js');\nconsole.log(foo);`); + writeFileSync(`${dir}/test.js`, `const {foo} = await import('${pathToFileURL(dir)}/index.js');\nconsole.log(foo);`); const { exitCode, stdout } = Bun.spawnSync({ cmd: [bunExe(), `${dir}/test.js`], @@ -133,11 +141,10 @@ it("file url with special characters in await import resolves", async () => { const dir = tempDirWithFiles("file url", { [filename]: "export const foo = 1;", }); + console.log(dir); writeFileSync( `${dir}/test.js`, - `const {foo} = await import('file://${dir.replace(/ /g, "%20")}/${encodeURIComponent( - filename, - )}');\nconsole.log(foo);`, + `const {foo} = await import('${pathToFileURL(dir)}/${encodeURIComponent(filename)}');\nconsole.log(foo);`, ); const { exitCode, stdout } = Bun.spawnSync({ @@ -154,7 +161,10 @@ it("file url with special characters not encoded in await import resolves", asyn const dir = tempDirWithFiles("file url", { [filename]: "export const foo = 1;", }); - writeFileSync(`${dir}/test.js`, `const {foo} = await import('file://${dir}/${filename}');\nconsole.log(foo);`); + writeFileSync( + `${dir}/test.js`, + `const {foo} = await import('${pathToFileURL(dir)}/${filename}');\nconsole.log(foo);`, + ); const { exitCode, stdout } = Bun.spawnSync({ cmd: [bunExe(), `${dir}/test.js`], @@ -172,7 +182,7 @@ it("file url with special characters in import statement resolves", async () => }); writeFileSync( `${dir}/test.js`, - `import {foo} from 'file://${dir.replace(/ /g, "%20")}/${encodeURIComponent(filename)}';\nconsole.log(foo);`, + `import {foo} from '${pathToFileURL(dir)}/${encodeURIComponent(filename)}';\nconsole.log(foo);`, ); const { exitCode, stdout } = Bun.spawnSync({ @@ -189,7 +199,7 @@ it("file url with special characters not encoded in import statement resolves", const dir = tempDirWithFiles("file url", { [filename]: "export const foo = 1;", }); - writeFileSync(`${dir}/test.js`, `import {foo} from 'file://${dir}/${filename}';\nconsole.log(foo);`); + writeFileSync(`${dir}/test.js`, `import {foo} from '${pathToFileURL(dir)}/${filename}';\nconsole.log(foo);`); const { exitCode, stdout } = Bun.spawnSync({ cmd: [bunExe(), `${dir}/test.js`], @@ -204,7 +214,7 @@ it("file url in require resolves", async () => { const dir = tempDirWithFiles("fileurl", { "index.js": "export const foo = 1;", }); - writeFileSync(`${dir}/test.js`, `const {foo} = require('file://${dir}/index.js');\nconsole.log(foo);`); + writeFileSync(`${dir}/test.js`, `const {foo} = require('${pathToFileURL(dir)}/index.js');\nconsole.log(foo);`); const { exitCode, stdout } = Bun.spawnSync({ cmd: [bunExe(), `${dir}/test.js`], @@ -222,7 +232,7 @@ it("file url with special characters in require resolves", async () => { }); writeFileSync( `${dir}/test.js`, - `const {foo} = require('file://${dir.replace(/ /g, "%20")}/${encodeURIComponent(filename)}');\nconsole.log(foo);`, + `const {foo} = require('${pathToFileURL(dir)}/${encodeURIComponent(filename)}');\nconsole.log(foo);`, ); const { exitCode, stdout } = Bun.spawnSync({ @@ -238,7 +248,7 @@ it("file url in require.resolve resolves", async () => { const dir = tempDirWithFiles("fileurl", { "index.js": "export const foo = 1;", }); - writeFileSync(`${dir}/test.js`, `const to = require.resolve('file://${dir}/index.js');\nconsole.log(to);`); + writeFileSync(`${dir}/test.js`, `const to = require.resolve('${pathToFileURL(dir)}/index.js');\nconsole.log(to);`); const { exitCode, stdout } = Bun.spawnSync({ cmd: [bunExe(), `${dir}/test.js`], @@ -246,7 +256,7 @@ it("file url in require.resolve resolves", async () => { cwd: import.meta.dir, }); expect(exitCode).toBe(0); - expect(stdout.toString("utf8")).toBe(`${dir}/index.js\n`); + expect(stdout.toString("utf8")).toBe(`${dir}${sep}index.js\n`); }); it("file url with special characters in require resolves", async () => { @@ -256,9 +266,7 @@ it("file url with special characters in require resolves", async () => { }); writeFileSync( `${dir}/test.js`, - `const to = require.resolve('file://${dir.replace(/ /g, "%20")}/${encodeURIComponent( - filename, - )}');\nconsole.log(to);`, + `const to = require.resolve('${pathToFileURL(dir)}/${encodeURIComponent(filename)}');\nconsole.log(to);`, ); const { exitCode, stdout } = Bun.spawnSync({ @@ -267,7 +275,7 @@ it("file url with special characters in require resolves", async () => { cwd: import.meta.dir, }); expect(exitCode).toBe(0); - expect(stdout.toString("utf8")).toBe(`${dir}/${filename}\n`); + expect(stdout.toString("utf8")).toBe(`${dir}${sep}${filename}\n`); }); it("import long string should not segfault", async () => { diff --git a/test/js/bun/util/fileUrl.test.js b/test/js/bun/util/fileUrl.test.js index 4befe02592..0f21fe55f2 100644 --- a/test/js/bun/util/fileUrl.test.js +++ b/test/js/bun/util/fileUrl.test.js @@ -1,6 +1,6 @@ -// @known-failing-on-windows: 1 failing import { expect, it, describe } from "bun:test"; import { pathToFileURL, fileURLToPath } from "bun"; + describe("pathToFileURL", () => { it("should convert a path to a file url", () => { expect(pathToFileURL("/path/to/file.js").href).toBe("file:///path/to/file.js"); @@ -9,10 +9,19 @@ describe("pathToFileURL", () => { describe("fileURLToPath", () => { it("should convert a file url to a path", () => { - expect(fileURLToPath("file:///path/to/file.js")).toBe("/path/to/file.js"); + if (process.platform === "win32") { + expect(() => fileURLToPath("file:///path/to/file.js")).toThrow("File URL path must be absolute"); + } else { + expect(fileURLToPath("file:///path/to/file.js")).toBe("/path/to/file.js"); + } }); + it("should convert a URL to a path", () => { - expect(fileURLToPath(new URL("file:///path/to/file.js"))).toBe("/path/to/file.js"); + if (process.platform === "win32") { + expect(() => fileURLToPath(new URL("file:///path/to/file.js"))).toThrow("File URL path must be absolute"); + } else { + expect(fileURLToPath(new URL("file:///path/to/file.js"))).toBe("/path/to/file.js"); + } }); it("should fail on non-file: URLs", () => { @@ -27,4 +36,9 @@ describe("fileURLToPath", () => { }); }); }); + + it("should add absolute part to relative file (#6456)", () => { + const url = pathToFileURL("foo.txt"); + expect(url.href).toBe(`${pathToFileURL(process.cwd())}/foo.txt`); + }); });