shrink bun error modal by 250 KB (#22813)

### What does this PR do?

### How did you verify your code works?

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Jarred Sumner
2025-09-19 23:54:18 -07:00
committed by GitHub
parent d3d68f45fd
commit f528dc5300
4 changed files with 40 additions and 29 deletions

View File

@@ -0,0 +1,14 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "bun-error",
"dependencies": {
"preact": "^10.27.2",
},
},
},
"packages": {
"preact": ["preact@10.27.2", "", {}, "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg=="],
}
}

View File

@@ -1,5 +1,6 @@
import React, { createContext, useContext } from "react"; import type { JSX } from "preact";
import { render, unmountComponentAtNode } from "react-dom"; import { createContext, render } from "preact";
import { useCallback, useContext, useEffect, useRef, useState } from "preact/hooks";
import type { import type {
FallbackMessageContainer, FallbackMessageContainer,
JSException, JSException,
@@ -164,17 +165,17 @@ const maybeBlobFileURL = (filename: string, line?: number, column?: number): str
return srcFileURL(filename, line, column); return srcFileURL(filename, line, column);
}; };
const openWithoutFlashOfNewTab: React.MouseEventHandler<HTMLAnchorElement> = event => { const openWithoutFlashOfNewTab: JSX.MouseEventHandler<HTMLAnchorElement> = event => {
const target = event.currentTarget; const target = event.currentTarget as HTMLAnchorElement;
const href = target.getAttribute("href"); const href = target.getAttribute("href");
if (!href || event.button !== 0) { if (!href || event.button !== 0) {
return true; return true;
} }
event.preventDefault(); event.preventDefault();
event.nativeEvent.preventDefault(); event.preventDefault();
event.nativeEvent.stopPropagation(); event.stopPropagation();
event.nativeEvent.stopImmediatePropagation(); event.stopImmediatePropagation();
const headers = new Headers(); const headers = new Headers();
headers.set("Accept", "text/plain"); headers.set("Accept", "text/plain");
@@ -317,17 +318,17 @@ const AsyncSourceLines = ({
highlight: number; highlight: number;
highlightColumnStart: number; highlightColumnStart: number;
highlightColumnEnd: number; highlightColumnEnd: number;
children?: React.ReactNode; children?: any;
buildURL: (line?: number, column?: number) => string; buildURL: (line?: number, column?: number) => string;
sourceLines: SourceLine[]; sourceLines: SourceLine[];
setSourceLines: (lines: SourceLine[]) => void; setSourceLines: (lines: SourceLine[]) => void;
}) => { }) => {
const [loadState, setLoadState] = React.useState(LoadState.pending); const [loadState, setLoadState] = useState(LoadState.pending);
const controller = React.useRef<AbortController | null>(null); const controller = useRef<AbortController | null>(null);
const url = React.useRef<string>(buildURL(0, 0)); const url = useRef<string>(buildURL(0, 0));
React.useEffect(() => { useEffect(() => {
controller.current = new AbortController(); controller.current = new AbortController();
var cancelled = false; var cancelled = false;
fetch(url.current, { fetch(url.current, {
@@ -432,7 +433,7 @@ const SourceLines = ({
highlight: number; highlight: number;
highlightColumnStart: number; highlightColumnStart: number;
highlightColumnEnd: number; highlightColumnEnd: number;
children?: React.ReactNode; children?: any;
buildURL: (line?: number, column?: number) => string; buildURL: (line?: number, column?: number) => string;
}) => { }) => {
let start = sourceLines.length; let start = sourceLines.length;
@@ -461,7 +462,7 @@ const SourceLines = ({
const leftPad = maxLineNumber.toString(10).length - minLineNumber.toString(10).length; const leftPad = maxLineNumber.toString(10).length - minLineNumber.toString(10).length;
const _sourceLines = sourceLines.slice(start, end); const _sourceLines = sourceLines.slice(start, end);
const lines = new Array(_sourceLines.length + React.Children.count(children)); const lines = new Array(_sourceLines.length + (Array.isArray(children) ? children.length : children ? 1 : 0));
let highlightI = 0; let highlightI = 0;
for (let i = 0; i < _sourceLines.length; i++) { for (let i = 0; i < _sourceLines.length; i++) {
@@ -513,7 +514,7 @@ const SourceLines = ({
const BuildErrorSourceLines = ({ location, filename }: { location: Location; filename: string }) => { const BuildErrorSourceLines = ({ location, filename }: { location: Location; filename: string }) => {
const { line, line_text, column } = location; const { line, line_text, column } = location;
const sourceLines: SourceLine[] = [{ line, text: line_text }]; const sourceLines: SourceLine[] = [{ line, text: line_text }];
const buildURL = React.useCallback((line, column) => srcFileURL(filename, line, column), [srcFileURL, filename]); const buildURL = useCallback((line, column) => srcFileURL(filename, line, column), [filename]);
return ( return (
<SourceLines <SourceLines
sourceLines={sourceLines} sourceLines={sourceLines}
@@ -669,15 +670,15 @@ const NativeStackTrace = ({
frames: StackFrame[]; frames: StackFrame[];
sourceLines: SourceLine[]; sourceLines: SourceLine[];
setSourceLines: (sourceLines: SourceLine[]) => void; setSourceLines: (sourceLines: SourceLine[]) => void;
children?: React.ReactNode; children?: any;
isClient: boolean; isClient: boolean;
}) => { }) => {
const { file = "", position } = frames[0]; const { file = "", position } = frames[0];
const { cwd } = useContext(ErrorGroupContext); const { cwd } = useContext(ErrorGroupContext);
const filename = normalizedFilename(file, cwd); const filename = normalizedFilename(file, cwd);
const urlBuilder = isClient ? clientURL : maybeBlobFileURL; const urlBuilder = isClient ? clientURL : maybeBlobFileURL;
const ref = React.useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const buildURL = React.useCallback((line, column) => urlBuilder(file, line, column), [file, urlBuilder]); const buildURL = useCallback((line, column) => urlBuilder(file, line, column), [file, urlBuilder]);
return ( return (
<div ref={ref} className={`BunError-NativeStackTrace`}> <div ref={ref} className={`BunError-NativeStackTrace`}>
@@ -732,7 +733,7 @@ const Indent = ({ by, children }) => {
const JSException = ({ value, isClient = false }: { value: JSExceptionType; isClient: boolean }) => { const JSException = ({ value, isClient = false }: { value: JSExceptionType; isClient: boolean }) => {
const tag = isClient ? ErrorTagType.client : ErrorTagType.server; const tag = isClient ? ErrorTagType.client : ErrorTagType.server;
const [sourceLines, _setSourceLines] = React.useState(value?.stack?.source_lines ?? []); const [sourceLines, _setSourceLines] = useState(value?.stack?.source_lines ?? []);
var message = value.message || ""; var message = value.message || "";
var name = value.name || ""; var name = value.name || "";
if (!name && !message) { if (!name && !message) {
@@ -1242,7 +1243,7 @@ export function renderRuntimeError(error: Error) {
export function dismissError() { export function dismissError() {
if (reactRoot) { if (reactRoot) {
unmountComponentAtNode(reactRoot); render(null, reactRoot);
const root = document.getElementById("__bun__error-root"); const root = document.getElementById("__bun__error-root");
if (root) root.remove(); if (root) root.remove();
reactRoot = null; reactRoot = null;

View File

@@ -5,14 +5,9 @@
"license": "MIT", "license": "MIT",
"private": true, "private": true,
"scripts": { "scripts": {
"build": "esbuild --define:process.env.NODE_ENV=\"'production'\" --minify index.tsx bun-error.css --bundle --outdir=dist --platform=browser --format=esm" "build": "bun build --production --define:process.env.NODE_ENV=\"'production'\" --minify index.tsx bun-error.css --outdir=dist --target=browser --format=esm"
}, },
"dependencies": { "dependencies": {
"esbuild": "latest", "preact": "^10.27.2"
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@types/react": "^17.0.39"
} }
} }

View File

@@ -1,10 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"jsx": "react",
"lib": ["ESNext", "DOM"], "lib": ["ESNext", "DOM"],
"module": "esnext", "module": "esnext",
"target": "esnext", "target": "esnext",
"moduleResolution": "node", "moduleResolution": "node",
"allowSyntheticDefaultImports": true "allowSyntheticDefaultImports": true,
"jsx": "react-jsx",
"jsxImportSource": "preact"
} }
} }