UNPKG

5.8 kBJavaScriptView Raw
1import { createStackParser } from '@sentry/utils';
2
3// global reference to slice
4const UNKNOWN_FUNCTION = '?';
5
6const OPERA10_PRIORITY = 10;
7const OPERA11_PRIORITY = 20;
8const CHROME_PRIORITY = 30;
9const WINJS_PRIORITY = 40;
10const GECKO_PRIORITY = 50;
11
12function createFrame(filename, func, lineno, colno) {
13 const frame = {
14 filename,
15 function: func,
16 in_app: true, // All browser frames are considered in_app
17 };
18
19 if (lineno !== undefined) {
20 frame.lineno = lineno;
21 }
22
23 if (colno !== undefined) {
24 frame.colno = colno;
25 }
26
27 return frame;
28}
29
30// Chromium based browsers: Chrome, Brave, new Opera, new Edge
31const chromeRegex =
32 /^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
33const chromeEvalRegex = /\((\S*)(?::(\d+))(?::(\d+))\)/;
34
35const chrome = line => {
36 const parts = chromeRegex.exec(line);
37
38 if (parts) {
39 const isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line
40
41 if (isEval) {
42 const subMatch = chromeEvalRegex.exec(parts[2]);
43
44 if (subMatch) {
45 // throw out eval line/column and use top-most line/column number
46 parts[2] = subMatch[1]; // url
47 parts[3] = subMatch[2]; // line
48 parts[4] = subMatch[3]; // column
49 }
50 }
51
52 // Kamil: One more hack won't hurt us right? Understanding and adding more rules on top of these regexps right now
53 // would be way too time consuming. (TODO: Rewrite whole RegExp to be more readable)
54 const [func, filename] = extractSafariExtensionDetails(parts[1] || UNKNOWN_FUNCTION, parts[2]);
55
56 return createFrame(filename, func, parts[3] ? +parts[3] : undefined, parts[4] ? +parts[4] : undefined);
57 }
58
59 return;
60};
61
62const chromeStackLineParser = [CHROME_PRIORITY, chrome];
63
64// gecko regex: `(?:bundle|\d+\.js)`: `bundle` is for react native, `\d+\.js` also but specifically for ram bundles because it
65// generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js
66// We need this specific case for now because we want no other regex to match.
67const geckoREgex =
68 /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
69const geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
70
71const gecko = line => {
72 const parts = geckoREgex.exec(line);
73
74 if (parts) {
75 const isEval = parts[3] && parts[3].indexOf(' > eval') > -1;
76 if (isEval) {
77 const subMatch = geckoEvalRegex.exec(parts[3]);
78
79 if (subMatch) {
80 // throw out eval line/column and use top-most line number
81 parts[1] = parts[1] || 'eval';
82 parts[3] = subMatch[1];
83 parts[4] = subMatch[2];
84 parts[5] = ''; // no column when eval
85 }
86 }
87
88 let filename = parts[3];
89 let func = parts[1] || UNKNOWN_FUNCTION;
90 [func, filename] = extractSafariExtensionDetails(func, filename);
91
92 return createFrame(filename, func, parts[4] ? +parts[4] : undefined, parts[5] ? +parts[5] : undefined);
93 }
94
95 return;
96};
97
98const geckoStackLineParser = [GECKO_PRIORITY, gecko];
99
100const winjsRegex = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:[-a-z]+):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
101
102const winjs = line => {
103 const parts = winjsRegex.exec(line);
104
105 return parts
106 ? createFrame(parts[2], parts[1] || UNKNOWN_FUNCTION, +parts[3], parts[4] ? +parts[4] : undefined)
107 : undefined;
108};
109
110const winjsStackLineParser = [WINJS_PRIORITY, winjs];
111
112const opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i;
113
114const opera10 = line => {
115 const parts = opera10Regex.exec(line);
116 return parts ? createFrame(parts[2], parts[3] || UNKNOWN_FUNCTION, +parts[1]) : undefined;
117};
118
119const opera10StackLineParser = [OPERA10_PRIORITY, opera10];
120
121const opera11Regex =
122 / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^)]+))\(.*\))? in (.*):\s*$/i;
123
124const opera11 = line => {
125 const parts = opera11Regex.exec(line);
126 return parts ? createFrame(parts[5], parts[3] || parts[4] || UNKNOWN_FUNCTION, +parts[1], +parts[2]) : undefined;
127};
128
129const opera11StackLineParser = [OPERA11_PRIORITY, opera11];
130
131const defaultStackLineParsers = [chromeStackLineParser, geckoStackLineParser, winjsStackLineParser];
132
133const defaultStackParser = createStackParser(...defaultStackLineParsers);
134
135/**
136 * Safari web extensions, starting version unknown, can produce "frames-only" stacktraces.
137 * What it means, is that instead of format like:
138 *
139 * Error: wat
140 * at function@url:row:col
141 * at function@url:row:col
142 * at function@url:row:col
143 *
144 * it produces something like:
145 *
146 * function@url:row:col
147 * function@url:row:col
148 * function@url:row:col
149 *
150 * Because of that, it won't be captured by `chrome` RegExp and will fall into `Gecko` branch.
151 * This function is extracted so that we can use it in both places without duplicating the logic.
152 * Unfortunately "just" changing RegExp is too complicated now and making it pass all tests
153 * and fix this case seems like an impossible, or at least way too time-consuming task.
154 */
155const extractSafariExtensionDetails = (func, filename) => {
156 const isSafariExtension = func.indexOf('safari-extension') !== -1;
157 const isSafariWebExtension = func.indexOf('safari-web-extension') !== -1;
158
159 return isSafariExtension || isSafariWebExtension
160 ? [
161 func.indexOf('@') !== -1 ? func.split('@')[0] : UNKNOWN_FUNCTION,
162 isSafariExtension ? `safari-extension:${filename}` : `safari-web-extension:${filename}`,
163 ]
164 : [func, filename];
165};
166
167export { chromeStackLineParser, defaultStackLineParsers, defaultStackParser, geckoStackLineParser, opera10StackLineParser, opera11StackLineParser, winjsStackLineParser };
168//# sourceMappingURL=stack-parsers.js.map