Skip to content

Commit d81ef26

Browse files
authored
feat(breaking): Hard Deprecate PPR Configuration (#84280)
This hard deprecates the `experimental.ppr` configuration, requiring users to opt-in instead via `experimental.cacheComponents`. This does mean that the previous `experimental.ppr = "incremental"` will no longer be supported. NAR-433
1 parent 6cecc75 commit d81ef26

File tree

145 files changed

+1023
-2873
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

145 files changed

+1023
-2873
lines changed

.github/workflows/build_and_test.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,6 @@ jobs:
700700
uses: ./.github/workflows/build_reusable.yml
701701
with:
702702
afterBuild: |
703-
export __NEXT_EXPERIMENTAL_PPR=true # for compatibility with the existing tests
704703
export __NEXT_EXPERIMENTAL_CACHE_COMPONENTS=true
705704
export __NEXT_EXPERIMENTAL_DEBUG_CHANNEL=true
706705
export NEXT_EXTERNAL_TESTS_FILTERS="test/experimental-tests-manifest.json"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@
110110
"test-storybook": "turbo run test-storybook",
111111
"with-rspack": "cross-env NEXT_RSPACK=1 NEXT_TEST_USE_RSPACK=1",
112112
"with-webpack": "cross-env IS_WEBPACK_TEST=1",
113-
"with-experimental": "cross-env __NEXT_EXPERIMENTAL_CACHE_COMPONENTS=true __NEXT_EXPERIMENTAL_PPR=true __NEXT_EXPERIMENTAL_DEBUG_CHANNEL=true"
113+
"with-experimental": "cross-env __NEXT_EXPERIMENTAL_CACHE_COMPONENTS=true __NEXT_EXPERIMENTAL_DEBUG_CHANNEL=true"
114114
},
115115
"devDependencies": {
116116
"@actions/core": "1.10.1",

packages/next/errors.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -862,5 +862,6 @@
862862
"861": "Client Max Body Size must be larger than 0 bytes",
863863
"862": "Request body exceeded %s",
864864
"863": "\\`<Link legacyBehavior>\\` received a direct child that is either a Server Component, or JSX that was loaded with React.lazy(). This is not supported. Either remove legacyBehavior, or make the direct child a Client Component that renders the Link's \\`<a>\\` tag.",
865-
"864": "Missing value for segment key: \"%s\" with dynamic param type: %s"
865+
"864": "Missing value for segment key: \"%s\" with dynamic param type: %s",
866+
"865": "`experimental.rdcForNavigations` is enabled, but `experimental.cacheComponents` is not."
866867
}

packages/next/src/server/config-shared.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type { SizeLimit } from '../types'
1313
import type { SupportedTestRunners } from '../cli/next-test'
1414
import type { ExperimentalPPRConfig } from './lib/experimental/ppr'
1515
import { INFINITE_CACHE } from '../lib/constants'
16-
import { isStableBuild } from '../shared/lib/canary-only'
16+
import { isStableBuild } from '../shared/lib/errors/canary-only-config-error'
1717
import type { FallbackRouteParam } from '../build/static-paths/types'
1818

1919
export type NextConfigComplete = Required<Omit<NextConfig, 'configFile'>> & {
@@ -573,8 +573,8 @@ export interface ExperimentalConfig {
573573
clientTraceMetadata?: string[]
574574

575575
/**
576-
* Enables experimental Partial Prerendering feature of Next.js.
577-
* Using this feature will enable the `react@experimental` for the `app` directory.
576+
* @deprecated This configuration option has been merged into `experimental.cacheComponents`.
577+
* The Partial Prerendering feature is still available via `experimental.cacheComponents`.
578578
*/
579579
ppr?: ExperimentalPPRConfig
580580

packages/next/src/server/config.test.ts

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,13 @@ describe('loadConfig', () => {
8686
const loadConfigPromise = loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
8787
customConfig: {
8888
experimental: {
89-
ppr: true,
89+
cacheComponents: true,
9090
},
9191
},
9292
})
9393

9494
await expect(loadConfigPromise).rejects.toThrow(
95-
/The experimental feature "experimental.ppr" can only be enabled when using the latest canary version of Next.js./
95+
/The experimental feature "experimental.cacheComponents" can only be enabled when using the latest canary version of Next.js./
9696
)
9797

9898
try {
@@ -115,7 +115,7 @@ describe('loadConfig', () => {
115115
},
116116
})
117117
).rejects.toThrow(
118-
/The experimental feature "experimental.ppr" can only be enabled when using the latest canary version of Next.js./
118+
/`experimental\.ppr` has been merged into `experimental\.cacheComponents`/
119119
)
120120
})
121121

@@ -157,76 +157,47 @@ describe('loadConfig', () => {
157157
delete process.env.__NEXT_VERSION
158158
})
159159

160-
it('errors when cacheComponents is enabled but PPR is disabled', async () => {
160+
it('errors when rdcForNavigations is enabled but cacheComponents is disabled', async () => {
161161
await expect(
162162
loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
163163
customConfig: {
164164
experimental: {
165-
cacheComponents: true,
165+
rdcForNavigations: true,
166166
ppr: false,
167167
},
168168
},
169169
})
170170
).rejects.toThrow(
171-
'`experimental.ppr` can not be `false` when `experimental.cacheComponents` is `true`. PPR is implicitly enabled when Cache Components is enabled.'
171+
'`experimental.rdcForNavigations` is enabled, but `experimental.cacheComponents` is not.'
172172
)
173173
})
174174

175-
it('errors when rdcForNavigations is enabled but ppr is disabled', async () => {
175+
it('errors when ppr is set to incremental', async () => {
176176
await expect(
177177
loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
178178
customConfig: {
179179
experimental: {
180-
rdcForNavigations: true,
181-
ppr: false,
180+
ppr: 'incremental',
182181
},
183182
},
184183
})
185184
).rejects.toThrow(
186-
'`experimental.rdcForNavigations` is enabled, but `experimental.ppr` is not.'
185+
/`experimental\.ppr` has been merged into `experimental\.cacheComponents`/
187186
)
188187
})
189188

190-
it('defaults rdcForNavigations to true when ppr is enabled', async () => {
189+
it('defaults rdcForNavigations to true when cacheComponents is enabled', async () => {
191190
const result = await loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
192191
customConfig: {
193192
experimental: {
194-
ppr: true,
193+
cacheComponents: true,
195194
},
196195
},
197196
})
198197

199198
expect(result.experimental.rdcForNavigations).toBe(true)
200199
})
201200

202-
it('allows explicitly disabling rdcForNavigations when ppr is enabled', async () => {
203-
const result = await loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
204-
customConfig: {
205-
experimental: {
206-
ppr: true,
207-
rdcForNavigations: false,
208-
},
209-
},
210-
})
211-
212-
expect(result.experimental.rdcForNavigations).toBe(false)
213-
})
214-
215-
it('errors when cacheComponents is enabled but PPR set to "incremental"', async () => {
216-
await expect(
217-
loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
218-
customConfig: {
219-
experimental: {
220-
cacheComponents: true,
221-
ppr: 'incremental',
222-
},
223-
},
224-
})
225-
).rejects.toThrow(
226-
'`experimental.ppr` can not be `"incremental"` when `experimental.cacheComponents` is `true`. PPR is implicitly enabled when Cache Components is enabled.'
227-
)
228-
})
229-
230201
it('migrates experimental.dynamicIO to experimental.cacheComponents', async () => {
231202
process.env.__NEXT_VERSION = 'canary'
232203

packages/next/src/server/config.ts

Lines changed: 34 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,14 @@ import { dset } from '../shared/lib/dset'
3838
import { normalizeZodErrors } from '../shared/lib/zod'
3939
import { HTML_LIMITED_BOT_UA_RE_STRING } from '../shared/lib/router/utils/is-bot'
4040
import { findDir } from '../lib/find-pages-dir'
41-
import { CanaryOnlyError, isStableBuild } from '../shared/lib/canary-only'
41+
import {
42+
CanaryOnlyConfigError,
43+
isStableBuild,
44+
} from '../shared/lib/errors/canary-only-config-error'
4245
import { interopDefault } from '../lib/interop-default'
4346
import { djb2Hash } from '../shared/lib/hash'
4447
import type { NextAdapter } from '../build/adapter/build-complete'
48+
import { HardDeprecatedConfigError } from '../shared/lib/errors/hard-deprecated-config-error'
4549

4650
export { normalizeConfig } from './config-shared'
4751
export type { DomainLocale, NextConfig } from './config-shared'
@@ -365,17 +369,23 @@ function assignDefaultsAndValidate(
365369

366370
if (isStableBuild()) {
367371
// Prevents usage of certain experimental features outside of canary
368-
if (result.experimental?.ppr) {
369-
throw new CanaryOnlyError({ feature: 'experimental.ppr' })
370-
} else if (result.experimental?.cacheComponents) {
371-
throw new CanaryOnlyError({ feature: 'experimental.cacheComponents' })
372+
if (result.experimental?.cacheComponents) {
373+
throw new CanaryOnlyConfigError({
374+
feature: 'experimental.cacheComponents',
375+
})
372376
} else if (result.experimental?.turbopackFileSystemCacheForBuild) {
373-
throw new CanaryOnlyError({
377+
throw new CanaryOnlyConfigError({
374378
feature: 'experimental.turbopackFileSystemCacheForBuild',
375379
})
376380
}
377381
}
378382

383+
if (result.experimental.ppr) {
384+
throw new HardDeprecatedConfigError(
385+
`\`experimental.ppr\` has been merged into \`experimental.cacheComponents\`. The Partial Prerendering feature is still available, but is now enabled via \`experimental.cacheComponents\`. Please update your ${configFileName} accordingly.`
386+
)
387+
}
388+
379389
if (result.output === 'export') {
380390
if (result.i18n) {
381391
throw new Error(
@@ -1178,46 +1188,22 @@ function assignDefaultsAndValidate(
11781188
result.experimental.mcpServer = true
11791189
}
11801190

1191+
// TODO: remove once we've finished migrating internally to cacheComponents.
1192+
if (result.experimental.cacheComponents) {
1193+
result.experimental.ppr = true
1194+
}
1195+
11811196
// "use cache" was originally implicitly enabled with the cacheComponents flag, so
11821197
// we transfer the value for cacheComponents to the explicit useCache flag to ensure
11831198
// backwards compatibility.
11841199
if (result.experimental.useCache === undefined) {
11851200
result.experimental.useCache = result.experimental.cacheComponents
11861201
}
11871202

1188-
// If cacheComponents is enabled, we also enable PPR.
1189-
if (result.experimental.cacheComponents) {
1190-
if (
1191-
userConfig.experimental?.ppr === false ||
1192-
userConfig.experimental?.ppr === 'incremental'
1193-
) {
1194-
throw new Error(
1195-
`\`experimental.ppr\` can not be \`${JSON.stringify(userConfig.experimental?.ppr)}\` when \`experimental.cacheComponents\` is \`true\`. PPR is implicitly enabled when Cache Components is enabled.`
1196-
)
1197-
}
1198-
1199-
result.experimental.ppr = true
1200-
1201-
if (
1202-
configuredExperimentalFeatures &&
1203-
// If we've already noted that the `process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS`
1204-
// has enabled the feature, we don't need to note it again.
1205-
process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS !== 'true' &&
1206-
process.env.__NEXT_EXPERIMENTAL_PPR !== 'true'
1207-
) {
1208-
addConfiguredExperimentalFeature(
1209-
configuredExperimentalFeatures,
1210-
'ppr',
1211-
true,
1212-
'enabled by `experimental.cacheComponents`'
1213-
)
1214-
}
1215-
}
1216-
1217-
// If ppr is enabled and the user hasn't configured rdcForNavigations, we
1218-
// enable it by default.
1203+
// If cacheComponents is enabled and the user hasn't configured
1204+
// rdcForNavigations, we enable it by default.
12191205
if (
1220-
result.experimental.ppr &&
1206+
result.experimental.cacheComponents &&
12211207
userConfig.experimental?.rdcForNavigations === undefined
12221208
) {
12231209
result.experimental.rdcForNavigations = true
@@ -1227,15 +1213,18 @@ function assignDefaultsAndValidate(
12271213
configuredExperimentalFeatures,
12281214
'rdcForNavigations',
12291215
true,
1230-
'enabled by `experimental.ppr`'
1216+
'enabled by `experimental.cacheComponents`'
12311217
)
12321218
}
12331219
}
12341220

1235-
// If rdcForNavigations is enabled, but ppr is not, we throw an error.
1236-
if (result.experimental.rdcForNavigations && !result.experimental.ppr) {
1221+
// If rdcForNavigations is enabled, but cacheComponents is not, we throw an error.
1222+
if (
1223+
result.experimental.rdcForNavigations &&
1224+
!result.experimental.cacheComponents
1225+
) {
12371226
throw new Error(
1238-
'`experimental.rdcForNavigations` is enabled, but `experimental.ppr` is not.'
1227+
'`experimental.rdcForNavigations` is enabled, but `experimental.cacheComponents` is not.'
12391228
)
12401229
}
12411230

@@ -1714,47 +1703,9 @@ function enforceExperimentalFeatures(
17141703
)
17151704
}
17161705

1717-
// TODO: Remove this once we've made Cache Components the default.
1718-
if (
1719-
process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS === 'true' &&
1720-
// We do respect an explicit value in the user config.
1721-
(config.experimental.ppr === undefined ||
1722-
(isDefaultConfig && !config.experimental.ppr))
1723-
) {
1724-
config.experimental.ppr = true
1725-
1726-
if (configuredExperimentalFeatures) {
1727-
addConfiguredExperimentalFeature(
1728-
configuredExperimentalFeatures,
1729-
'ppr',
1730-
true,
1731-
'enabled by `__NEXT_EXPERIMENTAL_CACHE_COMPONENTS`'
1732-
)
1733-
}
1734-
}
1735-
1736-
// TODO: Remove this once we've made Cache Components the default.
1737-
if (
1738-
process.env.__NEXT_EXPERIMENTAL_PPR === 'true' &&
1739-
// We do respect an explicit value in the user config.
1740-
(config.experimental.ppr === undefined ||
1741-
(isDefaultConfig && !config.experimental.ppr))
1742-
) {
1743-
config.experimental.ppr = true
1744-
1745-
if (configuredExperimentalFeatures) {
1746-
addConfiguredExperimentalFeature(
1747-
configuredExperimentalFeatures,
1748-
'ppr',
1749-
true,
1750-
'enabled by `__NEXT_EXPERIMENTAL_PPR`'
1751-
)
1752-
}
1753-
}
1754-
17551706
// TODO: Remove this once we've made Client Param Parsing the default.
17561707
if (
1757-
process.env.__NEXT_EXPERIMENTAL_PPR === 'true' &&
1708+
process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS === 'true' &&
17581709
// We do respect an explicit value in the user config.
17591710
(config.experimental.clientParamParsing === undefined ||
17601711
(isDefaultConfig && !config.experimental.clientParamParsing))
@@ -1766,7 +1717,7 @@ function enforceExperimentalFeatures(
17661717
configuredExperimentalFeatures,
17671718
'clientParamParsing',
17681719
true,
1769-
'enabled by `__NEXT_EXPERIMENTAL_PPR`'
1720+
'enabled by `__NEXT_EXPERIMENTAL_CACHE_COMPONENTS`'
17701721
)
17711722
}
17721723
}
@@ -1790,7 +1741,7 @@ function enforceExperimentalFeatures(
17901741
}
17911742
}
17921743

1793-
// TODO: Remove this once we've made RDC for Navigations the default for PPR.
1744+
// TODO: Remove this once we've made RDC for Navigations the default for cache components.
17941745
if (
17951746
process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS === 'true' &&
17961747
// We do respect an explicit value in the user config.
@@ -1828,25 +1779,6 @@ function enforceExperimentalFeatures(
18281779
}
18291780
}
18301781

1831-
// TODO: Remove this once we've made RDC for Navigations the default for PPR.
1832-
if (
1833-
process.env.__NEXT_EXPERIMENTAL_PPR === 'true' &&
1834-
// We do respect an explicit value in the user config.
1835-
(config.experimental.rdcForNavigations === undefined ||
1836-
(isDefaultConfig && !config.experimental.rdcForNavigations))
1837-
) {
1838-
config.experimental.rdcForNavigations = true
1839-
1840-
if (configuredExperimentalFeatures) {
1841-
addConfiguredExperimentalFeature(
1842-
configuredExperimentalFeatures,
1843-
'rdcForNavigations',
1844-
true,
1845-
'enabled by `__NEXT_EXPERIMENTAL_PPR`'
1846-
)
1847-
}
1848-
}
1849-
18501782
if (
18511783
process.env.__NEXT_ENABLE_REACT_COMPILER === 'true' &&
18521784
// We do respect an explicit value in the user config.

packages/next/src/shared/lib/canary-only.ts renamed to packages/next/src/shared/lib/errors/canary-only-config-error.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export function isStableBuild() {
66
)
77
}
88

9-
export class CanaryOnlyError extends Error {
9+
export class CanaryOnlyConfigError extends Error {
1010
constructor(arg: { feature: string } | string) {
1111
if (typeof arg === 'object' && 'feature' in arg) {
1212
super(
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export class HardDeprecatedConfigError extends Error {
2+
constructor(message: string) {
3+
super(message)
4+
5+
// This error is meant to interrupt the server start/build process
6+
// but the stack trace isn't meaningful, as it points to internal code.
7+
this.stack = undefined
8+
}
9+
}

test/development/acceptance-app/hydration-error.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
retry,
1010
} from 'next-test-utils'
1111

12-
const pprEnabled = process.env.__NEXT_EXPERIMENTAL_PPR === 'true'
12+
const pprEnabled = process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS === 'true'
1313

1414
describe('Error overlay for hydration errors in App router', () => {
1515
const { next, isTurbopack } = nextTestSetup({

0 commit comments

Comments
 (0)