import { nextTestSetup } from 'e2e-utils'
import path from 'path'
const { version: nextVersion } = require('next/package.json')
const cacheComponentsEnabled = process.env.__NEXT_CACHE_COMPONENTS === 'true'
describe('build-output-prerender', () => {
describe('with a next config file', () => {
describe('without --debug-prerender', () => {
const { next, isTurbopack, isRspack } = nextTestSetup({
files: path.join(__dirname, 'fixtures/with-config-file'),
skipStart: true,
})
beforeAll(() => next.build())
it('prints only the user-selected experimental flags (and the ones enabled via env variable)', async () => {
if (cacheComponentsEnabled) {
if (isTurbopack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (Turbopack)
- Cache Components enabled
- Experiments (use with caution):
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)"
`)
} else if (isRspack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (Rspack)
- Cache Components enabled
- Experiments (use with caution):
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)"
`)
} else {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (webpack)
- Cache Components enabled
- Experiments (use with caution):
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)"
`)
}
} else {
if (isTurbopack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (Turbopack)
- Cache Components enabled
- Experiments (use with caution):
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)"
`)
} else if (isRspack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (Rspack)
- Cache Components enabled
- Experiments (use with caution):
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)"
`)
} else {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (webpack)
- Cache Components enabled
- Experiments (use with caution):
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)"
`)
}
}
})
it('shows only a single prerender error with a mangled stack', async () => {
if (isTurbopack) {
// TODO(veil): Why is the location incomplete unless we enable --no-mangling?
expect(getPrerenderOutput(next.cliOutput)).toMatchInlineSnapshot(`
"Error: Route "/client" used \`new Date()\` inside a Client Component without a Suspense boundary above it. See more info here: https://nextjs.org/docs/messages/next-prerender-current-time-client
at <unknown> (app/client/page.tsx:4:28)
2 |
3 | export default function Page() {
> 4 | return <p>Current time: {new Date().toISOString()}</p>
| ^
5 | }
6 |
To get a more detailed stack trace and pinpoint the issue, try one of the following:
- Start the app in development mode by running \`next dev\`, then open "/client" in your browser to investigate the error.
- Rerun the production build with \`next build --debug-prerender\` to generate better stack traces.
Error occurred prerendering page "/client". Read more: https://nextjs.org/docs/messages/prerender-error
Export encountered an error on /client/page: /client, exiting the build."
`)
} else {
expect(getPrerenderOutput(next.cliOutput)).toMatchInlineSnapshot(`
"Error: Route "/client" used \`new Date()\` inside a Client Component without a Suspense boundary above it. See more info here: https://nextjs.org/docs/messages/next-prerender-current-time-client
at x (<next-dist-dir>)
To get a more detailed stack trace and pinpoint the issue, try one of the following:
- Start the app in development mode by running \`next dev\`, then open "/client" in your browser to investigate the error.
- Rerun the production build with \`next build --debug-prerender\` to generate better stack traces.
Error occurred prerendering page "/client". Read more: https://nextjs.org/docs/messages/prerender-error
Export encountered an error on /client/page: /client, exiting the build."
`)
}
})
})
describe('with --debug-prerender', () => {
const { next, isTurbopack, isRspack } = nextTestSetup({
files: path.join(__dirname, 'fixtures/with-config-file'),
skipStart: true,
buildArgs: ['--debug-prerender'],
})
beforeAll(() => next.build())
it('prints a warning and the customized experimental flags', async () => {
if (cacheComponentsEnabled) {
if (isTurbopack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (Turbopack)
- Cache Components enabled
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)
⨯ turbopackMinify (disabled by \`--debug-prerender\`)"
`)
} else if (isRspack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (Rspack)
- Cache Components enabled
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
⨯ serverMinification (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)"
`)
} else {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (webpack)
- Cache Components enabled
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
⨯ serverMinification (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)"
`)
}
} else {
if (isTurbopack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (Turbopack)
- Cache Components enabled
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)
⨯ turbopackMinify (disabled by \`--debug-prerender\`)"
`)
} else if (isRspack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (Rspack)
- Cache Components enabled
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
⨯ serverMinification (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)"
`)
} else {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (webpack)
- Cache Components enabled
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
⨯ serverMinification (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)"
`)
}
}
})
it('shows all prerender errors with readable stacks and code frames', async () => {
if (isTurbopack) {
expect(getPrerenderOutput(next.cliOutput)).toMatchInlineSnapshot(`
"Error: Route "/client" used \`new Date()\` inside a Client Component without a Suspense boundary above it. See more info here: https://nextjs.org/docs/messages/next-prerender-current-time-client
at Page (app/client/page.tsx:4:28)
2 |
3 | export default function Page() {
> 4 | return <p>Current time: {new Date().toISOString()}</p>
| ^
5 | }
6 |
To debug the issue, start the app in development mode by running \`next dev\`, then open "/client" in your browser to investigate the error.
Error occurred prerendering page "/client". Read more: https://nextjs.org/docs/messages/prerender-error
Error: Route "/server" used \`Math.random()\` before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing random values synchronously in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-random
at Page (app/server/page.tsx:13:27)
at Page (<anonymous>)
11 | await cachedDelay()
12 |
> 13 | return <p>Random: {Math.random()}</p>
| ^
14 | }
15 |
To debug the issue, start the app in development mode by running \`next dev\`, then open "/server" in your browser to investigate the error.
Error occurred prerendering page "/server". Read more: https://nextjs.org/docs/messages/prerender-error
> Export encountered errors on 2 paths:
/client/page: /client
/server/page: /server"
`)
} else {
// TODO(veil): Bundler protocols should not appear in stackframes.
expect(getPrerenderOutput(next.cliOutput)).toMatchInlineSnapshot(`
"Error: Route "/client" used \`new Date()\` inside a Client Component without a Suspense boundary above it. See more info here: https://nextjs.org/docs/messages/next-prerender-current-time-client
at Page (webpack:///app/client/page.tsx:4:28)
at ClientPageRoot (webpack:///src/client/components/client-page.tsx:61:12)
2 |
3 | export default function Page() {
> 4 | return <p>Current time: {new Date().toISOString()}</p>
| ^
5 | }
6 |
To debug the issue, start the app in development mode by running \`next dev\`, then open "/client" in your browser to investigate the error.
Error occurred prerendering page "/client". Read more: https://nextjs.org/docs/messages/prerender-error
Error: Route "/server" used \`Math.random()\` before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing random values synchronously in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-random
at Page (webpack:///app/server/page.tsx:13:27)
at Page (<anonymous>)
11 | await cachedDelay()
12 |
> 13 | return <p>Random: {Math.random()}</p>
| ^
14 | }
15 |
To debug the issue, start the app in development mode by running \`next dev\`, then open "/server" in your browser to investigate the error.
Error occurred prerendering page "/server". Read more: https://nextjs.org/docs/messages/prerender-error
> Export encountered errors on 2 paths:
/client/page: /client
/server/page: /server"
`)
}
})
})
})
describe('without a next config file', () => {
describe('without --debug-prerender', () => {
const { next, isTurbopack, isRspack } = nextTestSetup({
files: path.join(__dirname, 'fixtures/without-config-file'),
skipStart: true,
})
beforeAll(() => next.build())
it('prints no experimental flags (unless enabled via env variable)', async () => {
if (cacheComponentsEnabled) {
if (isTurbopack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (Turbopack)
- Cache Components enabled
- Experiments (use with caution):
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)"
`)
} else if (isRspack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (Rspack)
- Cache Components enabled
- Experiments (use with caution):
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)"
`)
} else {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (webpack)
- Cache Components enabled
- Experiments (use with caution):
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)"
`)
}
} else {
if (isTurbopack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (Turbopack)
- Experiments (use with caution):
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)"
`)
} else if (isRspack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (Rspack)
- Experiments (use with caution):
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)"
`)
} else {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"▲ Next.js x.y.z (webpack)
- Experiments (use with caution):
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)"
`)
}
}
})
})
describe('with --debug-prerender', () => {
const { next, isTurbopack, isRspack } = nextTestSetup({
files: path.join(__dirname, 'fixtures/without-config-file'),
skipStart: true,
buildArgs: ['--debug-prerender'],
})
beforeAll(() => next.build())
it('prints a warning and the customized experimental flags', async () => {
if (cacheComponentsEnabled) {
if (isTurbopack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (Turbopack)
- Cache Components enabled
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)
⨯ turbopackMinify (disabled by \`--debug-prerender\`)"
`)
} else if (isRspack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (Rspack)
- Cache Components enabled
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
⨯ serverMinification (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)"
`)
} else {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (webpack)
- Cache Components enabled
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
✓ appNewScrollHandler (enabled by \`__NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER\`)
✓ cachedNavigations (enabled by \`__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
⨯ serverMinification (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)"
`)
}
} else {
if (isTurbopack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (Turbopack)
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)
⨯ turbopackMinify (disabled by \`--debug-prerender\`)"
`)
} else if (isRspack) {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (Rspack)
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
⨯ serverMinification (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)"
`)
} else {
expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(`
"⚠ Prerendering is running in debug mode with NODE_ENV='development'. This will affect performance and should not be used for production.
▲ Next.js x.y.z (webpack)
- Experiments (use with caution):
✓ allowDevelopmentBuild (enabled by \`--debug-prerender\`)
⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`)
⨯ serverMinification (disabled by \`--debug-prerender\`)
✓ serverSourceMaps (enabled by \`--debug-prerender\`)
✓ strictRouteTypes (enabled by \`__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES\`)"
`)
}
}
})
})
})
})
function getPreambleOutput(cliOutput: string): string {
const lines: string[] = []
for (const line of cliOutput.split('\n')) {
if (line.includes('Creating an optimized production build')) {
break
}
lines.push(line.replace(nextVersion, 'x.y.z'))
}
return lines.join('\n').trim()
}
function getPrerenderOutput(cliOutput: string): string {
let foundPrerenderingLine = false
const lines: string[] = []
for (const line of cliOutput.split('\n')) {
if (line.includes('Collecting page data')) {
foundPrerenderingLine = true
continue
}
if (line.includes('Next.js build worker exited')) {
break
}
if (foundPrerenderingLine && !line.includes('Generating static pages')) {
lines.push(
line.replace(/at \w+ \(.next[^)]+\)/, 'at x (<next-dist-dir>)')
)
}
}
return lines.join('\n').trim()
}