@visulima/error
Version:
Error with more than just a message, stacktrace parsing.
166 lines (163 loc) • 6.57 kB
JavaScript
import { existsSync, readFileSync } from 'node:fs';
import { relative } from 'node:path';
import { cwd } from 'node:process';
import { fileURLToPath } from 'node:url';
import { codeFrame } from '../code-frame/index.mjs';
import parse from './parseStacktrace-BKGoWCwC.mjs';
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
const getPrefix = /* @__PURE__ */ __name((prefix, indentation, deep) => {
if (deep === 0) {
return prefix + "";
}
if (indentation === " ") {
return prefix + " ".repeat(deep);
}
return prefix + " ".repeat(indentation * deep);
}, "getPrefix");
const getRelativePath = /* @__PURE__ */ __name((filePath, cwdPath) => {
const path = filePath.replace("async file:", "file:");
return relative(cwdPath, path.startsWith("file:") ? fileURLToPath(path) : path);
}, "getRelativePath");
const getMessage = /* @__PURE__ */ __name((error, { color, hideErrorTitle, indentation, prefix }, deep) => getPrefix(prefix, indentation, deep) + (hideErrorTitle ? color.title(error.message) : color.title(error.name + (error.message ? ": " + error.message : ""))) + "\n", "getMessage");
const getHint = /* @__PURE__ */ __name((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 + "\n";
}
} else {
message += spaces + error.hint;
}
return color.hint(message);
}, "getHint");
const getMainFrame = /* @__PURE__ */ __name((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) + ":" + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
fileLine(trace.line + "");
}, "getMainFrame");
const getCode = /* @__PURE__ */ __name((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 }
);
}, "getCode");
const getErrors = /* @__PURE__ */ __name((error, options, deep) => {
if (error.errors.length === 0) {
return void 0;
}
let message = getPrefix(options.prefix, options.indentation, deep) + "Errors:\n\n";
let first = true;
for (const error_ of error.errors) {
if (first) {
first = false;
} else {
message += "\n\n";
}
message += internalRenderError(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
error_,
{ ...options, framesMaxLimit: 1, hideErrorCodeView: options.hideErrorErrorsCodeView },
deep + 1
);
}
return "\n" + message;
}, "getErrors");
const getCause = /* @__PURE__ */ __name((error, options, deep) => {
let message = getPrefix(options.prefix, options.indentation, deep) + "Caused by:\n\n";
const cause = error.cause;
message += getMessage(cause, options, deep);
const stacktrace = parse(cause);
const mainFrame = stacktrace.shift();
const hint = getHint(cause, options, deep);
if (hint) {
message += hint + "\n";
}
if (mainFrame) {
message += getMainFrame(mainFrame, options, deep);
if (!options.hideErrorCauseCodeView) {
const code = getCode(mainFrame, options, deep);
if (code !== void 0) {
message += "\n" + code;
}
}
}
if (cause.cause) {
message += "\n" + getCause(cause, options, deep + 1);
} else if (cause instanceof AggregateError) {
const errors = getErrors(cause, options, deep);
if (errors !== void 0) {
message += "\n" + errors;
}
}
return "\n" + message;
}, "getCause");
const getStacktrace = /* @__PURE__ */ __name((stack, options) => (stack.length > 0 ? "\n" : "") + stack.map((frame) => getMainFrame(frame, options)).join("\n"), "getStacktrace");
const internalRenderError = /* @__PURE__ */ __name((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: /* @__PURE__ */ __name((value) => value, "fileLine"),
gutter: /* @__PURE__ */ __name((value) => value, "gutter"),
hint: /* @__PURE__ */ __name((value) => value, "hint"),
marker: /* @__PURE__ */ __name((value) => value, "marker"),
message: /* @__PURE__ */ __name((value) => value, "message"),
method: /* @__PURE__ */ __name((value) => value, "method"),
title: /* @__PURE__ */ __name((value) => value, "title"),
...options.color
}
};
const stack = parse(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("\\", "/");
}, "internalRenderError");
const renderError = /* @__PURE__ */ __name((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);
}, "renderError");
export { renderError };