mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
Also add functions as allowed message input. This allows to have leavy message computation to become cheaper. PR-URL: https://github.com/nodejs/node/pull/58849 Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
184 lines
5.3 KiB
JavaScript
184 lines
5.3 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
Error,
|
|
ErrorCaptureStackTrace,
|
|
ErrorPrototypeToString,
|
|
StringPrototypeCharCodeAt,
|
|
StringPrototypeReplace,
|
|
} = primordials;
|
|
|
|
const {
|
|
codes: {
|
|
ERR_AMBIGUOUS_ARGUMENT,
|
|
ERR_INVALID_ARG_TYPE,
|
|
},
|
|
isErrorStackTraceLimitWritable,
|
|
} = require('internal/errors');
|
|
const AssertionError = require('internal/assert/assertion_error');
|
|
const { isError } = require('internal/util');
|
|
const { format } = require('internal/util/inspect');
|
|
|
|
const {
|
|
getErrorSourceExpression,
|
|
} = require('internal/errors/error_source');
|
|
|
|
// Escape control characters but not \n and \t to keep the line breaks and
|
|
// indentation intact.
|
|
// eslint-disable-next-line no-control-regex
|
|
const escapeSequencesRegExp = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
|
|
const meta = [
|
|
'\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004',
|
|
'\\u0005', '\\u0006', '\\u0007', '\\b', '',
|
|
'', '\\u000b', '\\f', '', '\\u000e',
|
|
'\\u000f', '\\u0010', '\\u0011', '\\u0012', '\\u0013',
|
|
'\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018',
|
|
'\\u0019', '\\u001a', '\\u001b', '\\u001c', '\\u001d',
|
|
'\\u001e', '\\u001f',
|
|
];
|
|
|
|
const escapeFn = (str) => meta[StringPrototypeCharCodeAt(str, 0)];
|
|
|
|
/**
|
|
* A function that derives the failure message from the actual and expected values.
|
|
* It is invoked only when the assertion fails.
|
|
*
|
|
* Other return values than a string are ignored.
|
|
* @callback MessageFactory
|
|
* @param {any} actual
|
|
* @param {any} expected
|
|
* @returns {string}
|
|
*/
|
|
|
|
/**
|
|
* Raw message input is always passed internally as a tuple array.
|
|
* Accepted shapes:
|
|
* - []
|
|
* - [string]
|
|
* - [string, ...any[]] (printf-like substitutions)
|
|
* - [Error]
|
|
* - [MessageFactory]
|
|
*
|
|
* Additional elements after [Error] or [MessageFactory] are rejected with ERR_AMBIGUOUS_ARGUMENT.
|
|
* A first element that is neither string, Error nor function is rejected with ERR_INVALID_ARG_TYPE.
|
|
* @typedef {[] | [string] | [string, ...any[]] | [Error] | [MessageFactory]} MessageTuple
|
|
*/
|
|
|
|
/**
|
|
* Options consumed by innerFail to construct and throw the AssertionError.
|
|
* @typedef {object} InnerFailOptions
|
|
* @property {any} actual Actual value
|
|
* @property {any} expected Expected value
|
|
* @property {MessageTuple} message Message
|
|
* @property {string} operator Operator
|
|
* @property {Function} stackStartFn Stack start function
|
|
* @property {'simple' | 'full'} [diff] Diff mode
|
|
* @property {boolean} [generatedMessage] Generated message
|
|
*/
|
|
|
|
function getErrMessage(fn) {
|
|
const tmpLimit = Error.stackTraceLimit;
|
|
const errorStackTraceLimitIsWritable = isErrorStackTraceLimitWritable();
|
|
// Make sure the limit is set to 1. Otherwise it could fail (<= 0) or it
|
|
// does to much work.
|
|
if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = 1;
|
|
// We only need the stack trace. To minimize the overhead use an object
|
|
// instead of an error.
|
|
const err = {};
|
|
ErrorCaptureStackTrace(err, fn);
|
|
if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = tmpLimit;
|
|
|
|
let source = getErrorSourceExpression(err);
|
|
if (source) {
|
|
source = StringPrototypeReplace(source, escapeSequencesRegExp, escapeFn);
|
|
return `The expression evaluated to a falsy value:\n\n ${source}\n`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {InnerFailOptions} obj
|
|
*/
|
|
function innerFail(obj) {
|
|
if (obj.message.length === 0) {
|
|
obj.message = undefined;
|
|
} else if (typeof obj.message[0] === 'string') {
|
|
if (obj.message.length > 1) {
|
|
obj.message = format(...obj.message);
|
|
} else {
|
|
obj.message = obj.message[0];
|
|
}
|
|
} else if (isError(obj.message[0])) {
|
|
if (obj.message.length > 1) {
|
|
throw new ERR_AMBIGUOUS_ARGUMENT(
|
|
'message',
|
|
`The error message was passed as error object "${ErrorPrototypeToString(obj.message[0])}" has trailing arguments that would be ignored.`,
|
|
);
|
|
}
|
|
throw obj.message[0];
|
|
} else if (typeof obj.message[0] === 'function') {
|
|
if (obj.message.length > 1) {
|
|
throw new ERR_AMBIGUOUS_ARGUMENT(
|
|
'message',
|
|
`The error message with function "${obj.message[0].name || 'anonymous'}" has trailing arguments that would be ignored.`,
|
|
);
|
|
}
|
|
try {
|
|
obj.message = obj.message[0](obj.actual, obj.expected);
|
|
if (typeof obj.message !== 'string') {
|
|
obj.message = undefined;
|
|
}
|
|
} catch {
|
|
// Ignore and use default message instead
|
|
obj.message = undefined;
|
|
}
|
|
} else {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'message',
|
|
['string', 'function'],
|
|
obj.message[0],
|
|
);
|
|
}
|
|
|
|
const error = new AssertionError(obj);
|
|
if (obj.generatedMessage !== undefined) {
|
|
error.generatedMessage = obj.generatedMessage;
|
|
}
|
|
throw error;
|
|
}
|
|
|
|
/**
|
|
* Internal ok handler delegating to innerFail for message handling.
|
|
* @param {Function} fn
|
|
* @param {...any} args
|
|
*/
|
|
function innerOk(fn, ...args) {
|
|
if (!args[0]) {
|
|
let generatedMessage = false;
|
|
let messageArgs;
|
|
|
|
if (args.length === 0) {
|
|
generatedMessage = true;
|
|
messageArgs = ['No value argument passed to `assert.ok()`'];
|
|
} else if (args.length === 1 || args[1] == null) {
|
|
generatedMessage = true;
|
|
messageArgs = [getErrMessage(fn)];
|
|
} else {
|
|
messageArgs = args.slice(1);
|
|
}
|
|
|
|
innerFail({
|
|
actual: args[0],
|
|
expected: true,
|
|
message: messageArgs,
|
|
operator: '==',
|
|
stackStartFn: fn,
|
|
generatedMessage,
|
|
});
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
innerOk,
|
|
innerFail,
|
|
};
|