mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
A new `GenerateStaticParamsStore` work unit store type is now provided during `generateStaticParams` execution. This enables root param getters (`import { lang } from 'next/root-params'`) to be called inside `generateStaticParams`, allowing shared helpers that internally access root params via the special import to be used in both Server Components and `generateStaticParams` without manually threading params.
Each `generateStaticParams` call now runs within a `workUnitAsyncStorage.run()` context carrying a `GenerateStaticParamsStore` with the correct `rootParams` (extracted from `parentParams` using the already-available `rootParamKeys`). The store extends `CommonWorkUnitStore`, providing `phase` and `implicitTags` which are not strictly necessary but convenient to keep call sites simple.
Request-time APIs (`headers()`, `cookies()`, `connection()`, `draftMode()`) now throw specific errors when called inside `generateStaticParams` instead of the previous generic "called outside a request scope" message. Framework-internal functions like `createSearchParamsFromClient` and `createParamsFromClient` throw `InvariantError` since they should never be reached in this context.
This change also unblocks a follow-up PR that removes `| undefined` from `PublicCacheContext.outerWorkUnitStore` in the use cache wrapper, since `"use cache"` is already supported inside `generateStaticParams` today but previously ran without a `WorkUnitStore`. With this store in place, requiring a `WorkUnitStore` in `"use cache"` won't break that existing usage.
53 lines
2.0 KiB
TypeScript
53 lines
2.0 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import cheerio from 'cheerio'
|
|
import { join } from 'path'
|
|
import { getCacheHeader } from 'next-test-utils'
|
|
|
|
describe('app-root-param-getters - generateStaticParams', () => {
|
|
const { next } = nextTestSetup({
|
|
files: join(__dirname, 'fixtures', 'generate-static-params'),
|
|
})
|
|
|
|
it('should be statically prerenderable', async () => {
|
|
const params = { lang: 'en', locale: 'us' }
|
|
const response = await next.fetch(`/${params.lang}/${params.locale}`)
|
|
expect(response.status).toBe(200)
|
|
expect(getCacheHeader(response)).toBeOneOf(['HIT', 'PRERENDER'])
|
|
const $ = cheerio.load(await response.text())
|
|
expect($('p').text()).toBe(`hello world ${JSON.stringify(params)}`)
|
|
})
|
|
|
|
it('should be part of the static shell', async () => {
|
|
const params = { lang: 'en', locale: 'us' }
|
|
const browser = await next.browser(
|
|
`/${params.lang}/${params.locale}/other/1`,
|
|
{
|
|
// prevent streaming (dynamic) content from being inserted into the DOM
|
|
disableJavaScript: true,
|
|
}
|
|
)
|
|
expect(await browser.elementByCss('main > p#root-params').text()).toBe(
|
|
JSON.stringify(params)
|
|
)
|
|
})
|
|
|
|
it('should allow reading root params that were not prerendered', async () => {
|
|
const params = { lang: 'sth', locale: 'else' }
|
|
const $ = await next.render$(`/${params.lang}/${params.locale}`)
|
|
expect($('p').text()).toBe(`hello world ${JSON.stringify(params)}`)
|
|
})
|
|
|
|
it('should allow reading root params inside generateStaticParams', async () => {
|
|
// The [slug] segment's generateStaticParams uses `lang()` to produce
|
|
// slugs like "en-post". If root params are available during
|
|
// generateStaticParams, this page should be statically prerenderable.
|
|
const response = await next.fetch('/en/us/other/en-post')
|
|
expect(response.status).toBe(200)
|
|
const $ = cheerio.load(await response.text())
|
|
expect($('#root-params').text()).toBe(
|
|
JSON.stringify({ lang: 'en', locale: 'us' })
|
|
)
|
|
expect($('#dynamic-params').text()).toBe('en-post')
|
|
})
|
|
})
|