Skip to content

Commit

Permalink
fix: empty generateStaticParams should still create an ISR route
Browse files Browse the repository at this point in the history
  • Loading branch information
ztanner committed Dec 9, 2024
1 parent 2fcba15 commit cdae93f
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 15 deletions.
11 changes: 4 additions & 7 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2278,10 +2278,7 @@ export default async function build(
})
}

if (
workerResult.prerenderedRoutes &&
workerResult.prerenderedRoutes.length > 0
) {
if (Array.isArray(workerResult.prerenderedRoutes)) {
staticPaths.set(
originalAppPath,
workerResult.prerenderedRoutes
Expand All @@ -2294,9 +2291,9 @@ export default async function build(

const appConfig = workerResult.appConfig || {}
if (appConfig.revalidate !== 0) {
const hasGenerateStaticParams =
workerResult.prerenderedRoutes &&
workerResult.prerenderedRoutes.length > 0
const hasGenerateStaticParams = Array.isArray(
workerResult.prerenderedRoutes
)

if (
config.output === 'export' &&
Expand Down
23 changes: 15 additions & 8 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1382,6 +1382,9 @@ export async function buildAppStaticPaths({
}
)

let dynamicParams = true
let hasGenerateStaticParams = false

for (const segment of segments) {
// Check to see if there are any missing params for segments that have
// dynamicParams set to false.
Expand All @@ -1402,6 +1405,17 @@ export async function buildAppStaticPaths({
)
}
}

// TODO: dynamic params should be allowed to be granular per segment but
// we need additional information stored/leveraged in the prerender
// manifest to allow this behavior.
if (segment.config?.dynamicParams === false) {
dynamicParams = false
}

if (segment.generateStaticParams) {
hasGenerateStaticParams = true
}
}

// Determine if all the segments have had their parameters provided. If there
Expand All @@ -1417,13 +1431,6 @@ export async function buildAppStaticPaths({
return true
}))

// TODO: dynamic params should be allowed to be granular per segment but
// we need additional information stored/leveraged in the prerender
// manifest to allow this behavior.
const dynamicParams = segments.every(
(segment) => segment.config?.dynamicParams !== false
)

const supportsRoutePreGeneration =
hadAllParamsGenerated || process.env.NODE_ENV === 'production'

Expand All @@ -1437,7 +1444,7 @@ export async function buildAppStaticPaths({

let result: PartialStaticPathsResult = {
fallbackMode,
prerenderedRoutes: undefined,
prerenderedRoutes: hasGenerateStaticParams ? [] : undefined,
}

if (hadAllParamsGenerated && fallbackMode) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Suspense } from 'react'

export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
return (
<div>
Hello World
<div>
<Params params={params} />
</div>
</div>
)
}

async function Params({ params }: { params: Promise<{ slug: string }> }) {
return <Suspense>{(await params).slug}</Suspense>
}

export async function generateStaticParams() {
return []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ReactNode, Suspense } from 'react'
export default function Root({ children }: { children: ReactNode }) {
return (
<html>
<body>
<Suspense>{children}</Suspense>
</body>
</html>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Page() {
return <p>hello world</p>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('empty-generate-static-params', () => {
const { next, skipped } = nextTestSetup({
files: __dirname,
skipDeployment: true,
})

if (skipped) return

it('should mark the page with empty generateStaticParams as SSG in build output', async () => {
const isPPREnabled = process.env.__NEXT_EXPERIMENTAL_PPR === 'true'
expect(next.cliOutput).toContain(`${isPPREnabled ? '◐' : '●'} /[slug]`)
})

it('should be a cache miss on the initial render followed by a HIT after being generated', async () => {
const firstResponse = await next.fetch('/foo')
expect(firstResponse.status).toBe(200)

// With PPR enabled, the initial request doesn't send back a cache header
const isPPREnabled = process.env.__NEXT_EXPERIMENTAL_PPR === 'true'

expect(firstResponse.headers.get('x-nextjs-cache')).toBe(
isPPREnabled ? null : 'MISS'
)

retry(async () => {
const secondResponse = await next.fetch('/foo')
expect(secondResponse.status).toBe(200)
expect(secondResponse.headers.get('x-nextjs-cache')).toBe('HIT')
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {}

module.exports = nextConfig

0 comments on commit cdae93f

Please sign in to comment.