Fix global-error for nested routes (#60539)

## What

This fixes when the deep nested routes throws a client side error, it
can still be caught by the `global-error.js`

## How

We should always resolve global-error from root app directory instead of
current route's layout. Also fixed a bad test before where the
gloabl-error.js is not located correctly


Fixes #53756
Closes NEXT-1760
This commit is contained in:
Jiachi Liu 2024-01-11 23:28:17 +01:00 committed by GitHub
parent e07161a563
commit 98b99e408b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 47 additions and 10 deletions

View file

@ -198,7 +198,7 @@ async function createTreeCodeFromPath(
const pages: string[] = [] const pages: string[] = []
let rootLayout: string | undefined let rootLayout: string | undefined
let globalError: string = defaultGlobalErrorPath let globalError: string | undefined
async function resolveAdjacentParallelSegments( async function resolveAdjacentParallelSegments(
segmentPath: string segmentPath: string
@ -356,20 +356,20 @@ async function createTreeCodeFromPath(
)?.[1] )?.[1]
rootLayout = layoutPath rootLayout = layoutPath
if (isDefaultNotFound && !layoutPath) { if (isDefaultNotFound && !layoutPath && !rootLayout) {
rootLayout = defaultLayoutPath rootLayout = defaultLayoutPath
definedFilePaths.push(['layout', rootLayout]) definedFilePaths.push(['layout', rootLayout])
} }
}
if (layoutPath) { if (!globalError) {
const resolvedGlobalErrorPath = await resolver( const resolvedGlobalErrorPath = await resolver(
`${path.dirname(layoutPath)}/${GLOBAL_ERROR_FILE_TYPE}` `${appDirPrefix}/${GLOBAL_ERROR_FILE_TYPE}`
) )
if (resolvedGlobalErrorPath) { if (resolvedGlobalErrorPath) {
globalError = resolvedGlobalErrorPath globalError = resolvedGlobalErrorPath
} }
} }
}
let parallelSegmentKey = Array.isArray(parallelSegment) let parallelSegmentKey = Array.isArray(parallelSegment)
? parallelSegment[0] ? parallelSegment[0]
@ -468,7 +468,7 @@ async function createTreeCodeFromPath(
treeCode: `${treeCode}.children;`, treeCode: `${treeCode}.children;`,
pages: `${JSON.stringify(pages)};`, pages: `${JSON.stringify(pages)};`,
rootLayout, rootLayout,
globalError, globalError: globalError ?? defaultGlobalErrorPath,
} }
} }

View file

@ -12,3 +12,6 @@ export default function GlobalError({ error }) {
</html> </html>
) )
} }
// for inspecting purpose
GlobalError.displayName = 'GlobalError'

View file

@ -0,0 +1,3 @@
export default function NestedLayout({ children }) {
return <div>{children}</div>
}

View file

@ -0,0 +1,19 @@
'use client'
import { useEffect, useState } from 'react'
export default function ClientPage() {
const [bad, setBad] = useState(false)
useEffect(() => {
setTimeout(() => {
setBad(true)
})
}, [])
if (bad) {
throw Error('nested error')
}
return <p>client page</p>
}

View file

@ -79,5 +79,17 @@ createNextDescribe(
) )
} }
}) })
it('should catch the client error thrown in the nested routes', async () => {
const browser = await next.browser('/nested/nested')
if (isNextDev) {
await testDev(browser, /Error: nested error/)
} else {
expect(await browser.elementByCss('h1').text()).toBe('Global Error')
expect(await browser.elementByCss('#error').text()).toBe(
'Global error: nested error'
)
}
})
} }
) )