Skip to content

Commit

Permalink
Make concurrent features independent from the global runtime option (#…
Browse files Browse the repository at this point in the history
…35245)

This PR depends on #35242 and #35243. It allows the global runtime to be unset, as well as enables static optimization for Fizz and RSC pages in the Node.js runtime. Currently for the Edge runtime pages are still always SSR'd.

Closes #31317.

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

## Feature

- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `yarn lint`


Co-authored-by: Jiachi Liu <[email protected]>
  • Loading branch information
shuding and huozhi authored Mar 16, 2022
1 parent 86c1bf6 commit 853442d
Show file tree
Hide file tree
Showing 40 changed files with 468 additions and 60 deletions.
3 changes: 0 additions & 3 deletions packages/next/build/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,6 @@ export async function getPageRuntime(
if (!pageRuntime) {
if (isRuntimeRequired) {
pageRuntime = globalRuntimeFallback
} else {
// @TODO: Remove this branch to fully implement the RFC.
pageRuntime = globalRuntimeFallback
}
}

Expand Down
35 changes: 25 additions & 10 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ import {
} from '../telemetry/events'
import { Telemetry } from '../telemetry/storage'
import { CompilerResult, runCompiler } from './compiler'
import { createEntrypoints, createPagesMapping } from './entries'
import {
createEntrypoints,
createPagesMapping,
getPageRuntime,
} from './entries'
import { generateBuildId } from './generate-build-id'
import { isWriteable } from './is-writeable'
import * as Log from './output/log'
Expand Down Expand Up @@ -153,11 +157,10 @@ export default async function build(
setGlobal('phase', PHASE_PRODUCTION_BUILD)
setGlobal('distDir', distDir)

// Currently, when the runtime option is set (either `nodejs` or `edge`),
// we enable concurrent features (Fizz-related rendering architecture).
const runtime = config.experimental.runtime
// We enable concurrent features (Fizz-related rendering architecture) when
// using React 18 or experimental.
const hasReactRoot = shouldUseReactRoot()
const hasConcurrentFeatures = !!runtime
const hasConcurrentFeatures = hasReactRoot

const hasServerComponents =
hasReactRoot && !!config.experimental.serverComponents
Expand Down Expand Up @@ -622,6 +625,7 @@ export default async function build(
entrypoints: entrypoints.client,
rewrites,
runWebpackSpan,
hasReactRoot,
}),
getBaseWebpackConfig(dir, {
buildId,
Expand All @@ -633,6 +637,7 @@ export default async function build(
entrypoints: entrypoints.server,
rewrites,
runWebpackSpan,
hasReactRoot,
}),
hasReactRoot
? getBaseWebpackConfig(dir, {
Expand All @@ -646,6 +651,7 @@ export default async function build(
entrypoints: entrypoints.edgeServer,
rewrites,
runWebpackSpan,
hasReactRoot,
})
: null,
])
Expand Down Expand Up @@ -954,10 +960,22 @@ export default async function build(
let ssgPageRoutes: string[] | null = null
let isMiddlewareRoute = !!page.match(MIDDLEWARE_ROUTE)

const pagePath = pagePaths.find((_path) =>
_path.startsWith(actualPage + '.')
)
const pageRuntime =
hasConcurrentFeatures && pagePath
? await getPageRuntime(
join(pagesDir, pagePath),
config.experimental.runtime
)
: null

if (
!isMiddlewareRoute &&
!isReservedPage(page) &&
!hasConcurrentFeatures
// We currently don't support staic optimization in the Edge runtime.
pageRuntime !== 'edge'
) {
try {
let isPageStaticSpan =
Expand Down Expand Up @@ -1483,10 +1501,7 @@ export default async function build(

const combinedPages = [...staticPages, ...ssgPages]

if (
!hasConcurrentFeatures &&
(combinedPages.length > 0 || useStatic404 || useDefaultStatic500)
) {
if (combinedPages.length > 0 || useStatic404 || useDefaultStatic500) {
const staticGenerationSpan = nextBuildSpan.traceChild('static-generation')
await staticGenerationSpan.traceAsyncFn(async () => {
detectConflictingPaths(
Expand Down
13 changes: 7 additions & 6 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import type { Span } from '../trace'
import { getRawPageExtensions } from './utils'
import browserslist from 'next/dist/compiled/browserslist'
import loadJsConfig from './load-jsconfig'
import { shouldUseReactRoot } from '../server/config'
import { getMiddlewareSourceMapPlugins } from './webpack/plugins/middleware-source-maps-plugin'

const watchOptions = Object.freeze({
Expand Down Expand Up @@ -310,6 +309,7 @@ export default async function getBaseWebpackConfig(
rewrites,
isDevFallback = false,
runWebpackSpan,
hasReactRoot,
}: {
buildId: string
config: NextConfigComplete
Expand All @@ -323,6 +323,7 @@ export default async function getBaseWebpackConfig(
rewrites: CustomRoutes['rewrites']
isDevFallback?: boolean
runWebpackSpan: Span
hasReactRoot: boolean
}
): Promise<webpack.Configuration> {
const { useTypeScript, jsConfig, resolvedBaseUrl } = await loadJsConfig(
Expand All @@ -335,10 +336,10 @@ export default async function getBaseWebpackConfig(
rewrites.afterFiles.length > 0 ||
rewrites.fallback.length > 0
const hasReactRefresh: boolean = dev && !isServer
const hasReactRoot = shouldUseReactRoot()

const runtime = config.experimental.runtime

// Make sure reactRoot is enabled when react 18 is detected
// Make sure `reactRoot` is enabled when React 18 or experimental is detected.
if (hasReactRoot) {
config.experimental.reactRoot = true
}
Expand All @@ -353,14 +354,14 @@ export default async function getBaseWebpackConfig(
'`experimental.runtime` requires `experimental.reactRoot` to be enabled along with React 18.'
)
}
if (config.experimental.serverComponents && !runtime) {
if (config.experimental.serverComponents && !hasReactRoot) {
throw new Error(
'`experimental.runtime` is required to be set along with `experimental.serverComponents`.'
'`experimental.serverComponents` requires React 18 to be installed.'
)
}

const targetWeb = isEdgeRuntime || !isServer
const hasConcurrentFeatures = !!runtime && hasReactRoot
const hasConcurrentFeatures = hasReactRoot
const hasServerComponents =
hasConcurrentFeatures && !!config.experimental.serverComponents
const disableOptimizedLoading = hasConcurrentFeatures
Expand Down
1 change: 1 addition & 0 deletions packages/next/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ export default async function exportApp(
nextConfig.experimental.disableOptimizedLoading,
parentSpanId: pageExportSpan.id,
httpAgentOptions: nextConfig.httpAgentOptions,
serverComponents: nextConfig.experimental.serverComponents,
})

for (const validation of result.ampValidations || []) {
Expand Down
11 changes: 9 additions & 2 deletions packages/next/export/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ interface ExportPageInput {
disableOptimizedLoading: any
parentSpanId: any
httpAgentOptions: NextConfigComplete['httpAgentOptions']
serverComponents?: boolean
}

interface ExportPageResults {
Expand Down Expand Up @@ -106,6 +107,7 @@ export default async function exportPage({
optimizeCss,
disableOptimizedLoading,
httpAgentOptions,
serverComponents,
}: ExportPageInput): Promise<ExportPageResults> {
setHttpAgentOptions(httpAgentOptions)
const exportPageSpan = trace('export-page-worker', parentSpanId)
Expand Down Expand Up @@ -260,7 +262,7 @@ export default async function exportPage({
getServerSideProps,
getStaticProps,
pageConfig,
} = await loadComponents(distDir, page, serverless)
} = await loadComponents(distDir, page, serverless, serverComponents)
const ampState = {
ampFirst: pageConfig?.amp === true,
hasQuery: Boolean(query.amp),
Expand Down Expand Up @@ -321,7 +323,12 @@ export default async function exportPage({
throw new Error(`Failed to render serverless page`)
}
} else {
const components = await loadComponents(distDir, page, serverless)
const components = await loadComponents(
distDir,
page,
serverless,
serverComponents
)
const ampState = {
ampFirst: components.pageConfig?.amp === true,
hasQuery: Boolean(query.amp),
Expand Down
4 changes: 1 addition & 3 deletions packages/next/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -585,11 +585,9 @@ export class Head extends Component<
disableOptimizedLoading,
optimizeCss,
optimizeFonts,
runtime,
hasConcurrentFeatures,
} = this.context

const hasConcurrentFeatures = !!runtime

const disableRuntimeJS = unstable_runtimeJS === false
const disableJsPreload =
unstable_JsPreload === false || !disableOptimizedLoading
Expand Down
13 changes: 9 additions & 4 deletions packages/next/server/dev/hot-reloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export default class HotReloader {
private config: NextConfigComplete
private runtime?: 'nodejs' | 'edge'
private hasServerComponents: boolean
private hasReactRoot: boolean
public clientStats: webpack5.Stats | null
public serverStats: webpack5.Stats | null
private clientError: Error | null = null
Expand Down Expand Up @@ -197,7 +198,9 @@ export default class HotReloader {

this.config = config
this.runtime = config.experimental.runtime
this.hasServerComponents = !!config.experimental.serverComponents
this.hasReactRoot = shouldUseReactRoot()
this.hasServerComponents =
this.hasReactRoot && !!config.experimental.serverComponents
this.previewProps = previewProps
this.rewrites = rewrites
this.hotReloaderSpan = trace('hot-reloader', undefined, {
Expand Down Expand Up @@ -340,8 +343,6 @@ export default class HotReloader {
)
)

const hasReactRoot = shouldUseReactRoot()

return webpackConfigSpan
.traceChild('generate-webpack-config')
.traceAsyncFn(() =>
Expand All @@ -356,6 +357,7 @@ export default class HotReloader {
rewrites: this.rewrites,
entrypoints: entrypoints.client,
runWebpackSpan: this.hotReloaderSpan,
hasReactRoot: this.hasReactRoot,
}),
getBaseWebpackConfig(this.dir, {
dev: true,
Expand All @@ -366,9 +368,10 @@ export default class HotReloader {
rewrites: this.rewrites,
entrypoints: entrypoints.server,
runWebpackSpan: this.hotReloaderSpan,
hasReactRoot: this.hasReactRoot,
}),
// The edge runtime is only supported with React root.
hasReactRoot
this.hasReactRoot
? getBaseWebpackConfig(this.dir, {
dev: true,
isServer: true,
Expand All @@ -379,6 +382,7 @@ export default class HotReloader {
rewrites: this.rewrites,
entrypoints: entrypoints.edgeServer,
runWebpackSpan: this.hotReloaderSpan,
hasReactRoot: this.hasReactRoot,
})
: null,
].filter(Boolean) as webpack.Configuration[]
Expand Down Expand Up @@ -417,6 +421,7 @@ export default class HotReloader {
this.pagesDir
)
).client,
hasReactRoot: this.hasReactRoot,
})
const fallbackCompiler = webpack(fallbackConfig)

Expand Down
18 changes: 13 additions & 5 deletions packages/next/server/load-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
import {
BUILD_MANIFEST,
REACT_LOADABLE_MANIFEST,
MIDDLEWARE_FLIGHT_MANIFEST,
} from '../shared/lib/constants'
import { join } from 'path'
import { requirePage } from './require'
Expand All @@ -30,6 +31,7 @@ export type LoadComponentsReturnType = {
pageConfig: PageConfig
buildManifest: BuildManifest
reactLoadableManifest: ReactLoadableManifest
serverComponentManifest?: any | null
Document: DocumentType
App: AppType
getStaticProps?: GetStaticProps
Expand Down Expand Up @@ -61,7 +63,8 @@ export async function loadDefaultErrorComponents(distDir: string) {
export async function loadComponents(
distDir: string,
pathname: string,
serverless: boolean
serverless: boolean,
serverComponents?: boolean
): Promise<LoadComponentsReturnType> {
if (serverless) {
const ComponentMod = await requirePage(pathname, distDir, serverless)
Expand Down Expand Up @@ -102,10 +105,14 @@ export async function loadComponents(
requirePage(pathname, distDir, serverless),
])

const [buildManifest, reactLoadableManifest] = await Promise.all([
require(join(distDir, BUILD_MANIFEST)),
require(join(distDir, REACT_LOADABLE_MANIFEST)),
])
const [buildManifest, reactLoadableManifest, serverComponentManifest] =
await Promise.all([
require(join(distDir, BUILD_MANIFEST)),
require(join(distDir, REACT_LOADABLE_MANIFEST)),
serverComponents
? require(join(distDir, 'server', MIDDLEWARE_FLIGHT_MANIFEST + '.json'))
: null,
])

const Component = interopDefault(ComponentMod)
const Document = interopDefault(DocumentMod)
Expand All @@ -125,5 +132,6 @@ export async function loadComponents(
getServerSideProps,
getStaticProps,
getStaticPaths,
serverComponentManifest,
}
}
2 changes: 1 addition & 1 deletion packages/next/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ export default class NextNodeServer extends BaseServer {
}

protected getServerComponentManifest() {
if (!this.nextConfig.experimental.runtime) return undefined
if (!this.nextConfig.experimental.serverComponents) return undefined
return require(join(
this.distDir,
'server',
Expand Down
15 changes: 8 additions & 7 deletions packages/next/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -450,12 +450,12 @@ export async function renderToHTML(
supportsDynamicHTML,
images,
reactRoot,
runtime,
runtime: globalRuntime,
ComponentMod,
AppMod,
} = renderOpts

const hasConcurrentFeatures = !!runtime
const hasConcurrentFeatures = reactRoot

let Document = renderOpts.Document
const OriginalComponent = renderOpts.Component
Expand All @@ -464,7 +464,7 @@ export async function renderToHTML(
const isServerComponent =
!!serverComponentManifest &&
hasConcurrentFeatures &&
ComponentMod.__next_rsc__
!!ComponentMod.__next_rsc__

let Component: React.ComponentType<{}> | ((props: any) => JSX.Element) =
renderOpts.Component
Expand Down Expand Up @@ -1243,7 +1243,7 @@ export async function renderToHTML(
| typeof Document
| undefined

if (runtime === 'edge' && Document.getInitialProps) {
if (process.browser && Document.getInitialProps) {
// In the Edge runtime, `Document.getInitialProps` isn't supported.
// We throw an error here if it's customized.
if (!builtinDocument) {
Expand Down Expand Up @@ -1329,7 +1329,8 @@ export async function renderToHTML(
) : (
<Body>
<AppContainerWithIsomorphicFiberStructure>
{renderOpts.serverComponents && AppMod.__next_rsc__ ? (
{isServerComponent && AppMod.__next_rsc__ ? (
// _app.server.js is used.
<Component {...props.pageProps} router={router} />
) : (
<App {...props} Component={Component} router={router} />
Expand Down Expand Up @@ -1361,7 +1362,6 @@ export async function renderToHTML(
),
generateStaticHTML: true,
})

const flushed = await streamToString(flushEffectStream)
return flushed
}
Expand Down Expand Up @@ -1489,7 +1489,8 @@ export async function renderToHTML(
optimizeCss: renderOpts.optimizeCss,
optimizeFonts: renderOpts.optimizeFonts,
nextScriptWorkers: renderOpts.nextScriptWorkers,
runtime,
runtime: globalRuntime,
hasConcurrentFeatures,
}

const document = (
Expand Down
Loading

0 comments on commit 853442d

Please sign in to comment.