UNPKG

@sentry/browser

Version:
198 lines (153 loc) 7.22 kB
Object.defineProperty(exports, '__esModule', { value: true }); const core = require('@sentry/core'); // This was originally forked from https://github.com/csnover/TraceKit, and was largely // re - written as part of raven - js. // // This code was later copied to the JavaScript mono - repo and further modified and // refactored over the years. const OPERA10_PRIORITY = 10; const OPERA11_PRIORITY = 20; const CHROME_PRIORITY = 30; const WINJS_PRIORITY = 40; const GECKO_PRIORITY = 50; function createFrame(filename, func, lineno, colno) { const frame = { filename, function: func === '<anonymous>' ? core.UNKNOWN_FUNCTION : func, in_app: true, // All browser frames are considered in_app }; if (lineno !== undefined) { frame.lineno = lineno; } if (colno !== undefined) { frame.colno = colno; } return frame; } // This regex matches frames that have no function name (ie. are at the top level of a module). // For example "at http://localhost:5000//script.js:1:126" // Frames _with_ function names usually look as follows: "at commitLayoutEffects (react-dom.development.js:23426:1)" const chromeRegexNoFnName = /^\s*at (\S+?)(?::(\d+))(?::(\d+))\s*$/i; // This regex matches all the frames that have a function name. const chromeRegex = /^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i; const chromeEvalRegex = /\((\S*)(?::(\d+))(?::(\d+))\)/; // Chromium based browsers: Chrome, Brave, new Opera, new Edge // We cannot call this variable `chrome` because it can conflict with global `chrome` variable in certain environments // See: https://github.com/getsentry/sentry-javascript/issues/6880 const chromeStackParserFn = line => { // If the stack line has no function name, we need to parse it differently const noFnParts = chromeRegexNoFnName.exec(line) ; if (noFnParts) { const [, filename, line, col] = noFnParts; return createFrame(filename, core.UNKNOWN_FUNCTION, +line, +col); } const parts = chromeRegex.exec(line) ; if (parts) { const isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line if (isEval) { const subMatch = chromeEvalRegex.exec(parts[2]) ; if (subMatch) { // 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 } } // Kamil: One more hack won't hurt us right? Understanding and adding more rules on top of these regexps right now // would be way too time consuming. (TODO: Rewrite whole RegExp to be more readable) const [func, filename] = extractSafariExtensionDetails(parts[1] || core.UNKNOWN_FUNCTION, parts[2]); return createFrame(filename, func, parts[3] ? +parts[3] : undefined, parts[4] ? +parts[4] : undefined); } return; }; const chromeStackLineParser = [CHROME_PRIORITY, chromeStackParserFn]; // gecko regex: `(?:bundle|\d+\.js)`: `bundle` is for react native, `\d+\.js` also but specifically for ram bundles because it // generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js // We need this specific case for now because we want no other regex to match. const geckoREgex = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i; const geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i; const gecko = line => { const parts = geckoREgex.exec(line) ; if (parts) { const isEval = parts[3] && parts[3].indexOf(' > eval') > -1; if (isEval) { const subMatch = geckoEvalRegex.exec(parts[3]) ; if (subMatch) { // throw out eval line/column and use top-most line number parts[1] = parts[1] || 'eval'; parts[3] = subMatch[1]; parts[4] = subMatch[2]; parts[5] = ''; // no column when eval } } let filename = parts[3]; let func = parts[1] || core.UNKNOWN_FUNCTION; [func, filename] = extractSafariExtensionDetails(func, filename); return createFrame(filename, func, parts[4] ? +parts[4] : undefined, parts[5] ? +parts[5] : undefined); } return; }; const geckoStackLineParser = [GECKO_PRIORITY, gecko]; const winjsRegex = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:[-a-z]+):.*?):(\d+)(?::(\d+))?\)?\s*$/i; const winjs = line => { const parts = winjsRegex.exec(line) ; return parts ? createFrame(parts[2], parts[1] || core.UNKNOWN_FUNCTION, +parts[3], parts[4] ? +parts[4] : undefined) : undefined; }; const winjsStackLineParser = [WINJS_PRIORITY, winjs]; const opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i; const opera10 = line => { const parts = opera10Regex.exec(line) ; return parts ? createFrame(parts[2], parts[3] || core.UNKNOWN_FUNCTION, +parts[1]) : undefined; }; const opera10StackLineParser = [OPERA10_PRIORITY, opera10]; const opera11Regex = / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^)]+))\(.*\))? in (.*):\s*$/i; const opera11 = line => { const parts = opera11Regex.exec(line) ; return parts ? createFrame(parts[5], parts[3] || parts[4] || core.UNKNOWN_FUNCTION, +parts[1], +parts[2]) : undefined; }; const opera11StackLineParser = [OPERA11_PRIORITY, opera11]; const defaultStackLineParsers = [chromeStackLineParser, geckoStackLineParser]; const defaultStackParser = core.createStackParser(...defaultStackLineParsers); /** * Safari web extensions, starting version unknown, can produce "frames-only" stacktraces. * What it means, is that instead of format like: * * Error: wat * at function@url:row:col * at function@url:row:col * at function@url:row:col * * it produces something like: * * function@url:row:col * function@url:row:col * function@url:row:col * * Because of that, it won't be captured by `chrome` RegExp and will fall into `Gecko` branch. * This function is extracted so that we can use it in both places without duplicating the logic. * Unfortunately "just" changing RegExp is too complicated now and making it pass all tests * and fix this case seems like an impossible, or at least way too time-consuming task. */ const extractSafariExtensionDetails = (func, filename) => { const isSafariExtension = func.indexOf('safari-extension') !== -1; const isSafariWebExtension = func.indexOf('safari-web-extension') !== -1; return isSafariExtension || isSafariWebExtension ? [ func.indexOf('@') !== -1 ? (func.split('@')[0] ) : core.UNKNOWN_FUNCTION, isSafariExtension ? `safari-extension:${filename}` : `safari-web-extension:${filename}`, ] : [func, filename]; }; exports.chromeStackLineParser = chromeStackLineParser; exports.defaultStackLineParsers = defaultStackLineParsers; exports.defaultStackParser = defaultStackParser; exports.geckoStackLineParser = geckoStackLineParser; exports.opera10StackLineParser = opera10StackLineParser; exports.opera11StackLineParser = opera11StackLineParser; exports.winjsStackLineParser = winjsStackLineParser; //# sourceMappingURL=stack-parsers.js.map