@jbrowse/core
Version:
JBrowse 2 core libraries used by plugins
109 lines (108 loc) • 4.72 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useState } from 'react';
import CloseIcon from '@mui/icons-material/Close';
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, } from '@mui/material';
import { SourceMapConsumer } from 'source-map-js';
import ErrorMessageStackTraceContents from "./ErrorMessageStackTraceContents.js";
import LoadingEllipses from "./LoadingEllipses.js";
async function myfetchtext(uri) {
const res = await fetch(uri);
if (!res.ok) {
throw new Error(`HTTP ${res.status} fetching ${uri}: ${await res.text()}`);
}
return res.text();
}
async function myfetchjson(uri) {
return JSON.parse(await myfetchtext(uri));
}
const sourceMaps = {};
const sourceMappingUrlRe = /\/\/# sourceMappingURL=(.*)/;
const stackLineRe = /(.*)((?:https?|file):\/\/.*):(\d+):(\d+)/;
async function getSourceMapFromUri(uri) {
if (sourceMaps[uri]) {
return sourceMaps[uri];
}
const uriQuery = new URL(uri).search;
const currentScriptContent = await myfetchtext(uri);
const mapUri = sourceMappingUrlRe.exec(currentScriptContent)?.[1] ?? '';
const data = await myfetchjson(new URL(mapUri, uri).href + uriQuery);
const map = new SourceMapConsumer(data);
sourceMaps[uri] = map;
return map;
}
async function mapStackTrace(stack) {
const mappedStack = [];
for (const line of stack.split('\n')) {
const match = stackLineRe.exec(line);
if (!match) {
mappedStack.push(line);
continue;
}
const uri = match[2];
const consumer = await getSourceMapFromUri(uri);
const pos = consumer.originalPositionFor({
line: +match[3],
column: +match[4],
});
if (!pos.source || !pos.line || !pos.column) {
mappedStack.push(line);
continue;
}
mappedStack.push(`${pos.source}:${pos.line}:${pos.column + 1} (${match[1].trim()})`);
}
return mappedStack.join('\n');
}
const MAX_ERR_LEN = 10_000;
function stripMessage(trace, error) {
return trace.startsWith('Error:') ? trace.slice(`${error}`.length) : trace;
}
function getStackTrace(error) {
return typeof error === 'object' && error !== null && 'stack' in error
? `${error.stack}`
: '';
}
export default function ErrorMessageStackTraceDialog({ error, onClose, extra, }) {
const [mappedStackTrace, setMappedStackTrace] = useState();
const [secondaryError, setSecondaryError] = useState();
const [clicked, setClicked] = useState(false);
const errorText = error ? `${error}` : '';
const stackTrace = stripMessage(getStackTrace(error), errorText);
useEffect(() => {
;
(async () => {
try {
setMappedStackTrace(await mapStackTrace(stackTrace));
}
catch (e) {
console.error(e);
setMappedStackTrace(stackTrace);
setSecondaryError(e);
}
})();
}, [stackTrace]);
const version = window.JBrowseSession?.version;
const errorBoxText = [
secondaryError
? 'Error loading source map, showing raw stack trace below:'
: '',
errorText.length > MAX_ERR_LEN
? `${errorText.slice(0, MAX_ERR_LEN)}...`
: errorText,
mappedStackTrace || 'No stack trace available',
version ? `JBrowse ${version}` : '',
]
.filter(Boolean)
.join('\n');
return (_jsxs(Dialog, { open: true, onClose: onClose, maxWidth: "xl", children: [_jsxs(DialogTitle, { children: ["Stack trace", _jsx(IconButton, { onClick: onClose, sx: {
position: 'absolute',
right: 8,
top: 8,
}, children: _jsx(CloseIcon, {}) })] }), _jsx(DialogContent, { children: mappedStackTrace === undefined ? (_jsx(LoadingEllipses, { variant: "h6" })) : (_jsx(ErrorMessageStackTraceContents, { text: errorBoxText, extra: extra })) }), _jsxs(DialogActions, { children: [_jsx(Button, { variant: "contained", color: "secondary", onClick: async () => {
const { default: copy } = await import('copy-to-clipboard');
copy(errorBoxText);
setClicked(true);
setTimeout(() => {
setClicked(false);
}, 1000);
}, children: clicked ? 'Copied!' : 'Copy stack trace to clipboard' }), _jsx(Button, { variant: "contained", color: "primary", onClick: onClose, children: "Close" })] })] }));
}