Files
8d6c076d14 Include additional prerender metadata about build-time routes (#96080)
Building on top of @agadzik work (see first commit)

Adds prerender route metadata to help downstream consumers understand
the nature of build-time prerenders

We break down prerender taxonomy as follows

routeType describes what the prerender serves:
* `route`: a non-UI endpoint, such as a Route Handler.
* `page`: a page whose route parameters are fully known.
* `shell`: a reusable PPR shell for a parameterized page.
* `fallback`: a fallback that may serve or specialize additional
parameter values.

response describes how complete the static response is:
* `complete`: the prerender contains the completed response.
* `initial`: an initial response is available, but the page UI is
incomplete; in practice this applies to partially prerenderable UI
routes.
* `empty`: there is no initial static response to send.

compute describes the request-time work required:
* `static`: no request-time server compute is needed.
* `resuming`: request-time compute resumes from the static response.
* `blocking`: the initial response is blocked until request-time compute
starts, but it may stream while that compute continues.

`htmlSize` reports the byte size of prerendered HTML when the output has
HTML.

The taxonomy is included only on a prerender group’s canonical primary
output—the HTML output for pages or the response output for Route
Handlers—and is omitted from secondary RSC and segment-data outputs.

---------

Co-authored-by: agadzik <[email protected]>
2026-07-24 12:00:49 +02:00

76 lines
2.4 KiB
TypeScript

import { nextTestSetup, isNextStart } from 'e2e-utils'
describe('404 Page Support SSG', () => {
const { next, skipped } = nextTestSetup({
files: __dirname,
disableAutoSkewProtection: true,
// Assertions don't apply to deploy mode (output differs vs. local Next.js server).
skipDeployment: true,
})
if (skipped) return
it('should respond to 404 correctly', async () => {
const res = await next.fetch('/404')
expect(res.status).toBe(404)
expect(await res.text()).toContain('custom 404 page')
})
it('should render error correctly', async () => {
const text = await next.render('/err')
if (isNextStart) {
expect(text).toContain('Internal Server Error')
} else {
expect(text).toContain('oops')
}
})
it('should not show an error in the logs for 404 SSG', async () => {
const gip404Err =
/`pages\/404` can not have getInitialProps\/getServerSideProps/
await next.render('/non-existent')
expect(next.cliOutput).not.toMatch(gip404Err)
})
it('should render index page normal', async () => {
const html = await next.render('/')
expect(html).toContain('hello from index')
})
if (isNextStart) {
it('should not revalidate custom 404 page', async () => {
const res1 = await next.render('/non-existent')
const res2 = await next.render('/non-existent')
const res3 = await next.render('/non-existent')
const res4 = await next.render('/non-existent')
expect(res1 === res2 && res2 === res3 && res3 === res4).toBe(true)
expect(res1).toContain('custom 404 page')
})
it('should set pages404 in routes-manifest correctly', async () => {
const data = await next.readJSON('.next/routes-manifest.json')
expect(data.pages404).toBe(true)
})
it('should have 404 page in prerender-manifest', async () => {
const data = await next.readJSON('.next/prerender-manifest.json')
expect(data.routes['/404']).toEqual({
routeType: 'page',
response: 'complete',
compute: 'static',
allowHeader: [
'host',
'x-matched-path',
'x-prerender-revalidate',
'x-prerender-revalidate-if-generated',
'x-next-revalidated-tags',
'x-next-revalidate-tag-token',
],
initialRevalidateSeconds: false,
srcRoute: null,
dataRoute: `/_next/data/${next.buildId}/404.json`,
})
})
}
})