Compare commits

...

11 Commits

Author SHA1 Message Date
Meghan Denny
70ce4d2006 fix 2025-02-04 16:44:31 -08:00
Meghan Denny
b2b245832a oops shadowing 2025-02-04 16:43:41 -08:00
Meghan Denny
b771fe2414 Merge remote-tracking branch 'origin/main' into nektro-patch-63201 2025-02-03 22:57:35 -08:00
Meghan Denny
c67766797f fix test-util-parse-env.js 2025-02-03 21:58:59 -08:00
Meghan Denny
bfdc0b78c7 more message assertions 2025-02-03 18:27:58 -08:00
Meghan Denny
83656c15b3 more test-util-styletext.js cases 2025-02-03 18:27:11 -08:00
Meghan Denny
dff1d3d73a fix ERR_INVALID_ARG_TYPE message once and for all 2025-01-31 21:49:47 -08:00
Meghan Denny
c6803990cd fix ERR_INVALID_ARG_TYPE messge regressions 2025-01-31 20:06:03 -08:00
Meghan Denny
e4adeb1939 add test-util-text-decoder.js 2025-01-31 19:43:24 -08:00
Meghan Denny
3b88c66a55 clean test-util-types.js 2025-01-31 19:10:20 -08:00
Meghan Denny
45bc34edcd more node:util fixes 2025-01-31 18:56:50 -08:00
26 changed files with 756 additions and 170 deletions

View File

@@ -16,6 +16,7 @@
#include "JavaScriptCore/JSType.h"
#include "JavaScriptCore/Symbol.h"
#include "wtf/Assertions.h"
#include "wtf/Vector.h"
#include "wtf/text/ASCIIFastPath.h"
#include "wtf/text/ASCIILiteral.h"
#include "wtf/text/MakeString.h"
@@ -350,12 +351,60 @@ extern "C" BunString Bun__ErrorCode__determineSpecificType(JSC::JSGlobalObject*
namespace Message {
void addList(WTF::StringBuilder& result, WTF::Vector<WTF::String>& types)
{
switch (types.size()) {
case 0:
return;
case 1:
result.append(types.at(0));
return;
case 2:
result.append(types.at(0));
result.append(" or "_s);
result.append(types.at(1));
return;
case 3:
result.append(types.at(0));
result.append(", "_s);
result.append(types.at(1));
result.append(", or "_s);
result.append(types.at(2));
return;
default: {
for (unsigned i = 0; i < types.size() - 1; i++) {
result.append(types.at(i));
result.append(", "_s);
}
result.append("or "_s);
result.append(types.at(types.size() - 1));
return;
}
}
}
WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, const StringView& arg_name, const StringView& expected_type, JSValue actual_value)
{
auto actual_value_string = determineSpecificType(globalObject, actual_value);
RETURN_IF_EXCEPTION(scope, {});
return makeString("The \""_s, arg_name, "\" argument must be of type "_s, expected_type, ". Received "_s, actual_value_string);
auto ty_first_char = expected_type[0];
WTF::StringBuilder result;
result.append("The "_s);
if (arg_name.endsWith(" argument"_s)) {
result.append(arg_name);
} else {
result.append("\""_s);
result.append(arg_name);
result.append("\" "_s);
result.append(arg_name.contains('.') ? "property"_s : "argument"_s);
}
result.append(" must be "_s);
result.append(ty_first_char >= 'A' && ty_first_char <= 'Z' ? "an instance of "_s : "of type "_s);
result.append(expected_type);
result.append(". Received "_s, actual_value_string);
return result.toString();
}
WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, const StringView& arg_name, ArgList expected_types, JSValue actual_value)
@@ -372,25 +421,32 @@ WTF::String ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* gl
} else {
result.append("\""_s);
result.append(arg_name);
result.append("\" argument"_s);
result.append("\" "_s);
result.append(arg_name.contains('.') ? "property"_s : "argument"_s);
}
result.append(" must be of type "_s);
result.append(" must be "_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++) {
JSValue expected_type = expected_types.at(i);
result.append(expected_type.toWTFString(globalObject));
result.append(", "_s);
}
result.append("or "_s);
result.append(expected_types.at(length - 1).toWTFString(globalObject));
WTF::Vector<WTF::String> types;
WTF::Vector<WTF::String> instances;
for (unsigned i = 0; i < expected_types.size(); i++) {
auto ty_str = expected_types.at(i).toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
auto ty_first_char = ty_str[0];
if (ty_first_char >= 'A' && ty_first_char <= 'Z')
instances.append(ty_str);
else
types.append(ty_str);
}
if (types.size() > 0) {
result.append(types.size() > 1 ? "one of type "_s : "of type "_s);
addList(result, types);
if (instances.size() > 0) result.append(" or "_s);
}
if (instances.size() > 0) {
result.append("an instance of "_s);
addList(result, instances);
}
result.append(". Received "_s, actual_value_string);
@@ -440,14 +496,8 @@ namespace ERR {
JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& arg_name, const WTF::String& expected_type, JSC::JSValue val_actual_value)
{
auto arg_kind = arg_name.contains('.') ? "property"_s : "argument"_s;
auto ty_first_char = expected_type[0];
auto ty_kind = ty_first_char >= 'A' && ty_first_char <= 'Z' ? "an instance of"_s : "of type"_s;
auto actual_value = determineSpecificType(globalObject, val_actual_value);
auto message = Message::ERR_INVALID_ARG_TYPE(throwScope, globalObject, arg_name, expected_type, val_actual_value);
RETURN_IF_EXCEPTION(throwScope, {});
auto message = makeString("The \""_s, arg_name, "\" "_s, arg_kind, " must be "_s, ty_kind, " "_s, expected_type, ". Received "_s, actual_value);
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message));
return {};
}
@@ -455,15 +505,9 @@ JSC::EncodedJSValue INVALID_ARG_TYPE(JSC::ThrowScope& throwScope, JSC::JSGlobalO
{
auto arg_name = val_arg_name.toWTFString(globalObject);
RETURN_IF_EXCEPTION(throwScope, {});
auto arg_kind = arg_name.contains('.') ? "property"_s : "argument"_s;
auto ty_first_char = expected_type[0];
auto ty_kind = ty_first_char >= 'A' && ty_first_char <= 'Z' ? "an instance of"_s : "of type"_s;
auto actual_value = determineSpecificType(globalObject, val_actual_value);
auto message = Message::ERR_INVALID_ARG_TYPE(throwScope, globalObject, arg_name, expected_type, val_actual_value);
RETURN_IF_EXCEPTION(throwScope, {});
auto message = makeString("The \""_s, arg_name, "\" "_s, arg_kind, " must be "_s, ty_kind, " "_s, expected_type, ". Received "_s, actual_value);
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_ARG_TYPE, message));
return {};
}

View File

@@ -7,6 +7,8 @@ const string = bun.string;
const Output = bun.Output;
const ZigString = JSC.ZigString;
const uv = bun.windows.libuv;
const validators = @import("./util/validators.zig");
const envloader = @import("./../../env_loader.zig");
pub fn internalErrorName(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue {
const arguments = callframe.arguments_old(1).slice();
@@ -212,3 +214,24 @@ pub fn normalizeEncoding(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFr
if (str.inMapCaseInsensitive(JSC.Node.Encoding.map)) |enc| return enc.toJS(globalThis);
return JSC.JSValue.jsUndefined();
}
pub fn parseEnv(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue {
const content = callframe.argument(0);
try validators.validateString(globalThis, content, "content", .{});
var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
defer arena.deinit();
const allocator = arena.allocator();
const str = content.asString().toSlice(globalThis, allocator);
var map = envloader.Map.init(allocator);
var p = envloader.Loader.init(&map, allocator);
p.loadFromString(str.slice(), true, false);
var obj = JSC.JSValue.createEmptyObject(globalThis, map.map.count());
for (map.map.keys(), map.map.values()) |k, v| {
obj.put(globalThis, JSC.ZigString.initUTF8(k), bun.String.createUTF8ForJS(globalThis, v.value));
}
return obj;
}

View File

@@ -538,9 +538,9 @@ pub const Loader = struct {
}
// mostly for tests
pub fn loadFromString(this: *Loader, str: string, comptime overwrite: bool) void {
pub fn loadFromString(this: *Loader, str: string, comptime overwrite: bool, comptime expand: bool) void {
var source = logger.Source.initPathString("test", str);
Parser.parse(&source, this.allocator, this.map, overwrite, false);
Parser.parse(&source, this.allocator, this.map, overwrite, false, expand);
std.mem.doNotOptimizeAway(&source);
}
@@ -803,6 +803,7 @@ pub const Loader = struct {
this.map,
override,
false,
true,
);
@field(this, base) = source;
@@ -873,6 +874,7 @@ pub const Loader = struct {
this.map,
override,
false,
true,
);
try this.custom_files_loaded.put(file_path, source);
@@ -1097,6 +1099,7 @@ const Parser = struct {
map: *Map,
comptime override: bool,
comptime is_process: bool,
comptime expand: bool,
) void {
var count = map.map.count();
while (this.pos < this.src.len) {
@@ -1120,7 +1123,7 @@ const Parser = struct {
.conditional = false,
};
}
if (comptime !is_process) {
if (comptime !is_process and expand) {
var it = map.iterator();
while (it.next()) |entry| {
if (count > 0) {
@@ -1142,9 +1145,10 @@ const Parser = struct {
map: *Map,
comptime override: bool,
comptime is_process: bool,
comptime expand: bool,
) void {
var parser = Parser{ .src = source.contents };
parser._parse(allocator, map, override, is_process);
parser._parse(allocator, map, override, is_process, expand);
}
};

View File

@@ -1,3 +1,7 @@
const { validateFunction } = require("internal/validators");
const ArrayPrototypePush = Array.prototype.push;
const kCustomPromisifiedSymbol = Symbol.for("nodejs.util.promisify.custom");
const kCustomPromisifyArgsSymbol = Symbol("customPromisifyArgs");
@@ -18,55 +22,74 @@ function defineCustomPromisifyArgs(target, args) {
return args;
}
var promisify = function promisify(original) {
if (typeof original !== "function") throw new TypeError('The "original" argument must be of type Function');
const custom = original[kCustomPromisifiedSymbol];
if (custom) {
if (typeof custom !== "function") {
throw new TypeError('The "util.promisify.custom" argument must be of type Function');
}
// ensure that we don't create another promisified function wrapper
return defineCustomPromisify(custom, custom);
function promisify(original) {
validateFunction(original, "original");
if (original[kCustomPromisifiedSymbol]) {
const fn = original[kCustomPromisifiedSymbol];
validateFunction(fn, "util.promisify.custom");
Object.defineProperty(fn, kCustomPromisifiedSymbol, {
__proto__: null,
value: fn,
enumerable: false,
writable: false,
configurable: true,
});
Object.defineProperty(fn, "name", { value: original.name });
return fn;
}
const callbackArgs = original[kCustomPromisifyArgsSymbol];
function fn(...originalArgs) {
const { promise, resolve, reject } = Promise.withResolvers();
try {
original.$apply(this, [
...originalArgs,
function (err, ...values) {
if (err) {
return reject(err);
}
// Names to create an object from in case the callback receives multiple
// arguments, e.g. ['bytesRead', 'buffer'] for fs.read.
const argumentNames = original[kCustomPromisifyArgsSymbol];
if (callbackArgs !== undefined) {
// if (!Array.isArray(callbackArgs)) {
// throw new TypeError('The "customPromisifyArgs" argument must be of type Array');
// }
// if (callbackArgs.length !== values.length) {
// throw new Error("Mismatched length in promisify callback args");
// }
const result = {};
for (let i = 0; i < callbackArgs.length; i++) {
result[callbackArgs[i]] = values[i];
}
resolve(result);
} else {
resolve(values[0]);
}
},
]);
} catch (err) {
reject(err);
}
return promise;
function fn(...args) {
return new Promise((resolve, reject) => {
ArrayPrototypePush.$call(args, (err, ...values) => {
if (err) {
return reject(err);
}
if (argumentNames !== undefined && values.length > 1) {
const obj = {};
for (let i = 0; i < argumentNames.length; i++) obj[argumentNames[i]] = values[i];
resolve(obj);
} else {
resolve(values[0]);
}
});
if ($isPromise(original.$apply(this, args))) {
process.emitWarning(
"Calling promisify on a function that returns a Promise is likely a mistake.",
"DeprecationWarning",
"DEP0174",
);
}
});
}
Object.setPrototypeOf(fn, Object.getPrototypeOf(original));
defineCustomPromisify(fn, fn);
return Object.defineProperties(fn, Object.getOwnPropertyDescriptors(original));
};
Object.defineProperty(fn, kCustomPromisifiedSymbol, {
__proto__: null,
value: fn,
enumerable: false,
writable: false,
configurable: true,
});
const descriptors = Object.getOwnPropertyDescriptors(original);
const propertiesValues = Object.values(descriptors);
for (let i = 0; i < propertiesValues.length; i++) {
// We want to use null-prototype objects to not rely on globally mutable
// %Object.prototype%.
Object.setPrototypeOf(propertiesValues[i], null);
}
Object.defineProperties(fn, descriptors);
Object.defineProperty(fn, "name", { value: original.name });
return fn;
}
promisify.custom = kCustomPromisifiedSymbol;
// Lazily load node:timers/promises promisified functions onto the global timers.

View File

@@ -71,7 +71,9 @@ Stream.pipeline = pipeline;
const { addAbortSignal } = require("internal/streams/add-abort-signal");
Stream.addAbortSignal = addAbortSignal;
Stream.finished = eos;
Object.defineProperty(Stream.finished, "name", { value: "finished" });
Stream.destroy = destroyer;
Object.defineProperty(Stream.destroy, "name", { value: "destroy" });
Stream.compose = compose;
Stream.setDefaultHighWaterMark = setDefaultHighWaterMark;
Stream.getDefaultHighWaterMark = getDefaultHighWaterMark;

View File

@@ -53,12 +53,11 @@ function warnOnDeactivatedColors(env) {
}
}
function getColorDepth(env: NodeJS.ProcessEnv) {
const FORCE_COLOR = env.FORCE_COLOR;
function getColorDepth(env: NodeJS.ProcessEnv = process.env) {
// Use level 0-3 to support the same levels as `chalk` does. This is done for
// consistency throughout the ecosystem.
if (FORCE_COLOR !== undefined) {
switch (FORCE_COLOR) {
if (env.FORCE_COLOR !== undefined) {
switch (env.FORCE_COLOR) {
case "":
case "1":
case "true":
@@ -121,14 +120,13 @@ function getColorDepth(env: NodeJS.ProcessEnv) {
return COLORS_2;
}
const TEAMCITY_VERSION = env.TEAMCITY_VERSION;
if (TEAMCITY_VERSION) {
return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(TEAMCITY_VERSION) ? COLORS_16 : COLORS_2;
if ("TEAMCITY_VERSION" in env) {
return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.exec(env.TEAMCITY_VERSION) !== null ? COLORS_16 : COLORS_2;
}
switch (env.TERM_PROGRAM) {
case "iTerm.app":
if (!env.TERM_PROGRAM_VERSION || /^[0-2]\./.test(env.TERM_PROGRAM_VERSION)) {
if (!env.TERM_PROGRAM_VERSION || /^[0-2]\./.exec(env.TERM_PROGRAM_VERSION) !== null) {
return COLORS_256;
}
return COLORS_16m;
@@ -141,25 +139,21 @@ function getColorDepth(env: NodeJS.ProcessEnv) {
return COLORS_256;
}
const COLORTERM = env.COLORTERM;
if (COLORTERM === "truecolor" || COLORTERM === "24bit") {
if (env.COLORTERM === "truecolor" || env.COLORTERM === "24bit") {
return COLORS_16m;
}
const TERM = env.TERM;
if (TERM) {
if (/^xterm-256/.test(TERM) !== null) {
if (env.TERM) {
if (/^xterm-256/.exec(env.TERM) !== null) {
return COLORS_256;
}
const termEnv = TERM.toLowerCase();
const termEnv = env.TERM.toLowerCase();
if (TERM_ENVS[termEnv]) {
return TERM_ENVS[termEnv];
}
if (TERM_ENVS_REG_EXP.some(term => term.test(termEnv))) {
if (TERM_ENVS_REG_EXP.some(term => term.exec(termEnv) !== null)) {
return COLORS_16;
}
}

View File

@@ -1,5 +1,13 @@
const isInsideNodeModules: () => boolean = $newZigFunction("node_util_binding.zig", "isInsideNodeModules", 0);
const isInsideNodeModules = $newZigFunction("node_util_binding.zig", "isInsideNodeModules", 0);
function shouldColorize(stream) {
if (process.env.FORCE_COLOR !== undefined) {
return require("internal/tty").getColorDepth() > 2;
}
return stream?.isTTY && (typeof stream.getColorDepth === "function" ? stream.getColorDepth() > 2 : true);
}
export default {
isInsideNodeModules,
shouldColorize,
};

View File

@@ -8,6 +8,7 @@ const {
validateString,
validateBoolean,
validateNumber,
validateInt32,
} = require("internal/validators");
const errorCodes = {
@@ -346,19 +347,14 @@ function validateResolverOptions(options) {
return;
}
for (const key of ["timeout", "tries"]) {
if (key in options) {
if (typeof options[key] !== "number") {
throw $ERR_INVALID_ARG_TYPE(key, "number", options[key]);
}
}
{
const { timeout = -1 } = options;
validateInt32(timeout, "options.timeout", -1);
}
if ("timeout" in options) {
const timeout = options.timeout;
if ((timeout < 0 && timeout != -1) || Math.floor(timeout) != timeout || timeout >= 2 ** 31) {
throw $ERR_OUT_OF_RANGE("Invalid timeout", timeout);
}
{
const { tries = 4 } = { ...options };
validateInt32(tries, "options.tries", 1);
}
}

View File

@@ -3,9 +3,11 @@ const types = require("node:util/types");
/** @type {import('node-inspect-extracted')} */
const utl = require("internal/util/inspect");
const { promisify } = require("internal/promisify");
const { validateString, validateOneOf } = require("internal/validators");
const { validateString, validateOneOf, validateBoolean } = require("internal/validators");
const { isReadableStream, isWritableStream, isNodeStream } = require("internal/streams/utils");
const internalErrorName = $newZigFunction("node_util_binding.zig", "internalErrorName", 1);
const parseEnv = $newZigFunction("node_util_binding.zig", "parseEnv", 1);
const NumberIsSafeInteger = Number.isSafeInteger;
const ObjectKeys = Object.keys;
@@ -233,8 +235,15 @@ var toUSVString = input => {
return (input + "").toWellFormed();
};
function styleText(format, text) {
function styleText(format, text, { validateStream = true, stream = process.stdout } = {}) {
validateString(text, "text");
validateBoolean(validateStream, "options.validateStream");
if (validateStream) {
if (!isReadableStream(stream) && !isWritableStream(stream) && !isNodeStream(stream)) {
throw $ERR_INVALID_ARG_TYPE("stream", ["ReadableStream", "WritableStream", "Stream"], stream);
}
}
if ($isJSArray(format)) {
let left = "";
@@ -251,11 +260,15 @@ function styleText(format, text) {
return `${left}${text}${right}`;
}
let formatCodes = inspect.colors[format];
const formatCodes = inspect.colors[format];
if (formatCodes == null) {
validateOneOf(format, "format", ObjectKeys(inspect.colors));
}
if (validateStream && (!stream || !require("internal/util").shouldColorize(stream))) {
return text;
}
return `\u001b[${formatCodes[0]}m${text}\u001b[${formatCodes[1]}m`;
}
@@ -347,7 +360,7 @@ cjs_exports = {
// transferableAbortController,
aborted,
types,
// parseEnv,
parseEnv,
parseArgs,
TextDecoder,
TextEncoder,

View File

@@ -657,7 +657,7 @@ describe("fork", () => {
code: "ERR_INVALID_ARG_TYPE",
name: "TypeError",
message: expect.stringContaining(
`The "modulePath" argument must be of type string, Buffer, or URL. Received `,
`The "modulePath" argument must be of type string or an instance of Buffer or URL. Received `,
),
}),
);

View File

@@ -38,6 +38,7 @@ RETAIN_INNER_QUOTES={"foo": "bar"}
RETAIN_INNER_QUOTES_AS_STRING='{"foo": "bar"}'
RETAIN_INNER_QUOTES_AS_BACKTICKS=`{"foo": "bar's"}`
TRIM_SPACE_FROM_UNQUOTED= some spaced out string
SPACE_BEFORE_DOUBLE_QUOTES= "space before double quotes"
EMAIL=therealnerdybeast@example.tld
SPACED_KEY = parsed
EDGE_CASE_INLINE_COMMENTS="VALUE1" # or "VALUE2" or "VALUE3"

View File

@@ -35,7 +35,8 @@ function checkArgs(connected) {
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "buffer" argument must be of type string, Buffer, TypedArray, or DataView. Received undefined'
message: 'The "buffer" argument must be of type string or an instance ' +
'of Buffer, TypedArray, or DataView. Received undefined'
}
);
@@ -129,7 +130,8 @@ function checkArgs(connected) {
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "buffer" argument must be of type string, Buffer, TypedArray, or DataView. Received type number (23)'
message: 'The "buffer" argument must be of type string or an instance ' +
'of Buffer, TypedArray, or DataView. Received type number (23)'
}
);
@@ -139,7 +141,8 @@ function checkArgs(connected) {
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "buffer list arguments" argument must be of type string, Buffer, TypedArray, or DataView. ' +
message: 'The "buffer list arguments" argument must be of type string ' +
'or an instance of Buffer, TypedArray, or DataView. ' +
'Received an instance of Array'
}
);

View File

@@ -17,13 +17,23 @@ for (const ctor of [dns.Resolver, dns.promises.Resolver]) {
assert.throws(() => new ctor({ timeout }), {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: `The "options.timeout" property must be of type number.` + common.invalidArgTypeHelper(timeout),
});
}
for (const timeout of [-2, 4.2, 2 ** 31]) {
for (const timeout of [4.2]) {
assert.throws(() => new ctor({ timeout }), {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: `The value of "options.timeout" is out of range. It must be an integer. Received ${timeout}`,
});
}
for (const timeout of [-2, 2 ** 31]) {
assert.throws(() => new ctor({ timeout }), {
code: 'ERR_OUT_OF_RANGE',
name: 'RangeError',
message: `The value of "options.timeout" is out of range. It must be >= -1 and <= 2147483647. Received ${timeout}`,
});
}

View File

@@ -14,6 +14,6 @@ assert.throws(
() => recurse(),
{
name: 'RangeError',
// message: 'Maximum call stack size exceeded'
message: 'Maximum call stack size exceeded.'
}
);

View File

@@ -225,6 +225,7 @@ const values = [
const errLines = stderr.trim().split(/[\r\n]+/);
const errLine = errLines.find((l) => /^error/.exec(l));
assert.strictEqual(errLine, `error: ${fixture}`);
assert.strictEqual(errLines.length, 10);
})
);
}
@@ -274,6 +275,8 @@ const values = [
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The last argument must be of type function.' +
common.invalidArgTypeHelper(value)
});
});
}
@@ -287,6 +290,7 @@ const values = [
const cbPromiseFn = callbackify(promiseFn);
cbPromiseFn(null, (err) => {
assert.strictEqual(err.message, 'Promise was rejected with a falsy value');
assert.strictEqual(err.code, 'ERR_FALSY_VALUE_REJECTION');
assert.strictEqual(err.reason, null);
// skipped, bun doesn't hide callbackifyOnRejected from the stack trace

View File

@@ -1,19 +0,0 @@
'use strict';
// emitExperimentalWarning is a node internal not used by bun, so this test is skipped
// // Flags: --expose-internals
// const common = require('../common');
// const assert = require('assert');
// const { emitExperimentalWarning } = require('internal/util');
// This test ensures that the emitExperimentalWarning in internal/util emits a
// warning when passed an unsupported feature and that it simply returns
// when passed the same feature multiple times.
// process.on('warning', common.mustCall((warning) => {
// assert.match(warning.message, /is an experimental feature/);
// }, 2));
// emitExperimentalWarning('feature1');
// emitExperimentalWarning('feature1'); // should not warn
// emitExperimentalWarning('feature2');

View File

@@ -88,6 +88,8 @@ assert.throws(() => {
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "superCtor.prototype" property must be of type object. ' +
'Received undefined'
});
assert.throws(() => {
@@ -95,6 +97,8 @@ assert.throws(() => {
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "superCtor" argument must be of type function. ' +
'Received null'
});
assert.throws(() => {
@@ -102,4 +106,5 @@ assert.throws(() => {
}, {
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "ctor" argument must be of type function. Received null'
});

View File

@@ -0,0 +1,70 @@
'use strict';
require('../common');
const fixtures = require('../../test/common/fixtures');
const assert = require('node:assert');
const util = require('node:util');
const fs = require('node:fs');
{
const validEnvFilePath = fixtures.path('dotenv/valid.env');
const validContent = fs.readFileSync(validEnvFilePath, 'utf8');
assert.deepStrictEqual(util.parseEnv(validContent), {
AFTER_LINE: 'after_line',
BACKTICKS: 'backticks',
BACKTICKS_INSIDE_DOUBLE: '`backticks` work inside double quotes',
BACKTICKS_INSIDE_SINGLE: '`backticks` work inside single quotes',
BACKTICKS_SPACED: ' backticks ',
BASIC: 'basic',
DONT_EXPAND_SQUOTED: 'dontexpand\\nnewlines',
DONT_EXPAND_UNQUOTED: 'dontexpand\\nnewlines',
DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS: "double \"quotes\" and single 'quotes' work inside backticks",
DOUBLE_QUOTES: 'double_quotes',
DOUBLE_QUOTES_INSIDE_BACKTICKS: 'double "quotes" work inside backticks',
DOUBLE_QUOTES_INSIDE_SINGLE: 'double "quotes" work inside single quotes',
DOUBLE_QUOTES_SPACED: ' double quotes ',
DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET: '{ port: $MONGOLAB_PORT}',
EDGE_CASE_INLINE_COMMENTS: 'VALUE1',
EMAIL: 'therealnerdybeast@example.tld',
EMPTY: '',
EMPTY_BACKTICKS: '',
EMPTY_DOUBLE_QUOTES: '',
EMPTY_SINGLE_QUOTES: '',
EQUAL_SIGNS: 'equals==',
EXPORT_EXAMPLE: 'ignore export',
EXPAND_NEWLINES: 'expand\nnew\nlines',
INLINE_COMMENTS: 'inline comments',
INLINE_COMMENTS_BACKTICKS: 'inline comments outside of #backticks',
INLINE_COMMENTS_DOUBLE_QUOTES: 'inline comments outside of #doublequotes',
INLINE_COMMENTS_SINGLE_QUOTES: 'inline comments outside of #singlequotes',
INLINE_COMMENTS_SPACE: 'inline comments start with a',
MULTI_BACKTICKED: 'THIS\nIS\nA\n"MULTILINE\'S"\nSTRING',
MULTI_DOUBLE_QUOTED: 'THIS\nIS\nA\nMULTILINE\nSTRING',
MULTI_NOT_VALID: 'THIS',
MULTI_NOT_VALID_QUOTE: '"',
MULTI_SINGLE_QUOTED: 'THIS\nIS\nA\nMULTILINE\nSTRING',
RETAIN_INNER_QUOTES: '{"foo": "bar"}',
RETAIN_INNER_QUOTES_AS_BACKTICKS: '{"foo": "bar\'s"}',
RETAIN_INNER_QUOTES_AS_STRING: '{"foo": "bar"}',
SINGLE_QUOTES: 'single_quotes',
SINGLE_QUOTES_INSIDE_BACKTICKS: "single 'quotes' work inside backticks",
SINGLE_QUOTES_INSIDE_DOUBLE: "single 'quotes' work inside double quotes",
SINGLE_QUOTES_SPACED: ' single quotes ',
SPACED_KEY: 'parsed',
SPACE_BEFORE_DOUBLE_QUOTES: 'space before double quotes',
TRIM_SPACE_FROM_UNQUOTED: 'some spaced out string',
});
}
assert.deepStrictEqual(util.parseEnv(''), {});
assert.deepStrictEqual(util.parseEnv('FOO=bar\nFOO=baz\n'), { FOO: 'baz' });
// Test for invalid input.
assert.throws(() => {
for (const value of [null, undefined, {}, []]) {
util.parseEnv(value);
}
}, {
code: 'ERR_INVALID_ARG_TYPE',
});

View File

@@ -0,0 +1,40 @@
import '../common/index.mjs';
import assert from 'node:assert';
import { promisify } from 'node:util';
// Test that customly promisified methods in [util.promisify.custom]
// have appropriate names
import fs from 'node:fs';
import readline from 'node:readline';
import stream from 'node:stream';
import timers from 'node:timers';
assert.strictEqual(
promisify(fs.exists).name,
'exists'
);
assert.strictEqual(
promisify(readline.Interface.prototype.question).name,
'question',
);
assert.strictEqual(
promisify(stream.finished).name,
'finished'
);
assert.strictEqual(
promisify(stream.pipeline).name,
'pipeline'
);
assert.strictEqual(
promisify(timers.setImmediate).name,
'setImmediate'
);
assert.strictEqual(
promisify(timers.setTimeout).name,
'setTimeout'
);

View File

@@ -0,0 +1,215 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const vm = require('vm');
const { promisify } = require('util');
{
const warningHandler = common.mustNotCall();
process.on('warning', warningHandler);
function foo() {}
foo.constructor = (async () => {}).constructor;
promisify(foo);
process.off('warning', warningHandler);
}
common.expectWarning(
'DeprecationWarning',
'Calling promisify on a function that returns a Promise is likely a mistake.',
'DEP0174');
promisify(async (callback) => { callback(); })().then(common.mustCall(() => {
// We must add the second `expectWarning` call in the `.then` handler, when
// the first warning has already been triggered.
common.expectWarning(
'DeprecationWarning',
'Calling promisify on a function that returns a Promise is likely a mistake.',
'DEP0174');
promisify(async () => {})().then(common.mustNotCall());
}));
const stat = promisify(fs.stat);
{
const promise = stat(__filename);
assert(promise instanceof Promise);
promise.then(common.mustCall((value) => {
assert.deepStrictEqual(value, fs.statSync(__filename));
}));
}
{
const promise = stat('/dontexist');
promise.catch(common.mustCall((error) => {
assert(error.message.includes('ENOENT: no such file or directory, stat'));
}));
}
{
function fn() {}
function promisifedFn() {}
fn[promisify.custom] = promisifedFn;
assert.strictEqual(promisify(fn), promisifedFn);
assert.strictEqual(promisify(promisify(fn)), promisifedFn);
}
{
function fn() {}
function promisifiedFn() {}
// util.promisify.custom is a shared symbol which can be accessed
// as `Symbol.for("nodejs.util.promisify.custom")`.
const kCustomPromisifiedSymbol = Symbol.for('nodejs.util.promisify.custom');
fn[kCustomPromisifiedSymbol] = promisifiedFn;
assert.strictEqual(kCustomPromisifiedSymbol, promisify.custom);
assert.strictEqual(promisify(fn), promisifiedFn);
assert.strictEqual(promisify(promisify(fn)), promisifiedFn);
}
{
function fn() {}
fn[promisify.custom] = 42;
assert.throws(
() => promisify(fn),
{ code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }
);
}
{
const fn = vm.runInNewContext('(function() {})');
assert.notStrictEqual(Object.getPrototypeOf(promisify(fn)),
Function.prototype);
}
{
function fn(callback) {
callback(null, 'foo', 'bar');
}
promisify(fn)().then(common.mustCall((value) => {
assert.strictEqual(value, 'foo');
}));
}
{
function fn(callback) {
callback(null);
}
promisify(fn)().then(common.mustCall((value) => {
assert.strictEqual(value, undefined);
}));
}
{
function fn(callback) {
callback();
}
promisify(fn)().then(common.mustCall((value) => {
assert.strictEqual(value, undefined);
}));
}
{
function fn(err, val, callback) {
callback(err, val);
}
promisify(fn)(null, 42).then(common.mustCall((value) => {
assert.strictEqual(value, 42);
}));
}
{
function fn(err, val, callback) {
callback(err, val);
}
promisify(fn)(new Error('oops'), null).catch(common.mustCall((err) => {
assert.strictEqual(err.message, 'oops');
}));
}
{
function fn(err, val, callback) {
callback(err, val);
}
(async () => {
const value = await promisify(fn)(null, 42);
assert.strictEqual(value, 42);
})().then(common.mustCall());
}
{
const o = {};
const fn = promisify(function(cb) {
cb(null, this === o);
});
o.fn = fn;
o.fn().then(common.mustCall((val) => assert(val)));
}
{
const err = new Error('Should not have called the callback with the error.');
const stack = err.stack;
const fn = promisify(function(cb) {
cb(null);
cb(err);
});
(async () => {
await fn();
await Promise.resolve();
return assert.strictEqual(stack, err.stack);
})().then(common.mustCall());
}
{
function c() { }
const a = promisify(function() { });
const b = promisify(a);
assert.notStrictEqual(c, a);
assert.strictEqual(a, b);
}
{
let errToThrow;
const thrower = promisify(function(a, b, c, cb) {
errToThrow = new Error();
throw errToThrow;
});
thrower(1, 2, 3)
.then(assert.fail)
.then(assert.fail, (e) => assert.strictEqual(e, errToThrow));
}
{
const err = new Error();
const a = promisify((cb) => cb(err))();
const b = promisify(() => { throw err; })();
Promise.all([
a.then(assert.fail, function(e) {
assert.strictEqual(err, e);
}),
b.then(assert.fail, function(e) {
assert.strictEqual(err, e);
}),
]);
}
[undefined, null, true, 0, 'str', {}, [], Symbol()].forEach((input) => {
assert.throws(
() => promisify(input),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
message: 'The "original" argument must be of type function.' +
common.invalidArgTypeHelper(input)
});
});

View File

@@ -1,7 +1,12 @@
'use strict';
require('../common');
const assert = require('assert');
const util = require('util');
const common = require('../common');
const assert = require('node:assert');
const util = require('node:util');
const { WriteStream } = require('node:tty');
const styled = '\u001b[31mtest\u001b[39m';
const noChange = 'test';
[
undefined,
@@ -31,13 +36,69 @@ assert.throws(() => {
code: 'ERR_INVALID_ARG_VALUE',
});
assert.strictEqual(util.styleText('red', 'test'), '\u001b[31mtest\u001b[39m');
assert.strictEqual(
util.styleText('red', 'test', { validateStream: false }),
'\u001b[31mtest\u001b[39m',
);
assert.strictEqual(util.styleText(['bold', 'red'], 'test'), '\u001b[1m\u001b[31mtest\u001b[39m\u001b[22m');
assert.strictEqual(util.styleText(['bold', 'red'], 'test'), util.styleText('bold', util.styleText('red', 'test')));
assert.strictEqual(
util.styleText(['bold', 'red'], 'test', { validateStream: false }),
'\u001b[1m\u001b[31mtest\u001b[39m\u001b[22m',
);
assert.strictEqual(
util.styleText(['bold', 'red'], 'test', { validateStream: false }),
util.styleText(
'bold',
util.styleText('red', 'test', { validateStream: false }),
{ validateStream: false },
),
);
assert.throws(() => {
util.styleText(['invalid'], 'text');
}, {
code: 'ERR_INVALID_ARG_VALUE',
});
assert.throws(() => {
util.styleText('red', 'text', { stream: {} });
}, {
code: 'ERR_INVALID_ARG_TYPE',
});
// does not throw
util.styleText('red', 'text', { stream: {}, validateStream: false });
assert.strictEqual(
util.styleText('red', 'test', { validateStream: false }),
styled,
);
const fd = common.getTTYfd();
if (fd !== -1) {
const writeStream = new WriteStream(fd);
const originalEnv = process.env;
[
{ isTTY: true, env: {}, expected: styled },
{ isTTY: false, env: {}, expected: noChange },
{ isTTY: true, env: { NODE_DISABLE_COLORS: '1' }, expected: noChange },
{ isTTY: true, env: { NO_COLOR: '1' }, expected: noChange },
{ isTTY: true, env: { FORCE_COLOR: '1' }, expected: styled },
{ isTTY: true, env: { FORCE_COLOR: '1', NODE_DISABLE_COLORS: '1' }, expected: styled },
{ isTTY: false, env: { FORCE_COLOR: '1', NO_COLOR: '1', NODE_DISABLE_COLORS: '1' }, expected: styled },
{ isTTY: true, env: { FORCE_COLOR: '1', NO_COLOR: '1', NODE_DISABLE_COLORS: '1' }, expected: styled },
].forEach((testCase) => {
writeStream.isTTY = testCase.isTTY;
process.env = {
...process.env,
...testCase.env
};
const output = util.styleText('red', 'test', { stream: writeStream });
assert.strictEqual(output, testCase.expected);
process.env = originalEnv;
});
} else {
common.skip('Could not create TTY fd');
}

View File

@@ -0,0 +1,13 @@
'use strict';
require('../common');
const assert = require('node:assert');
const latin1Bytes = new Uint8Array([0xc1, 0xe9, 0xf3]);
const expectedString = 'Áéó';
const decoder = new TextDecoder('windows-1252');
const decodedString = decoder.decode(latin1Bytes);
assert.strictEqual(decodedString, expectedString);

View File

@@ -1,18 +1,10 @@
'use strict';
// JSStream is disabled because it is a node internal
// vm tests are skipped because node:vm Module is not yet implemented
// // Flags: --experimental-vm-modules --expose-internals
const common = require('../common');
const assert = require('assert');
const { types, inspect } = require('util');
const vm = require('vm');
// const { internalBinding } = require('internal/test/binding');
// const { JSStream } = internalBinding('js_stream');
// const external = (new JSStream())._externalStream;
for (const [ value, _method ] of [
// [ external, 'isExternal' ],
[ new Date() ],
[ (function() { return arguments; })(), 'isArgumentsObject' ],
[ new Boolean(), 'isBooleanObject' ],
@@ -277,14 +269,6 @@ for (const [ value, _method ] of [
}
}
// (skipped)
// (async () => {
// const m = new vm.SourceTextModule('');
// await m.link(() => 0);
// await m.evaluate();
// assert.ok(types.isModuleNamespaceObject(m.namespace));
// })().then(common.mustCall());
{
// eslint-disable-next-line node-core/crypto-check
if (common.hasCrypto) {

View File

@@ -20,7 +20,6 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
// Flags: --expose-internals
const common = require('../common');
const assert = require('assert');
const util = require('util');
@@ -39,6 +38,68 @@ assert.strictEqual(util.isArray(/regexp/), false);
assert.strictEqual(util.isArray(new Error()), false);
assert.strictEqual(util.isArray({ __proto__: Array.prototype }), false);
// isRegExp
assert.strictEqual(util.isRegExp(/regexp/), true);
assert.strictEqual(util.isRegExp(RegExp(), 'foo'), true);
assert.strictEqual(util.isRegExp(new RegExp()), true);
assert.strictEqual(util.isRegExp(context('RegExp')()), true);
assert.strictEqual(util.isRegExp({}), false);
assert.strictEqual(util.isRegExp([]), false);
assert.strictEqual(util.isRegExp(new Date()), false);
assert.strictEqual(util.isRegExp({ __proto__: RegExp.prototype }), false);
// isDate
assert.strictEqual(util.isDate(new Date()), true);
assert.strictEqual(util.isDate(new Date(0), 'foo'), true);
assert.strictEqual(util.isDate(new (context('Date'))()), true);
assert.strictEqual(util.isDate(Date()), false);
assert.strictEqual(util.isDate({}), false);
assert.strictEqual(util.isDate([]), false);
assert.strictEqual(util.isDate(new Error()), false);
assert.strictEqual(util.isDate({ __proto__: Date.prototype }), false);
// isError
assert.strictEqual(util.isError(new Error()), true);
assert.strictEqual(util.isError(new TypeError()), true);
assert.strictEqual(util.isError(new SyntaxError()), true);
assert.strictEqual(util.isError(new (context('Error'))()), true);
assert.strictEqual(util.isError(new (context('TypeError'))()), true);
assert.strictEqual(util.isError(new (context('SyntaxError'))()), true);
assert.strictEqual(util.isError({}), false);
assert.strictEqual(util.isError({ name: 'Error', message: '' }), false);
assert.strictEqual(util.isError([]), false);
assert.strictEqual(util.isError({ __proto__: Error.prototype }), true);
// isObject
assert.strictEqual(util.isObject({}), true);
assert.strictEqual(util.isObject([]), true);
assert.strictEqual(util.isObject(new Number(3)), true);
assert.strictEqual(util.isObject(Number(4)), false);
assert.strictEqual(util.isObject(1), false);
// isPrimitive
assert.strictEqual(util.isPrimitive({}), false);
assert.strictEqual(util.isPrimitive(new Error()), false);
assert.strictEqual(util.isPrimitive(new Date()), false);
assert.strictEqual(util.isPrimitive([]), false);
assert.strictEqual(util.isPrimitive(/regexp/), false);
assert.strictEqual(util.isPrimitive(function() {}), false);
assert.strictEqual(util.isPrimitive(new Number(1)), false);
assert.strictEqual(util.isPrimitive(new String('bla')), false);
assert.strictEqual(util.isPrimitive(new Boolean(true)), false);
assert.strictEqual(util.isPrimitive(1), true);
assert.strictEqual(util.isPrimitive('bla'), true);
assert.strictEqual(util.isPrimitive(true), true);
assert.strictEqual(util.isPrimitive(undefined), true);
assert.strictEqual(util.isPrimitive(null), true);
assert.strictEqual(util.isPrimitive(Infinity), true);
assert.strictEqual(util.isPrimitive(NaN), true);
assert.strictEqual(util.isPrimitive(Symbol('symbol')), true);
// isBuffer
assert.strictEqual(util.isBuffer('foo'), false);
assert.strictEqual(util.isBuffer(Buffer.from('foo')), true);
// _extend
assert.deepStrictEqual(util._extend({ a: 1 }), { a: 1 });
assert.deepStrictEqual(util._extend({ a: 1 }, []), { a: 1 });
@@ -48,6 +109,43 @@ assert.deepStrictEqual(util._extend({ a: 1 }, false), { a: 1 });
assert.deepStrictEqual(util._extend({ a: 1 }, { b: 2 }), { a: 1, b: 2 });
assert.deepStrictEqual(util._extend({ a: 1, b: 2 }, { b: 3 }), { a: 1, b: 3 });
// deprecated
assert.strictEqual(util.isBoolean(true), true);
assert.strictEqual(util.isBoolean(false), true);
assert.strictEqual(util.isBoolean('string'), false);
assert.strictEqual(util.isNull(null), true);
assert.strictEqual(util.isNull(undefined), false);
assert.strictEqual(util.isNull(), false);
assert.strictEqual(util.isNull('string'), false);
assert.strictEqual(util.isUndefined(undefined), true);
assert.strictEqual(util.isUndefined(), true);
assert.strictEqual(util.isUndefined(null), false);
assert.strictEqual(util.isUndefined('string'), false);
assert.strictEqual(util.isNullOrUndefined(null), true);
assert.strictEqual(util.isNullOrUndefined(undefined), true);
assert.strictEqual(util.isNullOrUndefined(), true);
assert.strictEqual(util.isNullOrUndefined('string'), false);
assert.strictEqual(util.isNumber(42), true);
assert.strictEqual(util.isNumber(), false);
assert.strictEqual(util.isNumber('string'), false);
assert.strictEqual(util.isString('string'), true);
assert.strictEqual(util.isString(), false);
assert.strictEqual(util.isString(42), false);
assert.strictEqual(util.isSymbol(Symbol()), true);
assert.strictEqual(util.isSymbol(), false);
assert.strictEqual(util.isSymbol('string'), false);
assert.strictEqual(util.isFunction(() => {}), true);
assert.strictEqual(util.isFunction(function() {}), true);
assert.strictEqual(util.isFunction(), false);
assert.strictEqual(util.isFunction('string'), false);
assert.strictEqual(util.toUSVString('string\ud801'), 'string\ufffd');
{
@@ -73,11 +171,6 @@ assert.strictEqual(util.toUSVString('string\ud801'), 'string\ufffd');
util.types.isNativeError({ __proto__: Error.prototype }),
false
);
// skipped because errors is from internal
// assert.strictEqual(
// util.types.isNativeError(new errors.codes.ERR_IPC_CHANNEL_CLOSED()),
// true
// );
}
assert.throws(() => {

View File

@@ -25,6 +25,7 @@ import fs from "node:fs";
// import vm from 'node:vm';
import assert from "assert";
import { promisify } from "util";
import { inspect } from "bun";
const stat = promisify(fs.stat);
@@ -294,7 +295,7 @@ describe("util.promisify", () => {
[undefined, null, true, 0, "str", {}, [], Symbol()].forEach(input => {
expect(() => {
promisify(input);
}).toThrow('The "original" argument must be of type Function');
}).toThrow(`The "original" argument must be of type function.${invalidArgTypeHelper(input)}`);
});
});
});

View File

@@ -375,8 +375,6 @@ describe("util", () => {
code: "ERR_INVALID_ARG_VALUE",
},
);
assert.strictEqual(util.styleText("red", "test"), "\u001b[31mtest\u001b[39m");
});
describe("getSystemErrorName", () => {