UNPKG

@jbrowse/core

Version:

JBrowse 2 core libraries used by plugins

150 lines (149 loc) 6.74 kB
"use strict"; 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" })] })] })); }