diff --git a/packages/next/src/build/define-env.ts b/packages/next/src/build/define-env.ts index 072f03d3fec365..d9eb6bcd665cc1 100644 --- a/packages/next/src/build/define-env.ts +++ b/packages/next/src/build/define-env.ts @@ -199,6 +199,9 @@ export function getDefineEnv({ 'process.env.__NEXT_CLIENT_SEGMENT_CACHE': Boolean( config.experimental.clientSegmentCache ), + 'process.env.__NEXT_CLIENT_PARAM_PARSING': Boolean( + config.experimental.clientParamParsing + ), 'process.env.__NEXT_CLIENT_VALIDATE_RSC_REQUEST_HEADERS': Boolean( config.experimental.validateRSCRequestHeaders ), diff --git a/packages/next/src/build/templates/app-page.ts b/packages/next/src/build/templates/app-page.ts index 7ee24b6672ea33..7cb0f2b70c2b7f 100644 --- a/packages/next/src/build/templates/app-page.ts +++ b/packages/next/src/build/templates/app-page.ts @@ -533,6 +533,9 @@ export async function handler( clientSegmentCache: Boolean( nextConfig.experimental.clientSegmentCache ), + clientParamParsing: Boolean( + nextConfig.experimental.clientParamParsing + ), dynamicOnHover: Boolean(nextConfig.experimental.dynamicOnHover), inlineCss: Boolean(nextConfig.experimental.inlineCss), authInterrupts: Boolean(nextConfig.experimental.authInterrupts), diff --git a/packages/next/src/build/templates/edge-ssr-app.ts b/packages/next/src/build/templates/edge-ssr-app.ts index df2f2e729128b4..881f1a3cd96b8e 100644 --- a/packages/next/src/build/templates/edge-ssr-app.ts +++ b/packages/next/src/build/templates/edge-ssr-app.ts @@ -163,6 +163,7 @@ async function requestHandler( staleTimes: nextConfig.experimental.staleTimes, cacheComponents: Boolean(nextConfig.experimental.cacheComponents), clientSegmentCache: Boolean(nextConfig.experimental.clientSegmentCache), + clientParamParsing: Boolean(nextConfig.experimental.clientParamParsing), dynamicOnHover: Boolean(nextConfig.experimental.dynamicOnHover), inlineCss: Boolean(nextConfig.experimental.inlineCss), authInterrupts: Boolean(nextConfig.experimental.authInterrupts), diff --git a/packages/next/src/export/index.ts b/packages/next/src/export/index.ts index f007d0ffc09e84..9047b40a4375be 100644 --- a/packages/next/src/export/index.ts +++ b/packages/next/src/export/index.ts @@ -404,6 +404,7 @@ async function exportAppImpl( nextConfig.experimental.clientSegmentCache === 'client-only' ? 'client-only' : Boolean(nextConfig.experimental.clientSegmentCache), + clientParamParsing: nextConfig.experimental.clientParamParsing ?? false, dynamicOnHover: nextConfig.experimental.dynamicOnHover ?? false, inlineCss: nextConfig.experimental.inlineCss ?? false, authInterrupts: !!nextConfig.experimental.authInterrupts, diff --git a/packages/next/src/server/app-render/types.ts b/packages/next/src/server/app-render/types.ts index 253ba03eb8bbed..e33c5a78534486 100644 --- a/packages/next/src/server/app-render/types.ts +++ b/packages/next/src/server/app-render/types.ts @@ -261,6 +261,7 @@ export interface RenderOptsPartial { clientTraceMetadata: string[] | undefined cacheComponents: boolean clientSegmentCache: boolean | 'client-only' + clientParamParsing: boolean dynamicOnHover: boolean inlineCss: boolean authInterrupts: boolean diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index 512c01b0990fc2..a409ed8faea8ad 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -568,6 +568,8 @@ export default abstract class Server< this.nextConfig.experimental.clientSegmentCache === 'client-only' ? 'client-only' : Boolean(this.nextConfig.experimental.clientSegmentCache), + clientParamParsing: + this.nextConfig.experimental.clientParamParsing ?? false, dynamicOnHover: this.nextConfig.experimental.dynamicOnHover ?? false, inlineCss: this.nextConfig.experimental.inlineCss ?? false, authInterrupts: !!this.nextConfig.experimental.authInterrupts, diff --git a/packages/next/src/server/config-schema.ts b/packages/next/src/server/config-schema.ts index ad16ebf69c4ee8..322cee2a93ecaf 100644 --- a/packages/next/src/server/config-schema.ts +++ b/packages/next/src/server/config-schema.ts @@ -358,6 +358,7 @@ export const configSchema: zod.ZodType = z.lazy(() => clientSegmentCache: z .union([z.boolean(), z.literal('client-only')]) .optional(), + clientParamParsing: z.boolean().optional(), dynamicOnHover: z.boolean().optional(), disableOptimizedLoading: z.boolean().optional(), disablePostcssPresetEnv: z.boolean().optional(), diff --git a/packages/next/src/server/config-shared.ts b/packages/next/src/server/config-shared.ts index 0eee820fa0522f..a0af1c47e5b27c 100644 --- a/packages/next/src/server/config-shared.ts +++ b/packages/next/src/server/config-shared.ts @@ -331,6 +331,7 @@ export interface ExperimentalConfig { linkNoTouchStart?: boolean caseSensitiveRoutes?: boolean clientSegmentCache?: boolean | 'client-only' + clientParamParsing?: boolean dynamicOnHover?: boolean appDocumentPreloading?: boolean preloadEntriesOnStart?: boolean @@ -1400,6 +1401,7 @@ export const defaultConfig = Object.freeze({ linkNoTouchStart: false, caseSensitiveRoutes: false, clientSegmentCache: false, + clientParamParsing: false, dynamicOnHover: false, appDocumentPreloading: undefined, preloadEntriesOnStart: true, diff --git a/packages/next/src/server/config.ts b/packages/next/src/server/config.ts index f2d9ba9e6e3b02..2266e604c68e3c 100644 --- a/packages/next/src/server/config.ts +++ b/packages/next/src/server/config.ts @@ -1715,6 +1715,25 @@ function enforceExperimentalFeatures( } } + // TODO: Remove this once we've made Client Param Parsing the default. + if ( + process.env.__NEXT_EXPERIMENTAL_PPR === 'true' && + // We do respect an explicit value in the user config. + (config.experimental.clientParamParsing === undefined || + (isDefaultConfig && !config.experimental.clientParamParsing)) + ) { + config.experimental.clientParamParsing = true + + if (configuredExperimentalFeatures) { + addConfiguredExperimentalFeature( + configuredExperimentalFeatures, + 'clientParamParsing', + true, + 'enabled by `__NEXT_EXPERIMENTAL_PPR`' + ) + } + } + // TODO: Remove this once we've made Cache Components the default. if ( process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS === 'true' && diff --git a/test/production/app-dir/build-output-prerender/build-output-prerender.test.ts b/test/production/app-dir/build-output-prerender/build-output-prerender.test.ts index b9776f8fee6c46..2161dd1c42c306 100644 --- a/test/production/app-dir/build-output-prerender/build-output-prerender.test.ts +++ b/test/production/app-dir/build-output-prerender/build-output-prerender.test.ts @@ -26,6 +26,7 @@ describe('build-output-prerender', () => { ✓ ppr (enabled by \`__NEXT_EXPERIMENTAL_CACHE_COMPONENTS\`) ✓ cacheComponents ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`experimental.cacheComponents\`)" `) } else { @@ -35,6 +36,7 @@ describe('build-output-prerender', () => { ✓ ppr (enabled by \`__NEXT_EXPERIMENTAL_CACHE_COMPONENTS\`) ✓ cacheComponents ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`experimental.cacheComponents\`)" `) } @@ -46,6 +48,7 @@ describe('build-output-prerender', () => { ✓ ppr (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ cacheComponents ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`experimental.cacheComponents\`)" `) } else { @@ -55,6 +58,7 @@ describe('build-output-prerender', () => { ✓ ppr (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ cacheComponents ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`experimental.cacheComponents\`)" `) } @@ -131,6 +135,7 @@ describe('build-output-prerender', () => { ✓ serverSourceMaps (enabled by \`--debug-prerender\`) ⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`) ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`--debug-prerender\`)" `) } else { @@ -144,6 +149,7 @@ describe('build-output-prerender', () => { ⨯ serverMinification (disabled by \`--debug-prerender\`) ⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`) ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`--debug-prerender\`)" `) } @@ -159,6 +165,7 @@ describe('build-output-prerender', () => { ✓ serverSourceMaps (enabled by \`--debug-prerender\`) ⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`) ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`--debug-prerender\`)" `) } else { @@ -172,6 +179,7 @@ describe('build-output-prerender', () => { ⨯ serverMinification (disabled by \`--debug-prerender\`) ⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`) ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`--debug-prerender\`)" `) } @@ -251,6 +259,7 @@ describe('build-output-prerender', () => { ✓ ppr (enabled by \`__NEXT_EXPERIMENTAL_CACHE_COMPONENTS\`) ✓ cacheComponents (enabled by \`__NEXT_EXPERIMENTAL_CACHE_COMPONENTS\`) ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`experimental.cacheComponents\`)" `) } else { @@ -260,6 +269,7 @@ describe('build-output-prerender', () => { ✓ ppr (enabled by \`__NEXT_EXPERIMENTAL_CACHE_COMPONENTS\`) ✓ cacheComponents (enabled by \`__NEXT_EXPERIMENTAL_CACHE_COMPONENTS\`) ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`experimental.cacheComponents\`)" `) } @@ -269,14 +279,16 @@ describe('build-output-prerender', () => { "▲ Next.js x.y.z (Turbopack) - Experiments (use with caution): ✓ ppr (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) - ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`)" + ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`)" `) } else { expect(getPreambleOutput(next.cliOutput)).toMatchInlineSnapshot(` "▲ Next.js x.y.z - Experiments (use with caution): ✓ ppr (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) - ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`)" + ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`)" `) } } else { @@ -315,6 +327,7 @@ describe('build-output-prerender', () => { ✓ serverSourceMaps (enabled by \`--debug-prerender\`) ⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`) ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`--debug-prerender\`)" `) } else { @@ -328,6 +341,7 @@ describe('build-output-prerender', () => { ⨯ serverMinification (disabled by \`--debug-prerender\`) ⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`) ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`--debug-prerender\`)" `) } @@ -342,6 +356,7 @@ describe('build-output-prerender', () => { ✓ serverSourceMaps (enabled by \`--debug-prerender\`) ⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`) ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`--debug-prerender\`)" `) } else { @@ -354,6 +369,7 @@ describe('build-output-prerender', () => { ⨯ serverMinification (disabled by \`--debug-prerender\`) ⨯ prerenderEarlyExit (disabled by \`--debug-prerender\`) ✓ clientSegmentCache (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) + ✓ clientParamParsing (enabled by \`__NEXT_EXPERIMENTAL_PPR\`) ✓ enablePrerenderSourceMaps (enabled by \`--debug-prerender\`)" `) }