Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate experimental React opt-in & add ppr flag #55560

Merged
merged 2 commits into from
Sep 19, 2023
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
12 changes: 5 additions & 7 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ import { createClientRouterFilter } from '../lib/create-client-router-filter'
import { createValidFileMatcher } from '../server/lib/find-page-file'
import { startTypeChecking } from './type-check'
import { generateInterceptionRoutesRewrites } from '../lib/generate-interception-routes-rewrites'
import { needsExperimentalReact } from '../lib/needs-experimental-react'

import { buildDataRoute } from '../server/lib/router-utils/build-data-route'
import { defaultOverrides } from '../server/require-hook'
Expand Down Expand Up @@ -1255,11 +1256,9 @@ export default async function build(
__NEXT_INCREMENTAL_CACHE_IPC_PORT: incrementalCacheIpcPort + '',
__NEXT_INCREMENTAL_CACHE_IPC_KEY:
incrementalCacheIpcValidationKey,
__NEXT_PRIVATE_PREBUNDLED_REACT: hasAppDir
? config.experimental.serverActions
? 'experimental'
: 'next'
: '',
__NEXT_PRIVATE_PREBUNDLED_REACT: needsExperimentalReact(config)
? 'experimental'
: 'next',
},
},
enableWorkerThreads: config.experimental.workerThreads,
Expand Down Expand Up @@ -2438,8 +2437,7 @@ export default async function build(
outputFileTracingRoot,
requiredServerFiles.config,
middlewareManifest,
hasInstrumentationHook,
hasAppDir
hasInstrumentationHook
)
})
}
Expand Down
29 changes: 15 additions & 14 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NextConfigComplete } from '../server/config-shared'
import type { NextConfig, NextConfigComplete } from '../server/config-shared'
import type { AppBuildManifest } from './webpack/plugins/app-build-manifest-plugin'
import type { AssetBinding } from './webpack/loaders/get-module-build-info'
import type { GetStaticPaths, PageConfig, ServerRuntime } from 'next/types'
Expand Down Expand Up @@ -67,7 +67,8 @@ import { nodeFs } from '../server/lib/node-fs-methods'
import * as ciEnvironment from '../telemetry/ci-info'
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
import { denormalizeAppPagePath } from '../shared/lib/page-path/denormalize-app-path'
// import { AppRouteRouteModule } from '../server/future/route-modules/app-route/module'
import { needsExperimentalReact } from '../lib/needs-experimental-react'

const { AppRouteRouteModule } =
require('../server/future/route-modules/app-route/module.compiled') as typeof import('../server/future/route-modules/app-route/module')

Expand Down Expand Up @@ -1826,13 +1827,17 @@ export async function copyTracedFiles(
pageKeys: readonly string[],
appPageKeys: readonly string[] | undefined,
tracingRoot: string,
serverConfig: { [key: string]: any },
serverConfig: NextConfig,
middlewareManifest: MiddlewareManifest,
hasInstrumentationHook: boolean,
hasAppDir: boolean
hasInstrumentationHook: boolean
) {
const outputPath = path.join(distDir, 'standalone')
let moduleType = false
const nextConfig = {
...serverConfig,
distDir: `./${path.relative(dir, distDir)}`,
}
const hasExperimentalReact = needsExperimentalReact(nextConfig)
try {
const packageJsonPath = path.join(distDir, '../package.json')
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'))
Expand Down Expand Up @@ -1956,6 +1961,7 @@ export async function copyTracedFiles(
'server.js'
)
await fs.mkdir(path.dirname(serverOutputPath), { recursive: true })

await fs.writeFile(
serverOutputPath,
`${
Expand Down Expand Up @@ -1985,17 +1991,12 @@ const currentPort = parseInt(process.env.PORT, 10) || 3000
const hostname = process.env.HOSTNAME || '0.0.0.0'

let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10)
const nextConfig = ${JSON.stringify({
...serverConfig,
distDir: `./${path.relative(dir, distDir)}`,
})}
const nextConfig = ${JSON.stringify(nextConfig)}

process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)
process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = ${hasAppDir}
? nextConfig.experimental && nextConfig.experimental.serverActions
? 'experimental'
: 'next'
: '';
process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = ${hasExperimentalReact}
? 'experimental'
: 'next'

require('next')
const { startServer } = require('next/dist/server/lib/start-server')
Expand Down
13 changes: 7 additions & 6 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import { getSupportedBrowsers } from './utils'
import { MemoryWithGcCachePlugin } from './webpack/plugins/memory-with-gc-cache-plugin'
import { getBabelConfigFile } from './get-babel-config-file'
import { defaultOverrides } from '../server/require-hook'
import { needsExperimentalReact } from '../lib/needs-experimental-react'

type ExcludesFalse = <T>(x: T | false) => x is T
type ClientEntries = {
Expand Down Expand Up @@ -191,7 +192,6 @@ export function getDefineEnv({
isNodeServer,
middlewareMatchers,
previewModeId,
useServerActions,
}: {
allowedRevalidateHeaderKeys: string[] | undefined
clientRouterFilters: Parameters<
Expand All @@ -208,7 +208,6 @@ export function getDefineEnv({
isNodeServer: boolean
middlewareMatchers: MiddlewareMatcher[] | undefined
previewModeId: string | undefined
useServerActions: boolean
}) {
return {
// internal field to identify the plugin config
Expand Down Expand Up @@ -373,8 +372,9 @@ export function getDefineEnv({
'process.env.TURBOPACK': JSON.stringify(false),
...(isNodeServer
? {
'process.env.__NEXT_EXPERIMENTAL_REACT':
JSON.stringify(useServerActions),
'process.env.__NEXT_EXPERIMENTAL_REACT': JSON.stringify(
needsExperimentalReact(config)
),
}
: undefined),
}
Expand Down Expand Up @@ -808,7 +808,9 @@ export default async function getBaseWebpackConfig(
const disableOptimizedLoading = true
const enableTypedRoutes = !!config.experimental.typedRoutes && hasAppDir
const useServerActions = !!config.experimental.serverActions && hasAppDir
const bundledReactChannel = useServerActions ? '-experimental' : ''
const bundledReactChannel = needsExperimentalReact(config)
? '-experimental'
: ''

if (isClient) {
if (
Expand Down Expand Up @@ -2543,7 +2545,6 @@ export default async function getBaseWebpackConfig(
isNodeServer,
middlewareMatchers,
previewModeId,
useServerActions,
})
),
isClient &&
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/cli/next-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
getReservedPortExplanation,
isPortIsReserved,
} from '../lib/helpers/get-reserved-port'
import { needsExperimentalReact } from '../lib/needs-experimental-react'

let dir: string
let child: undefined | ReturnType<typeof fork>
Expand Down Expand Up @@ -198,8 +199,7 @@ const nextDev: CliCommand = async (args) => {
},
})

process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = config.experimental
.serverActions
process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = needsExperimentalReact(config)
? 'experimental'
: 'next'

Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { MiddlewareManifest } from '../build/webpack/plugins/middleware-plugin'
import { isAppRouteRoute } from '../lib/is-app-route-route'
import { isAppPageRoute } from '../lib/is-app-page-route'
import isError from '../lib/is-error'
import { needsExperimentalReact } from '../lib/needs-experimental-react'

const exists = promisify(existsOrig)

Expand Down Expand Up @@ -730,7 +731,7 @@ export default async function exportApp(
fetchCacheKeyPrefix: nextConfig.experimental.fetchCacheKeyPrefix,
incrementalCacheHandlerPath:
nextConfig.experimental.incrementalCacheHandlerPath,
serverActions: nextConfig.experimental.serverActions,
enableExperimentalReact: needsExperimentalReact(nextConfig),
})

for (const validation of result.ampValidations || []) {
Expand Down
6 changes: 3 additions & 3 deletions packages/next/src/export/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ interface ExportPageInput {
incrementalCacheHandlerPath?: string
fetchCacheKeyPrefix?: string
nextConfigOutput?: NextConfigComplete['output']
serverActions?: boolean
enableExperimentalReact?: boolean
}

interface ExportPageResults {
Expand Down Expand Up @@ -154,7 +154,7 @@ export default async function exportPage({
fetchCache,
fetchCacheKeyPrefix,
incrementalCacheHandlerPath,
serverActions,
enableExperimentalReact,
}: ExportPageInput): Promise<ExportPageResults> {
setHttpClientAndAgentOptions({
httpAgentOptions,
Expand All @@ -171,7 +171,7 @@ export default async function exportPage({
if (renderOpts.deploymentId) {
process.env.NEXT_DEPLOYMENT_ID = renderOpts.deploymentId
}
if (serverActions) {
if (enableExperimentalReact) {
process.env.__NEXT_EXPERIMENTAL_REACT = 'true'
}
const { query: originalQuery = {} } = pathMap
Expand Down
5 changes: 5 additions & 0 deletions packages/next/src/lib/needs-experimental-react.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { NextConfig } from '../server/config-shared'

export function needsExperimentalReact(config: NextConfig) {
return Boolean(config.experimental?.serverActions || config.experimental?.ppr)
}
3 changes: 3 additions & 0 deletions packages/next/src/server/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@ const configSchema = {
outputFileTracingIncludes: {
type: 'object',
},
ppr: {
type: 'boolean',
},
proxyTimeout: {
minimum: 0,
type: 'number',
Expand Down
8 changes: 7 additions & 1 deletion packages/next/src/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,16 @@ export interface ExperimentalConfig {
instrumentationHook?: boolean

/**
* Enable `react@experimental` channel for the `app` directory.
* Enables server actions. Using this feature will enable the `react@experimental` for the `app` directory.
* @see https://nextjs.org/docs/app/api-reference/functions/server-actions
*/
serverActions?: boolean

/**
* Using this feature will enable the `react@experimental` for the `app` directory.
*/
ppr?: boolean

/**
* Allows adjusting body parser size limit for server actions.
*/
Expand Down
1 change: 0 additions & 1 deletion packages/next/src/server/lib/router-utils/setup-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1733,7 +1733,6 @@ async function startWatcher(opts: SetupOpts) {
isNodeServer,
middlewareMatchers: undefined,
previewModeId: undefined,
useServerActions: !!nextConfig.experimental.serverActions,
})

Object.keys(plugin.definitions).forEach((key) => {
Expand Down
2 changes: 1 addition & 1 deletion scripts/minimal-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ console.time('next-wall-time')

process.env.NODE_ENV = 'production'

// Change this to 'experimental' for server actions
// Change this to 'experimental' to opt into the React experimental channel (needed for server actions, ppr)
process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = 'next'

if (process.env.LOG_REQUIRE) {
Expand Down
49 changes: 49 additions & 0 deletions test/e2e/app-dir/rsc-basic/rsc-basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -592,5 +592,54 @@ createNextDescribe(
await Promise.all(promises)
})
}

describe('react@experimental', () => {
it.each([{ flag: 'ppr' }, { flag: 'serverActions' }])(
'should opt into the react@experimental when enabling $flag',
async ({ flag }) => {
await next.stop()
await next.patchFile(
'next.config.js',
`
module.exports = {
experimental: {
${flag}: true
}
}
`
)

await next.start()
const resPages$ = await next.render$('/app-react')
const ssrPagesReactVersions = [
await resPages$('#react').text(),
await resPages$('#react-dom').text(),
await resPages$('#react-dom-server').text(),
await resPages$('#client-react').text(),
await resPages$('#client-react-dom').text(),
await resPages$('#client-react-dom-server').text(),
]

ssrPagesReactVersions.forEach((version) => {
expect(version).toMatch('-experimental-')
})

const browser = await next.browser('/app-react')
const browserAppReactVersions = await browser.eval(`
[
document.querySelector('#react').innerText,
document.querySelector('#react-dom').innerText,
document.querySelector('#react-dom-server').innerText,
document.querySelector('#client-react').innerText,
document.querySelector('#client-react-dom').innerText,
document.querySelector('#client-react-dom-server').innerText,
]
`)
browserAppReactVersions.forEach((version) =>
expect(version).toMatch('-experimental-')
)
}
)
})
}
)
Loading