mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
When SSR fails in development — whether the failing page is in the App Router or the Pages Router — Next.js falls back to rendering the Pages Router `/_error` page and serializes the original error into `__NEXT_DATA__.err`. The client bootstrap in `packages/next/src/client/ index.tsx` later re-throws a fresh `new Error(initialErr.message)` so that the dev overlay picks it up. The error-code SWC plugin stamps that new `Error` with the generic code mapped to `%s` in `errors.json` because the message argument is an identifier, not a statically-known string, which is how the overlay ended up showing the wrong code for errors like `UseCacheTimeoutError`. The pipeline has two problems on the way to the overlay. First, `errorToJSON` in `packages/next/src/server/render.tsx` only copies the error's standard fields (`name`, `source`, `message`, `stack`, `digest`) into `__NEXT_DATA__.err`, so the real `__NEXT_ERROR_CODE` attached to the thrown error never reaches the client. Second, the subsequent rewrap in `getServerError` at `packages/next/src/server/dev/node-stack- frames.ts` creates yet another `new Error(...)` that the plugin stamps with the same generic code, clobbering anything the caller might have set on the rewrapped instance. This change plumbs the code through. `errorToJSON` now also emits `__NEXT_ERROR_CODE` using `extractNextErrorCode` so the value survives JSON serialization into `__NEXT_DATA__.err`; the type in `packages/next/src/shared/lib/utils.ts` is updated to match. Both rewrap sites — the client bootstrap and `getServerError` — copy the code from the source error onto the fresh `Error` via `Object.defineProperty` with `enumerable: false` and `configurable: true`, matching how the plugin itself stores the property, which overrides the generic code the plugin stamped on the rewrapped instance. The `use-cache-hanging` e2e test snapshot is updated from `E394` to `E236` now that the real code surfaces in the dev overlay.
79 lines
2.6 KiB
TypeScript
79 lines
2.6 KiB
TypeScript
import path from 'path'
|
|
import { isNextStart, nextTestSetup } from 'e2e-utils'
|
|
|
|
describe('Undefined default export', () => {
|
|
const { next, isNextDev } = nextTestSetup({
|
|
files: path.join(__dirname),
|
|
skipStart: isNextStart,
|
|
skipDeployment: true,
|
|
})
|
|
|
|
if (isNextDev) {
|
|
it('should error if page component does not have default export', async () => {
|
|
const browser = await next.browser('/specific-path/1')
|
|
|
|
await expect(browser).toDisplayRedbox(`
|
|
{
|
|
"code": "E45",
|
|
"description": "The default export is not a React Component in "/specific-path/1/page"",
|
|
"environmentLabel": null,
|
|
"label": "Runtime Error",
|
|
"source": null,
|
|
"stack": [],
|
|
}
|
|
`)
|
|
})
|
|
|
|
it('should error if layout component does not have default export', async () => {
|
|
const browser = await next.browser('/specific-path/2')
|
|
|
|
await expect(browser).toDisplayRedbox(`
|
|
{
|
|
"code": "E45",
|
|
"description": "The default export is not a React Component in "/specific-path/2/layout"",
|
|
"environmentLabel": null,
|
|
"label": "Runtime Error",
|
|
"source": null,
|
|
"stack": [],
|
|
}
|
|
`)
|
|
})
|
|
|
|
it('should error if not-found component does not have default export when trigger not-found boundary', async () => {
|
|
const browser = await next.browser('/will-not-found')
|
|
|
|
await expect(browser).toDisplayRedbox(`
|
|
{
|
|
"code": "E45",
|
|
"description": "The default export is not a React Component in "/will-not-found/not-found"",
|
|
"environmentLabel": null,
|
|
"label": "Runtime Error",
|
|
"source": null,
|
|
"stack": [],
|
|
}
|
|
`)
|
|
})
|
|
} else {
|
|
it('errors the build with helpful error messages', async () => {
|
|
const { cliOutput, exitCode } = await next.build()
|
|
|
|
expect(exitCode).toBe(1)
|
|
|
|
expect(cliOutput).toContain(
|
|
`Error occurred prerendering page "/specific-path/1". Read more: https://nextjs.org/docs/messages/prerender-error
|
|
Error: The default export is not a React Component in "/specific-path/1/page"`
|
|
)
|
|
|
|
expect(cliOutput).toContain(
|
|
`Error occurred prerendering page "/specific-path/2". Read more: https://nextjs.org/docs/messages/prerender-error
|
|
Error: The default export is not a React Component in "/specific-path/2/layout"`
|
|
)
|
|
|
|
expect(cliOutput).toContain(
|
|
`Error occurred prerendering page "/will-not-found". Read more: https://nextjs.org/docs/messages/prerender-error
|
|
Error: The default export is not a React Component in "/will-not-found/not-found"`
|
|
)
|
|
})
|
|
}
|
|
})
|