Skip to content

Commit

Permalink
Drop experimental.reactRoot in favor of auto detection (#37956)
Browse files Browse the repository at this point in the history
* Drop experimental.reactRoot in favor of auto detection

* fix it hydraion metric
  • Loading branch information
huozhi committed Jun 23, 2022
1 parent 11b1307 commit 3881bbd
Show file tree
Hide file tree
Showing 21 changed files with 28 additions and 81 deletions.
3 changes: 1 addition & 2 deletions packages/next/bin/next.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env node
import * as log from '../build/output/log'
import arg from 'next/dist/compiled/arg/index.js'
import React from 'react'
import { NON_STANDARD_NODE_ENV } from '../lib/constants'
import { shouldUseReactRoot } from '../server/utils'
;['react', 'react-dom'].forEach((dependency) => {
try {
// When 'npm link' is used it checks the clone location. Not the project.
Expand Down Expand Up @@ -107,7 +107,6 @@ if (process.env.NODE_ENV) {
;(process.env as any).NODE_ENV = process.env.NODE_ENV || defaultEnv
;(process.env as any).NEXT_RUNTIME = 'nodejs'

const shouldUseReactRoot = parseInt(React.version) >= 18
if (shouldUseReactRoot) {
;(process.env as any).__NEXT_REACT_ROOT = 'true'
}
Expand Down
1 change: 0 additions & 1 deletion packages/next/build/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,6 @@ export function getServerlessEntry(opts: {
page: opts.page,
poweredByHeader: opts.config.poweredByHeader ? 'true' : '',
previewProps: JSON.stringify(opts.previewMode),
reactRoot: !!opts.config.experimental.reactRoot ? 'true' : '',
runtimeConfig:
Object.keys(opts.config.publicRuntimeConfig).length > 0 ||
Object.keys(opts.config.serverRuntimeConfig).length > 0
Expand Down
32 changes: 11 additions & 21 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,26 +364,17 @@ export default async function getBaseWebpackConfig(
rewrites.afterFiles.length > 0 ||
rewrites.fallback.length > 0

// Make sure `reactRoot` is enabled when React 18 or experimental is detected.
if (hasReactRoot) {
config.experimental.reactRoot = true
}

// Only inform during one of the builds
if (isClient && config.experimental.reactRoot && !hasReactRoot) {
// It's fine to only mention React 18 here as we don't recommend people to try experimental.
Log.warn('You have to use React 18 to use `experimental.reactRoot`.')
}

if (isClient && config.experimental.runtime && !hasReactRoot) {
throw new Error(
'`experimental.runtime` requires `experimental.reactRoot` to be enabled along with React 18.'
)
}
if (config.experimental.serverComponents && !hasReactRoot) {
throw new Error(
'`experimental.serverComponents` requires React 18 to be installed.'
)
if (isClient && !hasReactRoot) {
if (config.experimental.runtime) {
throw new Error(
'`experimental.runtime` requires React 18 to be installed.'
)
}
if (config.experimental.serverComponents) {
throw new Error(
'`experimental.serverComponents` requires React 18 to be installed.'
)
}
}

const hasConcurrentFeatures = hasReactRoot
Expand Down Expand Up @@ -1830,7 +1821,6 @@ export default async function getBaseWebpackConfig(
reactProductionProfiling,
webpack: !!config.webpack,
hasRewrites,
reactRoot: config.experimental.reactRoot,
runtime: config.experimental.runtime,
swcMinify: config.swcMinify,
swcLoader: useSWCLoader,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export function getRender({
page,
extendRenderOpts: {
buildId,
reactRoot: true,
runtime: 'edge',
supportsDynamicHTML: true,
disableOptimizedLoading: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export type ServerlessLoaderQuery = {
previewProps: string
loadedEnvFiles: string
i18n: string
reactRoot: string
}

const nextServerlessLoader: webpack.loader.Loader = function () {
Expand All @@ -53,7 +52,6 @@ const nextServerlessLoader: webpack.loader.Loader = function () {
previewProps,
loadedEnvFiles,
i18n,
reactRoot,
}: ServerlessLoaderQuery =
typeof this.query === 'string' ? parse(this.query.slice(1)) : this.query

Expand Down Expand Up @@ -187,7 +185,6 @@ const nextServerlessLoader: webpack.loader.Loader = function () {
canonicalBase: "${canonicalBase}",
generateEtags: ${generateEtags || 'false'},
poweredByHeader: ${poweredByHeader || 'false'},
reactRoot: ${reactRoot || 'false'},
runtimeConfig,
buildManifest,
Expand Down
1 change: 0 additions & 1 deletion packages/next/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,6 @@ export default async function exportApp(
optimizeCss: nextConfig.experimental.optimizeCss,
nextScriptWorkers: nextConfig.experimental.nextScriptWorkers,
optimizeFonts: nextConfig.optimizeFonts,
reactRoot: nextConfig.experimental.reactRoot || false,
largePageDataBytes: nextConfig.experimental.largePageDataBytes,
}

Expand Down
1 change: 0 additions & 1 deletion packages/next/server/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export type RenderOptsPartial = {
supportsDynamicHTML?: boolean
runtime?: 'nodejs' | 'edge'
serverComponents?: boolean
reactRoot: boolean
}

export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
Expand Down
4 changes: 1 addition & 3 deletions packages/next/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ export default abstract class Server<ServerOptions extends Options = Options> {
serverComponentManifest?: any
renderServerComponentData?: boolean
serverComponentProps?: any
reactRoot: boolean
largePageDataBytes?: number
}
protected serverOptions: ServerOptions
Expand Down Expand Up @@ -328,7 +327,6 @@ export default abstract class Server<ServerOptions extends Options = Options> {
crossOrigin: this.nextConfig.crossOrigin
? this.nextConfig.crossOrigin
: undefined,
reactRoot: this.nextConfig.experimental.reactRoot === true,
largePageDataBytes: this.nextConfig.experimental.largePageDataBytes,
}

Expand Down Expand Up @@ -1292,7 +1290,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
typeof components.Document?.getInitialProps !== 'function' ||
// When concurrent features is enabled, the built-in `Document`
// component also supports dynamic HTML.
(this.renderOpts.reactRoot &&
(!!process.env.__NEXT_REACT_ROOT &&
NEXT_BUILTIN_DOCUMENT in components.Document)

// Disable dynamic HTML in cases that we know it won't be generated,
Expand Down
2 changes: 0 additions & 2 deletions packages/next/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ export interface ExperimentalConfig {
validator?: string
skipValidation?: boolean
}
reactRoot?: boolean
disableOptimizedLoading?: boolean
gzipSize?: boolean
craCompat?: boolean
Expand Down Expand Up @@ -511,7 +510,6 @@ export const defaultConfig: NextConfig = {
nextScriptWorkers: false,
scrollRestoration: false,
externalDir: false,
reactRoot: Number(process.env.NEXT_PRIVATE_REACT_ROOT) > 0,
disableOptimizedLoading: false,
gzipSize: true,
swcFileReading: true,
Expand Down
20 changes: 0 additions & 20 deletions packages/next/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,6 @@ function assignDefaults(userConfig: { [key: string]: any }) {
delete userConfig.exportTrailingSlash
}

if (typeof userConfig.experimental?.reactMode !== 'undefined') {
console.warn(
chalk.yellow.bold('Warning: ') +
`The experimental "reactMode" option has been replaced with "reactRoot". Please update your ${configFileName}.`
)
if (typeof userConfig.experimental?.reactRoot === 'undefined') {
userConfig.experimental.reactRoot = ['concurrent', 'blocking'].includes(
userConfig.experimental.reactMode
)
}
delete userConfig.experimental.reactMode
}

const config = Object.keys(userConfig).reduce<{ [key: string]: any }>(
(currentConfig, key) => {
const value = userConfig[key]
Expand Down Expand Up @@ -233,13 +220,6 @@ function assignDefaults(userConfig: { [key: string]: any }) {
}
}

const hasReactRoot = process.env.__NEXT_REACT_ROOT
if (hasReactRoot) {
// users might not have the `experimental` key in their config
result.experimental = result.experimental || {}
result.experimental.reactRoot = true
}

if (result?.images) {
const images: ImageConfig = result.images

Expand Down
3 changes: 1 addition & 2 deletions packages/next/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import type {
import fs from 'fs'
import { join, relative, resolve, sep } from 'path'
import { IncomingMessage, ServerResponse } from 'http'
import React from 'react'
import { addRequestMeta, getRequestMeta } from './request-meta'

import {
Expand Down Expand Up @@ -88,8 +87,8 @@ import {
} from './body-streams'
import { checkIsManualRevalidate } from './api-utils'
import { isDynamicRoute } from '../shared/lib/router/utils'
import { shouldUseReactRoot } from './utils'

const shouldUseReactRoot = parseInt(React.version) >= 18
if (shouldUseReactRoot) {
;(process.env as any).__NEXT_REACT_ROOT = 'true'
}
Expand Down
3 changes: 1 addition & 2 deletions packages/next/server/next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { NodeRequestHandler } from './next-server'
import type { UrlWithParsedQuery } from 'url'

import './node-polyfill-fetch'
import React from 'react'
import { default as Server } from './next-server'
import * as log from '../build/output/log'
import loadConfig from './config'
Expand All @@ -13,6 +12,7 @@ import { PHASE_DEVELOPMENT_SERVER } from '../shared/lib/constants'
import { PHASE_PRODUCTION_SERVER } from '../shared/lib/constants'
import { IncomingMessage, ServerResponse } from 'http'
import { NextUrlWithParsedQuery } from './request-meta'
import { shouldUseReactRoot } from './utils'

let ServerImpl: typeof Server

Expand Down Expand Up @@ -183,7 +183,6 @@ function createServer(options: NextServerOptions): NextServer {
)
}

const shouldUseReactRoot = parseInt(React.version) >= 18
if (shouldUseReactRoot) {
;(process.env as any).__NEXT_REACT_ROOT = 'true'
}
Expand Down
10 changes: 4 additions & 6 deletions packages/next/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,15 @@ import stripAnsi from 'next/dist/compiled/strip-ansi'
import { urlQueryToSearchParams } from '../shared/lib/router/utils/querystring'
import { postProcessHTML } from './post-process'
import { htmlEscapeJsonString } from './htmlescape'
import { stripInternalQueries } from './utils'
import { shouldUseReactRoot, stripInternalQueries } from './utils'

let tryGetPreviewData: typeof import('./api-utils/node').tryGetPreviewData
let warn: typeof import('../build/output/log').warn

const DOCTYPE = '<!DOCTYPE html>'
const ReactDOMServer =
parseInt(React.version) >= 18
? require('react-dom/server.browser')
: require('react-dom/server')
const ReactDOMServer = shouldUseReactRoot
? require('react-dom/server.browser')
: require('react-dom/server')

if (process.env.NEXT_RUNTIME !== 'edge') {
require('./node-polyfill-web-streams')
Expand Down Expand Up @@ -244,7 +243,6 @@ export type RenderOptsPartial = {
customServer?: boolean
crossOrigin?: string
images: ImageConfigComplete
reactRoot: boolean
largePageDataBytes?: number
}

Expand Down
4 changes: 4 additions & 0 deletions packages/next/server/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { NextParsedUrlQuery } from './request-meta'
import React from 'react'
import { BLOCKED_PAGES } from '../shared/lib/constants'

export function isBlockedPage(pathname: string): boolean {
Expand Down Expand Up @@ -42,3 +43,6 @@ export function stripInternalQueries(query: NextParsedUrlQuery) {

return query
}

// When react version is >= 18 opt-in using reactRoot
export const shouldUseReactRoot = parseInt(React.version) >= 18
1 change: 0 additions & 1 deletion test/e2e/app-dir/app-rendering/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module.exports = {
experimental: {
appDir: true,
runtime: 'nodejs',
reactRoot: true,
serverComponents: true,
},
}
1 change: 0 additions & 1 deletion test/e2e/app-dir/app/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module.exports = {
experimental: {
appDir: true,
runtime: 'nodejs',
reactRoot: true,
serverComponents: true,
},
}
13 changes: 3 additions & 10 deletions test/integration/react-18-invalid-config/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ function writeNextConfig(config, reactVersion = 17) {
}

describe('Invalid react 18 webpack config', () => {
it('should enable `experimental.reactRoot` when `experimental.runtime` is enabled', async () => {
it('should install react 18 when `experimental.runtime` is enabled', async () => {
writeNextConfig({
runtime: 'edge',
})
const { stderr } = await nextBuild(appDir, [], { stderr: true, nodeArgs })
nextConfig.restore()

expect(stderr).toContain(
'`experimental.runtime` requires `experimental.reactRoot` to be enabled along with React 18.'
'`experimental.runtime` requires React 18 to be installed.'
)
})
})
Expand All @@ -68,20 +68,13 @@ describe('React 17 with React 18 config', () => {
join(reactDomPackagePah, 'package.json'),
JSON.stringify({ name: 'react-dom', version: '17.0.0' })
)
writeNextConfig({ reactRoot: true })
writeNextConfig({})
})
afterAll(async () => {
await fs.remove(reactDomPackagePah)
nextConfig.restore()
})

it('should warn user when not using react 18 and `experimental.reactRoot` is enabled', async () => {
const { stderr } = await nextBuild(appDir, [], { stderr: true, nodeArgs })
expect(stderr).toContain(
'You have to use React 18 to use `experimental.reactRoot`.'
)
})

it('suspense is not allowed in blocking rendering mode', async () => {
const { stderr, code } = await nextBuild(appDir, [], {
stderr: true,
Expand Down
2 changes: 1 addition & 1 deletion test/integration/react-18-invalid-config/next.config.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = { experimental: { reactRoot: true } }
module.exports = {}
1 change: 0 additions & 1 deletion test/integration/react-18/app/next.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module.exports = {
// reactStrictMode: true,
experimental: {
reactRoot: true,
// runtime: 'edge',
},
images: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
module.exports = {
experimental: {
reactRoot: true,
serverComponents: true,
},
}
2 changes: 1 addition & 1 deletion test/unit/parse-page-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const fixtureDir = join(__dirname, 'fixtures')

function createNextConfig(runtime?: 'edge' | 'nodejs') {
return {
experimental: { reactRoot: true, runtime },
experimental: { runtime },
}
}

Expand Down

0 comments on commit 3881bbd

Please sign in to comment.