Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,6 @@ jobs:
uses: ./.github/workflows/build_reusable.yml
with:
afterBuild: |
export __NEXT_EXPERIMENTAL_PPR=true # for compatibility with the existing tests
export __NEXT_EXPERIMENTAL_CACHE_COMPONENTS=true
export __NEXT_EXPERIMENTAL_DEBUG_CHANNEL=true
export NEXT_EXTERNAL_TESTS_FILTERS="test/experimental-tests-manifest.json"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"test-storybook": "turbo run test-storybook",
"with-rspack": "cross-env NEXT_RSPACK=1 NEXT_TEST_USE_RSPACK=1",
"with-webpack": "cross-env IS_WEBPACK_TEST=1",
"with-experimental": "cross-env __NEXT_EXPERIMENTAL_CACHE_COMPONENTS=true __NEXT_EXPERIMENTAL_PPR=true __NEXT_EXPERIMENTAL_DEBUG_CHANNEL=true"
"with-experimental": "cross-env __NEXT_EXPERIMENTAL_CACHE_COMPONENTS=true __NEXT_EXPERIMENTAL_DEBUG_CHANNEL=true"
},
"devDependencies": {
"@actions/core": "1.10.1",
Expand Down
3 changes: 2 additions & 1 deletion packages/next/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -862,5 +862,6 @@
"861": "Client Max Body Size must be larger than 0 bytes",
"862": "Request body exceeded %s",
"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.",
"864": "Missing value for segment key: \"%s\" with dynamic param type: %s"
"864": "Missing value for segment key: \"%s\" with dynamic param type: %s",
"865": "`experimental.rdcForNavigations` is enabled, but `experimental.cacheComponents` is not."
}
6 changes: 3 additions & 3 deletions packages/next/src/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { SizeLimit } from '../types'
import type { SupportedTestRunners } from '../cli/next-test'
import type { ExperimentalPPRConfig } from './lib/experimental/ppr'
import { INFINITE_CACHE } from '../lib/constants'
import { isStableBuild } from '../shared/lib/canary-only'
import { isStableBuild } from '../shared/lib/errors/canary-only-config-error'
import type { FallbackRouteParam } from '../build/static-paths/types'

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

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

Expand Down
51 changes: 11 additions & 40 deletions packages/next/src/server/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,13 @@ describe('loadConfig', () => {
const loadConfigPromise = loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
customConfig: {
experimental: {
ppr: true,
cacheComponents: true,
},
},
})

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

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

Expand Down Expand Up @@ -157,76 +157,47 @@ describe('loadConfig', () => {
delete process.env.__NEXT_VERSION
})

it('errors when cacheComponents is enabled but PPR is disabled', async () => {
it('errors when rdcForNavigations is enabled but cacheComponents is disabled', async () => {
await expect(
loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
customConfig: {
experimental: {
cacheComponents: true,
rdcForNavigations: true,
ppr: false,
},
},
})
).rejects.toThrow(
'`experimental.ppr` can not be `false` when `experimental.cacheComponents` is `true`. PPR is implicitly enabled when Cache Components is enabled.'
'`experimental.rdcForNavigations` is enabled, but `experimental.cacheComponents` is not.'
)
})

it('errors when rdcForNavigations is enabled but ppr is disabled', async () => {
it('errors when ppr is set to incremental', async () => {
await expect(
loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
customConfig: {
experimental: {
rdcForNavigations: true,
ppr: false,
ppr: 'incremental',
},
},
})
).rejects.toThrow(
'`experimental.rdcForNavigations` is enabled, but `experimental.ppr` is not.'
/`experimental\.ppr` has been merged into `experimental\.cacheComponents`/
)
})

it('defaults rdcForNavigations to true when ppr is enabled', async () => {
it('defaults rdcForNavigations to true when cacheComponents is enabled', async () => {
const result = await loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
customConfig: {
experimental: {
ppr: true,
cacheComponents: true,
},
},
})

expect(result.experimental.rdcForNavigations).toBe(true)
})

it('allows explicitly disabling rdcForNavigations when ppr is enabled', async () => {
const result = await loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
customConfig: {
experimental: {
ppr: true,
rdcForNavigations: false,
},
},
})

expect(result.experimental.rdcForNavigations).toBe(false)
})

it('errors when cacheComponents is enabled but PPR set to "incremental"', async () => {
await expect(
loadConfig(PHASE_PRODUCTION_BUILD, __dirname, {
customConfig: {
experimental: {
cacheComponents: true,
ppr: 'incremental',
},
},
})
).rejects.toThrow(
'`experimental.ppr` can not be `"incremental"` when `experimental.cacheComponents` is `true`. PPR is implicitly enabled when Cache Components is enabled.'
)
})

it('migrates experimental.dynamicIO to experimental.cacheComponents', async () => {
process.env.__NEXT_VERSION = 'canary'

Expand Down
136 changes: 34 additions & 102 deletions packages/next/src/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,14 @@ import { dset } from '../shared/lib/dset'
import { normalizeZodErrors } from '../shared/lib/zod'
import { HTML_LIMITED_BOT_UA_RE_STRING } from '../shared/lib/router/utils/is-bot'
import { findDir } from '../lib/find-pages-dir'
import { CanaryOnlyError, isStableBuild } from '../shared/lib/canary-only'
import {
CanaryOnlyConfigError,
isStableBuild,
} from '../shared/lib/errors/canary-only-config-error'
import { interopDefault } from '../lib/interop-default'
import { djb2Hash } from '../shared/lib/hash'
import type { NextAdapter } from '../build/adapter/build-complete'
import { HardDeprecatedConfigError } from '../shared/lib/errors/hard-deprecated-config-error'

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

if (isStableBuild()) {
// Prevents usage of certain experimental features outside of canary
if (result.experimental?.ppr) {
throw new CanaryOnlyError({ feature: 'experimental.ppr' })
} else if (result.experimental?.cacheComponents) {
throw new CanaryOnlyError({ feature: 'experimental.cacheComponents' })
if (result.experimental?.cacheComponents) {
throw new CanaryOnlyConfigError({
feature: 'experimental.cacheComponents',
})
} else if (result.experimental?.turbopackFileSystemCacheForBuild) {
throw new CanaryOnlyError({
throw new CanaryOnlyConfigError({
feature: 'experimental.turbopackFileSystemCacheForBuild',
})
}
}

if (result.experimental.ppr) {
throw new HardDeprecatedConfigError(
`\`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.`
)
}

if (result.output === 'export') {
if (result.i18n) {
throw new Error(
Expand Down Expand Up @@ -1178,46 +1188,22 @@ function assignDefaultsAndValidate(
result.experimental.mcpServer = true
}

// TODO: remove once we've finished migrating internally to cacheComponents.
if (result.experimental.cacheComponents) {
result.experimental.ppr = true
}

// "use cache" was originally implicitly enabled with the cacheComponents flag, so
// we transfer the value for cacheComponents to the explicit useCache flag to ensure
// backwards compatibility.
if (result.experimental.useCache === undefined) {
result.experimental.useCache = result.experimental.cacheComponents
}

// If cacheComponents is enabled, we also enable PPR.
if (result.experimental.cacheComponents) {
if (
userConfig.experimental?.ppr === false ||
userConfig.experimental?.ppr === 'incremental'
) {
throw new Error(
`\`experimental.ppr\` can not be \`${JSON.stringify(userConfig.experimental?.ppr)}\` when \`experimental.cacheComponents\` is \`true\`. PPR is implicitly enabled when Cache Components is enabled.`
)
}

result.experimental.ppr = true

if (
configuredExperimentalFeatures &&
// If we've already noted that the `process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS`
// has enabled the feature, we don't need to note it again.
process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS !== 'true' &&
process.env.__NEXT_EXPERIMENTAL_PPR !== 'true'
) {
addConfiguredExperimentalFeature(
configuredExperimentalFeatures,
'ppr',
true,
'enabled by `experimental.cacheComponents`'
)
}
}

// If ppr is enabled and the user hasn't configured rdcForNavigations, we
// enable it by default.
// If cacheComponents is enabled and the user hasn't configured
// rdcForNavigations, we enable it by default.
if (
result.experimental.ppr &&
result.experimental.cacheComponents &&
userConfig.experimental?.rdcForNavigations === undefined
) {
result.experimental.rdcForNavigations = true
Expand All @@ -1227,15 +1213,18 @@ function assignDefaultsAndValidate(
configuredExperimentalFeatures,
'rdcForNavigations',
true,
'enabled by `experimental.ppr`'
'enabled by `experimental.cacheComponents`'
)
}
}

// If rdcForNavigations is enabled, but ppr is not, we throw an error.
if (result.experimental.rdcForNavigations && !result.experimental.ppr) {
// If rdcForNavigations is enabled, but cacheComponents is not, we throw an error.
if (
result.experimental.rdcForNavigations &&
!result.experimental.cacheComponents
) {
throw new Error(
'`experimental.rdcForNavigations` is enabled, but `experimental.ppr` is not.'
'`experimental.rdcForNavigations` is enabled, but `experimental.cacheComponents` is not.'
)
}

Expand Down Expand Up @@ -1714,47 +1703,9 @@ function enforceExperimentalFeatures(
)
}

// TODO: Remove this once we've made Cache Components the default.
if (
process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS === 'true' &&
// We do respect an explicit value in the user config.
(config.experimental.ppr === undefined ||
(isDefaultConfig && !config.experimental.ppr))
) {
config.experimental.ppr = true

if (configuredExperimentalFeatures) {
addConfiguredExperimentalFeature(
configuredExperimentalFeatures,
'ppr',
true,
'enabled by `__NEXT_EXPERIMENTAL_CACHE_COMPONENTS`'
)
}
}

// TODO: Remove this once we've made Cache Components the default.
if (
process.env.__NEXT_EXPERIMENTAL_PPR === 'true' &&
// We do respect an explicit value in the user config.
(config.experimental.ppr === undefined ||
(isDefaultConfig && !config.experimental.ppr))
) {
config.experimental.ppr = true

if (configuredExperimentalFeatures) {
addConfiguredExperimentalFeature(
configuredExperimentalFeatures,
'ppr',
true,
'enabled by `__NEXT_EXPERIMENTAL_PPR`'
)
}
}

// TODO: Remove this once we've made Client Param Parsing the default.
if (
process.env.__NEXT_EXPERIMENTAL_PPR === 'true' &&
process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS === 'true' &&
// We do respect an explicit value in the user config.
(config.experimental.clientParamParsing === undefined ||
(isDefaultConfig && !config.experimental.clientParamParsing))
Expand All @@ -1766,7 +1717,7 @@ function enforceExperimentalFeatures(
configuredExperimentalFeatures,
'clientParamParsing',
true,
'enabled by `__NEXT_EXPERIMENTAL_PPR`'
'enabled by `__NEXT_EXPERIMENTAL_CACHE_COMPONENTS`'
)
}
}
Expand All @@ -1790,7 +1741,7 @@ function enforceExperimentalFeatures(
}
}

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

// TODO: Remove this once we've made RDC for Navigations the default for PPR.
if (
process.env.__NEXT_EXPERIMENTAL_PPR === 'true' &&
// We do respect an explicit value in the user config.
(config.experimental.rdcForNavigations === undefined ||
(isDefaultConfig && !config.experimental.rdcForNavigations))
) {
config.experimental.rdcForNavigations = true

if (configuredExperimentalFeatures) {
addConfiguredExperimentalFeature(
configuredExperimentalFeatures,
'rdcForNavigations',
true,
'enabled by `__NEXT_EXPERIMENTAL_PPR`'
)
}
}

if (
process.env.__NEXT_ENABLE_REACT_COMPILER === 'true' &&
// We do respect an explicit value in the user config.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function isStableBuild() {
)
}

export class CanaryOnlyError extends Error {
export class CanaryOnlyConfigError extends Error {
constructor(arg: { feature: string } | string) {
if (typeof arg === 'object' && 'feature' in arg) {
super(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class HardDeprecatedConfigError extends Error {
constructor(message: string) {
super(message)

// This error is meant to interrupt the server start/build process
// but the stack trace isn't meaningful, as it points to internal code.
this.stack = undefined
}
}
2 changes: 1 addition & 1 deletion test/development/acceptance-app/hydration-error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
retry,
} from 'next-test-utils'

const pprEnabled = process.env.__NEXT_EXPERIMENTAL_PPR === 'true'
const pprEnabled = process.env.__NEXT_EXPERIMENTAL_CACHE_COMPONENTS === 'true'

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