@visulima/error
Version:
Error with more than just a message, stacktrace parsing.
207 lines (198 loc) • 6.77 kB
JavaScript
import { createRequire as __cjs_createRequire } from "node:module";
const __cjs_require = __cjs_createRequire(import.meta.url);
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
const __cjs_getBuiltinModule = (module) => {
// Check if we're in Node.js and version supports getBuiltinModule
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
// Node.js 20.16.0+ and 22.3.0+
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
return __cjs_getProcess.getBuiltinModule(module);
}
}
// Fallback to createRequire
return __cjs_require(module);
};
const {
existsSync,
readFileSync
} = __cjs_getBuiltinModule("node:fs");
const {
relative
} = __cjs_getBuiltinModule("node:path");
const {
cwd
} = __cjs_getProcess;
const {
fileURLToPath
} = __cjs_getBuiltinModule("node:url");
import { codeFrame } from '../code-frame/index.js';
import parseStacktrace from './parseStacktrace-oQvk7wYp.js';
const getPrefix = (prefix, indentation, deep) => {
if (deep === 0) {
return prefix.toString();
}
if (indentation === " ") {
return prefix + " ".repeat(deep);
}
return prefix + " ".repeat(indentation * deep);
};
const getRelativePath = (filePath, cwdPath) => {
const path = filePath.replace("async file:", "file:");
return relative(cwdPath, path.startsWith("file:") ? fileURLToPath(path) : path);
};
const getTitleText = (error, hideErrorTitle, color) => {
if (hideErrorTitle) {
return color.title(error.message);
}
const messagePart = error.message ? `: ${error.message}` : "";
return color.title(error.name + messagePart);
};
const getMessage = (error, { color, hideErrorTitle, indentation, prefix }, deep) => `${getPrefix(prefix, indentation, deep)}${getTitleText(error, hideErrorTitle, color)}
`;
const getHint = (error, { color, indentation, prefix }, deep) => {
if (error.hint === void 0) {
return void 0;
}
const spaces = getPrefix(prefix, indentation, deep);
let message = "";
if (Array.isArray(error.hint)) {
for (const line of error.hint) {
message += `${(spaces + line).toString()}
`;
}
} else {
message += spaces + error.hint;
}
return color.hint(message);
};
const getMainFrame = (trace, { color, cwd: cwdPath, displayShortPath, indentation, prefix }, deep = 0) => {
const filePath = displayShortPath ? getRelativePath(trace.file, cwdPath) : trace.file;
const { fileLine, method } = color;
return `${getPrefix(prefix, indentation, deep)}at ${trace.methodName ? `${method(trace.methodName)} ` : ""}${fileLine(filePath)}:${fileLine(
trace.line?.toString() ?? ""
)}`.toString();
};
const getCode = (trace, { color, indentation, linesAbove, linesBelow, prefix, showGutter, showLineNumbers, tabWidth }, deep) => {
if (trace.file === void 0) {
return void 0;
}
const filePath = trace.file.replace("file://", "");
if (!existsSync(filePath)) {
return void 0;
}
const fileContent = readFileSync(filePath, "utf8");
return codeFrame(
fileContent,
{
start: { column: trace.column, line: trace.line }
},
{ color, linesAbove, linesBelow, prefix: getPrefix(prefix, indentation, deep), showGutter, showLineNumbers, tabWidth }
);
};
const getErrors = (error, options, deep) => {
if (error.errors.length === 0) {
return void 0;
}
let message = `${getPrefix(options.prefix, options.indentation, deep)}Errors:
`;
let first = true;
for (const error_ of error.errors) {
if (first) {
first = false;
} else {
message += "\n\n";
}
message += internalRenderError(error_, { ...options, framesMaxLimit: 1, hideErrorCodeView: options.hideErrorErrorsCodeView }, deep + 1);
}
return `
${message}`;
};
const getCause = (error, options, deep) => {
let message = `${getPrefix(options.prefix, options.indentation, deep)}Caused by:
`;
const cause = error.cause;
message += getMessage(cause, options, deep);
const stacktrace = parseStacktrace(cause);
const mainFrame = stacktrace.shift();
const hint = getHint(cause, options, deep);
if (hint) {
message += `${hint}
`;
}
if (mainFrame) {
message += getMainFrame(mainFrame, options, deep);
if (!options.hideErrorCauseCodeView) {
const code = getCode(mainFrame, options, deep);
if (code !== void 0) {
message += `
${code}`;
}
}
}
if (cause.cause) {
message += `
${getCause(cause, options, deep + 1)}`;
} else if (cause instanceof AggregateError) {
const errors = getErrors(cause, options, deep);
if (errors !== void 0) {
message += `
${errors}`;
}
}
return `
${message}`;
};
const getStacktrace = (stack, options) => (stack.length > 0 ? "\n" : "") + stack.map((frame) => getMainFrame(frame, options)).join("\n");
const internalRenderError = (error, options, deep) => {
const config = {
cwd: cwd(),
displayShortPath: false,
filterStacktrace: void 0,
framesMaxLimit: Number.POSITIVE_INFINITY,
hideErrorCauseCodeView: false,
hideErrorCodeView: false,
hideErrorErrorsCodeView: false,
hideErrorTitle: false,
hideMessage: false,
indentation: 4,
linesAbove: 2,
linesBelow: 3,
prefix: "",
showGutter: true,
showLineNumbers: true,
tabWidth: 4,
...options,
color: {
fileLine: (value) => value,
gutter: (value) => value,
hint: (value) => value,
marker: (value) => value,
message: (value) => value,
method: (value) => value,
title: (value) => value,
...options.color
}
};
const stack = parseStacktrace(error, {
filter: options.filterStacktrace,
frameLimit: config.framesMaxLimit
});
const mainFrame = stack.shift();
return [
options.hideMessage ? void 0 : getMessage(error, config, deep),
getHint(error, config, deep),
mainFrame ? getMainFrame(mainFrame, config, deep) : void 0,
mainFrame && !config.hideErrorCodeView ? getCode(mainFrame, config, deep) : void 0,
error instanceof AggregateError ? getErrors(error, config, deep) : void 0,
error.cause === void 0 ? void 0 : getCause(error, config, deep),
stack.length > 0 ? getStacktrace(stack, config) : void 0
].filter(Boolean).join("\n").replaceAll("\\", "/");
};
const renderError = (error, options = {}) => {
if (options.framesMaxLimit !== void 0 && options.framesMaxLimit <= 0) {
throw new RangeError("The 'framesMaxLimit' option must be a positive number");
}
return internalRenderError(error, options, 0);
};
export { renderError };