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