/** @jsxImportSource react */ import { existsSync, readFileSync } from "node:fs"; import { cwd } from "node:process"; import { codeExcerpt, type CodeExcerpt } from "#/code-excerpt.ts"; import { Box } from "#/components/Box.tsx"; import { Text } from "#/components/Text.tsx"; import { parseStackLine } from "#/parse-stack-line.ts"; // Error's source file is reported as file:///home/user/file.js // This function removes the file://[cwd] part const cleanupPath = (path: string | undefined): string | undefined => { return path?.replace(`file://${cwd()}/`, ""); }; type Props = { readonly error: Error; }; export function ErrorOverview({ error }: Props) { const stack = error.stack ? error.stack.split("\n").slice(1) : undefined; const origin = stack ? parseStackLine(stack[0]!) : undefined; const filePath = cleanupPath(origin?.file); let excerpt: CodeExcerpt[] | undefined; let lineWidth = 0; const stackLineCounts = new Map(); if (filePath && origin?.line && existsSync(filePath)) { const sourceCode = readFileSync(filePath, "utf8"); excerpt = codeExcerpt(sourceCode, origin.line); if (excerpt) { for (const { line } of excerpt) { lineWidth = Math.max(lineWidth, String(line).length); } } } return ( {" "} ERROR{" "} {error.message} {origin && filePath ? ( {filePath}:{origin.line}:{origin.column} ) : null} {origin && excerpt ? ( {excerpt.map(({ line, value }) => ( {String(line).padStart(lineWidth, " ")}: {" " + value} ))} ) : null} {error.stack ? ( {error.stack .split("\n") .slice(1) .map((line) => { const parsedLine = parseStackLine(line); const lineCount = stackLineCounts.get(line) ?? 0; stackLineCounts.set(line, lineCount + 1); const key = `${line}-${lineCount}`; // If the line from the stack cannot be parsed, or parsed into an incomplete // frame without source location data (for example, "at native"), we print // out the unparsed line. if (!parsedLine?.file || !parsedLine.line || !parsedLine.column) { return ( - {line} \t{" "} ); } return ( - {parsedLine.function} {" "} ({cleanupPath(parsedLine.file) ?? ""}:{parsedLine.line}:{parsedLine.column}) ); })} ) : null} ); }