[bun dev] Implement copy as markdown

This commit is contained in:
Jarred Sumner
2022-02-20 23:12:15 -08:00
parent 59109a309f
commit efb4baacdf
4 changed files with 1215 additions and 125 deletions

View File

@@ -5,12 +5,12 @@
"Source Code Pro", "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
}
a {
:host a {
color: inherit;
text-decoration: none;
}
a:hover {
:host a:hover {
text-decoration: underline;
}
#BunErrorOverlay-container {
@@ -32,7 +32,7 @@ a:hover {
Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
#BunErrorOverlay-container a {
:host a {
color: inherit;
}
@@ -61,7 +61,7 @@ a:hover {
.BunError-Summary {
display: grid;
grid-template-columns: min-content auto min-content;
grid-template-columns: min-content auto min-content min-content;
grid-template-rows: 46px;
align-items: center;
padding: 0 18px;
@@ -69,11 +69,16 @@ a:hover {
}
.BunError-footer {
display: grid;
padding: 12px 18px;
justify-content: flex-end;
display: flex;
border-top: 1px solid rgb(220, 220, 220);
align-items: center;
grid-template-columns: auto auto;
justify-content: space-between;
}
.BunError-footerItem {
padding: 12px 18px;
}
.BunError-Summary-Title {
@@ -142,24 +147,113 @@ a:hover {
text-decoration: underline;
}
.BunError-SourceLines-lines a {
text-decoration: none;
}
.BunError-SourceLine-text::selection,
.BunError-SourceLine-text *::selection {
background-color: #6437e3;
color: white;
}
.BunError-SourceLine-text:active:before {
width: 61px;
z-index: -1;
pointer-events: none;
}
.BunError-SourceLines-lines {
}
.BunError-SourceLines {
.BunError-SourceLine {
display: grid;
grid-template-columns: min-content auto;
grid-template-rows: repeat(16px, 6);
column-gap: 13px;
}
.BunError-SourceLines {
font-size: 14px;
padding-left: 24px;
align-items: center;
position: relative;
overflow-x: auto;
}
.BunError-CopyButton {
font-weight: 500;
font-size: 1em;
vertical-align: middle;
display: flex;
align-content: center;
align-items: center;
border-right: 1px solid #ccc;
background: #fcfcfc;
border-bottom-left-radius: 12px;
color: #333;
cursor: pointer;
user-select: none;
-webkit-user-select: none;
transition: transform 0.1s linear;
}
.BunError-CopyButton,
#BunError-poweredBy {
padding: 12px 12px;
}
.BunError-Summary-help {
display: flex;
white-space: nowrap;
gap: 9px;
margin-right: 18px;
border-right: 1px solid rgb(220, 220, 220);
height: 100%;
align-items: center;
padding-right: 18px;
text-decoration: none;
cursor: pointer;
}
.BunError-Summary-help svg {
opacity: 0.5;
}
#BunErrorOverlay-container .BunError-Summary-help {
color: #999;
}
#BunErrorOverlay-container .BunError-Summary-help:hover {
color: #5865f2;
}
#BunErrorOverlay-container .BunError-Summary-help:hover svg {
opacity: 1;
}
.BunError-CopyButton svg {
width: 18px;
margin-right: 0.7em;
opacity: 0.75;
margin-left: 6px;
fill: currentColor;
stroke: currentColor;
}
.BunError-CopyButton:hover {
background: #6437e3;
color: white;
border-right-color: #6437e3;
}
.BunError-CopyButton:active {
transform: scale(1.03, 1.03);
transform-origin: center center;
}
.BunError-SourceLine-text {
white-space: pre;
cursor: text;
display: block;
position: relative;
font-family: var(--bun-error-monospace);
}
@@ -167,21 +261,41 @@ a:hover {
.BunError-SourceLine-number {
font-variant: tabular-nums;
display: block;
border-left: 4px solid transparent;
margin-left: -1px;
cursor: pointer;
padding-left: 14px;
padding-right: 12px;
text-align: right;
user-select: none;
text-decoration: none;
position: relative;
}
.BunError-SourceLine-number,
.BunError-SourceLine-number * {
user-select: none;
-webkit-user-select: none;
}
.BunError-SourceLine-number:active + .BunError-SourceLine-text {
background-color: #6437e3;
color: white;
}
.BunError-SourceLine-number:hover {
background-color: #7443fa;
color: white;
}
.BunError-SourceLine:hover .BunError-SourceLine-number {
border-left-color: #7443fa;
}
.BunError-SourceLine-number--empty {
color: rgb(165, 165, 165);
}
.BunError-SourceLine-number:hover {
text-decoration: underline;
}
.BunError-SourceLine-number,
.BunError-SourceLine-text {
height: 18px;
@@ -196,7 +310,7 @@ a:hover {
position: absolute;
}
.BunError-SourceLine-text--highlight {
#BunErrorOverlay-container .BunError-SourceLine-text--highlight {
color: #e33737;
}
@@ -288,12 +402,13 @@ a:hover {
.BunError-StackFrames {
display: table;
table-layout: auto;
padding: 16px 12px;
width: 100%;
box-sizing: border-box;
margin: 0 auto;
border-radius: 4px;
line-height: 1.2;
border-collapse: separate;
border-spacing: 12px 6px;
background-color: rgb(244, 244, 244);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
const UNKNOWN_FUNCTION = "<unknown>";
import type {
FallbackMessageContainer,
JSException,
Location,
Message,
SourceLine,
StackFrame,
WebsocketMessageBuildFailure,
} from "../../src/api/schema";
/**
* This parses the different stack traces and puts them into one format
* This borrows heavily from TraceKit (https://github.com/csnover/TraceKit)
*/
export function parse(stackString): StackFrame[] {
const lines = stackString.split("\n");
return lines.reduce((stack, line) => {
const parseResult =
parseChrome(line) ||
parseWinjs(line) ||
parseGecko(line) ||
parseNode(line) ||
parseJSC(line);
if (parseResult) {
stack.push(parseResult);
}
return stack;
}, []);
}
const formatFile = (file) => {
if (!file) {
return "";
}
if (file.startsWith("blob:")) {
if (globalThis["__BUN"]?.client) {
const replacement =
globalThis["__BUN"]?.client.dependencies.getFilePathFromBlob(file);
if (replacement) {
file = replacement;
}
}
}
var _file = String(file);
if (_file.startsWith(globalThis.location?.origin)) {
_file = _file.substring(globalThis.location?.origin.length);
}
while (_file.startsWith("/")) {
_file = _file.substring(1);
}
if (_file.endsWith(".bun")) {
_file = "node_modules.bun";
}
return _file;
};
const chromeRe =
/^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack|<anonymous>|\/|[a-z]:\\|\\\\).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
const chromeEvalRe = /\((\S*)(?::(\d+))(?::(\d+))\)/;
function parseChrome(line) {
const parts = chromeRe.exec(line);
if (!parts) {
return null;
}
const isNative = parts[2] && parts[2].indexOf("native") === 0; // start of line
const isEval = parts[2] && parts[2].indexOf("eval") === 0; // start of line
const submatch = chromeEvalRe.exec(parts[2]);
if (isEval && submatch != null) {
// throw out eval line/column and use top-most line/column number
parts[2] = submatch[1]; // url
parts[3] = submatch[2]; // line
parts[4] = submatch[3]; // column
}
return {
file: formatFile(!isNative ? parts[2] : null),
function_name: parts[1] || "",
position: {
line: parts[3] ? +parts[3] : null,
column_start: parts[4] ? +parts[4] : null,
},
};
}
const winjsRe =
/^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
function parseWinjs(line) {
const parts = winjsRe.exec(line);
if (!parts) {
return null;
}
return {
file: formatFile(parts[2]),
function_name: parts[1],
position: {
line: +parts[3],
column_start: parts[4] ? +parts[4] : null,
},
};
}
const geckoRe =
/^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i;
const geckoEvalRe = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
function parseGecko(line) {
const parts = geckoRe.exec(line);
if (!parts) {
return null;
}
const isEval = parts[3] && parts[3].indexOf(" > eval") > -1;
const submatch = geckoEvalRe.exec(parts[3]);
if (isEval && submatch != null) {
// throw out eval line/column and use top-most line number
parts[3] = submatch[1];
parts[4] = submatch[2];
parts[5] = null; // no column when eval
}
return {
file: formatFile(parts[3]),
function_name: parts[1] || "",
position: {
line: parts[4] ? +parts[4] : null,
column_start: parts[5] ? +parts[5] : null,
},
};
}
const javaScriptCoreRe =
/^\s*(?:([^@]*)(?:\((.*?)\))?@)?(\S.*?):(\d+)(?::(\d+))?\s*$/i;
function parseJSC(line) {
const parts = javaScriptCoreRe.exec(line);
if (!parts) {
return null;
}
return {
file: formatFile(parts[3]),
function_name: parts[1] || "",
position: {
line: +parts[4],
column_start: parts[5] ? +parts[5] : null,
},
};
}
const nodeRe =
/^\s*at (?:((?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$/i;
function parseNode(line) {
const parts = nodeRe.exec(line);
if (!parts) {
return null;
}
return {
file: formatFile(parts[2]),
function_name: parts[1] || "",
position: {
line: +parts[3],
column_start: parts[4] ? +parts[4] : null,
},
};
}