Skip to content

Commit 15abee8

Browse files
committed
refactor to capture stack trace
1 parent 3f9bfde commit 15abee8

File tree

5 files changed

+30
-50
lines changed

5 files changed

+30
-50
lines changed

packages/next/src/client/components/globals/intercept-console-error.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import isError from '../../../lib/is-error'
22
import { isNextRouterError } from '../is-next-router-error'
3-
import { stripStackByFrame } from '../react-dev-overlay/internal/helpers/strip-stack-frame'
3+
import { captureStackTrace } from '../react-dev-overlay/internal/helpers/capture-stack-trace'
44
import { handleClientError } from '../react-dev-overlay/internal/helpers/use-error-handler'
55

66
const NEXT_CONSOLE_STACK_FRAME = 'next-console-stack-frame'
77

8-
const stripBeforeNextConsoleFrame = (stack: string) =>
9-
stripStackByFrame(stack, NEXT_CONSOLE_STACK_FRAME, false)
10-
118
export const originConsoleError = window.console.error
129

1310
// Patch console.error to collect information about hydration errors
@@ -38,17 +35,15 @@ export function patchConsoleError() {
3835
if (!isNextRouterError(maybeError)) {
3936
if (process.env.NODE_ENV !== 'production') {
4037
// Create an origin stack that pointing to the origin location of the error
41-
const captureStackErrorStackTrace = new Error().stack || ''
42-
const strippedStack = stripBeforeNextConsoleFrame(
43-
captureStackErrorStackTrace
44-
)
38+
if (!isReplayedError && isError(maybeError)) {
39+
captureStackTrace(maybeError)
40+
}
4541

4642
handleClientError(
4743
// replayed errors have their own complex format string that should be used,
4844
// but if we pass the error directly, `handleClientError` will ignore it
4945
maybeError,
50-
args,
51-
isReplayedError ? '' : strippedStack
46+
args
5247
)
5348
}
5449

packages/next/src/client/components/react-dev-overlay/internal/helpers/stitched-error.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import React from 'react'
22
import isError from '../../../../../lib/is-error'
3-
import { stripStackByFrame } from './strip-stack-frame'
4-
5-
const REACT_ERROR_STACK_BOTTOM_FRAME = 'react-stack-bottom-frame'
6-
7-
const stripAfterReactBottomFrame = (stack: string) =>
8-
stripStackByFrame(stack, REACT_ERROR_STACK_BOTTOM_FRAME, true)
3+
import { stripReactStackTrace } from './strip-stack-frame'
94

105
export function getReactStitchedError<T = unknown>(err: T): Error | T {
116
if (typeof (React as any).captureOwnerStack !== 'function') {
@@ -15,7 +10,7 @@ export function getReactStitchedError<T = unknown>(err: T): Error | T {
1510
const isErrorInstance = isError(err)
1611
const originStack = isErrorInstance ? err.stack || '' : ''
1712
const originMessage = isErrorInstance ? err.message : ''
18-
let newStack = stripAfterReactBottomFrame(originStack)
13+
let newStack = stripReactStackTrace(originStack)
1914

2015
const newError = new Error(originMessage)
2116
// Copy all enumerable properties, e.g. digest

packages/next/src/client/components/react-dev-overlay/internal/helpers/strip-stack-frame.test.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,31 @@
1-
import { stripStackByFrame } from './strip-stack-frame'
1+
import { stripReactStackTrace } from './strip-stack-frame'
22

3-
describe('stripStackByFrame', () => {
3+
describe('stripReactStackTrace', () => {
44
it('strips stack after frame', () => {
5-
const stripStackByFrameBefore = (stack: string) =>
6-
stripStackByFrame(stack, 'special-stack-frame', true)
7-
85
const stack = `Error: test
96
at page (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
10-
at special-stack-frame (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
7+
at react-stack-bottom-frame (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
118
at foo (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
129
`
1310

14-
const strippedStack = stripStackByFrameBefore(stack)
11+
const strippedStack = stripReactStackTrace(stack)
1512
expect(strippedStack).toMatchInlineSnapshot(`
16-
"Error: test
17-
at page (http://localhost:3000/_next/static/chunks/webpack.js:1:1)"
13+
" at foo (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
14+
"
1815
`)
1916
})
2017

21-
it('strips stack before frame', () => {
22-
const stripStackByFrameAfter = (stack: string) =>
23-
stripStackByFrame(stack, 'special-stack-frame', false)
24-
18+
it('strips nothing if there is no react stack', () => {
2519
const stack = `Error: test
2620
at page (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
27-
at special-stack-frame (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
2821
at foo (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
2922
`
3023

31-
const strippedStack = stripStackByFrameAfter(stack)
24+
const strippedStack = stripReactStackTrace(stack)
3225
expect(strippedStack).toMatchInlineSnapshot(`
33-
" at foo (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
26+
"Error: test
27+
at page (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
28+
at foo (http://localhost:3000/_next/static/chunks/webpack.js:1:1)
3429
"
3530
`)
3631
})

packages/next/src/client/components/react-dev-overlay/internal/helpers/strip-stack-frame.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
export function stripStackByFrame(
2-
stack: string,
3-
framePivot: string,
4-
stripAfter: boolean
5-
): string {
6-
const framePivotRegex = new RegExp(`(at ${framePivot} )|(${framePivot}\\@)`)
1+
const REACT_ERROR_STACK_BOTTOM_FRAME = 'react-stack-bottom-frame'
2+
3+
// Create a regex that matches the pivot frame in the stack trace
4+
// chrome: at react-stack-bottom-frame
5+
// safari: react-stack-bottom-frame@...
6+
const createLocationRegex = (pivot: string) =>
7+
new RegExp(`(at ${pivot} )|(${pivot}\\@)`)
8+
9+
export function stripReactStackTrace(stack: string): string {
10+
const framePivotRegex = createLocationRegex(REACT_ERROR_STACK_BOTTOM_FRAME)
711
const stackLines = stack.split('\n')
812
const indexOfSplit = stackLines.findIndex((line) =>
913
framePivotRegex.test(line)
1014
)
1115
const isOriginalReactError = indexOfSplit >= 0 // has the frame pivot
1216
const strippedStack = isOriginalReactError
13-
? stripAfter
14-
? // Keep the frames before pivot, from 1st line of stack (error.message) to the pivot
15-
stackLines.slice(0, indexOfSplit).join('\n')
16-
: // Keep the frames after pivot
17-
stackLines.slice(indexOfSplit + 1).join('\n')
17+
? stackLines.slice(indexOfSplit + 1).join('\n')
1818
: stack
1919

2020
return strippedStack

packages/next/src/client/components/react-dev-overlay/internal/helpers/use-error-handler.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,13 @@ const rejectionHandlers: Array<ErrorHandler> = []
1616

1717
export function handleClientError(
1818
originError: unknown,
19-
consoleErrorArgs: any[],
20-
originStack?: string
19+
consoleErrorArgs: any[]
2120
) {
2221
let error: Error
2322
if (!originError || !isError(originError)) {
2423
// If it's not an error, format the args into an error
2524
const formattedErrorMessage = formatConsoleArgs(consoleErrorArgs)
2625
error = createUnhandledError(formattedErrorMessage)
27-
// When the originStack is provided, strip the stack after the react-bottom-stack-frame
28-
if (originStack) {
29-
error.stack = originStack
30-
}
3126
} else {
3227
error = originError
3328
}

0 commit comments

Comments
 (0)