From 3242024501332a26c77366f56ae0bcb2e5409c4a Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Tue, 9 Jul 2024 01:21:55 +0200 Subject: [PATCH] Tweak the display for error trace --- .../internal/container/RuntimeError/index.tsx | 12 +++---- .../internal/helpers/stack-frame.ts | 5 ++- .../acceptance-app/ReactRefreshLogBox.test.ts | 32 ++++++++++--------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx index 3dc9b478a48b1..8c3d6eea17ee0 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx +++ b/packages/next/src/client/components/react-dev-overlay/internal/container/RuntimeError/index.tsx @@ -11,13 +11,11 @@ export type RuntimeErrorProps = { error: ReadyRuntimeError } export function RuntimeError({ error }: RuntimeErrorProps) { const { firstFrame, allLeadingFrames, allCallStackFrames } = React.useMemo(() => { - const filteredFrames = error.frames.filter( - (f) => - !( - f.sourceStackFrame.file === '' && - ['stringify', ''].includes(f.sourceStackFrame.methodName) - ) && !f.sourceStackFrame.file?.startsWith('node:internal') - ) + const filteredFrames = error.frames + // Filter out nodejs internal frames since you can't do anything about them. + // e.g. node:internal/timers shows up pretty often due to timers, but not helpful to users. + // Only present the last line before nodejs internal trace. + .filter((f) => !f.sourceStackFrame.file?.startsWith('node:')) const firstFirstPartyFrameIndex = filteredFrames.findIndex( (entry) => diff --git a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts index f1f8af8cdea38..340471f10198c 100644 --- a/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts +++ b/packages/next/src/client/components/react-dev-overlay/internal/helpers/stack-frame.ts @@ -117,8 +117,11 @@ function isWebpackBundled(file: string) { * webpack://_N_E/./src/hello.tsx => ./src/hello.tsx * webpack://./src/hello.tsx => ./src/hello.tsx * webpack:///./src/hello.tsx => ./src/hello.tsx + * + * => '' */ function formatFrameSourceFile(file: string) { + if (file === '') return '' for (const regex of webpackRegExes) file = file.replace(regex, '') return file } @@ -147,7 +150,7 @@ export function getFrameSource(frame: StackFrame): string { str += ' ' str = formatFrameSourceFile(str) } catch { - str += formatFrameSourceFile(frame.file || '(unknown)') + ' ' + str += formatFrameSourceFile(frame.file || '') + ' ' } if (!isWebpackBundled(frame.file) && frame.lineNumber != null) { diff --git a/test/development/acceptance-app/ReactRefreshLogBox.test.ts b/test/development/acceptance-app/ReactRefreshLogBox.test.ts index b1ec352bd7902..c04ca77c4ed51 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox.test.ts @@ -840,17 +840,17 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => { new Map([ [ 'app/page.js', - // TODO: repro stringify () outdent` - export default function Page() { - const e = new Error("Boom!"); - e.stack += \` - at stringify () - at () - at foo (bar:1:1)\`; - throw e; - } - `, + export default function Page() { + try { + (function() { + throw new Error("This is an error from an anonymous function"); + })(); + } catch (e) { + throw e + } + } + `, ], ]) ) @@ -859,15 +859,16 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => { let callStackFrames = await browser.elementsByCss( '[data-nextjs-call-stack-frame]' ) - let texts = await Promise.all(callStackFrames.map((f) => f.innerText())) - expect(texts).not.toContain('stringify\n') - expect(texts).not.toContain('\n') - expect(texts).toContain('foo\nbar (1:1)') + const text = ( + await Promise.all(callStackFrames.map((f) => f.innerText())) + ).join('') + expect(text).not.toContain('') + expect(text).toContain('app/page.js') await cleanup() }) - test('should hide unrelated frames in stack trace with node:internal calls', async () => { + test('should hide unrelated frames in stack trace with nodejs internal calls', async () => { const { session, browser, cleanup } = await sandbox( next, new Map([ @@ -897,6 +898,7 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => { const texts = await Promise.all(callStackFrames.map((f) => f.innerText())) expect(texts.filter((t) => t.includes('node:internal'))).toHaveLength(0) + expect(texts.filter((t) => t.includes('node:async_hooks'))).toHaveLength(0) await cleanup() })