UNPKG

@visulima/error

Version:

Error with more than just a message, stacktrace parsing.

207 lines (198 loc) 6.77 kB
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 };