diff --git a/.size-limit.js b/.size-limit.js index 0784ca597a7c..a8969c330838 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -22,7 +22,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, - limit: '70 KB', + limit: '71 KB', }, { name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags', diff --git a/packages/browser/src/stack-parsers.ts b/packages/browser/src/stack-parsers.ts index a5a303cd1375..033d6ee3411b 100644 --- a/packages/browser/src/stack-parsers.ts +++ b/packages/browser/src/stack-parsers.ts @@ -50,14 +50,29 @@ function createFrame(filename: string, func: string, lineno?: number, colno?: nu return frame; } -// Chromium based browsers: Chrome, Brave, new Opera, new Edge +// 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 )?((?:|[-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: StackLineParserFn = 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, UNKNOWN_FUNCTION, +line, +col); + } + const parts = chromeRegex.exec(line); if (parts) { diff --git a/packages/browser/test/unit/tracekit/chromium.test.ts b/packages/browser/test/unit/tracekit/chromium.test.ts index 56b5844711e7..790f36e0ddc3 100644 --- a/packages/browser/test/unit/tracekit/chromium.test.ts +++ b/packages/browser/test/unit/tracekit/chromium.test.ts @@ -579,6 +579,7 @@ describe('Tracekit - Chrome Tests', () => { name: 'Error', stack: `Error: bad at something (http://localhost:5000/(some)/(thing)/index.html:20:16) + at http://localhost:5000/(group)/[route]/script.js:1:126 at more (http://localhost:5000/(some)/(thing)/index.html:25:7)`, }; @@ -596,6 +597,13 @@ describe('Tracekit - Chrome Tests', () => { colno: 7, in_app: true, }, + { + filename: 'http://localhost:5000/(group)/[route]/script.js', + function: '?', + lineno: 1, + colno: 126, + in_app: true, + }, { filename: 'http://localhost:5000/(some)/(thing)/index.html', function: 'something', diff --git a/packages/browser/test/unit/tracekit/firefox.test.ts b/packages/browser/test/unit/tracekit/firefox.test.ts index f75dd7ccf010..5e05930f8078 100644 --- a/packages/browser/test/unit/tracekit/firefox.test.ts +++ b/packages/browser/test/unit/tracekit/firefox.test.ts @@ -311,6 +311,41 @@ describe('Tracekit - Firefox Tests', () => { }); }); + it('should correctly parse parentheses', () => { + const PARENTHESIS_FRAME_EXCEPTION = { + message: 'aha', + name: 'Error', + stack: + 'onClick@http://localhost:3002/_next/static/chunks/app/(group)/[route]/script.js:1:644\n' + + '@http://localhost:3002/_next/static/chunks/app/(group)/[route]/script.js:1:126', + }; + + const stacktrace = exceptionFromError(parser, PARENTHESIS_FRAME_EXCEPTION); + + expect(stacktrace).toEqual({ + value: 'aha', + type: 'Error', + stacktrace: { + frames: [ + { + colno: 126, + filename: 'http://localhost:3002/_next/static/chunks/app/(group)/[route]/script.js', + function: '?', + in_app: true, + lineno: 1, + }, + { + colno: 644, + filename: 'http://localhost:3002/_next/static/chunks/app/(group)/[route]/script.js', + function: 'onClick', + in_app: true, + lineno: 1, + }, + ], + }, + }); + }); + it('should parse Firefox errors with `file` inside an identifier', () => { const FIREFOX_FILE_IN_IDENTIFIER = { stack: diff --git a/packages/browser/test/unit/tracekit/safari.test.ts b/packages/browser/test/unit/tracekit/safari.test.ts index 657ffc7daecc..470ed1d7b8dc 100644 --- a/packages/browser/test/unit/tracekit/safari.test.ts +++ b/packages/browser/test/unit/tracekit/safari.test.ts @@ -320,4 +320,39 @@ describe('Tracekit - Safari Tests', () => { }, }); }); + + it('should correctly parse parentheses', () => { + const PARENTHESIS_FRAME_EXCEPTION = { + message: 'aha', + name: 'Error', + stack: + '@http://localhost:3000/(group)/[route]/script.js:1:131\n' + + 'global code@http://localhost:3000/(group)/[route]/script.js:1:334', + }; + + const ex = exceptionFromError(parser, PARENTHESIS_FRAME_EXCEPTION); + + expect(ex).toEqual({ + value: 'aha', + type: 'Error', + stacktrace: { + frames: [ + { + colno: 334, + filename: 'http://localhost:3000/(group)/[route]/script.js', + function: 'global code', + in_app: true, + lineno: 1, + }, + { + colno: 131, + filename: 'http://localhost:3000/(group)/[route]/script.js', + function: '?', + in_app: true, + lineno: 1, + }, + ], + }, + }); + }); });