Skip to content

Commit 8723236

Browse files
committed
split into functions
1 parent 245eee9 commit 8723236

File tree

1 file changed

+128
-96
lines changed

1 file changed

+128
-96
lines changed

packages/browser/src/tracekit.ts

Lines changed: 128 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
12
import { StackFrame } from '@sentry/types';
23

3-
/**
4-
* This was originally forked from https://github.com/occ/TraceKit, but has since been
5-
* largely modified and is now maintained as part of Sentry JS SDK.
6-
*/
7-
8-
/* eslint-disable @typescript-eslint/no-unsafe-member-access, max-lines */
4+
// global reference to slice
5+
const UNKNOWN_FUNCTION = '?';
96

107
/**
118
* An object representing a JavaScript stack trace.
@@ -20,27 +17,133 @@ export interface StackTrace {
2017
stack: StackFrame[];
2118
}
2219

23-
// global reference to slice
24-
const UNKNOWN_FUNCTION = '?';
20+
type StackLineParser = (line: string) => StackFrame | undefined;
2521

2622
// Chromium based browsers: Chrome, Brave, new Opera, new Edge
27-
const chrome =
23+
const chromeRegex =
2824
/^\s*at (?:(.*?) ?\((?:address at )?)?((?:file|https?|blob|chrome-extension|address|native|eval|webpack|<anonymous>|[-a-z]+:|.*bundle|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
25+
const chromeEvalRegex = /\((\S*)(?::(\d+))(?::(\d+))\)/;
26+
27+
const chrome: StackLineParser = line => {
28+
const parts = chromeRegex.exec(line);
29+
30+
if (parts) {
31+
const isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line
32+
33+
if (isEval) {
34+
const subMatch = chromeEvalRegex.exec(parts[2]);
35+
36+
if (subMatch) {
37+
// throw out eval line/column and use top-most line/column number
38+
parts[2] = subMatch[1]; // url
39+
parts[3] = subMatch[2]; // line
40+
parts[4] = subMatch[3]; // column
41+
}
42+
}
43+
44+
// Kamil: One more hack won't hurt us right? Understanding and adding more rules on top of these regexps right now
45+
// would be way too time consuming. (TODO: Rewrite whole RegExp to be more readable)
46+
const [func, filename] = extractSafariExtensionDetails(parts[1] || UNKNOWN_FUNCTION, parts[2]);
47+
48+
return {
49+
filename,
50+
function: func,
51+
lineno: parts[3] ? +parts[3] : undefined,
52+
colno: parts[4] ? +parts[4] : undefined,
53+
};
54+
}
55+
56+
return;
57+
};
58+
2959
// gecko regex: `(?:bundle|\d+\.js)`: `bundle` is for react native, `\d+\.js` also but specifically for ram bundles because it
3060
// generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js
3161
// We need this specific case for now because we want no other regex to match.
32-
const gecko =
62+
const geckoREgex =
3363
/^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:file|https?|blob|chrome|webpack|resource|moz-extension|capacitor).*?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
34-
const winjs =
64+
const geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
65+
66+
const gecko: StackLineParser = line => {
67+
const parts = geckoREgex.exec(line);
68+
69+
if (parts) {
70+
const isEval = parts[3] && parts[3].indexOf(' > eval') > -1;
71+
if (isEval) {
72+
const subMatch = geckoEvalRegex.exec(parts[3]);
73+
74+
if (subMatch) {
75+
// throw out eval line/column and use top-most line number
76+
parts[1] = parts[1] || `eval`;
77+
parts[3] = subMatch[1];
78+
parts[4] = subMatch[2];
79+
parts[5] = ''; // no column when eval
80+
}
81+
}
82+
83+
let filename = parts[3];
84+
let func = parts[1] || UNKNOWN_FUNCTION;
85+
[func, filename] = extractSafariExtensionDetails(func, filename);
86+
87+
return {
88+
filename,
89+
function: func,
90+
lineno: parts[4] ? +parts[4] : undefined,
91+
colno: parts[5] ? +parts[5] : undefined,
92+
};
93+
}
94+
95+
return;
96+
};
97+
98+
const winjsRegex =
3599
/^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
36-
const geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
37-
const chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/;
38-
// Based on our own mapping pattern - https://github.com/getsentry/sentry/blob/9f08305e09866c8bd6d0c24f5b0aabdd7dd6c59c/src/sentry/lang/javascript/errormapping.py#L83-L108
39-
const reactMinifiedRegexp = /Minified React error #\d+;/i;
100+
101+
const winjs: StackLineParser = line => {
102+
const parts = winjsRegex.exec(line);
103+
104+
return parts
105+
? {
106+
filename: parts[2],
107+
function: parts[1] || UNKNOWN_FUNCTION,
108+
lineno: +parts[3],
109+
colno: parts[4] ? +parts[4] : undefined,
110+
}
111+
: undefined;
112+
};
113+
40114
const opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i;
115+
116+
const opera10: StackLineParser = line => {
117+
const parts = opera10Regex.exec(line);
118+
119+
return parts
120+
? {
121+
filename: parts[2],
122+
function: parts[3] || UNKNOWN_FUNCTION,
123+
lineno: +parts[1],
124+
}
125+
: undefined;
126+
};
127+
41128
const opera11Regex =
42129
/ line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^)]+))\(.*\))? in (.*):\s*$/i;
43130

131+
const opera11: StackLineParser = line => {
132+
const parts = opera11Regex.exec(line);
133+
134+
return parts
135+
? {
136+
filename: parts[5],
137+
function: parts[3] || parts[4] || UNKNOWN_FUNCTION,
138+
lineno: +parts[1],
139+
colno: +parts[2],
140+
}
141+
: undefined;
142+
};
143+
144+
// Based on our own mapping pattern - https://github.com/getsentry/sentry/blob/9f08305e09866c8bd6d0c24f5b0aabdd7dd6c59c/src/sentry/lang/javascript/errormapping.py#L83-L108
145+
const reactMinifiedRegexp = /Minified React error #\d+;/i;
146+
44147
/** JSDoc */
45148
export function computeStackTrace(ex: Error & { framesToPop?: number; stacktrace?: string }): StackTrace {
46149
let frames: StackFrame[] = [];
@@ -60,7 +163,16 @@ export function computeStackTrace(ex: Error & { framesToPop?: number; stacktrace
60163
// reliably in other circumstances.
61164
const stacktrace = ex.stacktrace || ex.stack || '';
62165

63-
frames = parseFrames(stacktrace);
166+
for (const line of stacktrace.split('\n')) {
167+
for (const parser of [opera10, opera11, chrome, winjs, gecko]) {
168+
const frame = parser(line);
169+
170+
if (frame) {
171+
frames.push(frame);
172+
break;
173+
}
174+
}
175+
}
64176
} catch (e) {
65177
// no-empty
66178
}
@@ -76,86 +188,6 @@ export function computeStackTrace(ex: Error & { framesToPop?: number; stacktrace
76188
};
77189
}
78190

79-
/** JSDoc */
80-
// eslint-disable-next-line complexity
81-
function parseFrames(stackString: string): StackFrame[] {
82-
const frames: StackFrame[] = [];
83-
const lines = stackString.split('\n');
84-
let isEval;
85-
let submatch;
86-
let parts;
87-
let element: StackFrame | undefined;
88-
89-
for (const line of lines) {
90-
if ((parts = opera10Regex.exec(line))) {
91-
element = {
92-
filename: parts[2],
93-
function: parts[3] || UNKNOWN_FUNCTION,
94-
lineno: +parts[1],
95-
};
96-
} else if ((parts = opera11Regex.exec(line))) {
97-
element = {
98-
filename: parts[5],
99-
function: parts[3] || parts[4] || UNKNOWN_FUNCTION,
100-
lineno: +parts[1],
101-
colno: +parts[2],
102-
};
103-
} else if ((parts = chrome.exec(line))) {
104-
isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line
105-
if (isEval && (submatch = chromeEval.exec(parts[2]))) {
106-
// throw out eval line/column and use top-most line/column number
107-
parts[2] = submatch[1]; // url
108-
parts[3] = submatch[2]; // line
109-
parts[4] = submatch[3]; // column
110-
}
111-
112-
// Kamil: One more hack won't hurt us right? Understanding and adding more rules on top of these regexps right now
113-
// would be way too time consuming. (TODO: Rewrite whole RegExp to be more readable)
114-
const [func, filename] = extractSafariExtensionDetails(parts[1] || UNKNOWN_FUNCTION, parts[2]);
115-
116-
element = {
117-
filename,
118-
function: func,
119-
lineno: parts[3] ? +parts[3] : undefined,
120-
colno: parts[4] ? +parts[4] : undefined,
121-
};
122-
} else if ((parts = winjs.exec(line))) {
123-
element = {
124-
filename: parts[2],
125-
function: parts[1] || UNKNOWN_FUNCTION,
126-
lineno: +parts[3],
127-
colno: parts[4] ? +parts[4] : undefined,
128-
};
129-
} else if ((parts = gecko.exec(line))) {
130-
isEval = parts[3] && parts[3].indexOf(' > eval') > -1;
131-
if (isEval && (submatch = geckoEval.exec(parts[3]))) {
132-
// throw out eval line/column and use top-most line number
133-
parts[1] = parts[1] || `eval`;
134-
parts[3] = submatch[1];
135-
parts[4] = submatch[2];
136-
parts[5] = ''; // no column when eval
137-
}
138-
139-
let filename = parts[3];
140-
let func = parts[1] || UNKNOWN_FUNCTION;
141-
[func, filename] = extractSafariExtensionDetails(func, filename);
142-
143-
element = {
144-
filename,
145-
function: func,
146-
lineno: parts[4] ? +parts[4] : undefined,
147-
colno: parts[5] ? +parts[5] : undefined,
148-
};
149-
} else {
150-
continue;
151-
}
152-
153-
frames.push(element);
154-
}
155-
156-
return frames;
157-
}
158-
159191
/**
160192
* Safari web extensions, starting version unknown, can produce "frames-only" stacktraces.
161193
* What it means, is that instead of format like:

0 commit comments

Comments
 (0)