diff options
author | 2023-03-19 14:08:20 -0700 | |
---|---|---|
committer | 2023-03-19 14:08:20 -0700 | |
commit | 5a23d176208bb38483b65b9420b18c8597fabfef (patch) | |
tree | f163dc03fdc75b7827ee7f51e17355a9cc82329f /src | |
parent | 8f02ef829474cbd5453ffcb6485d40f93424ad26 (diff) | |
download | bun-5a23d176208bb38483b65b9420b18c8597fabfef.tar.gz bun-5a23d176208bb38483b65b9420b18c8597fabfef.tar.zst bun-5a23d176208bb38483b65b9420b18c8597fabfef.zip |
Several bug fixes (#2427)
* Fix test
* Fix segfault when unexpected type is passed in `expect().toThrow`
* Fix issues with request constructor
* Don't bother cloning headers when its empty
* woops
* more tests
* fix incorrect test
* Make the fetch error messages better
* Update response.zig
* Fix test that failed on macOS
* Fix test
* Remove extra hash table lookups
* Support running dummy registry directly
cc @alexlamsl
* Update test
* Update test
* fixup
* Workaround crash in test runner
* Fixup test
* Fixup test
* Update os.test.js
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 27 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 81 | ||||
-rw-r--r-- | src/bun.js/bindings/headers-cpp.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.h | 4 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.zig | 2 | ||||
-rw-r--r-- | src/bun.js/builtins/BunBuiltinNames.h | 2 | ||||
-rw-r--r-- | src/bun.js/test/jest.zig | 178 | ||||
-rw-r--r-- | src/bun.js/webcore/body.zig | 14 | ||||
-rw-r--r-- | src/bun.js/webcore/request.zig | 300 | ||||
-rw-r--r-- | src/bun.js/webcore/response.zig | 20 | ||||
-rw-r--r-- | src/env_loader.zig | 2 | ||||
-rw-r--r-- | src/install/extract_tarball.zig | 2 | ||||
-rw-r--r-- | src/install/install.zig | 48 |
13 files changed, 462 insertions, 220 deletions
diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 774c14d0c..a5af29128 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -700,6 +700,11 @@ bool Bun__deepEquals(JSC__JSGlobalObject* globalObject, JSValue v1, JSValue v2, extern "C" { +bool WebCore__FetchHeaders__isEmpty(WebCore__FetchHeaders* arg0) +{ + return arg0->size() == 0; +} + void WebCore__FetchHeaders__toUWSResponse(WebCore__FetchHeaders* arg0, bool is_ssl, void* arg2) { if (is_ssl) { @@ -3566,11 +3571,14 @@ enum class BuiltinNamesMap : uint8_t { url, body, data, + toString, + redirect, }; static JSC::Identifier builtinNameMap(JSC::JSGlobalObject* globalObject, unsigned char name) { - auto clientData = WebCore::clientData(globalObject->vm()); + auto& vm = globalObject->vm(); + auto clientData = WebCore::clientData(vm); switch (static_cast<BuiltinNamesMap>(name)) { case BuiltinNamesMap::method: { return clientData->builtinNames().methodPublicName(); @@ -3590,7 +3598,24 @@ static JSC::Identifier builtinNameMap(JSC::JSGlobalObject* globalObject, unsigne case BuiltinNamesMap::data: { return clientData->builtinNames().dataPublicName(); } + case BuiltinNamesMap::toString: { + return vm.propertyNames->toString; + } + case BuiltinNamesMap::redirect: { + return clientData->builtinNames().redirectPublicName(); } + } +} + +JSC__JSValue JSC__JSValue__fastGetDirect_(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, unsigned char arg2) +{ + JSC::JSValue value = JSC::JSValue::decode(JSValue0); + if (!value.isCell()) { + return JSValue::encode({}); + } + + return JSValue::encode( + value.getObject()->getDirect(globalObject->vm(), PropertyName(builtinNameMap(globalObject, arg2)))); } JSC__JSValue JSC__JSValue__fastGet_(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, unsigned char arg2) diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 51f9c9fde..1bd045218 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -1041,6 +1041,12 @@ pub const FetchHeaders = opaque { }); } + pub fn isEmpty(this: *FetchHeaders) bool { + return shim.cppFn("isEmpty", .{ + this, + }); + } + pub fn createFromUWS( global: *JSGlobalObject, uws_request: *anyopaque, @@ -1422,6 +1428,7 @@ pub const FetchHeaders = opaque { "remove", "toJS", "toUWSResponse", + "isEmpty", }; }; @@ -3604,6 +3611,10 @@ pub const JSValue = enum(JSValueReprInt) { return FFI.EncodedJSValue{ .asJSValue = this }; } + pub fn fromCell(ptr: *anyopaque) JSValue { + return (FFI.EncodedJSValue{ .asPtr = ptr }).asJSValue; + } + pub fn isInt32(this: JSValue) bool { return FFI.JSVALUE_IS_INT32(.{ .asJSValue = this }); } @@ -3773,6 +3784,13 @@ pub const JSValue = enum(JSValueReprInt) { return str; } + /// Convert a JSValue to a string, potentially calling `toString` on the + /// JSValue in JavaScript. + /// + /// This function can throw an exception in the `JSC::VM`. **If + /// the exception is not handled correctly, Bun will segfault** + /// + /// To handle exceptions, use `JSValue.toSliceOrNull`. pub inline fn toSlice(this: JSValue, global: *JSGlobalObject, allocator: std.mem.Allocator) ZigString.Slice { return getZigString(this, global).toSlice(allocator); } @@ -3786,11 +3804,39 @@ pub const JSValue = enum(JSValueReprInt) { return cppFn("jsonStringify", .{ this, globalThis, indent, out }); } - // On exception, this returns null, to make exception checks faster. + /// On exception, this returns null, to make exception checks clearer. pub fn toStringOrNull(this: JSValue, globalThis: *JSGlobalObject) ?*JSString { return cppFn("toStringOrNull", .{ this, globalThis }); } + /// Call `toString()` on the JSValue and clone the result. + /// On exception, this returns null. + pub fn toSliceOrNull(this: JSValue, globalThis: *JSGlobalObject) ?ZigString.Slice { + var str = this.toStringOrNull(globalThis) orelse return null; + return str.toSlice(globalThis, globalThis.allocator()); + } + + /// Call `toString()` on the JSValue and clone the result. + /// On exception or out of memory, this returns null. + /// + /// Remember that `Symbol` throws an exception when you call `toString()`. + pub fn toSliceClone(this: JSValue, globalThis: *JSGlobalObject) ?ZigString.Slice { + return this.toSliceCloneWithAllocator(globalThis, globalThis.allocator()); + } + + /// On exception or out of memory, this returns null, to make exception checks clearer. + pub fn toSliceCloneWithAllocator( + this: JSValue, + globalThis: *JSGlobalObject, + allocator: std.mem.Allocator, + ) ?ZigString.Slice { + var str = this.toStringOrNull(globalThis) orelse return null; + return str.toSlice(globalThis, allocator).cloneIfNeeded(allocator) catch { + globalThis.throwOutOfMemory(); + return null; + }; + } + pub fn toObject(this: JSValue, globalThis: *JSGlobalObject) *JSObject { return cppFn("toObject", .{ this, globalThis }); } @@ -3807,7 +3853,16 @@ pub const JSValue = enum(JSValueReprInt) { return cppFn("eqlCell", .{ this, other }); } - pub const BuiltinName = enum(u8) { method, headers, status, url, body, data }; + pub const BuiltinName = enum(u8) { + method, + headers, + status, + url, + body, + data, + toString, + redirect, + }; // intended to be more lightweight than ZigString pub fn fastGet(this: JSValue, global: *JSGlobalObject, builtin_name: BuiltinName) ?JSValue { @@ -3819,11 +3874,24 @@ pub const JSValue = enum(JSValueReprInt) { return result; } + pub fn fastGetDirect(this: JSValue, global: *JSGlobalObject, builtin_name: BuiltinName) ?JSValue { + const result = fastGetDirect_(this, global, @enumToInt(builtin_name)); + if (result == .zero) { + return null; + } + + return result; + } + pub fn fastGet_(this: JSValue, global: *JSGlobalObject, builtin_name: u8) JSValue { return cppFn("fastGet_", .{ this, global, builtin_name }); } - // intended to be more lightweight than ZigString + pub fn fastGetDirect_(this: JSValue, global: *JSGlobalObject, builtin_name: u8) JSValue { + return cppFn("fastGetDirect_", .{ this, global, builtin_name }); + } + + /// Do not use this directly! Use `get` instead. pub fn getIfPropertyExistsImpl(this: JSValue, global: *JSGlobalObject, ptr: [*]const u8, len: u32) JSValue { return cppFn("getIfPropertyExistsImpl", .{ this, global, ptr, len }); } @@ -3865,6 +3933,12 @@ pub const JSValue = enum(JSValueReprInt) { return if (@enumToInt(value) != 0) value else return null; } + pub fn implementsToString(this: JSValue, global: *JSGlobalObject) bool { + std.debug.assert(this.isCell()); + const function = this.fastGet(global, BuiltinName.toString) orelse return false; + return function.isCell() and function.isCallable(global.vm()); + } + pub fn getTruthy(this: JSValue, global: *JSGlobalObject, property: []const u8) ?JSValue { if (get(this, global, property)) |prop| { if (prop.isEmptyOrUndefinedOrNull()) return null; @@ -4131,6 +4205,7 @@ pub const JSValue = enum(JSValueReprInt) { "eqlCell", "eqlValue", "fastGet_", + "fastGetDirect_", "forEach", "forEachProperty", "forEachPropertyOrdered", diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h index 49391bb42..dafdaed17 100644 --- a/src/bun.js/bindings/headers-cpp.h +++ b/src/bun.js/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1679048516 +//-- AUTOGENERATED FILE -- 1679200292 // clang-format off #pragma once diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index baefaf0b9..8c9b5c1d0 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -1,5 +1,5 @@ // clang-format off -//-- AUTOGENERATED FILE -- 1679083592 +//-- AUTOGENERATED FILE -- 1679200292 #pragma once #include <stddef.h> @@ -183,6 +183,7 @@ CPP_DECL bool WebCore__FetchHeaders__fastHas_(WebCore__FetchHeaders* arg0, unsig CPP_DECL void WebCore__FetchHeaders__fastRemove_(WebCore__FetchHeaders* arg0, unsigned char arg1); CPP_DECL void WebCore__FetchHeaders__get_(WebCore__FetchHeaders* arg0, const ZigString* arg1, ZigString* arg2, JSC__JSGlobalObject* arg3); CPP_DECL bool WebCore__FetchHeaders__has(WebCore__FetchHeaders* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2); +CPP_DECL bool WebCore__FetchHeaders__isEmpty(WebCore__FetchHeaders* arg0); CPP_DECL void WebCore__FetchHeaders__put_(WebCore__FetchHeaders* arg0, const ZigString* arg1, const ZigString* arg2, JSC__JSGlobalObject* arg3); CPP_DECL void WebCore__FetchHeaders__remove(WebCore__FetchHeaders* arg0, const ZigString* arg1, JSC__JSGlobalObject* arg2); CPP_DECL JSC__JSValue WebCore__FetchHeaders__toJS(WebCore__FetchHeaders* arg0, JSC__JSGlobalObject* arg1); @@ -300,6 +301,7 @@ CPP_DECL bool JSC__JSValue__deepEquals(JSC__JSValue JSValue0, JSC__JSValue JSVal CPP_DECL bool JSC__JSValue__eqlCell(JSC__JSValue JSValue0, JSC__JSCell* arg1); CPP_DECL bool JSC__JSValue__eqlValue(JSC__JSValue JSValue0, JSC__JSValue JSValue1); CPP_DECL JSC__JSValue JSC__JSValue__fastGet_(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, unsigned char arg2); +CPP_DECL JSC__JSValue JSC__JSValue__fastGetDirect_(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, unsigned char arg2); CPP_DECL void JSC__JSValue__forEach(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void* arg2, void(* ArgFn3)(JSC__VM* arg0, JSC__JSGlobalObject* arg1, void* arg2, JSC__JSValue JSValue3)) __attribute__((nonnull (3))); CPP_DECL void JSC__JSValue__forEachProperty(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void* arg2, void(* ArgFn3)(JSC__JSGlobalObject* arg0, void* arg1, ZigString* arg2, JSC__JSValue JSValue3, bool arg4)) __attribute__((nonnull (3))); CPP_DECL void JSC__JSValue__forEachPropertyOrdered(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void* arg2, void(* ArgFn3)(JSC__JSGlobalObject* arg0, void* arg1, ZigString* arg2, JSC__JSValue JSValue3, bool arg4)) __attribute__((nonnull (3))); diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index a6a3b52fa..5779655fa 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -123,6 +123,7 @@ pub extern fn WebCore__FetchHeaders__fastHas_(arg0: ?*bindings.FetchHeaders, arg pub extern fn WebCore__FetchHeaders__fastRemove_(arg0: ?*bindings.FetchHeaders, arg1: u8) void; pub extern fn WebCore__FetchHeaders__get_(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: [*c]ZigString, arg3: *bindings.JSGlobalObject) void; pub extern fn WebCore__FetchHeaders__has(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) bool; +pub extern fn WebCore__FetchHeaders__isEmpty(arg0: ?*bindings.FetchHeaders) bool; pub extern fn WebCore__FetchHeaders__put_(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: [*c]const ZigString, arg3: *bindings.JSGlobalObject) void; pub extern fn WebCore__FetchHeaders__remove(arg0: ?*bindings.FetchHeaders, arg1: [*c]const ZigString, arg2: *bindings.JSGlobalObject) void; pub extern fn WebCore__FetchHeaders__toJS(arg0: ?*bindings.FetchHeaders, arg1: *bindings.JSGlobalObject) JSC__JSValue; @@ -213,6 +214,7 @@ pub extern fn JSC__JSValue__deepEquals(JSValue0: JSC__JSValue, JSValue1: JSC__JS pub extern fn JSC__JSValue__eqlCell(JSValue0: JSC__JSValue, arg1: [*c]bindings.JSCell) bool; pub extern fn JSC__JSValue__eqlValue(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) bool; pub extern fn JSC__JSValue__fastGet_(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, arg2: u8) JSC__JSValue; +pub extern fn JSC__JSValue__fastGetDirect_(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, arg2: u8) JSC__JSValue; pub extern fn JSC__JSValue__forEach(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, arg2: ?*anyopaque, ArgFn3: ?*const fn (*bindings.VM, *bindings.JSGlobalObject, ?*anyopaque, JSC__JSValue) callconv(.C) void) void; pub extern fn JSC__JSValue__forEachProperty(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, arg2: ?*anyopaque, ArgFn3: ?*const fn (*bindings.JSGlobalObject, ?*anyopaque, [*c]ZigString, JSC__JSValue, bool) callconv(.C) void) void; pub extern fn JSC__JSValue__forEachPropertyOrdered(JSValue0: JSC__JSValue, arg1: *bindings.JSGlobalObject, arg2: ?*anyopaque, ArgFn3: ?*const fn (*bindings.JSGlobalObject, ?*anyopaque, [*c]ZigString, JSC__JSValue, bool) callconv(.C) void) void; diff --git a/src/bun.js/builtins/BunBuiltinNames.h b/src/bun.js/builtins/BunBuiltinNames.h index b9a9dfe13..ebc2c2c05 100644 --- a/src/bun.js/builtins/BunBuiltinNames.h +++ b/src/bun.js/builtins/BunBuiltinNames.h @@ -129,7 +129,6 @@ using namespace JSC; macro(isAbsolute) \ macro(isDisturbed) \ macro(isPaused) \ - macro(isSecureContext) \ macro(isWindows) \ macro(join) \ macro(kind) \ @@ -183,6 +182,7 @@ using namespace JSC; macro(reader) \ macro(readyPromise) \ macro(readyPromiseCapability) \ + macro(redirect) \ macro(relative) \ macro(releaseLock) \ macro(removeEventListener) \ diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 883a3a48d..85bdb459e 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -2163,18 +2163,11 @@ pub const Expect = struct { if (expected_value.isEmpty()) { const signature_no_args = comptime getSignature("toThrow", "", true); - if (result.isError()) { - const name = result.getIfPropertyExistsImpl(globalObject, "name", 4); - const message = result.getIfPropertyExistsImpl(globalObject, "message", 7); + if (result.toError()) |err| { + const name = err.get(globalObject, "name") orelse JSValue.undefined; + const message = err.get(globalObject, "message") orelse JSValue.undefined; const fmt = signature_no_args ++ "\n\nError name: <red>{any}<r>\nError message: <red>{any}<r>\n"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{ - name.toFmt(globalObject, &formatter), - message.toFmt(globalObject, &formatter), - }); - return .zero; - } - globalObject.throw(Output.prettyFmt(fmt, false), .{ + globalObject.throwPretty(fmt, .{ name.toFmt(globalObject, &formatter), message.toFmt(globalObject, &formatter), }); @@ -2183,41 +2176,25 @@ pub const Expect = struct { // non error thrown const fmt = signature_no_args ++ "\n\nThrown value: <red>{any}<r>\n"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{result.toFmt(globalObject, &formatter)}); - return .zero; - } - globalObject.throw(Output.prettyFmt(fmt, false), .{result.toFmt(globalObject, &formatter)}); + globalObject.throwPretty(fmt, .{result.toFmt(globalObject, &formatter)}); return .zero; } if (expected_value.isString()) { const received_message = result.getIfPropertyExistsImpl(globalObject, "message", 7); + // TODO: remove this allocation // partial match { - var expected_string = ZigString.Empty; - var received_string = ZigString.Empty; - expected_value.toZigString(&expected_string, globalObject); - received_message.toZigString(&received_string, globalObject); - const expected_slice = expected_string.toSlice(default_allocator); - const received_slice = received_string.toSlice(default_allocator); - defer { - expected_slice.deinit(); - received_slice.deinit(); - } + const expected_slice = expected_value.toSliceOrNull(globalObject) orelse return .zero; + defer expected_slice.deinit(); + const received_slice = received_message.toSliceOrNull(globalObject) orelse return .zero; + defer received_slice.deinit(); if (!strings.contains(received_slice.slice(), expected_slice.slice())) return thisValue; } const fmt = signature ++ "\n\nExpected substring: not <green>{any}<r>\nReceived message: <red>{any}<r>\n"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{ - expected_value.toFmt(globalObject, &formatter), - received_message.toFmt(globalObject, &formatter), - }); - return .zero; - } - globalObject.throw(Output.prettyFmt(fmt, false), .{ + globalObject.throwPretty(fmt, .{ expected_value.toFmt(globalObject, &formatter), received_message.toFmt(globalObject, &formatter), }); @@ -2227,20 +2204,14 @@ pub const Expect = struct { if (expected_value.isRegExp()) { const received_message = result.getIfPropertyExistsImpl(globalObject, "message", 7); + // TODO: REMOVE THIS GETTER! Expose a binding to call .test on the RegExp object directly. if (expected_value.get(globalObject, "test")) |test_fn| { const matches = test_fn.callWithThis(globalObject, expected_value, &.{received_message}); if (!matches.toBooleanSlow(globalObject)) return thisValue; } const fmt = signature ++ "\n\nExpected pattern: not <green>{any}<r>\nReceived message: <red>{any}<r>\n"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{ - expected_value.toFmt(globalObject, &formatter), - received_message.toFmt(globalObject, &formatter), - }); - return .zero; - } - globalObject.throw(Output.prettyFmt(fmt, false), .{ + globalObject.throwPretty(fmt, .{ expected_value.toFmt(globalObject, &formatter), received_message.toFmt(globalObject, &formatter), }); @@ -2253,11 +2224,7 @@ pub const Expect = struct { if (!expected_message.isSameValue(received_message, globalObject)) return thisValue; const fmt = signature ++ "\n\nExpected message: not <green>{any}<r>\n"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{expected_message.toFmt(globalObject, &formatter)}); - return .zero; - } - globalObject.throw(Output.prettyFmt(fmt, false), .{expected_message.toFmt(globalObject, &formatter)}); + globalObject.throwPretty(fmt, .{expected_message.toFmt(globalObject, &formatter)}); return .zero; } @@ -2279,22 +2246,26 @@ pub const Expect = struct { if (did_throw) { if (expected_value.isEmpty()) return thisValue; - const result: JSValue = result_.?; - const _received_message = result.get(globalObject, "message"); + const result: JSValue = if (result_.?.toError()) |r| + r + else + result_.?; + + const _received_message: ?JSValue = if (result.isObject()) + result.get(globalObject, "message") + else if (result.toStringOrNull(globalObject)) |js_str| + JSC.JSValue.fromCell(js_str) + else + null; if (expected_value.isString()) { if (_received_message) |received_message| { + // TODO: remove this allocation // partial match - var expected_string = ZigString.Empty; - var received_string = ZigString.Empty; - expected_value.toZigString(&expected_string, globalObject); - received_message.toZigString(&received_string, globalObject); - const expected_slice = expected_string.toSlice(default_allocator); - const received_slice = received_string.toSlice(default_allocator); - defer { - expected_slice.deinit(); - received_slice.deinit(); - } + const expected_slice = expected_value.toSliceOrNull(globalObject) orelse return .zero; + defer expected_slice.deinit(); + const received_slice = received_message.toSlice(globalObject, globalObject.allocator()); + defer received_slice.deinit(); if (strings.contains(received_slice.slice(), expected_slice.slice())) return thisValue; } @@ -2305,29 +2276,21 @@ pub const Expect = struct { const expected_value_fmt = expected_value.toFmt(globalObject, &formatter); const received_message_fmt = received_message.toFmt(globalObject, &formatter); const fmt = signature ++ "\n\n" ++ "Expected substring: <green>{any}<r>\nReceived message: <red>{any}<r>\n"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{ expected_value_fmt, received_message_fmt }); - return .zero; - } - - globalObject.throw(Output.prettyFmt(fmt, false), .{ expected_value_fmt, received_message_fmt }); + globalObject.throwPretty(fmt, .{ expected_value_fmt, received_message_fmt }); return .zero; } const expected_fmt = expected_value.toFmt(globalObject, &formatter); const received_fmt = result.toFmt(globalObject, &formatter); const fmt = signature ++ "\n\n" ++ "Expected substring: <green>{any}<r>\nReceived value: <red>{any}<r>"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{ expected_fmt, received_fmt }); - return .zero; - } + globalObject.throwPretty(fmt, .{ expected_fmt, received_fmt }); - globalObject.throw(Output.prettyFmt(fmt, false), .{ expected_fmt, received_fmt }); return .zero; } if (expected_value.isRegExp()) { if (_received_message) |received_message| { + // TODO: REMOVE THIS GETTER! Expose a binding to call .test on the RegExp object directly. if (expected_value.get(globalObject, "test")) |test_fn| { const matches = test_fn.callWithThis(globalObject, expected_value, &.{received_message}); if (matches.toBooleanSlow(globalObject)) return thisValue; @@ -2341,27 +2304,21 @@ pub const Expect = struct { const expected_value_fmt = expected_value.toFmt(globalObject, &formatter); const received_message_fmt = received_message.toFmt(globalObject, &formatter); const fmt = signature ++ "\n\n" ++ "Expected pattern: <green>{any}<r>\nReceived message: <red>{any}<r>\n"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{ expected_value_fmt, received_message_fmt }); - return .zero; - } + globalObject.throwPretty(fmt, .{ expected_value_fmt, received_message_fmt }); - globalObject.throw(Output.prettyFmt(fmt, false), .{ expected_value_fmt, received_message_fmt }); return .zero; } const expected_fmt = expected_value.toFmt(globalObject, &formatter); const received_fmt = result.toFmt(globalObject, &formatter); const fmt = signature ++ "\n\n" ++ "Expected pattern: <green>{any}<r>\nReceived value: <red>{any}<r>"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{ expected_fmt, received_fmt }); - return .zero; - } - - globalObject.throw(Output.prettyFmt(fmt, false), .{ expected_fmt, received_fmt }); + globalObject.throwPretty(fmt, .{ expected_fmt, received_fmt }); return .zero; } + // If it's not an object, we are going to crash here. + std.debug.assert(expected_value.isObject()); + if (expected_value.get(globalObject, "message")) |expected_message| { if (_received_message) |received_message| { if (received_message.isSameValue(expected_message, globalObject)) return thisValue; @@ -2374,24 +2331,14 @@ pub const Expect = struct { const expected_fmt = expected_message.toFmt(globalObject, &formatter); const received_fmt = received_message.toFmt(globalObject, &formatter); const fmt = signature ++ "\n\nExpected message: <green>{any}<r>\nReceived message: <red>{any}<r>\n"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{ expected_fmt, received_fmt }); - return .zero; - } - - globalObject.throw(Output.prettyFmt(fmt, false), .{ expected_fmt, received_fmt }); + globalObject.throwPretty(fmt, .{ expected_fmt, received_fmt }); return .zero; } const expected_fmt = expected_message.toFmt(globalObject, &formatter); const received_fmt = result.toFmt(globalObject, &formatter); const fmt = signature ++ "\n\nExpected message: <green>{any}<r>\nReceived value: <red>{any}<r>\n"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(fmt, true), .{ expected_fmt, received_fmt }); - return .zero; - } - - globalObject.throw(Output.prettyFmt(fmt, false), .{ expected_fmt, received_fmt }); + globalObject.throwPretty(fmt, .{ expected_fmt, received_fmt }); return .zero; } @@ -2408,16 +2355,8 @@ pub const Expect = struct { if (_received_message) |received_message| { const message_fmt = fmt ++ "Received message: <red>{any}<r>\n"; const received_message_fmt = received_message.toFmt(globalObject, &formatter); - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(message_fmt, true), .{ - expected_class, - received_class, - received_message_fmt, - }); - return .zero; - } - globalObject.throw(Output.prettyFmt(message_fmt, false), .{ + globalObject.throwPretty(message_fmt, .{ expected_class, received_class, received_message_fmt, @@ -2427,16 +2366,8 @@ pub const Expect = struct { const received_fmt = result.toFmt(globalObject, &formatter); const value_fmt = fmt ++ "Received value: <red>{any}<r>\n"; - if (Output.enable_ansi_colors) { - globalObject.throw(Output.prettyFmt(value_fmt, true), .{ - expected_class, - received_class, - received_fmt, - }); - return .zero; - } - globalObject.throw(Output.prettyFmt(value_fmt, false), .{ + globalObject.throwPretty(value_fmt, .{ expected_class, received_class, received_fmt, @@ -2824,7 +2755,7 @@ pub const Expect = struct { } unreachable; }; - + if (not) pass = !pass; if (pass) return thisValue; @@ -3503,7 +3434,7 @@ pub const DescribeScope = struct { .test_id = i, .describe = this, .globalThis = ctx, - .source = source, + .source_file_path = source.path.text, .value = JSC.Strong.create(this_object, ctx), }; runner.ref.ref(ctx.bunVM()); @@ -3608,7 +3539,7 @@ pub const TestRunnerTask = struct { test_id: TestRunner.Test.ID, describe: *DescribeScope, globalThis: *JSC.JSGlobalObject, - source: logger.Source, + source_file_path: string = "", value: JSC.Strong = .{}, needs_before_each: bool = true, ref: JSC.Ref = JSC.Ref.init(), @@ -3677,7 +3608,7 @@ pub const TestRunnerTask = struct { const beforeEach = this.describe.runCallback(globalThis, .beforeEach); if (!beforeEach.isEmpty()) { - Jest.runner.?.reportFailure(test_id, this.source.path.text, label, 0, this.describe); + Jest.runner.?.reportFailure(test_id, this.source_file_path, label, 0, this.describe); globalThis.bunVM().runErrorHandler(beforeEach, null); return false; } @@ -3754,9 +3685,9 @@ pub const TestRunnerTask = struct { fn processTestResult(this: *TestRunnerTask, globalThis: *JSC.JSGlobalObject, result: Result, test_: TestScope, test_id: u32, describe: *DescribeScope) void { switch (result) { - .pass => |count| Jest.runner.?.reportPass(test_id, this.source.path.text, test_.label, count, describe), - .fail => |count| Jest.runner.?.reportFailure(test_id, this.source.path.text, test_.label, count, describe), - .skip => Jest.runner.?.reportSkip(test_id, this.source.path.text, test_.label, describe), + .pass => |count| Jest.runner.?.reportPass(test_id, this.source_file_path, test_.label, count, describe), + .fail => |count| Jest.runner.?.reportFailure(test_id, this.source_file_path, test_.label, count, describe), + .skip => Jest.runner.?.reportSkip(test_id, this.source_file_path, test_.label, describe), .pending => @panic("Unexpected pending test"), } describe.onTestComplete(globalThis, test_id, result == .skip); @@ -3773,7 +3704,16 @@ pub const TestRunnerTask = struct { this.value.deinit(); this.ref.unref(vm); - default_allocator.destroy(this); + + // there is a double free here involving async before/after callbacks + // + // Fortunately: + // + // - TestRunnerTask doesn't use much memory. + // - we don't have watch mode yet. + // + // TODO: fix this bug + // default_allocator.destroy(this); } }; diff --git a/src/bun.js/webcore/body.zig b/src/bun.js/webcore/body.zig index 85a52bc3f..37a70343d 100644 --- a/src/bun.js/webcore/body.zig +++ b/src/bun.js/webcore/body.zig @@ -149,7 +149,9 @@ pub const Body = struct { // we can skip calling JS getters if (response_init.as(Request)) |req| { if (req.headers) |headers| { - result.headers = headers.cloneThis(ctx); + if (!headers.isEmpty()) { + result.headers = headers.cloneThis(ctx); + } } result.method = req.method; @@ -163,7 +165,9 @@ pub const Body = struct { if (response_init.fastGet(ctx, .headers)) |headers| { if (headers.as(FetchHeaders)) |orig| { - result.headers = orig.cloneThis(ctx); + if (!orig.isEmpty()) { + result.headers = orig.cloneThis(ctx); + } } else { result.headers = FetchHeaders.createFromJS(ctx.ptr(), headers); } @@ -185,7 +189,7 @@ pub const Body = struct { } } else if (status_value.isNumber()) { const number = status_value.to(i32); - if (100 <= number and number < 600) + if (100 <= number and number < 999) result.status_code = @truncate(u16, @intCast(u32, number)); } } @@ -849,12 +853,12 @@ pub const Body = struct { if (tag == .InternalBlob) { this.InternalBlob.clearAndFree(); - this.* = Value{ .Null = {} }; //Value.empty; + this.* = Value{ .Null = {} }; } if (tag == .Blob) { this.Blob.deinit(); - this.* = Value{ .Null = {} }; //Value.empty; + this.* = Value{ .Null = {} }; } if (tag == .Error) { diff --git a/src/bun.js/webcore/request.zig b/src/bun.js/webcore/request.zig index ede8cab5d..fe8fd9c9c 100644 --- a/src/bun.js/webcore/request.zig +++ b/src/bun.js/webcore/request.zig @@ -379,100 +379,242 @@ pub const Request = struct { } } + const Fields = enum { + method, + headers, + body, + // referrer, + // referrerPolicy, + // mode, + // credentials, + // redirect, + // integrity, + // keepalive, + signal, + // proxy, + // timeout, + url, + }; + pub fn constructInto( globalThis: *JSC.JSGlobalObject, arguments: []const JSC.JSValue, ) ?Request { - var request = Request{}; - - switch (arguments.len) { - 0 => {}, - 1 => { - const urlOrObject = arguments[0]; - const url_or_object_type = urlOrObject.jsType(); - if (url_or_object_type.isStringLike()) { - request.url = (arguments[0].toSlice(globalThis, bun.default_allocator).cloneIfNeeded(bun.default_allocator) catch { - return null; - }).slice(); - request.url_was_allocated = request.url.len > 0; - request.body = .{ - .Null = {}, - }; - } else { - if (Body.Init.init(getAllocator(globalThis), globalThis, arguments[0]) catch null) |req_init| { - request.headers = req_init.headers; - request.method = req_init.method; + var req = Request{}; + + if (arguments.len == 0) { + globalThis.throw("Failed to construct 'Request': 1 argument required, but only 0 present.", .{}); + return null; + } else if (arguments[0].isEmptyOrUndefinedOrNull() or !arguments[0].isCell()) { + globalThis.throw("Failed to construct 'Request': expected non-empty string or object, got undefined", .{}); + return null; + } + + const url_or_object = arguments[0]; + const url_or_object_type = url_or_object.jsType(); + var fields = std.EnumSet(Fields).initEmpty(); + + const is_first_argument_a_url = + // fastest path: + url_or_object_type.isStringLike() or + // slower path: + url_or_object.as(JSC.DOMURL) != null; + + if (is_first_argument_a_url) { + const slice = arguments[0].toSliceOrNull(globalThis) orelse { + req.finalizeWithoutDeinit(); + return null; + }; + req.url = (slice.cloneIfNeeded(globalThis.allocator()) catch { + req.finalizeWithoutDeinit(); + return null; + }).slice(); + req.url_was_allocated = req.url.len > 0; + if (req.url.len > 0) + fields.insert(.url); + } else if (!url_or_object_type.isObject()) { + globalThis.throw("Failed to construct 'Request': expected non-empty string or object", .{}); + return null; + } + + const values_to_try_ = [_]JSValue{ + if (arguments.len > 1 and arguments[1].isObject()) + arguments[1] + else if (is_first_argument_a_url) + JSValue.undefined + else + url_or_object, + if (is_first_argument_a_url) JSValue.undefined else url_or_object, + }; + const values_to_try = values_to_try_[0 .. @as(usize, @boolToInt(!is_first_argument_a_url)) + + @as(usize, @boolToInt(arguments.len > 1 and arguments[1].isObject()))]; + + for (values_to_try) |value| { + const value_type = value.jsType(); + + if (value_type == .DOMWrapper) { + if (value.as(Request)) |request| { + if (values_to_try.len == 1) { + request.cloneInto(&req, globalThis.allocator(), globalThis); + if (req.url_was_allocated) { + req.url = req.url; + req.url_was_allocated = true; + } + return req; } - if (urlOrObject.fastGet(globalThis, .body)) |body_| { - if (Body.Value.fromJS(globalThis, body_)) |body| { - request.body = body; - } else { - request.finalizeWithoutDeinit(); - return null; + if (!fields.contains(.method)) { + req.method = request.method; + fields.insert(.method); + } + + if (!fields.contains(.headers)) { + if (request.cloneHeaders(globalThis)) |headers| { + req.headers = headers; + fields.insert(.headers); } - } else { - request.body = .{ - .Null = {}, - }; } - if (urlOrObject.fastGet(globalThis, .url)) |url| { - request.url = (url.toSlice(globalThis, bun.default_allocator).cloneIfNeeded(bun.default_allocator) catch { - return null; - }).slice(); - request.url_was_allocated = request.url.len > 0; + if (!fields.contains(.body)) { + switch (request.body) { + .Null, .Empty, .Used => {}, + else => { + req.body = request.body.clone(globalThis); + fields.insert(.body); + }, + } } } - }, - else => { - if (arguments[1].get(globalThis, "signal")) |signal_| { - if (AbortSignal.fromJS(signal_)) |signal| { - //Keep it alive - signal_.ensureStillAlive(); - request.signal = signal.ref(); - } else { - globalThis.throw("Failed to construct 'Request': member signal is not of type AbortSignal.", .{}); - request.finalizeWithoutDeinit(); - return null; + if (value.as(JSC.WebCore.Response)) |response| { + if (!fields.contains(.method)) { + req.method = response.body.init.method; + fields.insert(.method); + } + + if (!fields.contains(.headers)) { + if (response.body.init.headers) |headers| { + req.headers = headers.cloneThis(globalThis); + fields.insert(.headers); + } } - } - if (Body.Init.init(getAllocator(globalThis), globalThis, arguments[1]) catch null) |req_init| { - request.headers = req_init.headers; - request.method = req_init.method; + if (!fields.contains(.url)) { + if (response.url.len > 0) { + req.url = globalThis.allocator().dupe(u8, response.url) catch unreachable; + req.url_was_allocated = true; + fields.insert(.url); + } + } + + if (!fields.contains(.body)) { + switch (response.body.value) { + .Null, .Empty, .Used => {}, + else => { + req.body = response.body.value.clone(globalThis); + fields.insert(.body); + }, + } + } } + } - if (arguments[1].fastGet(globalThis, .body)) |body_| { + if (!fields.contains(.body)) { + if (value.fastGet(globalThis, .body)) |body_| { + fields.insert(.body); if (Body.Value.fromJS(globalThis, body_)) |body| { - request.body = body; + req.body = body; } else { - request.finalizeWithoutDeinit(); + req.finalizeWithoutDeinit(); return null; } - } else { - request.body = .{ - .Null = {}, + } + } + + if (!fields.contains(.url)) { + if (value.fastGet(globalThis, .url)) |url| { + req.url = (url.toSlice(globalThis, bun.default_allocator).cloneIfNeeded(bun.default_allocator) catch { + return null; + }).slice(); + req.url_was_allocated = req.url.len > 0; + if (req.url.len > 0) + fields.insert(.url); + + // first value + } else if (@enumToInt(value) == @enumToInt(values_to_try[values_to_try.len - 1]) and !is_first_argument_a_url and + value.implementsToString(globalThis)) + { + const slice = value.toSliceOrNull(globalThis) orelse { + req.finalizeWithoutDeinit(); + return null; }; + req.url = (slice.cloneIfNeeded(globalThis.allocator()) catch { + req.finalizeWithoutDeinit(); + return null; + }).slice(); + req.url_was_allocated = req.url.len > 0; + if (req.url.len > 0) + fields.insert(.url); } + } - request.url = (arguments[0].toSlice(globalThis, bun.default_allocator).cloneIfNeeded(bun.default_allocator) catch { - return null; - }).slice(); - request.url_was_allocated = request.url.len > 0; - }, + if (!fields.contains(.signal)) { + if (value.get(globalThis, "signal")) |signal_| { + fields.insert(.signal); + + if (AbortSignal.fromJS(signal_)) |signal| { + //Keep it alive + signal_.ensureStillAlive(); + req.signal = signal.ref(); + } else { + globalThis.throw("Failed to construct 'Request': signal is not of type AbortSignal.", .{}); + req.finalizeWithoutDeinit(); + return null; + } + } + } + + if (!fields.contains(.method) or !fields.contains(.headers)) { + if (Body.Init.init(globalThis.allocator(), globalThis, value) catch null) |init| { + if (!fields.contains(.method)) { + req.method = init.method; + fields.insert(.method); + } + + if (init.headers) |headers| { + if (!fields.contains(.headers)) { + req.headers = headers; + fields.insert(.headers); + } else { + headers.deref(); + } + } + } + } } - if (request.body == .Blob and - request.headers != null and - request.body.Blob.content_type.len > 0 and - !request.headers.?.fastHas(.ContentType)) + if (req.url.len == 0) { + globalThis.throw("Failed to construct 'Request': url is required.", .{}); + req.finalizeWithoutDeinit(); + return null; + } + + const parsed_url = ZigURL.parse(req.url); + if (parsed_url.hostname.len == 0) { + globalThis.throw("Failed to construct 'Request': Invalid URL (missing a hostname)", .{}); + req.finalizeWithoutDeinit(); + return null; + } + + if (req.body == .Blob and + req.headers != null and + req.body.Blob.content_type.len > 0 and + !req.headers.?.fastHas(.ContentType)) { - request.headers.?.put("content-type", request.body.Blob.content_type, globalThis); + req.headers.?.put("content-type", req.body.Blob.content_type, globalThis); } - return request; + return req; } pub fn constructor( @@ -531,6 +673,24 @@ pub const Request = struct { return this.headers.?.toJS(globalThis); } + pub fn cloneHeaders(this: *Request, globalThis: *JSGlobalObject) ?*FetchHeaders { + if (this.headers == null) { + if (this.uws_request) |uws_req| { + this.headers = FetchHeaders.createFromUWS(globalThis, uws_req); + } + } + + if (this.headers) |head| { + if (head.isEmpty()) { + return null; + } + + return head.cloneThis(globalThis); + } + + return null; + } + pub fn cloneInto( this: *Request, req: *Request, @@ -546,15 +706,9 @@ pub const Request = struct { return; }, .method = this.method, + .headers = this.cloneHeaders(globalThis), }; - if (this.headers) |head| { - req.headers = head.cloneThis(globalThis); - } else if (this.uws_request) |uws_req| { - req.headers = FetchHeaders.createFromUWS(globalThis, uws_req); - this.headers = req.headers.?.cloneThis(globalThis).?; - } - if (this.signal) |signal| { req.signal = signal.ref(); } diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index 9dfee821a..a19ee9ca4 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -490,7 +490,14 @@ pub const Response = struct { globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, ) callconv(.C) ?*Response { - const args_list = callframe.arguments(2); + const args_list = brk: { + var args = callframe.arguments(2); + if (args.len > 1 and args.ptr[1].isEmptyOrUndefinedOrNull()) { + args.len = 1; + } + break :brk args; + }; + const arguments = args_list.ptr[0..args_list.len]; const body: Body = @as(?Body, brk: { switch (arguments.len) { @@ -501,11 +508,12 @@ pub const Response = struct { break :brk Body.extract(globalThis, arguments[0]); }, else => { - if (arguments[1].isUndefinedOrNull()) break :brk Body.extract(globalThis, arguments[0]); if (arguments[1].isObject()) { break :brk Body.extractWithInit(globalThis, arguments[0], arguments[1]); } + std.debug.assert(!arguments[1].isEmptyOrUndefinedOrNull()); + const err = globalThis.createTypeErrorInstance("Expected options to be one of: null, undefined, or object", .{}); globalThis.throwValue(err); break :brk null; @@ -731,7 +739,13 @@ pub const Fetch = struct { const fetch_error = JSC.SystemError{ .code = ZigString.init(@errorName(this.result.fail)), - .message = ZigString.init("fetch() failed"), + .message = switch (this.result.fail) { + error.ConnectionClosed => ZigString.init("The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()"), + error.FailedToOpenSocket => ZigString.init("Was there a typo in the url or port?"), + error.TooManyRedirects => ZigString.init("The response redirected too many times. For more information, pass `verbose: true` in the second argument to fetch()"), + error.ConnectionRefused => ZigString.init("Unable to connect. Is the computer able to access the url?"), + else => ZigString.init("fetch() failed. For more information, pass `verbose: true` in the second argument to fetch()"), + }, .path = ZigString.init(this.http.?.url.href), }; diff --git a/src/env_loader.zig b/src/env_loader.zig index 41fb25fe5..38c0744a3 100644 --- a/src/env_loader.zig +++ b/src/env_loader.zig @@ -451,7 +451,7 @@ pub const Loader = struct { } } - //NO_PROXY filter + // NO_PROXY filter if (http_proxy != null) { if (this.map.get("no_proxy") orelse this.map.get("NO_PROXY")) |no_proxy_text| { if (no_proxy_text.len == 0) return http_proxy; diff --git a/src/install/extract_tarball.zig b/src/install/extract_tarball.zig index 0df11ffcd..bec5f5eff 100644 --- a/src/install/extract_tarball.zig +++ b/src/install/extract_tarball.zig @@ -110,7 +110,7 @@ pub fn buildURLWithPrinter( var name = full_name; if (name[0] == '@') { - if (std.mem.indexOfScalar(u8, name, '/')) |i| { + if (strings.indexOfChar(name, '/')) |i| { name = name[i + 1 ..]; } } diff --git a/src/install/install.zig b/src/install/install.zig index 0c280ccaa..0927c9335 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -323,11 +323,21 @@ const NetworkTask = struct { this.response_buffer = try MutableString.init(allocator, 0); this.allocator = allocator; - const env = this.package_manager.env; - var url = URL.parse(this.url_buf); - var http_proxy: ?URL = env.getHttpProxy(url); - this.http = AsyncHTTP.init(allocator, .GET, url, header_builder.entries, header_builder.content.ptr.?[0..header_builder.content.len], &this.response_buffer, "", 0, this.getCompletionCallback(), http_proxy, null); + const url = URL.parse(this.url_buf); + this.http = AsyncHTTP.init( + allocator, + .GET, + url, + header_builder.entries, + header_builder.content.ptr.?[0..header_builder.content.len], + &this.response_buffer, + "", + 0, + this.getCompletionCallback(), + this.package_manager.httpProxy(url), + null, + ); this.http.max_retry_count = this.package_manager.options.max_retry_count; this.callback = .{ .package_manifest = .{ @@ -389,12 +399,21 @@ const NetworkTask = struct { header_buf = header_builder.content.ptr.?[0..header_builder.content.len]; } - const env = this.package_manager.env; - - var url = URL.parse(this.url_buf); - var http_proxy: ?URL = env.getHttpProxy(url); + const url = URL.parse(this.url_buf); - this.http = AsyncHTTP.init(allocator, .GET, url, header_builder.entries, header_buf, &this.response_buffer, "", 0, this.getCompletionCallback(), http_proxy, null); + this.http = AsyncHTTP.init( + allocator, + .GET, + url, + header_builder.entries, + header_buf, + &this.response_buffer, + "", + 0, + this.getCompletionCallback(), + this.package_manager.httpProxy(url), + null, + ); this.http.max_retry_count = this.package_manager.options.max_retry_count; this.callback = .{ .extract = tarball }; } @@ -1590,6 +1609,10 @@ pub const PackageManager = struct { 80, ); + pub fn httpProxy(this: *PackageManager, url: URL) ?URL { + return this.env.getHttpProxy(url); + } + pub const WakeHandler = struct { // handler: fn (ctx: *anyopaque, pm: *PackageManager) void = undefined, // onDependencyError: fn (ctx: *anyopaque, Dependency, PackageID, anyerror) void = undefined, @@ -6561,11 +6584,14 @@ pub const PackageManager = struct { const cwd = std.fs.cwd(); while (iterator.nextNodeModulesFolder()) |node_modules| { - try cwd.makePath(bun.span(node_modules.relative_path)); // We deliberately do not close this folder. // If the package hasn't been downloaded, we will need to install it later // We use this file descriptor to know where to put it. - installer.node_modules_folder = try cwd.openIterableDir(node_modules.relative_path, .{}); + installer.node_modules_folder = cwd.openIterableDir(node_modules.relative_path, .{}) catch brk: { + // Avoid extra mkdir() syscall + try cwd.makePath(bun.span(node_modules.relative_path)); + break :brk try cwd.openIterableDir(node_modules.relative_path, .{}); + }; var remaining = node_modules.dependencies; |