import React from "react"; import { useContext, useState, useCallback, createContext } from "react"; import { render, unmountComponentAtNode } from "react-dom"; import type { FallbackMessageContainer, JSException, JSException as JSExceptionType, Location, Message, Problems, SourceLine, StackFrame, WebsocketMessageBuildFailure, } from "../../src/api/schema"; enum StackFrameScope { Eval = 1, Module = 2, Function = 3, Global = 4, Wasm = 5, Constructor = 6, } enum JSErrorCode { Error = 0, EvalError = 1, RangeError = 2, ReferenceError = 3, SyntaxError = 4, TypeError = 5, URIError = 6, AggregateError = 7, // StackOverflow & OutOfMemoryError is not an ErrorType in within JSC, so the number here is just totally made up OutOfMemoryError = 8, BundlerError = 252, StackOverflow = 253, UserErrorCode = 254, } const JSErrorCodeLabel = { 0: "Error", 1: "EvalError", 2: "RangeError", 3: "ReferenceError", 4: "SyntaxError", 5: "TypeError", 6: "URIError", 7: "AggregateError", 253: "StackOverflow", 8: "OutOfMemory", }; const BUN_ERROR_CONTAINER_ID = "__bun-error__"; enum RuntimeType { Nothing = 0x0, Function = 0x1, Undefined = 0x2, Null = 0x4, Boolean = 0x8, AnyInt = 0x10, Number = 0x20, String = 0x40, Object = 0x80, Symbol = 0x100, BigInt = 0x200, } enum ErrorTagType { build, resolve, server, client, hmr, } const ErrorTag = ({ type }: { type: ErrorTagType }) => (
{ErrorTagType[type]}
); const errorTags = [ , , , , , ]; const normalizedFilename = (filename: string, cwd: string): string => { if (cwd && filename.startsWith(cwd)) { return filename.substring(cwd.length); } return filename; }; const blobFileURL = ( filename: string, line?: number, column?: number ): string => { var base = `/blob:${filename}`; if (Number.isFinite(line)) { base += `:${line}`; if (Number.isFinite(column)) { base += `:${column}`; } } return new URL(base, location.href).href; }; const openWithoutFlashOfNewTab = (event: MouseEvent) => { const target = event.currentTarget as HTMLAnchorElement; const href = target.getAttribute("href"); if (!href || event.button !== 0) { return true; } event.preventDefault(); event.nativeEvent.preventDefault(); event.nativeEvent.stopPropagation(); event.nativeEvent.stopImmediatePropagation(); const headers = new Headers(); headers.set("Accept", "text/plain"); if (target.dataset.line) { headers.set("Editor-Line", target.dataset.line); } if (target.dataset.column) { headers.set("Editor-Column", target.dataset.line); } headers.set("Open-In-Editor", "1"); globalThis .fetch(href.split("?")[0], { headers: headers, }) .then( () => {}, (er) => {} ); return false; }; const srcFileURL = (filename: string, line: number, column: number): string => { if (filename.endsWith(".bun")) { return new URL("/" + filename, location.href).href; } var base = `/src:${filename}`; if (Number.isFinite(line) && line > -1) { base = base + `:${line}`; if (Number.isFinite(column) && column > -1) { base = base + `:${column}`; } } return new URL(base, location.href).href; }; class FancyTypeError { constructor(exception: JSException) { this.runtimeType = exception.runtime_type || 0; this.runtimeTypeName = RuntimeType[this.runtimeType] || "undefined"; this.message = exception.message; this.explain = ""; this.normalize(exception); } runtimeType: RuntimeType; explain: string; runtimeTypeName: string; message: string; normalize(exception: JSException) { let i = exception.message.lastIndexOf(" is "); if (i === -1) return; const partial = exception.message.substring(i + " is ".length); const nextWord = /(["a-zA-Z0-9_\.]+)\)$/.exec(partial); if (nextWord && nextWord[0]) { this.runtimeTypeName = nextWord[0]; this.runtimeTypeName = this.runtimeTypeName.substring( 0, this.runtimeTypeName.length - 1 ); switch (this.runtimeTypeName.toLowerCase()) { case "undefined": { this.runtimeType = RuntimeType.Undefined; break; } case "null": { this.runtimeType = RuntimeType.Null; break; } case "string": { this.runtimeType = RuntimeType.String; break; } case "true": case "false": { this.runtimeType = RuntimeType.Boolean; break; } case "number": this.runtimeType = RuntimeType.Number; break; case "bigint": this.runtimeType = RuntimeType.BigInt; break; case "symbol": this.runtimeType = RuntimeType.Symbol; break; default: { this.runtimeType = RuntimeType.Object; break; } } this.message = exception.message.substring(0, i); this.message = this.message.substring( 0, this.message.lastIndexOf("(In ") ); } } } var onClose = dismissError; const clientURL = (filename) => { return `/${filename.replace(/^(\/)?/g, "")}`; }; function bunInfoToMarkdown(_info) { if (!_info) return; const info = { ..._info, platform: { ..._info.platform } }; var operatingSystemVersion = info.platform.version; if (info.platform.os.toLowerCase() === "macos") { const [major, minor, patch] = operatingSystemVersion.split("."); switch (major) { case "22": { operatingSystemVersion = `13.${minor}.${patch}`; break; } case "21": { operatingSystemVersion = `12.${minor}.${patch}`; break; } case "20": { operatingSystemVersion = `11.${minor}.${patch}`; break; } case "19": { operatingSystemVersion = `10.15.${patch}`; break; } case "18": { operatingSystemVersion = `10.14.${patch}`; break; } case "17": { operatingSystemVersion = `10.13.${patch}`; break; } case "16": { operatingSystemVersion = `10.12.${patch}`; break; } case "15": { operatingSystemVersion = `10.11.${patch}`; break; } } info.platform.os = "macOS"; } if (info.platform.arch === "arm" && info.platform.os === "macOS") { info.platform.arch = "Apple Silicon"; } else if (info.platform.arch === "arm") { info.platform.arch = "aarch64"; } var base = `Info: > bun v${info.bun_version} `; if (info.framework && info.framework_version) { base += `> framework: ${info.framework}@${info.framework_version}`; } else if (info.framework) { base += `> framework: ${info.framework}`; } base = base.trim() + ` > ${info.platform.os} ${operatingSystemVersion} (${info.platform.arch}) > User-Agent: ${navigator.userAgent} > Pathname: ${location.pathname} `; return base; } var bunInfoMemoized; function getBunInfo() { if (bunInfoMemoized) return bunInfoMemoized; if ("sessionStorage" in globalThis) { try { const bunInfoMemoizedString = sessionStorage.getItem("__bunInfo"); if (bunInfoMemoizedString) { bunInfoMemoized = JSON.parse(bunInfoMemoized); return bunInfoMemoized; } } catch (exception) {} } const controller = new AbortController(); const timeout = 1000; const id = setTimeout(() => controller.abort(), timeout); return fetch("/bun:info", { signal: controller.signal, headers: { Accept: "application/json", }, }) .then((resp) => resp.json()) .then((bunInfo) => { clearTimeout(id); bunInfoMemoized = bunInfo; if ("sessionStorage" in globalThis) { try { sessionStorage.setItem("__bunInfo", JSON.stringify(bunInfo)); } catch (exception) {} } return bunInfo; }); } const IndentationContext = createContext(0); enum LoadState { pending, loaded, failed, } const AsyncSourceLines = ({ highlight = -1, highlightColumnStart = 0, highlightColumnEnd = Infinity, children, buildURL, isClient, sourceLines, setSourceLines, }: { highlightColumnStart: number; highlightColumnEnd: number; highlight: number; buildURL: (line?: number, column?: number) => string; sourceLines: string[]; setSourceLines: (lines: SourceLine[]) => void; }) => { const [loadState, setLoadState] = React.useState(LoadState.pending); const controller = React.useRef(null); const url = React.useRef(buildURL(0, 0)); React.useEffect(() => { controller.current = new AbortController(); var cancelled = false; fetch(url.current, { signal: controller.current.signal, headers: { Accept: "text/plain", }, }) .then((resp) => { return resp.text(); }) .then((text) => { if (cancelled) return; // TODO: make this faster const lines = text.split("\n"); const startLineNumber = Math.max( Math.min(Math.max(highlight - 4, 0), lines.length - 1), 0 ); const endLineNumber = Math.min(startLineNumber + 8, lines.length); const sourceLines: SourceLine[] = new Array( endLineNumber - startLineNumber ); var index = 0; for (let i = startLineNumber; i < endLineNumber; i++) { const currentLine = lines[i - 1]; if (typeof currentLine === "undefined") break; sourceLines[index++] = { line: i, text: currentLine, }; } setSourceLines( index !== sourceLines.length ? sourceLines.slice(0, index) : sourceLines ); setLoadState(LoadState.loaded); }) .catch((err) => { if (!cancelled) { console.error(err); setLoadState(LoadState.failed); } }); return () => { cancelled = true; if (controller.current) { controller.current.abort(); controller.current = null; } }; }, [controller, setLoadState, setSourceLines, url, highlight]); switch (loadState) { case LoadState.pending: { return (
); } case LoadState.failed: { return null; } case LoadState.loaded: { return ( {children} ); } default: { throw new Error("Invalid state"); } } }; const SourceLines = ({ sourceLines, highlight = -1, highlightColumnStart = 0, highlightColumnEnd = Infinity, children, isClient = false, buildURL, }: { sourceLines: SourceLine[]; highlightColumnStart: number; highlightColumnEnd: number; highlight: number; buildURL: (line?: number, column?: number) => string; }) => { let start = sourceLines.length; let end = 0; let dedent = Infinity; let _i = 0; var minLineNumber = sourceLines.length + highlight + 1; var maxLineNumber = 0; for (let i = 0; i < sourceLines.length; i++) { // bun only prints \n, no \r\n, so this should work fine sourceLines[i].text = sourceLines[i].text.replaceAll("\n", ""); // This will now only trim spaces (and vertical tab character which never prints) const left = sourceLines[i].text.trimStart(); minLineNumber = Math.min(sourceLines[i].line, minLineNumber); maxLineNumber = Math.max(sourceLines[i].line, maxLineNumber); if (left.length > 0) { start = Math.min(start, i); end = Math.max(end, i + 1); dedent = Math.min(sourceLines[i].text.length - left.length, dedent); } } const leftPad = maxLineNumber.toString(10).length - minLineNumber.toString(10).length; const _sourceLines = sourceLines.slice(start, end); const childrenArray = children || []; const lines = new Array(_sourceLines.length + childrenArray.length); let highlightI = 0; for (let i = 0; i < _sourceLines.length; i++) { const { line, text } = _sourceLines[i]; const classes = { empty: text.trim().length === 0, highlight: highlight + +!isClient === line || _sourceLines.length === 1, }; if (classes.highlight) highlightI = i; const _text = classes.empty ? "" : text.substring(dedent); lines[i] = (
{line.toString(10).padStart(leftPad, " ")}
{classes.highlight ? ( <> {_text.substring(0, highlightColumnStart - dedent)} {_text.substring( highlightColumnStart - dedent, highlightColumnEnd - dedent )} {_text.substring(highlightColumnEnd - dedent)} ) : ( _text )}
); } return (
{lines}
); }; const BuildErrorSourceLines = ({ location, filename, }: { location: Location; filename: string; }) => { const { line, line_text, column } = location; const sourceLines: SourceLine[] = [{ line, text: line_text }]; const buildURL = React.useCallback( (line, column) => srcFileURL(filename, line, column), [srcFileURL, filename] ); return ( ); }; const BuildErrorStackTrace = ({ location }: { location: Location }) => { const { cwd } = useContext(ErrorGroupContext); const filename = normalizedFilename(location.file, cwd); const { line, column } = location; return (
{filename}:{line}:{column}
); }; const StackFrameIdentifier = ({ functionName, scope, markdown = false, }: { functionName?: string; markdown: boolean; scope: StackFrameScope; }) => { switch (scope) { case StackFrameScope.Constructor: { functionName = markdown && functionName ? "`" + functionName + "`" : functionName; return functionName ? `new ${functionName}` : "new (anonymous)"; break; } case StackFrameScope.Eval: { return "eval"; break; } case StackFrameScope.Module: { return "(esm)"; break; } case StackFrameScope.Global: { return "(global)"; break; } case StackFrameScope.Wasm: { return "(wasm)"; break; } default: { return functionName ? markdown ? "`" + functionName + "`" : functionName : "λ()"; break; } } }; const getNativeStackFrameIdentifier = (frame) => { const { file, function_name: functionName, scope } = frame; return StackFrameIdentifier({ functionName, scope, markdown: false, }); }; const NativeStackFrame = ({ frame, isTop, maxLength, urlBuilder, }: { frame: StackFrame; isTop: boolean; maxLength: number; }) => { const { cwd } = useContext(ErrorGroupContext); const { file, function_name: functionName, position: { line, column_start: column }, scope, } = frame; const fileName = normalizedFilename(file, cwd); return (
{getNativeStackFrameIdentifier(frame)}
{fileName}
{line > -1 &&
:{line}
} {column > -1 && (
:{column}
)}
); }; const NativeStackFrames = ({ frames, urlBuilder }) => { const items = new Array(frames.length); var maxLength = 0; for (let i = 0; i < frames.length; i++) { maxLength = Math.max( getNativeStackFrameIdentifier(frames[i]).length, maxLength ); } for (let i = 0; i < frames.length; i++) { items[i] = ( ); } return (
{items}
); }; const NativeStackTrace = ({ frames, sourceLines, setSourceLines, children, isClient = false, }: { frames: StackFrame[]; sourceLines: SourceLine[]; isClient: boolean; setSourceLines: (sourceLines: SourceLine[]) => void; }) => { const { file = "", position } = frames[0]; const { cwd } = useContext(ErrorGroupContext); const filename = normalizedFilename(file, cwd); const urlBuilder = isClient ? clientURL : blobFileURL; // const [isFocused, setFocused] = React.useState(false); const ref = React.useRef(); const buildURL = React.useCallback( (line, column) => urlBuilder(file, line, column), [file, urlBuilder] ); // React.useLayoutEffect(() => { // var handler1, handler2; // handler1 = document.addEventListener( // "selectionchange", // (event) => { // if (event.target && ref.current && ref.current.contains(event.target)) { // setFocused(true); // } // }, // { passive: true } // ); // handler2 = document.addEventListener( // "selectstart", // (event) => { // console.log(event); // if (event.target && ref.current && ref.current.contains(event.target)) { // setFocused(true); // } // }, // { passive: true } // ); // return () => { // if (handler1) { // document.removeEventListener("selectionchange", handler1); // handler1 = null; // } // if (handler2) { // document.removeEventListener("selectstart", handler2); // handler2 = null; // } // }; // }, [setFocused, ref]); return (
{filename}:{position.line}:{position.column_start} {sourceLines.length > 0 && ( {children} )} {sourceLines.length === 0 && isClient && ( {children} )} {frames.length > 1 && ( )}
); }; const divet = ^; const DivetRange = ({ start, stop }) => { const length = Math.max(stop - start, 0); if (length === 0) return null; return ( ); }; const Indent = ({ by, children }) => { const amount = useContext(IndentationContext); return ( <> {` `.repeat(by - amount)} {children} ); }; const JSException = ({ value, isClient = false, }: { value: JSExceptionType; isClient: boolean; }) => { const tag = isClient ? ErrorTagType.client : ErrorTagType.server; const [sourceLines, _setSourceLines] = React.useState( value?.stack?.source_lines ?? [] ); // mutating a prop is sacrilege function setSourceLines(sourceLines: SourceLine[]) { _setSourceLines(sourceLines); if (!value.stack) { value.stack = { frames: [], source_lines: sourceLines, }; } else { value.stack.source_lines = sourceLines; } } switch (value.code) { case JSErrorCode.TypeError: { const fancyTypeError = new FancyTypeError(value); if (fancyTypeError.runtimeType !== RuntimeType.Nothing) { return (
TypeError
{errorTags[tag]}
{fancyTypeError.message}
{fancyTypeError.runtimeTypeName.length && (
It's{" "} {fancyTypeError.runtimeTypeName} .
)} {value.stack && ( {fancyTypeError.runtimeTypeName} )}
); } } default: { const newline = value.message.indexOf("\n"); if (newline > -1) { const subtitle = value.message.substring(newline + 1).trim(); const message = value.message.substring(0, newline).trim(); if (subtitle.length) { return (
{value.name}
{errorTags[tag]}
{message}
{subtitle}
{value.stack && ( )}
); } } return (
{value.name}
{errorTags[tag]}
{value.message}
{value.stack && ( )}
); } } }; const Summary = ({ errorCount, onClose, }: { errorCount: number; onClose: Function; }) => { return (
{errorCount} error{errorCount > 1 ? "s" : ""} on this page
Want help?
); }; const BuildError = ({ message }: { message: Message }) => { let title = (message.data.text || "").trim(); const newline = title.indexOf("\n"); let subtitle = ""; if (newline > -1) { subtitle = title.slice(newline + 1).trim(); title = title.slice(0, newline); } return (
BuildError
{title}
{subtitle.length > 0 && (
{subtitle}
)} {message.data.location && ( )}
); }; const ResolveError = ({ message }: { message: Message }) => { const { cwd } = useContext(ErrorGroupContext); let title = (message.data.text || "").trim(); const newline = title.indexOf("\n"); let subtitle = null; if (newline > -1) { subtitle = title.slice(newline + 1).trim(); title = title.slice(0, newline); } return (
ResolveError
Can't import{" "} {message.on.resolve}
{subtitle &&
{subtitle}
} {message.data.location && ( )}
); }; const OverlayMessageContainer = ({ problems, reason, router, isClient = false, }: FallbackMessageContainer) => { return (
{problems.exceptions.map((problem, index) => ( ))} {problems.build.msgs.map((buildMessage, index) => { if (buildMessage.on.build) { return ; } else if (buildMessage.on.resolve) { return ; } else { throw new Error("Unknown build message type"); } })}
); }; function problemsToMarkdown(problems: Problems) { var markdown = ""; if (problems?.build?.msgs?.length) { markdown += messagesToMarkdown(problems.build.msgs); } if (problems?.exceptions?.length) { markdown += exceptionsToMarkdown(problems.exceptions); } return markdown; } // we can ignore the synchronous copy to clipboard API...I think function copyToClipboard(input: string | Promise) { if (!input) return; if (input && typeof input === "object" && "then" in input) { return input.then((str) => copyToClipboard(str)); } return navigator.clipboard.writeText(input).then(() => {}); } function messagesToMarkdown(messages: Message[]): string { return messages .map(messageToMarkdown) .map((a) => a.trim()) .join("\n"); } function exceptionsToMarkdown(exceptions: JSExceptionType[]): string { return exceptions .map(exceptionToMarkdown) .map((a) => a.trim()) .join("\n"); } function exceptionToMarkdown(exception: JSException): string { const { name, message, stack } = exception; let markdown = ""; if (name === "Error" || name === "RangeError" || name === "TypeError") { markdown += `**${message}**\n`; } else { markdown += `**${name}**\n ${message}\n`; } if (stack.frames.length > 0) { var frames = stack.frames; if (stack.source_lines.length > 0) { const { file = "", function_name = "", position: { line = -1, column_start: column = -1, column_stop: columnEnd = column, } = { line: -1, column_start: -1, column_stop: -1, }, scope = 0, } = stack.frames[0]; if (file) { if (function_name.length > 0) { markdown += `In \`${function_name}\` – ${file}`; } else if (scope > 0 && scope < StackFrameScope.Constructor + 1) { markdown += `${StackFrameIdentifier({ functionName: function_name, scope, markdown: true, })} ${file}`; } else { markdown += `In ${file}`; } if (line > -1) { markdown += `:${line}`; if (column > -1) { markdown += `:${column}`; } } if (stack.source_lines.length > 0) { // TODO: include loader const extnameI = file.lastIndexOf("."); const extname = extnameI > -1 ? file.slice(extnameI + 1) : ""; markdown += "\n```"; markdown += extname; markdown += "\n"; stack.source_lines.forEach((sourceLine) => { markdown += sourceLine.text + "\n"; if (sourceLine.line === line && stack.source_lines.length > 1) { markdown += ("/* " + "^".repeat(Math.max(columnEnd - column, 1))).padStart( Math.max(column + 2, 0) ) + " happened here */\n"; } }); markdown += "\n```"; } } } if (frames.length > 0) { markdown += "\nStack trace:\n"; var padding = 0; for (let frame of frames) { const { function_name = "", position: { line = -1, column_start: column = -1 } = { line: -1, column_start: -1, }, scope = 0, } = frame; padding = Math.max( padding, StackFrameIdentifier({ scope, functionName: function_name, markdown: true, }).length ); } markdown += "```js\n"; for (let frame of frames) { const { file = "", function_name = "", position: { line = -1, column_start: column = -1 } = { line: -1, column_start: -1, }, scope = 0, } = frame; markdown += ` ${StackFrameIdentifier({ scope, functionName: function_name, markdown: true, }).padEnd(padding, " ")}`; if (file) { markdown += ` ${file}`; if (line > -1) { markdown += `:${line}`; if (column > -1) { markdown += `:${column}`; } } } } markdown += "\n```\n"; } } return markdown; } function messageToMarkdown(message: Message): string { var tag = "Error"; if (message.on.build) { tag = "BuildError"; } var lines = (message.data.text ?? "").split("\n"); var markdown = ""; if (message?.on?.resolve) { markdown += `**ResolveError**: "${message.on.resolve}" failed to resolve\n`; } else { var firstLine = lines[0]; lines = lines.slice(1); if (firstLine.length > 120) { const words = firstLine.split(" "); var end = 0; for (let i = 0; i < words.length; i++) { if (end + words[i].length >= 120) { firstLine = words.slice(0, i).join(" "); lines.unshift(words.slice(i).join(" ")); break; } } } markdown += `**${tag}**${firstLine.length > 0 ? ": " + firstLine : ""}\n`; } if (message.data?.location?.file) { markdown += `In ${normalizedFilename(message.data.location.file, thisCwd)}`; if (message.data.location.line > -1) { markdown += `:${message.data.location.line}`; if (message.data.location.column > -1) { markdown += `:${message.data.location.column}`; } } if (message.data.location.line_text.length) { const extnameI = message.data.location.file.lastIndexOf("."); const extname = extnameI > -1 ? message.data.location.file.slice(extnameI + 1) : ""; markdown += "\n```" + extname + "\n" + message.data.location.line_text + "\n```\n"; } else { markdown += "\n"; } if (lines.length > 0) { markdown += lines.join("\n"); } } return markdown; } const withBunInfo = (text) => { const bunInfo = getBunInfo(); const trimmed = text.trim(); if (bunInfo && "then" in bunInfo) { return bunInfo.then( (info) => { const markdown = bunInfoToMarkdown(info).trim(); return trimmed + "\n" + markdown + "\n"; }, () => trimmed + "\n" ); } if (bunInfo) { const markdown = bunInfoToMarkdown(bunInfo).trim(); return trimmed + "\n" + markdown + "\n"; } return trimmed + "\n"; }; const Footer = ({ toMarkdown, data }) => (
copyToClipboard(withBunInfo(String(toMarkdown(data))))} > {" "} Copy as markdown
); const BuildFailureMessageContainer = ({ messages, }: { messages: Message[]; }) => { return (
{messages.map((buildMessage, index) => { if (buildMessage.on.build) { return ; } else if (buildMessage.on.resolve) { return ; } else { throw new Error("Unknown build message type"); } })}
); }; var thisCwd = ""; const ErrorGroupContext = createContext<{ cwd: string }>(null); var reactRoot; function renderWithFunc(func) { if (!reactRoot) { const root = document.createElement("div"); root.id = "__bun__error-root"; reactRoot = document.createElement("div"); reactRoot.id = BUN_ERROR_CONTAINER_ID; reactRoot.style.visibility = "hidden"; const link = document.createElement("link"); link.rel = "stylesheet"; link.href = new URL("/bun:erro.css", document.baseURI).href; link.onload = () => { reactRoot.style.visibility = "visible"; }; const shadowRoot = root.attachShadow({ mode: "closed" }); shadowRoot.appendChild(link); shadowRoot.appendChild(reactRoot); document.body.appendChild(root); render(func(), reactRoot); } else { render(func(), reactRoot); } } export function renderFallbackError(fallback: FallbackMessageContainer) { if (fallback && fallback.cwd) { thisCwd = fallback.cwd; } // Not an error if (fallback?.problems?.name === "JSDisabled") return; return renderWithFunc(() => ( )); } import { parse as getStackTrace } from "./stack-trace-parser"; export function renderRuntimeError(error: Error) { if (typeof error === "string") { error = { name: "Error", message: error, }; } const exception: JSException = { name: String(error.name), message: String(error.message), runtime_type: 0, stack: { frames: error.stack ? getStackTrace(error.stack) : [], source_lines: [], }, }; var lineNumberProperty = ""; var columnNumberProperty = ""; var fileNameProperty = ""; if (error && typeof error === "object") { // safari if ("line" in error) { lineNumberProperty = "line"; // firefox } else if ("lineNumber" in error) { lineNumberProperty = "lineNumber"; } // safari if ("column" in error) { columnNumberProperty = "column"; // firefox } else if ("columnNumber" in error) { columnNumberProperty = "columnNumber"; } // safari if ("sourceURL" in error) { fileNameProperty = "sourceURL"; // firefox } else if ("fileName" in error) { fileNameProperty = "fileName"; } } if (Number.isFinite(error[lineNumberProperty])) { if (exception.stack.frames.length == 0) { exception.stack.frames.push({ file: error[fileNameProperty] || "", position: { line: +error[lineNumberProperty] || 1, column_start: +error[columnNumberProperty] || 1, }, }); } else if (exception.stack.frames.length > 0) { exception.stack.frames[0].position.line = error[lineNumberProperty]; if (Number.isFinite(error[columnNumberProperty])) { exception.stack.frames[0].position.column_start = error[columnNumberProperty]; } } } const fallback: FallbackMessageContainer = { message: error.message, problems: { build: { warnings: 0, errors: 0, msgs: [], }, code: 0, name: error.name, exceptions: [exception], }, }; return renderWithFunc(() => ( )); } export function dismissError() { if (reactRoot) { unmountComponentAtNode(reactRoot); const root = document.getElementById("__bun__error-root"); if (root) root.remove(); reactRoot = null; } } export const renderBuildFailure = ( failure: WebsocketMessageBuildFailure, cwd: string ) => { thisCwd = cwd; renderWithFunc(() => ( )); }; export const clearBuildFailure = dismissError; globalThis.__BunClearBuildFailure = dismissError;