@jbrowse/core
Version:
JBrowse 2 core libraries used by plugins
150 lines (149 loc) • 6.74 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = ErrorMessageStackTraceDialog;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const material_1 = require("@mui/material");
const copy_to_clipboard_1 = __importDefault(require("copy-to-clipboard"));
const source_map_js_1 = require("source-map-js");
const mui_1 = require("tss-react/mui");
const Dialog_1 = __importDefault(require("./Dialog"));
const ExternalLink_1 = __importDefault(require("./ExternalLink"));
const LoadingEllipses_1 = __importDefault(require("./LoadingEllipses"));
async function myfetch(uri) {
const res = await fetch(uri);
if (!res.ok) {
throw new Error(`HTTP ${res.status} fetching ${uri}: ${await res.text()}`);
}
return res;
}
async function myfetchjson(uri) {
const res = await myfetch(uri);
return res.json();
}
async function myfetchtext(uri) {
const res = await myfetch(uri);
return res.text();
}
const sourceMaps = {};
async function getSourceMapFromUri(uri) {
var _a;
if (sourceMaps[uri] !== undefined) {
return sourceMaps[uri];
}
const uriQuery = new URL(uri).search;
const currentScriptContent = await myfetchtext(uri);
let mapUri = ((_a = new RegExp(/\/\/# sourceMappingURL=(.*)/).exec(currentScriptContent)) === null || _a === void 0 ? void 0 : _a[1]) ||
'';
mapUri = new URL(mapUri, uri).href + uriQuery;
const data = await myfetchjson(mapUri);
const map = new source_map_js_1.SourceMapConsumer(data);
sourceMaps[uri] = map;
return map;
}
async function mapStackTrace(stack) {
const stackLines = stack.split('\n');
const mappedStack = [];
for (const line of stackLines) {
const match = new RegExp(/(.*)(https?:\/\/.*):(\d+):(\d+)/).exec(line);
if (match === null) {
mappedStack.push(line);
continue;
}
const uri = match[2];
const consumer = await getSourceMapFromUri(uri);
const originalPosition = consumer.originalPositionFor({
line: Number.parseInt(match[3]),
column: Number.parseInt(match[4]),
});
if (!originalPosition.source ||
!originalPosition.line ||
!originalPosition.column) {
mappedStack.push(line);
continue;
}
mappedStack.push(`${originalPosition.source}:${originalPosition.line}:${originalPosition.column + 1} (${match[1].trim()})`);
}
return mappedStack.join('\n');
}
const MAX_ERR_LEN = 10000;
function stripMessage(trace, error) {
if (trace.startsWith('Error:')) {
const err = `${error}`;
return trace.slice(err.length);
}
else {
return trace;
}
}
const useStyles = (0, mui_1.makeStyles)()(theme => ({
pre: {
background: (0, material_1.alpha)(theme.palette.error.main, 0.2),
border: `1px solid ${theme.palette.divider}`,
overflow: 'auto',
margin: 20,
maxHeight: 300,
},
}));
function Contents({ text, extra }) {
const { classes } = useStyles();
const err = encodeURIComponent(`${[
'I got this error from JBrowse, here is the stack trace:\n',
'```',
text,
'```',
extra ? `supporting data: ${JSON.stringify(extra, null, 2)}` : '',
].join('\n')}\n`);
const err2 = [
text,
extra ? `supporting data: ${JSON.stringify(extra, null, 2)}` : '',
].join('\n');
const email = 'jbrowse2@berkeley.edu';
const githubLink = `https://github.com/GMOD/jbrowse-components/issues/new?labels=bug&title=JBrowse+issue&body=${err}`;
const emailLink = `mailto:${email}?subject=JBrowse%202%20error&body=${err}`;
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(material_1.Typography, { children: ["Post a new issue at", ' ', (0, jsx_runtime_1.jsx)(ExternalLink_1.default, { href: githubLink, children: "GitHub" }), " or send an email to ", (0, jsx_runtime_1.jsx)(ExternalLink_1.default, { href: emailLink, children: email }), ' '] }), (0, jsx_runtime_1.jsx)("pre", { className: classes.pre, children: err2 })] }));
}
function ErrorMessageStackTraceDialog({ error, onClose, extra, }) {
const [mappedStackTrace, setMappedStackTrace] = (0, react_1.useState)();
const [secondaryError, setSecondaryError] = (0, react_1.useState)();
const [clicked, setClicked] = (0, react_1.useState)(false);
const stackTracePreProcessed = `${typeof error === 'object' && error !== null && 'stack' in error ? error.stack : ''}`;
const errorText = error ? `${error}` : '';
const stackTrace = stripMessage(stackTracePreProcessed, errorText);
(0, react_1.useEffect)(() => {
;
(async () => {
try {
const res = await mapStackTrace(stackTrace);
setMappedStackTrace(res);
}
catch (e) {
console.error(e);
setMappedStackTrace(stackTrace);
setSecondaryError(e);
}
})();
}, [stackTrace]);
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',
window.JBrowseSession ? `JBrowse ${window.JBrowseSession.version}` : '',
]
.filter(f => !!f)
.join('\n');
return ((0, jsx_runtime_1.jsxs)(Dialog_1.default, { open: true, onClose: onClose, title: "Stack trace", maxWidth: "xl", children: [(0, jsx_runtime_1.jsx)(material_1.DialogContent, { children: mappedStackTrace === undefined ? ((0, jsx_runtime_1.jsx)(LoadingEllipses_1.default, { variant: "h6" })) : ((0, jsx_runtime_1.jsx)(Contents, { text: errorBoxText, extra: extra })) }), (0, jsx_runtime_1.jsxs)(material_1.DialogActions, { children: [(0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", color: "secondary", onClick: () => {
(0, copy_to_clipboard_1.default)(errorBoxText);
setClicked(true);
setTimeout(() => {
setClicked(false);
}, 1000);
}, children: clicked ? 'Copied!' : 'Copy stack trace to clipboard' }), (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", color: "primary", onClick: onClose, children: "Close" })] })] }));
}