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

Hoist the desired ReactDOM import expression #36552

Merged
merged 6 commits into from
Apr 28, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/next/bin/next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ const args = arg(
}
)

// Detect if react-dom is enabled streaming rendering mode
const shouldUseReactRoot = !!require('react-dom/server.browser')
.renderToReadableStream

// Version is inlined into the file using taskr build pipeline
if (args['--version']) {
console.log(`Next.js v${process.env.__NEXT_VERSION}`)
Expand Down Expand Up @@ -105,6 +109,9 @@ if (process.env.NODE_ENV) {

;(process.env as any).NODE_ENV = process.env.NODE_ENV || defaultEnv
;(process.env as any).NEXT_RUNTIME = 'nodejs'
if (shouldUseReactRoot) {
;(process.env as any).__NEXT_REACT_ROOT = 'true'
}

// x-ref: https://github.com/vercel/next.js/pull/34688#issuecomment-1047994505
if (process.versions.pnp === '3') {
Expand Down
5 changes: 2 additions & 3 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { webpack5 as webpack } from 'next/dist/compiled/webpack/webpack'
import { loadEnvConfig } from '@next/env'
import chalk from 'next/dist/compiled/chalk'
import crypto from 'crypto'
Expand Down Expand Up @@ -109,9 +110,7 @@ import { NextConfigComplete } from '../server/config-shared'
import isError, { NextError } from '../lib/is-error'
import { TelemetryPlugin } from './webpack/plugins/telemetry-plugin'
import { MiddlewareManifest } from './webpack/plugins/middleware-plugin'
import type { webpack5 as webpack } from 'next/dist/compiled/webpack/webpack'
import { recursiveCopy } from '../lib/recursive-copy'
import { shouldUseReactRoot } from '../server/config'
import { recursiveReadDir } from '../lib/recursive-readdir'
import { lockfilePatchPromise } from './swc'

Expand Down Expand Up @@ -164,7 +163,7 @@ export default async function build(

// We enable concurrent features (Fizz-related rendering architecture) when
// using React 18 or experimental.
const hasReactRoot = shouldUseReactRoot()
const hasReactRoot = !!process.env.__NEXT_REACT_ROOT
const hasConcurrentFeatures = hasReactRoot
const hasServerComponents =
hasReactRoot && !!config.experimental.serverComponents
Expand Down
3 changes: 0 additions & 3 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1399,9 +1399,6 @@ export default async function getBaseWebpackConfig(
config.reactStrictMode
),
'process.env.__NEXT_REACT_ROOT': JSON.stringify(hasReactRoot),
'process.env.__NEXT_CONCURRENT_FEATURES': JSON.stringify(
hasConcurrentFeatures
),
'process.env.__NEXT_RSC': JSON.stringify(hasServerComponents),
'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify(
config.optimizeFonts && !dev
Expand Down
16 changes: 1 addition & 15 deletions packages/next/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { basename, extname, relative, isAbsolute, resolve } from 'path'
import { pathToFileURL } from 'url'
import { Agent as HttpAgent } from 'http'
import { Agent as HttpsAgent } from 'https'
import semver from 'next/dist/compiled/semver'
import findUp from 'next/dist/compiled/find-up'
import chalk from '../lib/chalk'
import * as Log from '../build/output/log'
Expand Down Expand Up @@ -188,7 +187,7 @@ function assignDefaults(userConfig: { [key: string]: any }) {
}
}

const hasReactRoot = shouldUseReactRoot()
const hasReactRoot = process.env.__NEXT_REACT_ROOT
if (hasReactRoot) {
// users might not have the `experimental` key in their config
result.experimental = result.experimental || {}
Expand Down Expand Up @@ -753,19 +752,6 @@ export default async function loadConfig(
return completeConfig
}

export const shouldUseReactRoot = execOnce(() => {
const reactDomVersion = require('react-dom').version
const isReactExperimental = Boolean(
reactDomVersion && /0\.0\.0-experimental/.test(reactDomVersion)
)
const hasReact18: boolean =
Boolean(reactDomVersion) &&
(semver.gte(reactDomVersion!, '18.0.0') ||
semver.coerce(reactDomVersion)?.version === '18.0.0')

return hasReact18 || isReactExperimental
})

export function setHttpAgentOptions(
options: NextConfigComplete['httpAgentOptions']
) {
Expand Down
3 changes: 1 addition & 2 deletions packages/next/server/dev/hot-reloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import { getProperError } from '../../lib/is-error'
import ws from 'next/dist/compiled/ws'
import { promises as fs } from 'fs'
import { getPageRuntime } from '../../build/entries'
import { shouldUseReactRoot } from '../config'

const wsServer = new ws.Server({ noServer: true })

Expand Down Expand Up @@ -199,7 +198,7 @@ export default class HotReloader {

this.config = config
this.runtime = config.experimental.runtime
this.hasReactRoot = shouldUseReactRoot()
this.hasReactRoot = !!process.env.__NEXT_REACT_ROOT
this.hasServerComponents =
this.hasReactRoot && !!config.experimental.serverComponents
this.previewProps = previewProps
Expand Down
6 changes: 3 additions & 3 deletions packages/next/server/node-web-streams-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,18 @@ export function createFlushEffectStream(
})
}

export async function renderToInitialStream({
export function renderToInitialStream({
ReactDOMServer,
element,
}: {
ReactDOMServer: typeof import('react-dom/server')
ReactDOMServer: any
element: React.ReactElement
}): Promise<
ReadableStream<Uint8Array> & {
allReady?: Promise<void>
}
> {
return await (ReactDOMServer as any).renderToReadableStream(element)
return ReactDOMServer.renderToReadableStream(element)
}

export async function continueFromInitialStream({
Expand Down
17 changes: 7 additions & 10 deletions packages/next/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ let warn: typeof import('../build/output/log').warn
let postProcess: typeof import('../shared/lib/post-process').default

const DOCTYPE = '<!DOCTYPE html>'
const ReactDOMServer = process.env.__NEXT_REACT_ROOT
? require('react-dom/server.browser')
: require('react-dom/server')

if (process.env.NEXT_RUNTIME !== 'edge') {
require('./node-polyfill-web-streams')
Expand Down Expand Up @@ -486,20 +489,17 @@ export async function renderToHTML(
devOnlyCacheBusterQueryString,
supportsDynamicHTML,
images,
reactRoot,
runtime: globalRuntime,
ComponentMod,
AppMod,
AppServerMod,
} = renderOpts

const hasConcurrentFeatures = reactRoot

let Document = renderOpts.Document

// We don't need to opt-into the flight inlining logic if the page isn't a RSC.
const isServerComponent =
hasConcurrentFeatures &&
!!process.env.__NEXT_REACT_ROOT &&
!!serverComponentManifest &&
!!ComponentMod.__next_rsc__?.server

Expand Down Expand Up @@ -1292,10 +1292,6 @@ export async function renderToHTML(
return inAmpMode ? children : <div id="__next">{children}</div>
}

const ReactDOMServer = hasConcurrentFeatures
? require('react-dom/server.browser')
: require('react-dom/server')

/**
* Rules of Static & Dynamic HTML:
*
Expand Down Expand Up @@ -1436,7 +1432,7 @@ export async function renderToHTML(
)
}

if (!hasConcurrentFeatures) {
if (!process.env.__NEXT_REACT_ROOT) {
if (Document.getInitialProps) {
const documentInitialPropsRes = await documentInitialProps()
if (documentInitialPropsRes === null) return null
Expand Down Expand Up @@ -1543,7 +1539,8 @@ export async function renderToHTML(
renderStream,
suffix,
dataStream: serverComponentsInlinedTransformStream?.readable,
generateStaticHTML: generateStaticHTML || !hasConcurrentFeatures,
generateStaticHTML:
generateStaticHTML || !process.env.__NEXT_REACT_ROOT,
flushEffectHandler,
})
}
Expand Down
2 changes: 1 addition & 1 deletion packages/next/shared/lib/dynamic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export default function dynamic<P = {}>(
loader: Loader<P>
}
// Error if Fizz rendering is not enabled and `suspense` option is set to true
if (!process.env.__NEXT_CONCURRENT_FEATURES && suspenseOptions.suspense) {
if (!process.env.__NEXT_REACT_ROOT && suspenseOptions.suspense) {
throw new Error(
`Invalid suspense option usage in next/dynamic. Read more: https://nextjs.org/docs/messages/invalid-dynamic-suspense`
)
Expand Down
2 changes: 1 addition & 1 deletion packages/next/shared/lib/head.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ function reduceComponents(
}
if (
process.env.NODE_ENV === 'development' &&
process.env.__NEXT_CONCURRENT_FEATURES
process.env.__NEXT_REACT_ROOT
) {
// omit JSON-LD structured data snippets from the warning
if (c.type === 'script' && c.props['type'] !== 'application/ld+json') {
Expand Down