From 8d7d58606bfe8e6cf7aa8fc65940436a4eb99ee5 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Tue, 10 Sep 2024 16:07:22 -0700 Subject: [PATCH] Add generator for $ERR_* as fake private globals (#13843) Co-authored-by: Jarred-Sumner --- src/bun.js/bindings/ErrorCode.cpp | 241 ++++++++++++++---- src/bun.js/bindings/ErrorCode.h | 1 + src/bun.js/bindings/ZigGlobalObject.cpp | 1 + src/codegen/bundle-modules.ts | 26 ++ src/codegen/replacements.ts | 9 + src/js/builtins.d.ts | 7 + src/js/builtins/BunBuiltinNames.h | 1 + src/js/node/readline.ts | 26 +- .../child_process/child_process-node.test.js | 26 +- .../node/child_process/child_process.test.ts | 2 +- test/js/node/readline/readline.node.test.ts | 32 ++- 11 files changed, 291 insertions(+), 81 deletions(-) diff --git a/src/bun.js/bindings/ErrorCode.cpp b/src/bun.js/bindings/ErrorCode.cpp index f5f0f8b552..a962c516df 100644 --- a/src/bun.js/bindings/ErrorCode.cpp +++ b/src/bun.js/bindings/ErrorCode.cpp @@ -183,68 +183,166 @@ WTF::String JSValueToStringSafe(JSC::JSGlobalObject* globalObject, JSValue arg) { ASSERT(!arg.isEmpty()); if (!arg.isCell()) - return arg.toString(globalObject)->getString(globalObject); + return arg.toWTFStringForConsole(globalObject); auto cell = arg.asCell(); - auto jstype = cell->type(); - - if (jstype == JSC::JSType::StringType) { - return cell->toStringInline(globalObject)->getString(globalObject); + switch (cell->type()) { + case JSC::JSType::StringType: { + return arg.toWTFStringForConsole(globalObject); } - if (jstype == JSC::JSType::SymbolType) { + case JSC::JSType::SymbolType: { + auto symbol = jsCast(cell); auto result = symbol->tryGetDescriptiveString(); if (result.has_value()) return result.value(); + return "Symbol"_s; } - return arg.toString(globalObject)->getString(globalObject); + case JSC::JSType::InternalFunctionType: + case JSC::JSType::JSFunctionType: { + auto& vm = globalObject->vm(); + auto catchScope = DECLARE_CATCH_SCOPE(vm); + auto name = JSC::getCalculatedDisplayName(vm, cell->getObject()); + if (catchScope.exception()) { + catchScope.clearException(); + name = "Function"_s; + } + + if (!name.isNull() && name.length() > 0) { + return makeString("[Function: "_s, name, ']'); + } + + return "Function"_s; + break; + } + + default: { + break; + } + } + + return arg.toWTFStringForConsole(globalObject); } -JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +namespace Message { + +WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, const StringView& arg_name, const StringView& expected_type, JSValue actual_value) { - JSC::VM& vm = globalObject->vm(); - auto scope = DECLARE_THROW_SCOPE(vm); - EXPECT_ARG_COUNT(3); + auto actual_value_string = JSValueToStringSafe(globalObject, actual_value); + RETURN_IF_EXCEPTION(scope, {}); - auto arg_name = callFrame->argument(0); - auto expected_type = callFrame->argument(1); - auto actual_value = callFrame->argument(2); - return Bun__ERR_INVALID_ARG_TYPE(globalObject, JSValue::encode(arg_name), JSValue::encode(expected_type), JSValue::encode(actual_value)); + return makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received: "_s, actual_value_string); } + +WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, const StringView& arg_name, ArgList expected_types, JSValue actual_value) +{ + WTF::StringBuilder result; + + auto actual_value_string = JSValueToStringSafe(globalObject, actual_value); + RETURN_IF_EXCEPTION(scope, {}); + + result.append("The \""_s, arg_name, "\" argument must be of type "_s); + + unsigned length = expected_types.size(); + if (length == 1) { + result.append(expected_types.at(0).toWTFString(globalObject)); + } else if (length == 2) { + result.append(expected_types.at(0).toWTFString(globalObject)); + result.append(" or "_s); + result.append(expected_types.at(1).toWTFString(globalObject)); + } else { + for (unsigned i = 0; i < length - 1; i++) { + if (i > 0) + result.append(", "_s); + JSValue expected_type = expected_types.at(i); + result.append(expected_type.toWTFString(globalObject)); + } + result.append(" or "_s); + result.append(expected_types.at(length - 1).toWTFString(globalObject)); + } + + result.append(". Received: "_s, actual_value_string); + + return result.toString(); +} + +WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, const ZigString* arg_name_string, const ZigString* expected_type_string, JSValue actual_value) +{ + auto arg_name = std::span(arg_name_string->ptr, arg_name_string->len); + ASSERT(WTF::charactersAreAllASCII(arg_name)); + + auto expected_type = std::span(expected_type_string->ptr, expected_type_string->len); + ASSERT(WTF::charactersAreAllASCII(expected_type)); + + return ERR_INVALID_ARG_TYPE(scope, globalObject, arg_name, expected_type, actual_value); +} + +WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue val_arg_name, JSValue val_expected_type, JSValue val_actual_value) +{ + + auto arg_name = val_arg_name.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto expected_type = val_expected_type.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + return ERR_INVALID_ARG_TYPE(scope, globalObject, arg_name, expected_type, val_actual_value); +} + +WTF::String ERR_OUT_OF_RANGE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue val_arg_name, JSValue val_range, JSValue val_input) +{ + auto arg_name = val_arg_name.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto range = val_range.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + auto input = JSValueToStringSafe(globalObject, val_input); + RETURN_IF_EXCEPTION(scope, {}); + + return makeString("The value of \""_s, arg_name, "\" is out of range. It must be "_s, range, ". Received: \""_s, input, '"'); +} +} + +static JSC::JSValue ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue arg0, JSValue arg1, JSValue arg2) +{ + + if (auto* array = jsDynamicCast(arg1)) { + const WTF::String argName = arg0.toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + MarkedArgumentBuffer expected_types; + for (unsigned i = 0, length = array->length(); i < length; i++) { + expected_types.append(array->getDirectIndex(globalObject, i)); + RETURN_IF_EXCEPTION(scope, {}); + } + + const auto msg = Bun::Message::ERR_INVALID_ARG_TYPE(scope, globalObject, argName, expected_types, arg2); + return createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, msg); + } + + const auto msg = Bun::Message::ERR_INVALID_ARG_TYPE(scope, globalObject, arg0, arg1, arg2); + return createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, msg); +} + extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue val_arg_name, JSC::EncodedJSValue val_expected_type, JSC::EncodedJSValue val_actual_value) { JSC::VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - auto arg_name = JSValue::decode(val_arg_name).toWTFString(globalObject); + auto message = Message::ERR_INVALID_ARG_TYPE(scope, globalObject, JSValue::decode(val_arg_name), JSValue::decode(val_expected_type), JSValue::decode(val_actual_value)); RETURN_IF_EXCEPTION(scope, {}); - - auto expected_type = JSValue::decode(val_expected_type).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - auto actual_value = JSValueToStringSafe(globalObject, JSValue::decode(val_actual_value)); - RETURN_IF_EXCEPTION(scope, {}); - - auto message = makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received "_s, actual_value); - return JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); } extern "C" JSC::EncodedJSValue Bun__ERR_INVALID_ARG_TYPE_static(JSC::JSGlobalObject* globalObject, const ZigString* val_arg_name, const ZigString* val_expected_type, JSC::EncodedJSValue val_actual_value) { JSC::VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - auto arg_name = std::span(val_arg_name->ptr, val_arg_name->len); - ASSERT(WTF::charactersAreAllASCII(arg_name)); - - auto expected_type = std::span(val_expected_type->ptr, val_expected_type->len); - ASSERT(WTF::charactersAreAllASCII(expected_type)); - - auto actual_value = JSValueToStringSafe(globalObject, JSValue::decode(val_actual_value)); + WTF::String message = Message::ERR_INVALID_ARG_TYPE(scope, globalObject, val_arg_name, val_expected_type, JSValue::decode(val_actual_value)); RETURN_IF_EXCEPTION(scope, {}); - - auto message = makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received "_s, actual_value); - return JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); + return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message)); } JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) @@ -254,16 +352,8 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_OUT_OF_RANGE, (JSC::JSGlobalObject * glo EXPECT_ARG_COUNT(3); - auto arg_name = callFrame->argument(0).toWTFString(globalObject); + auto message = Message::ERR_OUT_OF_RANGE(scope, globalObject, callFrame->argument(0), callFrame->argument(1), callFrame->argument(2)); RETURN_IF_EXCEPTION(scope, {}); - - auto range = callFrame->argument(1).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - auto input = callFrame->argument(2).toWTFString(globalObject); - RETURN_IF_EXCEPTION(scope, {}); - - auto message = makeString("The value of \""_s, arg_name, "\" is out of range. It must be "_s, range, ". Received "_s, input); return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_OUT_OF_RANGE, message)); } @@ -384,6 +474,19 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_INVALID_PROTOCOL, (JSC::JSGlobalObject * return JSC::JSValue::encode(createError(globalObject, ErrorCode::ERR_INVALID_PROTOCOL, message)); } +JSC_DEFINE_HOST_FUNCTION(jsFunction_ERR_INVALID_ARG_TYPE, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + EXPECT_ARG_COUNT(3); + + auto arg_name = callFrame->argument(0); + auto expected_type = callFrame->argument(1); + auto actual_value = callFrame->argument(2); + return JSValue::encode(ERR_INVALID_ARG_TYPE(scope, globalObject, arg_name, expected_type, actual_value)); +} + } // namespace Bun JSC::JSValue WebCore::toJS(JSC::JSGlobalObject* globalObject, CommonAbortReason abortReason) @@ -432,4 +535,52 @@ JSC::JSObject* Bun::createInvalidThisError(JSC::JSGlobalObject* globalObject, JS JSC::EncodedJSValue Bun::throwError(JSC::JSGlobalObject* globalObject, JSC::ThrowScope& scope, Bun::ErrorCode code, const WTF::String& message) { return JSC::JSValue::encode(scope.throwException(globalObject, createError(globalObject, code, message))); -} \ No newline at end of file +} + +JSC_DEFINE_HOST_FUNCTION(Bun::jsFunctionMakeErrorWithCode, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + EXPECT_ARG_COUNT(2); + + JSC::JSValue codeValue = callFrame->argument(0); + RETURN_IF_EXCEPTION(scope, {}); + +#if BUN_DEBUG + if (!codeValue.isNumber()) { + JSC::throwTypeError(globalObject, scope, "First argument to $ERR_ must be a number"_s); + return {}; + } +#endif + + int code = codeValue.toInt32(globalObject); + +#if BUN_DEBUG + if (code > Bun::NODE_ERROR_COUNT - 1 || code < 0) { + JSC::throwTypeError(globalObject, scope, "Invalid error code. Use $ERR_* constants"_s); + return {}; + } +#endif + + Bun::ErrorCode error = static_cast(code); + + switch (error) { + case Bun::ErrorCode::ERR_INVALID_ARG_TYPE: { + JSValue arg0 = callFrame->argument(1); + JSValue arg1 = callFrame->argument(2); + JSValue arg2 = callFrame->argument(3); + + return JSValue::encode(ERR_INVALID_ARG_TYPE(scope, globalObject, arg0, arg1, arg2)); + } + + default: { + break; + } + } + + auto message = callFrame->argument(1).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + return JSC::JSValue::encode(createError(globalObject, error, message)); +} diff --git a/src/bun.js/bindings/ErrorCode.h b/src/bun.js/bindings/ErrorCode.h index 6d0828bea8..4e2d1d6010 100644 --- a/src/bun.js/bindings/ErrorCode.h +++ b/src/bun.js/bindings/ErrorCode.h @@ -62,5 +62,6 @@ JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_SERVER_NOT_RUNNING); JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_IPC_CHANNEL_CLOSED); JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_SOCKET_BAD_TYPE); JSC_DECLARE_HOST_FUNCTION(jsFunction_ERR_INVALID_PROTOCOL); +JSC_DECLARE_HOST_FUNCTION(jsFunctionMakeErrorWithCode); } diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 8c02316b80..8f99baefca 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -3437,6 +3437,7 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm) GlobalPropertyInfo(builtinNames.processBindingConstantsPrivateName(), this->processBindingConstants(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.requireMapPrivateName(), this->requireMap(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | 0), GlobalPropertyInfo(builtinNames.TextEncoderStreamEncoderPrivateName(), JSTextEncoderStreamEncoderConstructor(), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | 0), + GlobalPropertyInfo(builtinNames.makeErrorWithCodePrivateName(), JSFunction::create(vm, this, 2, String(), jsFunctionMakeErrorWithCode, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), }; addStaticGlobals(staticGlobals, std::size(staticGlobals)); diff --git a/src/codegen/bundle-modules.ts b/src/codegen/bundle-modules.ts index a7289ce5d3..aaa86834d4 100644 --- a/src/codegen/bundle-modules.ts +++ b/src/codegen/bundle-modules.ts @@ -12,6 +12,7 @@ import fs from "fs"; import { mkdir, writeFile } from "fs/promises"; import { builtinModules } from "node:module"; import path from "path"; +import ErrorCode from "../bun.js/bindings/ErrorCode"; import { sliceSourceCode } from "./builtin-parser"; import { createAssertClientJS, createLogClientJS } from "./client-js"; import { getJS2NativeCPP, getJS2NativeZig } from "./generate-js2native"; @@ -432,6 +433,31 @@ writeIfNotChanged(path.join(CODEGEN_DIR, "GeneratedJS2Native.h"), getJS2NativeCP const js2nativeZigPath = path.join(import.meta.dir, "../bun.js/bindings/GeneratedJS2Native.zig"); writeIfNotChanged(js2nativeZigPath, getJS2NativeZig(js2nativeZigPath)); +const generatedDTSPath = path.join(CODEGEN_DIR, "generated.d.ts"); +writeIfNotChanged( + generatedDTSPath, + (() => { + let dts = ` +// GENERATED TEMP FILE - DO NOT EDIT +`; + + for (let i = 0; i < ErrorCode.length; i++) { + const [code, _, name] = ErrorCode[i]; + dts += ` +/** + * Generate a ${name} error with the \`code\` property set to ${code}. + * + * @param msg The error message + * @param args Additional arguments + */ +declare function $${code}(msg: string, ...args: any[]): ${name}; +`; + } + + return dts; + })(), +); + mark("Generate Code"); if (!silent) { diff --git a/src/codegen/replacements.ts b/src/codegen/replacements.ts index 35b005aea9..3d20dc4fa1 100644 --- a/src/codegen/replacements.ts +++ b/src/codegen/replacements.ts @@ -1,4 +1,5 @@ import { LoaderKeys } from "../api/schema"; +import NodeErrors from "../bun.js/bindings/ErrorCode.ts"; import { sliceSourceCode } from "./builtin-parser"; import { registerNativeCall } from "./generate-js2native"; @@ -12,6 +13,14 @@ export const replacements: ReplacementRule[] = [ { from: /\bexport\s*default/g, to: "$exports =" }, ]; +for (let i = 0; i < NodeErrors.length; i++) { + const [code] = NodeErrors[i]; + replacements.push({ + from: new RegExp(`\\b\\__intrinsic__${code}\\(`, "g"), + to: `$makeErrorWithCode(${i}, `, + }); +} + // These rules are run on the entire file, including within strings. export const globalReplacements: ReplacementRule[] = [ { diff --git a/src/js/builtins.d.ts b/src/js/builtins.d.ts index 54cb496c42..23c0985329 100644 --- a/src/js/builtins.d.ts +++ b/src/js/builtins.d.ts @@ -1,3 +1,4 @@ +/// // Typedefs for JSC intrinsics. Instead of @, we use $ type TODO = any; @@ -539,3 +540,9 @@ declare var $Buffer: { declare interface Error { code?: string; } + +/** + * -- Error Codes with manual messages + */ +declare function $ERR_INVALID_ARG_TYPE(argName: string, expectedType: string, actualValue: string): TypeError; +declare function $ERR_INVALID_ARG_TYPE(argName: string, expectedTypes: any[], actualValue: string): TypeError; diff --git a/src/js/builtins/BunBuiltinNames.h b/src/js/builtins/BunBuiltinNames.h index 058f741512..fbec2c56bd 100644 --- a/src/js/builtins/BunBuiltinNames.h +++ b/src/js/builtins/BunBuiltinNames.h @@ -82,6 +82,7 @@ using namespace JSC; macro(encoding) \ macro(end) \ macro(errno) \ + macro(makeErrorWithCode) \ macro(errorSteps) \ macro(evaluateCommonJSModule) \ macro(evaluated) \ diff --git a/src/js/node/readline.ts b/src/js/node/readline.ts index 916f115b32..0c9ae8b2d0 100644 --- a/src/js/node/readline.ts +++ b/src/js/node/readline.ts @@ -258,14 +258,6 @@ var NodeError = getNodeErrorByName("Error"); var NodeTypeError = getNodeErrorByName("TypeError"); var NodeRangeError = getNodeErrorByName("RangeError"); -class ERR_INVALID_ARG_TYPE extends NodeTypeError { - constructor(name, type, value) { - super(`The "${name}" argument must be of type ${type}. Received type ${typeof value}`, { - code: "ERR_INVALID_ARG_TYPE", - }); - } -} - class ERR_INVALID_ARG_VALUE extends NodeTypeError { constructor(name, value, reason = "not specified") { super(`The value "${String(value)}" is invalid for argument '${name}'. Reason: ${reason}`, { @@ -315,7 +307,7 @@ class AbortError extends Error { * @returns {asserts value is Function} */ function validateFunction(value, name) { - if (typeof value !== "function") throw new ERR_INVALID_ARG_TYPE(name, "Function", value); + if (typeof value !== "function") throw $ERR_INVALID_ARG_TYPE(name, "Function", value); } /** @@ -325,7 +317,7 @@ function validateFunction(value, name) { */ function validateAbortSignal(signal, name) { if (signal !== undefined && (signal === null || typeof signal !== "object" || !("aborted" in signal))) { - throw new ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); + throw $ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); } } @@ -339,7 +331,7 @@ function validateAbortSignal(signal, name) { function validateArray(value, name, minLength = 0) { // var validateArray = hideStackFrames((value, name, minLength = 0) => { if (!$isJSArray(value)) { - throw new ERR_INVALID_ARG_TYPE(name, "Array", value); + throw $ERR_INVALID_ARG_TYPE(name, "Array", value); } if (value.length < minLength) { var reason = `must be longer than ${minLength}`; @@ -354,7 +346,7 @@ function validateArray(value, name, minLength = 0) { * @returns {asserts value is string} */ function validateString(value, name) { - if (typeof value !== "string") throw new ERR_INVALID_ARG_TYPE(name, "string", value); + if (typeof value !== "string") throw $ERR_INVALID_ARG_TYPE(name, "string", value); } /** @@ -364,7 +356,7 @@ function validateString(value, name) { * @returns {asserts value is boolean} */ function validateBoolean(value, name) { - if (typeof value !== "boolean") throw new ERR_INVALID_ARG_TYPE(name, "boolean", value); + if (typeof value !== "boolean") throw $ERR_INVALID_ARG_TYPE(name, "boolean", value); } /** @@ -387,7 +379,7 @@ function validateObject(value, name, options = null) { (!allowArray && $isJSArray.$call(null, value)) || (typeof value !== "object" && (!allowFunction || typeof value !== "function")) ) { - throw new ERR_INVALID_ARG_TYPE(name, "object", value); + throw $ERR_INVALID_ARG_TYPE(name, "object", value); } } @@ -400,7 +392,7 @@ function validateObject(value, name, options = null) { * @returns {asserts value is number} */ function validateInteger(value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) { - if (typeof value !== "number") throw new ERR_INVALID_ARG_TYPE(name, "number", value); + if (typeof value !== "number") throw $ERR_INVALID_ARG_TYPE(name, "number", value); if (!NumberIsInteger(value)) throw new ERR_OUT_OF_RANGE(name, "an integer", value); if (value < min || value > max) throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); } @@ -414,7 +406,7 @@ function validateInteger(value, name, min = NumberMIN_SAFE_INTEGER, max = Number */ function validateUint32(value, name, positive = false) { if (typeof value !== "number") { - throw new ERR_INVALID_ARG_TYPE(name, "number", value); + throw $ERR_INVALID_ARG_TYPE(name, "number", value); } if (!NumberIsInteger(value)) { @@ -2897,7 +2889,7 @@ class Readline { constructor(stream, options = undefined) { isWritable ??= require("node:stream").isWritable; - if (!isWritable(stream)) throw new ERR_INVALID_ARG_TYPE("stream", "Writable", stream); + if (!isWritable(stream)) throw $ERR_INVALID_ARG_TYPE("stream", "Writable", stream); this.#stream = stream; if (options?.autoCommit != null) { validateBoolean(options.autoCommit, "options.autoCommit"); diff --git a/test/js/node/child_process/child_process-node.test.js b/test/js/node/child_process/child_process-node.test.js index 9c99935d75..42931a8dcf 100644 --- a/test/js/node/child_process/child_process-node.test.js +++ b/test/js/node/child_process/child_process-node.test.js @@ -654,11 +654,15 @@ describe("fork", () => { const invalidModulePath = [0, true, undefined, null, [], {}, () => {}, Symbol("t")]; invalidModulePath.forEach(modulePath => { it(`Ensure that first argument \`modulePath\` must be provided and be of type string :: ${String(modulePath)}`, () => { - expect(() => fork(modulePath, { env: bunEnv })).toThrow({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: `The "modulePath" argument must be of type string,Buffer,URL. Received ${String(modulePath)}`, - }); + expect(() => fork(modulePath, { env: bunEnv })).toThrow( + expect.objectContaining({ + code: "ERR_INVALID_ARG_TYPE", + name: "TypeError", + message: expect.stringContaining( + `The "modulePath" argument must be of type string, Buffer or URL. Received: `, + ), + }), + ); }); }); // This test fails due to a DataCloneError or due to "Unable to deserialize data." @@ -710,11 +714,13 @@ describe("fork", () => { it(`Ensure that the third argument should be type of object if provided :: ${String(arg)}`, () => { expect(() => { fork(fixtures.path("child-process-echo-options.js"), [], arg); - }).toThrow({ - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: `The "options" argument must be of type object. Received ${String(arg)}`, - }); + }).toThrow( + expect.objectContaining({ + code: "ERR_INVALID_ARG_TYPE", + name: "TypeError", + message: expect.stringContaining(`The "options" argument must be of type object. Received: `), + }), + ); }); }); }); diff --git a/test/js/node/child_process/child_process.test.ts b/test/js/node/child_process/child_process.test.ts index 540ab4d688..448f030ec7 100644 --- a/test/js/node/child_process/child_process.test.ts +++ b/test/js/node/child_process/child_process.test.ts @@ -69,7 +69,7 @@ describe("spawn()", () => { it("should disallow invalid filename", () => { // @ts-ignore expect(() => spawn(123)).toThrow({ - message: 'The "file" argument must be of type string. Received 123', + message: 'The "file" argument must be of type string. Received: 123', code: "ERR_INVALID_ARG_TYPE", }); }); diff --git a/test/js/node/readline/readline.node.test.ts b/test/js/node/readline/readline.node.test.ts index d955285440..caa38dcfa5 100644 --- a/test/js/node/readline/readline.node.test.ts +++ b/test/js/node/readline/readline.node.test.ts @@ -90,9 +90,13 @@ describe("readline.clearScreenDown()", () => { it("should throw on invalid callback", () => { // Verify that clearScreenDown() throws on invalid callback. - assert.throws(() => { + expect(() => { readline.clearScreenDown(writable, null); - }, /ERR_INVALID_ARG_TYPE/); + }).toThrowError( + expect.objectContaining({ + code: "ERR_INVALID_ARG_TYPE", + }), + ); }); it("should that clearScreenDown() does not throw on null or undefined stream", done => { @@ -138,9 +142,13 @@ describe("readline.clearLine()", () => { it("should throw on an invalid callback", () => { // Verify that clearLine() throws on invalid callback. - assert.throws(() => { + expect(() => { readline.clearLine(writable, 0, null); - }, /ERR_INVALID_ARG_TYPE/); + }).toThrowError( + expect.objectContaining({ + code: "ERR_INVALID_ARG_TYPE", + }), + ); }); it("should not throw on null or undefined stream", done => { @@ -188,9 +196,13 @@ describe("readline.moveCursor()", () => { it("should throw on invalid callback", () => { // Verify that moveCursor() throws on invalid callback. - assert.throws(() => { + expect(() => { readline.moveCursor(writable, 1, 1, null); - }, /ERR_INVALID_ARG_TYPE/); + }).toThrowError( + expect.objectContaining({ + code: "ERR_INVALID_ARG_TYPE", + }), + ); }); it("should not throw on null or undefined stream", done => { @@ -281,9 +293,13 @@ describe("readline.cursorTo()", () => { it("should throw on invalid callback", () => { // Verify that cursorTo() throws on invalid callback. - assert.throws(() => { + expect(() => { readline.cursorTo(writable, 1, 1, null); - }, /ERR_INVALID_ARG_TYPE/); + }).toThrowError( + expect.objectContaining({ + code: "ERR_INVALID_ARG_TYPE", + }), + ); }); it("should throw if x or y is NaN", () => {