1 | import { createStackParser } from '@sentry/utils';
|
2 |
|
3 |
|
4 | const UNKNOWN_FUNCTION = '?';
|
5 |
|
6 | const OPERA10_PRIORITY = 10;
|
7 | const OPERA11_PRIORITY = 20;
|
8 | const CHROME_PRIORITY = 30;
|
9 | const WINJS_PRIORITY = 40;
|
10 | const GECKO_PRIORITY = 50;
|
11 |
|
12 | function 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 |
|
31 | const chromeRegex =
|
32 | /^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
|
33 | const chromeEvalRegex = /\((\S*)(?::(\d+))(?::(\d+))\)/;
|
34 |
|
35 | const 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 |
|
62 | const 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.
|
67 | const geckoREgex =
|
68 | /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
|
69 | const geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
|
70 |
|
71 | const 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 |
|
98 | const geckoStackLineParser = [GECKO_PRIORITY, gecko];
|
99 |
|
100 | const winjsRegex = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:[-a-z]+):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
|
101 |
|
102 | const 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 |
|
110 | const winjsStackLineParser = [WINJS_PRIORITY, winjs];
|
111 |
|
112 | const opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i;
|
113 |
|
114 | const opera10 = line => {
|
115 | const parts = opera10Regex.exec(line);
|
116 | return parts ? createFrame(parts[2], parts[3] || UNKNOWN_FUNCTION, +parts[1]) : undefined;
|
117 | };
|
118 |
|
119 | const opera10StackLineParser = [OPERA10_PRIORITY, opera10];
|
120 |
|
121 | const opera11Regex =
|
122 | / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^)]+))\(.*\))? in (.*):\s*$/i;
|
123 |
|
124 | const 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 |
|
129 | const opera11StackLineParser = [OPERA11_PRIORITY, opera11];
|
130 |
|
131 | const defaultStackLineParsers = [chromeStackLineParser, geckoStackLineParser, winjsStackLineParser];
|
132 |
|
133 | const 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 | */
|
155 | const 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 |
|
167 | export { chromeStackLineParser, defaultStackLineParsers, defaultStackParser, geckoStackLineParser, opera10StackLineParser, opera11StackLineParser, winjsStackLineParser };
|
168 | //# sourceMappingURL=stack-parsers.js.map
|