Files
bun.sh/src/js/internal/assert/utils.ts
2025-05-19 12:18:21 -07:00

275 lines
10 KiB
TypeScript

/* prettier-ignore */
'use strict';
var AssertionError;
function loadAssertionError() {
if (AssertionError === undefined) {
AssertionError = require("internal/assert/assertion_error");
}
}
// const { Buffer } = require('node:buffer');
// const {
// isErrorStackTraceLimitWritable,
// overrideStackTrace,
// } = require('internal/errors');
// const { openSync, closeSync, readSync } = require('node:fs');
// // const { EOL } = require('internal/constants');
// // const { BuiltinModule } = require('internal/bootstrap/realm');
// // const { isError } = require('internal/util');
// const errorCache = new SafeMap();
// // const { fileURLToPath } = require('internal/url');
// let parseExpressionAt;
// let findNodeAround;
// let tokenizer;
// let decoder;
// // 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)];
// function findColumn(fd, column: number, code: string) {
// if (code.length > column + 100) {
// try {
// return parseCode(code, column);
// } catch {
// // End recursion in case no code could be parsed. The expression should
// // have been found after 2500 characters, so stop trying.
// if (code.length - column > 2500) {
// // eslint-disable-next-line no-throw-literal
// throw null;
// }
// }
// }
// // Read up to 2500 bytes more than necessary in columns. That way we address
// // multi byte characters and read enough data to parse the code.
// const bytesToRead = column - code.length + 2500;
// const buffer = Buffer.allocUnsafe(bytesToRead);
// const bytesRead = readSync(fd, buffer, 0, bytesToRead);
// code += decoder.write(buffer.slice(0, bytesRead));
// // EOF: fast path.
// if (bytesRead < bytesToRead) {
// return parseCode(code, column);
// }
// // Read potentially missing code.
// return findColumn(fd, column, code);
// }
// function getCode(fd, line: number, column: number) {
// let bytesRead = 0;
// if (line === 0) {
// // Special handle line number one. This is more efficient and simplifies the
// // rest of the algorithm. Read more than the regular column number in bytes
// // to prevent multiple reads in case multi byte characters are used.
// return findColumn(fd, column, '');
// }
// let lines = 0;
// // Prevent blocking the event loop by limiting the maximum amount of
// // data that may be read.
// let maxReads = 32; // bytesPerRead * maxReads = 512 KiB
// const bytesPerRead = 16384;
// // Use a single buffer up front that is reused until the call site is found.
// let buffer = Buffer.allocUnsafe(bytesPerRead);
// while (maxReads-- !== 0) {
// // Only allocate a new buffer in case the needed line is found. All data
// // before that can be discarded.
// buffer = lines < line ? buffer : Buffer.allocUnsafe(bytesPerRead);
// bytesRead = readSync(fd, buffer, 0, bytesPerRead);
// // Read the buffer until the required code line is found.
// for (let i = 0; i < bytesRead; i++) {
// if (buffer[i] === 10 && ++lines === line) {
// // If the end of file is reached, directly parse the code and return.
// if (bytesRead < bytesPerRead) {
// return parseCode(buffer.toString('utf8', i + 1, bytesRead), column);
// }
// // Check if the read code is sufficient or read more until the whole
// // expression is read. Make sure multi byte characters are preserved
// // properly by using the decoder.
// const code = decoder.write(buffer.slice(i + 1, bytesRead));
// return findColumn(fd, column, code);
// }
// }
// }
// }
// TODO: parse source to get assertion message
// function parseCode(code, offset) {
// // Lazy load acorn.
// if (parseExpressionAt === undefined) {
// const Parser = require('internal/deps/acorn/acorn/dist/acorn').Parser;
// ({ findNodeAround } = require('internal/deps/acorn/acorn-walk/dist/walk'));
// parseExpressionAt = FunctionPrototypeBind(Parser.parseExpressionAt, Parser);
// tokenizer = FunctionPrototypeBind(Parser.tokenizer, Parser);
// }
// let node;
// let start;
// // Parse the read code until the correct expression is found.
// for (const token of tokenizer(code, { ecmaVersion: 'latest' })) {
// start = token.start;
// if (start > offset) {
// // No matching expression found. This could happen if the assert
// // expression is bigger than the provided buffer.
// break;
// }
// try {
// node = parseExpressionAt(code, start, { ecmaVersion: 'latest' });
// // Find the CallExpression in the tree.
// node = findNodeAround(node, offset, 'CallExpression');
// if (node?.node.end >= offset) {
// return [
// node.node.start,
// StringPrototypeReplace(StringPrototypeSlice(code,
// node.node.start, node.node.end),
// escapeSequencesRegExp, escapeFn),
// ];
// }
// // eslint-disable-next-line no-unused-vars
// } catch (err) {
// continue;
// }
// }
// // eslint-disable-next-line no-throw-literal
// throw null;
// }
function getErrMessage(_message: string, _value: unknown, _fn: Function): string | undefined {
// 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;
// overrideStackTrace.set(err, (_, stack) => stack);
// const call = err.stack[0];
//
// if (fn.name === "ok") {
// return `The expression evaluated to a falsy value:\n\n assert.ok(${value})\n`;
// }
// let filename = call.getFileName();
// const line = call.getLineNumber() - 1;
// let column = call.getColumnNumber() - 1;
// let identifier;
// let code;
// if (filename) {
// identifier = `${filename}${line}${column}`;
// // Skip Node.js modules!
// if (StringPrototypeStartsWith(filename, 'node:') &&
// BuiltinModule.exists(StringPrototypeSlice(filename, 5))) {
// errorCache.set(identifier, undefined);
// return;
// }
// } else {
// return message;
// }
// if (errorCache.has(identifier)) {
// return errorCache.get(identifier);
// }
// let fd;
// try {
// // Set the stack trace limit to zero. This makes sure unexpected token
// // errors are handled faster.
// if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = 0;
// if (filename) {
// if (decoder === undefined) {
// const { StringDecoder } = require('string_decoder');
// decoder = new StringDecoder('utf8');
// }
// // ESM file prop is a file proto. Convert that to path.
// // This ensure opensync will not throw ENOENT for ESM files.
// const fileProtoPrefix = 'file://';
// if (StringPrototypeStartsWith(filename, fileProtoPrefix)) {
// filename = Bun.fileURLToPath(filename);
// }
// fd = openSync(filename, 'r', 0o666);
// // Reset column and message.
// ({ 0: column, 1: message } = getCode(fd, line, column));
// // Flush unfinished multi byte characters.
// decoder.end();
// } else {
// for (let i = 0; i < line; i++) {
// code = StringPrototypeSlice(code,
// StringPrototypeIndexOf(code, '\n') + 1);
// }
// // ({ 0: column, 1: message } = parseCode(code, column));
// throw new Error("todo: parseCode");
// }
// // Always normalize indentation, otherwise the message could look weird.
// if (StringPrototypeIncludes(message, '\n')) {
// if (process.platform === 'win32') {
// message = RegExpPrototypeSymbolReplace(/\r\n/g, message, '\n');
// }
// const frames = StringPrototypeSplit(message, '\n');
// message = ArrayPrototypeShift(frames);
// for (const frame of frames) {
// let pos = 0;
// while (pos < column && (frame[pos] === ' ' || frame[pos] === '\t')) {
// pos++;
// }
// message += `\n ${StringPrototypeSlice(frame, pos)}`;
// }
// }
// message = `The expression evaluated to a falsy value:\n\n ${message}\n`;
// // Make sure to always set the cache! No matter if the message is
// // undefined or not
// errorCache.set(identifier, message);
// return message;
// } catch {
// // Invalidate cache to prevent trying to read this part again.
// errorCache.set(identifier, undefined);
// } finally {
// // Reset limit.
// if (errorStackTraceLimitIsWritable) Error.stackTraceLimit = tmpLimit;
// if (fd !== undefined)
// closeSync(fd);
// }
}
function innerOk(fn, argLen, value, message) {
if (!value) {
let generatedMessage = false;
if (argLen === 0) {
generatedMessage = true;
message = "No value argument passed to `assert.ok()`";
} else if (message == null) {
generatedMessage = true;
message = getErrMessage(message, value, fn);
// TODO: message
} else if (Error.isError(message)) {
throw message;
}
if (AssertionError === undefined) loadAssertionError();
const err = new AssertionError({
actual: value,
expected: true,
message,
operator: "==",
stackStartFn: fn,
});
err.generatedMessage = generatedMessage;
throw err;
}
}
export default innerOk;