Make concurrent features independent from the global runtime option (#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 <4800338+huozhi@users.noreply.github.com>
This commit is contained in:
parent
86c1bf6d2b
commit
853442dfc3
40 changed files with 468 additions and 60 deletions
|
@ -187,9 +187,6 @@ export async function getPageRuntime(
|
||||||
if (!pageRuntime) {
|
if (!pageRuntime) {
|
||||||
if (isRuntimeRequired) {
|
if (isRuntimeRequired) {
|
||||||
pageRuntime = globalRuntimeFallback
|
pageRuntime = globalRuntimeFallback
|
||||||
} else {
|
|
||||||
// @TODO: Remove this branch to fully implement the RFC.
|
|
||||||
pageRuntime = globalRuntimeFallback
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,11 @@ import {
|
||||||
} from '../telemetry/events'
|
} from '../telemetry/events'
|
||||||
import { Telemetry } from '../telemetry/storage'
|
import { Telemetry } from '../telemetry/storage'
|
||||||
import { CompilerResult, runCompiler } from './compiler'
|
import { CompilerResult, runCompiler } from './compiler'
|
||||||
import { createEntrypoints, createPagesMapping } from './entries'
|
import {
|
||||||
|
createEntrypoints,
|
||||||
|
createPagesMapping,
|
||||||
|
getPageRuntime,
|
||||||
|
} from './entries'
|
||||||
import { generateBuildId } from './generate-build-id'
|
import { generateBuildId } from './generate-build-id'
|
||||||
import { isWriteable } from './is-writeable'
|
import { isWriteable } from './is-writeable'
|
||||||
import * as Log from './output/log'
|
import * as Log from './output/log'
|
||||||
|
@ -153,11 +157,10 @@ export default async function build(
|
||||||
setGlobal('phase', PHASE_PRODUCTION_BUILD)
|
setGlobal('phase', PHASE_PRODUCTION_BUILD)
|
||||||
setGlobal('distDir', distDir)
|
setGlobal('distDir', distDir)
|
||||||
|
|
||||||
// Currently, when the runtime option is set (either `nodejs` or `edge`),
|
// We enable concurrent features (Fizz-related rendering architecture) when
|
||||||
// we enable concurrent features (Fizz-related rendering architecture).
|
// using React 18 or experimental.
|
||||||
const runtime = config.experimental.runtime
|
|
||||||
const hasReactRoot = shouldUseReactRoot()
|
const hasReactRoot = shouldUseReactRoot()
|
||||||
const hasConcurrentFeatures = !!runtime
|
const hasConcurrentFeatures = hasReactRoot
|
||||||
|
|
||||||
const hasServerComponents =
|
const hasServerComponents =
|
||||||
hasReactRoot && !!config.experimental.serverComponents
|
hasReactRoot && !!config.experimental.serverComponents
|
||||||
|
@ -622,6 +625,7 @@ export default async function build(
|
||||||
entrypoints: entrypoints.client,
|
entrypoints: entrypoints.client,
|
||||||
rewrites,
|
rewrites,
|
||||||
runWebpackSpan,
|
runWebpackSpan,
|
||||||
|
hasReactRoot,
|
||||||
}),
|
}),
|
||||||
getBaseWebpackConfig(dir, {
|
getBaseWebpackConfig(dir, {
|
||||||
buildId,
|
buildId,
|
||||||
|
@ -633,6 +637,7 @@ export default async function build(
|
||||||
entrypoints: entrypoints.server,
|
entrypoints: entrypoints.server,
|
||||||
rewrites,
|
rewrites,
|
||||||
runWebpackSpan,
|
runWebpackSpan,
|
||||||
|
hasReactRoot,
|
||||||
}),
|
}),
|
||||||
hasReactRoot
|
hasReactRoot
|
||||||
? getBaseWebpackConfig(dir, {
|
? getBaseWebpackConfig(dir, {
|
||||||
|
@ -646,6 +651,7 @@ export default async function build(
|
||||||
entrypoints: entrypoints.edgeServer,
|
entrypoints: entrypoints.edgeServer,
|
||||||
rewrites,
|
rewrites,
|
||||||
runWebpackSpan,
|
runWebpackSpan,
|
||||||
|
hasReactRoot,
|
||||||
})
|
})
|
||||||
: null,
|
: null,
|
||||||
])
|
])
|
||||||
|
@ -954,10 +960,22 @@ export default async function build(
|
||||||
let ssgPageRoutes: string[] | null = null
|
let ssgPageRoutes: string[] | null = null
|
||||||
let isMiddlewareRoute = !!page.match(MIDDLEWARE_ROUTE)
|
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 (
|
if (
|
||||||
!isMiddlewareRoute &&
|
!isMiddlewareRoute &&
|
||||||
!isReservedPage(page) &&
|
!isReservedPage(page) &&
|
||||||
!hasConcurrentFeatures
|
// We currently don't support staic optimization in the Edge runtime.
|
||||||
|
pageRuntime !== 'edge'
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
let isPageStaticSpan =
|
let isPageStaticSpan =
|
||||||
|
@ -1483,10 +1501,7 @@ export default async function build(
|
||||||
|
|
||||||
const combinedPages = [...staticPages, ...ssgPages]
|
const combinedPages = [...staticPages, ...ssgPages]
|
||||||
|
|
||||||
if (
|
if (combinedPages.length > 0 || useStatic404 || useDefaultStatic500) {
|
||||||
!hasConcurrentFeatures &&
|
|
||||||
(combinedPages.length > 0 || useStatic404 || useDefaultStatic500)
|
|
||||||
) {
|
|
||||||
const staticGenerationSpan = nextBuildSpan.traceChild('static-generation')
|
const staticGenerationSpan = nextBuildSpan.traceChild('static-generation')
|
||||||
await staticGenerationSpan.traceAsyncFn(async () => {
|
await staticGenerationSpan.traceAsyncFn(async () => {
|
||||||
detectConflictingPaths(
|
detectConflictingPaths(
|
||||||
|
|
|
@ -48,7 +48,6 @@ import type { Span } from '../trace'
|
||||||
import { getRawPageExtensions } from './utils'
|
import { getRawPageExtensions } from './utils'
|
||||||
import browserslist from 'next/dist/compiled/browserslist'
|
import browserslist from 'next/dist/compiled/browserslist'
|
||||||
import loadJsConfig from './load-jsconfig'
|
import loadJsConfig from './load-jsconfig'
|
||||||
import { shouldUseReactRoot } from '../server/config'
|
|
||||||
import { getMiddlewareSourceMapPlugins } from './webpack/plugins/middleware-source-maps-plugin'
|
import { getMiddlewareSourceMapPlugins } from './webpack/plugins/middleware-source-maps-plugin'
|
||||||
|
|
||||||
const watchOptions = Object.freeze({
|
const watchOptions = Object.freeze({
|
||||||
|
@ -310,6 +309,7 @@ export default async function getBaseWebpackConfig(
|
||||||
rewrites,
|
rewrites,
|
||||||
isDevFallback = false,
|
isDevFallback = false,
|
||||||
runWebpackSpan,
|
runWebpackSpan,
|
||||||
|
hasReactRoot,
|
||||||
}: {
|
}: {
|
||||||
buildId: string
|
buildId: string
|
||||||
config: NextConfigComplete
|
config: NextConfigComplete
|
||||||
|
@ -323,6 +323,7 @@ export default async function getBaseWebpackConfig(
|
||||||
rewrites: CustomRoutes['rewrites']
|
rewrites: CustomRoutes['rewrites']
|
||||||
isDevFallback?: boolean
|
isDevFallback?: boolean
|
||||||
runWebpackSpan: Span
|
runWebpackSpan: Span
|
||||||
|
hasReactRoot: boolean
|
||||||
}
|
}
|
||||||
): Promise<webpack.Configuration> {
|
): Promise<webpack.Configuration> {
|
||||||
const { useTypeScript, jsConfig, resolvedBaseUrl } = await loadJsConfig(
|
const { useTypeScript, jsConfig, resolvedBaseUrl } = await loadJsConfig(
|
||||||
|
@ -335,10 +336,10 @@ export default async function getBaseWebpackConfig(
|
||||||
rewrites.afterFiles.length > 0 ||
|
rewrites.afterFiles.length > 0 ||
|
||||||
rewrites.fallback.length > 0
|
rewrites.fallback.length > 0
|
||||||
const hasReactRefresh: boolean = dev && !isServer
|
const hasReactRefresh: boolean = dev && !isServer
|
||||||
const hasReactRoot = shouldUseReactRoot()
|
|
||||||
const runtime = config.experimental.runtime
|
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) {
|
if (hasReactRoot) {
|
||||||
config.experimental.reactRoot = true
|
config.experimental.reactRoot = true
|
||||||
}
|
}
|
||||||
|
@ -353,14 +354,14 @@ export default async function getBaseWebpackConfig(
|
||||||
'`experimental.runtime` requires `experimental.reactRoot` to be enabled along with React 18.'
|
'`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(
|
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 targetWeb = isEdgeRuntime || !isServer
|
||||||
const hasConcurrentFeatures = !!runtime && hasReactRoot
|
const hasConcurrentFeatures = hasReactRoot
|
||||||
const hasServerComponents =
|
const hasServerComponents =
|
||||||
hasConcurrentFeatures && !!config.experimental.serverComponents
|
hasConcurrentFeatures && !!config.experimental.serverComponents
|
||||||
const disableOptimizedLoading = hasConcurrentFeatures
|
const disableOptimizedLoading = hasConcurrentFeatures
|
||||||
|
|
|
@ -588,6 +588,7 @@ export default async function exportApp(
|
||||||
nextConfig.experimental.disableOptimizedLoading,
|
nextConfig.experimental.disableOptimizedLoading,
|
||||||
parentSpanId: pageExportSpan.id,
|
parentSpanId: pageExportSpan.id,
|
||||||
httpAgentOptions: nextConfig.httpAgentOptions,
|
httpAgentOptions: nextConfig.httpAgentOptions,
|
||||||
|
serverComponents: nextConfig.experimental.serverComponents,
|
||||||
})
|
})
|
||||||
|
|
||||||
for (const validation of result.ampValidations || []) {
|
for (const validation of result.ampValidations || []) {
|
||||||
|
|
|
@ -59,6 +59,7 @@ interface ExportPageInput {
|
||||||
disableOptimizedLoading: any
|
disableOptimizedLoading: any
|
||||||
parentSpanId: any
|
parentSpanId: any
|
||||||
httpAgentOptions: NextConfigComplete['httpAgentOptions']
|
httpAgentOptions: NextConfigComplete['httpAgentOptions']
|
||||||
|
serverComponents?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExportPageResults {
|
interface ExportPageResults {
|
||||||
|
@ -106,6 +107,7 @@ export default async function exportPage({
|
||||||
optimizeCss,
|
optimizeCss,
|
||||||
disableOptimizedLoading,
|
disableOptimizedLoading,
|
||||||
httpAgentOptions,
|
httpAgentOptions,
|
||||||
|
serverComponents,
|
||||||
}: ExportPageInput): Promise<ExportPageResults> {
|
}: ExportPageInput): Promise<ExportPageResults> {
|
||||||
setHttpAgentOptions(httpAgentOptions)
|
setHttpAgentOptions(httpAgentOptions)
|
||||||
const exportPageSpan = trace('export-page-worker', parentSpanId)
|
const exportPageSpan = trace('export-page-worker', parentSpanId)
|
||||||
|
@ -260,7 +262,7 @@ export default async function exportPage({
|
||||||
getServerSideProps,
|
getServerSideProps,
|
||||||
getStaticProps,
|
getStaticProps,
|
||||||
pageConfig,
|
pageConfig,
|
||||||
} = await loadComponents(distDir, page, serverless)
|
} = await loadComponents(distDir, page, serverless, serverComponents)
|
||||||
const ampState = {
|
const ampState = {
|
||||||
ampFirst: pageConfig?.amp === true,
|
ampFirst: pageConfig?.amp === true,
|
||||||
hasQuery: Boolean(query.amp),
|
hasQuery: Boolean(query.amp),
|
||||||
|
@ -321,7 +323,12 @@ export default async function exportPage({
|
||||||
throw new Error(`Failed to render serverless page`)
|
throw new Error(`Failed to render serverless page`)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const components = await loadComponents(distDir, page, serverless)
|
const components = await loadComponents(
|
||||||
|
distDir,
|
||||||
|
page,
|
||||||
|
serverless,
|
||||||
|
serverComponents
|
||||||
|
)
|
||||||
const ampState = {
|
const ampState = {
|
||||||
ampFirst: components.pageConfig?.amp === true,
|
ampFirst: components.pageConfig?.amp === true,
|
||||||
hasQuery: Boolean(query.amp),
|
hasQuery: Boolean(query.amp),
|
||||||
|
|
|
@ -585,11 +585,9 @@ export class Head extends Component<
|
||||||
disableOptimizedLoading,
|
disableOptimizedLoading,
|
||||||
optimizeCss,
|
optimizeCss,
|
||||||
optimizeFonts,
|
optimizeFonts,
|
||||||
runtime,
|
hasConcurrentFeatures,
|
||||||
} = this.context
|
} = this.context
|
||||||
|
|
||||||
const hasConcurrentFeatures = !!runtime
|
|
||||||
|
|
||||||
const disableRuntimeJS = unstable_runtimeJS === false
|
const disableRuntimeJS = unstable_runtimeJS === false
|
||||||
const disableJsPreload =
|
const disableJsPreload =
|
||||||
unstable_JsPreload === false || !disableOptimizedLoading
|
unstable_JsPreload === false || !disableOptimizedLoading
|
||||||
|
|
|
@ -154,6 +154,7 @@ export default class HotReloader {
|
||||||
private config: NextConfigComplete
|
private config: NextConfigComplete
|
||||||
private runtime?: 'nodejs' | 'edge'
|
private runtime?: 'nodejs' | 'edge'
|
||||||
private hasServerComponents: boolean
|
private hasServerComponents: boolean
|
||||||
|
private hasReactRoot: boolean
|
||||||
public clientStats: webpack5.Stats | null
|
public clientStats: webpack5.Stats | null
|
||||||
public serverStats: webpack5.Stats | null
|
public serverStats: webpack5.Stats | null
|
||||||
private clientError: Error | null = null
|
private clientError: Error | null = null
|
||||||
|
@ -197,7 +198,9 @@ export default class HotReloader {
|
||||||
|
|
||||||
this.config = config
|
this.config = config
|
||||||
this.runtime = config.experimental.runtime
|
this.runtime = config.experimental.runtime
|
||||||
this.hasServerComponents = !!config.experimental.serverComponents
|
this.hasReactRoot = shouldUseReactRoot()
|
||||||
|
this.hasServerComponents =
|
||||||
|
this.hasReactRoot && !!config.experimental.serverComponents
|
||||||
this.previewProps = previewProps
|
this.previewProps = previewProps
|
||||||
this.rewrites = rewrites
|
this.rewrites = rewrites
|
||||||
this.hotReloaderSpan = trace('hot-reloader', undefined, {
|
this.hotReloaderSpan = trace('hot-reloader', undefined, {
|
||||||
|
@ -340,8 +343,6 @@ export default class HotReloader {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const hasReactRoot = shouldUseReactRoot()
|
|
||||||
|
|
||||||
return webpackConfigSpan
|
return webpackConfigSpan
|
||||||
.traceChild('generate-webpack-config')
|
.traceChild('generate-webpack-config')
|
||||||
.traceAsyncFn(() =>
|
.traceAsyncFn(() =>
|
||||||
|
@ -356,6 +357,7 @@ export default class HotReloader {
|
||||||
rewrites: this.rewrites,
|
rewrites: this.rewrites,
|
||||||
entrypoints: entrypoints.client,
|
entrypoints: entrypoints.client,
|
||||||
runWebpackSpan: this.hotReloaderSpan,
|
runWebpackSpan: this.hotReloaderSpan,
|
||||||
|
hasReactRoot: this.hasReactRoot,
|
||||||
}),
|
}),
|
||||||
getBaseWebpackConfig(this.dir, {
|
getBaseWebpackConfig(this.dir, {
|
||||||
dev: true,
|
dev: true,
|
||||||
|
@ -366,9 +368,10 @@ export default class HotReloader {
|
||||||
rewrites: this.rewrites,
|
rewrites: this.rewrites,
|
||||||
entrypoints: entrypoints.server,
|
entrypoints: entrypoints.server,
|
||||||
runWebpackSpan: this.hotReloaderSpan,
|
runWebpackSpan: this.hotReloaderSpan,
|
||||||
|
hasReactRoot: this.hasReactRoot,
|
||||||
}),
|
}),
|
||||||
// The edge runtime is only supported with React root.
|
// The edge runtime is only supported with React root.
|
||||||
hasReactRoot
|
this.hasReactRoot
|
||||||
? getBaseWebpackConfig(this.dir, {
|
? getBaseWebpackConfig(this.dir, {
|
||||||
dev: true,
|
dev: true,
|
||||||
isServer: true,
|
isServer: true,
|
||||||
|
@ -379,6 +382,7 @@ export default class HotReloader {
|
||||||
rewrites: this.rewrites,
|
rewrites: this.rewrites,
|
||||||
entrypoints: entrypoints.edgeServer,
|
entrypoints: entrypoints.edgeServer,
|
||||||
runWebpackSpan: this.hotReloaderSpan,
|
runWebpackSpan: this.hotReloaderSpan,
|
||||||
|
hasReactRoot: this.hasReactRoot,
|
||||||
})
|
})
|
||||||
: null,
|
: null,
|
||||||
].filter(Boolean) as webpack.Configuration[]
|
].filter(Boolean) as webpack.Configuration[]
|
||||||
|
@ -417,6 +421,7 @@ export default class HotReloader {
|
||||||
this.pagesDir
|
this.pagesDir
|
||||||
)
|
)
|
||||||
).client,
|
).client,
|
||||||
|
hasReactRoot: this.hasReactRoot,
|
||||||
})
|
})
|
||||||
const fallbackCompiler = webpack(fallbackConfig)
|
const fallbackCompiler = webpack(fallbackConfig)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import type {
|
||||||
import {
|
import {
|
||||||
BUILD_MANIFEST,
|
BUILD_MANIFEST,
|
||||||
REACT_LOADABLE_MANIFEST,
|
REACT_LOADABLE_MANIFEST,
|
||||||
|
MIDDLEWARE_FLIGHT_MANIFEST,
|
||||||
} from '../shared/lib/constants'
|
} from '../shared/lib/constants'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { requirePage } from './require'
|
import { requirePage } from './require'
|
||||||
|
@ -30,6 +31,7 @@ export type LoadComponentsReturnType = {
|
||||||
pageConfig: PageConfig
|
pageConfig: PageConfig
|
||||||
buildManifest: BuildManifest
|
buildManifest: BuildManifest
|
||||||
reactLoadableManifest: ReactLoadableManifest
|
reactLoadableManifest: ReactLoadableManifest
|
||||||
|
serverComponentManifest?: any | null
|
||||||
Document: DocumentType
|
Document: DocumentType
|
||||||
App: AppType
|
App: AppType
|
||||||
getStaticProps?: GetStaticProps
|
getStaticProps?: GetStaticProps
|
||||||
|
@ -61,7 +63,8 @@ export async function loadDefaultErrorComponents(distDir: string) {
|
||||||
export async function loadComponents(
|
export async function loadComponents(
|
||||||
distDir: string,
|
distDir: string,
|
||||||
pathname: string,
|
pathname: string,
|
||||||
serverless: boolean
|
serverless: boolean,
|
||||||
|
serverComponents?: boolean
|
||||||
): Promise<LoadComponentsReturnType> {
|
): Promise<LoadComponentsReturnType> {
|
||||||
if (serverless) {
|
if (serverless) {
|
||||||
const ComponentMod = await requirePage(pathname, distDir, serverless)
|
const ComponentMod = await requirePage(pathname, distDir, serverless)
|
||||||
|
@ -102,10 +105,14 @@ export async function loadComponents(
|
||||||
requirePage(pathname, distDir, serverless),
|
requirePage(pathname, distDir, serverless),
|
||||||
])
|
])
|
||||||
|
|
||||||
const [buildManifest, reactLoadableManifest] = await Promise.all([
|
const [buildManifest, reactLoadableManifest, serverComponentManifest] =
|
||||||
require(join(distDir, BUILD_MANIFEST)),
|
await Promise.all([
|
||||||
require(join(distDir, REACT_LOADABLE_MANIFEST)),
|
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 Component = interopDefault(ComponentMod)
|
||||||
const Document = interopDefault(DocumentMod)
|
const Document = interopDefault(DocumentMod)
|
||||||
|
@ -125,5 +132,6 @@ export async function loadComponents(
|
||||||
getServerSideProps,
|
getServerSideProps,
|
||||||
getStaticProps,
|
getStaticProps,
|
||||||
getStaticPaths,
|
getStaticPaths,
|
||||||
|
serverComponentManifest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -693,7 +693,7 @@ export default class NextNodeServer extends BaseServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getServerComponentManifest() {
|
protected getServerComponentManifest() {
|
||||||
if (!this.nextConfig.experimental.runtime) return undefined
|
if (!this.nextConfig.experimental.serverComponents) return undefined
|
||||||
return require(join(
|
return require(join(
|
||||||
this.distDir,
|
this.distDir,
|
||||||
'server',
|
'server',
|
||||||
|
|
|
@ -450,12 +450,12 @@ export async function renderToHTML(
|
||||||
supportsDynamicHTML,
|
supportsDynamicHTML,
|
||||||
images,
|
images,
|
||||||
reactRoot,
|
reactRoot,
|
||||||
runtime,
|
runtime: globalRuntime,
|
||||||
ComponentMod,
|
ComponentMod,
|
||||||
AppMod,
|
AppMod,
|
||||||
} = renderOpts
|
} = renderOpts
|
||||||
|
|
||||||
const hasConcurrentFeatures = !!runtime
|
const hasConcurrentFeatures = reactRoot
|
||||||
|
|
||||||
let Document = renderOpts.Document
|
let Document = renderOpts.Document
|
||||||
const OriginalComponent = renderOpts.Component
|
const OriginalComponent = renderOpts.Component
|
||||||
|
@ -464,7 +464,7 @@ export async function renderToHTML(
|
||||||
const isServerComponent =
|
const isServerComponent =
|
||||||
!!serverComponentManifest &&
|
!!serverComponentManifest &&
|
||||||
hasConcurrentFeatures &&
|
hasConcurrentFeatures &&
|
||||||
ComponentMod.__next_rsc__
|
!!ComponentMod.__next_rsc__
|
||||||
|
|
||||||
let Component: React.ComponentType<{}> | ((props: any) => JSX.Element) =
|
let Component: React.ComponentType<{}> | ((props: any) => JSX.Element) =
|
||||||
renderOpts.Component
|
renderOpts.Component
|
||||||
|
@ -1243,7 +1243,7 @@ export async function renderToHTML(
|
||||||
| typeof Document
|
| typeof Document
|
||||||
| undefined
|
| undefined
|
||||||
|
|
||||||
if (runtime === 'edge' && Document.getInitialProps) {
|
if (process.browser && Document.getInitialProps) {
|
||||||
// In the Edge runtime, `Document.getInitialProps` isn't supported.
|
// In the Edge runtime, `Document.getInitialProps` isn't supported.
|
||||||
// We throw an error here if it's customized.
|
// We throw an error here if it's customized.
|
||||||
if (!builtinDocument) {
|
if (!builtinDocument) {
|
||||||
|
@ -1329,7 +1329,8 @@ export async function renderToHTML(
|
||||||
) : (
|
) : (
|
||||||
<Body>
|
<Body>
|
||||||
<AppContainerWithIsomorphicFiberStructure>
|
<AppContainerWithIsomorphicFiberStructure>
|
||||||
{renderOpts.serverComponents && AppMod.__next_rsc__ ? (
|
{isServerComponent && AppMod.__next_rsc__ ? (
|
||||||
|
// _app.server.js is used.
|
||||||
<Component {...props.pageProps} router={router} />
|
<Component {...props.pageProps} router={router} />
|
||||||
) : (
|
) : (
|
||||||
<App {...props} Component={Component} router={router} />
|
<App {...props} Component={Component} router={router} />
|
||||||
|
@ -1361,7 +1362,6 @@ export async function renderToHTML(
|
||||||
),
|
),
|
||||||
generateStaticHTML: true,
|
generateStaticHTML: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const flushed = await streamToString(flushEffectStream)
|
const flushed = await streamToString(flushEffectStream)
|
||||||
return flushed
|
return flushed
|
||||||
}
|
}
|
||||||
|
@ -1489,7 +1489,8 @@ export async function renderToHTML(
|
||||||
optimizeCss: renderOpts.optimizeCss,
|
optimizeCss: renderOpts.optimizeCss,
|
||||||
optimizeFonts: renderOpts.optimizeFonts,
|
optimizeFonts: renderOpts.optimizeFonts,
|
||||||
nextScriptWorkers: renderOpts.nextScriptWorkers,
|
nextScriptWorkers: renderOpts.nextScriptWorkers,
|
||||||
runtime,
|
runtime: globalRuntime,
|
||||||
|
hasConcurrentFeatures,
|
||||||
}
|
}
|
||||||
|
|
||||||
const document = (
|
const document = (
|
||||||
|
|
|
@ -38,6 +38,7 @@ export type HtmlProps = {
|
||||||
optimizeFonts?: boolean
|
optimizeFonts?: boolean
|
||||||
nextScriptWorkers?: boolean
|
nextScriptWorkers?: boolean
|
||||||
runtime?: 'edge' | 'nodejs'
|
runtime?: 'edge' | 'nodejs'
|
||||||
|
hasConcurrentFeatures?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HtmlContext = createContext<HtmlProps>(null as any)
|
export const HtmlContext = createContext<HtmlProps>(null as any)
|
||||||
|
|
|
@ -25,19 +25,6 @@ describe('Invalid react 18 webpack config', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should require `experimental.runtime` for server components', async () => {
|
|
||||||
writeNextConfig({
|
|
||||||
reactRoot: true,
|
|
||||||
serverComponents: true,
|
|
||||||
})
|
|
||||||
const { stderr } = await nextBuild(appDir, [], { stderr: true })
|
|
||||||
nextConfig.restore()
|
|
||||||
|
|
||||||
expect(stderr).toContain(
|
|
||||||
'`experimental.runtime` is required to be set along with `experimental.serverComponents`.'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should warn user when not using react 18 and `experimental.reactRoot` is enabled', async () => {
|
it('should warn user when not using react 18 and `experimental.reactRoot` is enabled', async () => {
|
||||||
const reactDomPackagePah = join(appDir, 'node_modules/react-dom')
|
const reactDomPackagePah = join(appDir, 'node_modules/react-dom')
|
||||||
await fs.mkdirp(reactDomPackagePah)
|
await fs.mkdirp(reactDomPackagePah)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Suspense } from 'react'
|
||||||
import dynamic from 'next/dynamic'
|
import dynamic from 'next/dynamic'
|
||||||
|
|
||||||
const Bar = dynamic(() => import('../../components/bar'), {
|
const Bar = dynamic(() => import('../../components/bar'), {
|
||||||
|
ssr: false,
|
||||||
suspense: true,
|
suspense: true,
|
||||||
// Explicitly declare loaded modules.
|
// Explicitly declare loaded modules.
|
||||||
// For suspense cases, they'll be ignored.
|
// For suspense cases, they'll be ignored.
|
||||||
|
@ -14,7 +15,7 @@ const Bar = dynamic(() => import('../../components/bar'), {
|
||||||
|
|
||||||
export default function NoPreload() {
|
export default function NoPreload() {
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={'rab'}>
|
<Suspense fallback={'fallback'}>
|
||||||
<Bar />
|
<Bar />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
)
|
||||||
|
|
2
test/integration/react-18/test/basics.js
vendored
2
test/integration/react-18/test/basics.js
vendored
|
@ -31,7 +31,7 @@ export default (context) => {
|
||||||
const nextData = JSON.parse($('#__NEXT_DATA__').text())
|
const nextData = JSON.parse($('#__NEXT_DATA__').text())
|
||||||
const content = $('#__next').text()
|
const content = $('#__next').text()
|
||||||
// <Bar> is suspended
|
// <Bar> is suspended
|
||||||
expect(content).toBe('rab')
|
expect(content).toBe('fallback')
|
||||||
expect(nextData.dynamicIds).toBeUndefined()
|
expect(nextData.dynamicIds).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
7
test/integration/react-18/test/blocking.js
vendored
7
test/integration/react-18/test/blocking.js
vendored
|
@ -8,15 +8,16 @@ export default (context, render) => {
|
||||||
return cheerio.load(html)
|
return cheerio.load(html)
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should render fallback on server side if suspense without preload', async () => {
|
it('should render fallback on server side if suspense without ssr', async () => {
|
||||||
const $ = await get$('/suspense/no-preload')
|
const $ = await get$('/suspense/no-preload')
|
||||||
const nextData = JSON.parse($('#__NEXT_DATA__').text())
|
const nextData = JSON.parse($('#__NEXT_DATA__').text())
|
||||||
const content = $('#__next').text()
|
const content = $('#__next').text()
|
||||||
expect(content).toBe('rab')
|
expect(content).toBe('fallback')
|
||||||
expect(nextData.dynamicIds).toBeUndefined()
|
expect(nextData.dynamicIds).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should render fallback on server side if suspended on server with preload', async () => {
|
// Testing the same thing as above.
|
||||||
|
it.skip('should render import fallback on server side if suspended without ssr', async () => {
|
||||||
const $ = await get$('/suspense/thrown')
|
const $ = await get$('/suspense/thrown')
|
||||||
const html = $('body').html()
|
const html = $('body').html()
|
||||||
expect(html).toContain('loading')
|
expect(html).toContain('loading')
|
||||||
|
|
|
@ -5,3 +5,7 @@ export default function MyError() {
|
||||||
throw new Error('oops')
|
throw new Error('oops')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
||||||
|
|
|
@ -18,3 +18,7 @@ export default function page() {
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
||||||
|
|
|
@ -7,3 +7,7 @@ const Page = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page
|
export default Page
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
||||||
|
|
|
@ -15,3 +15,7 @@ export default function LinkPage({ router }) {
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
||||||
|
|
|
@ -40,3 +40,7 @@ export default function () {
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
export default function Pid({ router }) {
|
export default function Pid({ router }) {
|
||||||
return <div>{`query: ${router.query.dynamic}`}</div>
|
return <div>{`query: ${router.query.dynamic}`}</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
||||||
|
|
|
@ -21,3 +21,7 @@ export default function Page() {
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
||||||
|
|
|
@ -21,3 +21,7 @@ export default function Page() {
|
||||||
</Suspense>
|
</Suspense>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
const withReact18 = require('../../react-18/test/with-react-18')
|
||||||
|
|
||||||
|
module.exports = withReact18({
|
||||||
|
reactStrictMode: true,
|
||||||
|
experimental: {
|
||||||
|
serverComponents: true,
|
||||||
|
// runtime: 'edge',
|
||||||
|
},
|
||||||
|
})
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"lnext": "node -r ../../react-18/test/require-hook.js ../../../../packages/next/dist/bin/next",
|
||||||
|
"dev": "yarn lnext dev",
|
||||||
|
"build": "yarn lnext build",
|
||||||
|
"start": "yarn lnext start"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"/_app": "pages/_app.js",
|
||||||
|
"/_error": "pages/_error.js",
|
||||||
|
"/edge-rsc": "pages/edge-rsc.js",
|
||||||
|
"/static": "pages/static.js",
|
||||||
|
"/node-rsc": "pages/node-rsc.js",
|
||||||
|
"/node": "pages/node.js",
|
||||||
|
"/edge": "pages/edge.js"
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import getRuntime from '../utils/runtime'
|
||||||
|
import getTime from '../utils/time'
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
This is a SSR RSC page.
|
||||||
|
<br />
|
||||||
|
{'Runtime: ' + getRuntime()}
|
||||||
|
<br />
|
||||||
|
{'Time: ' + getTime()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
18
test/integration/react-streaming-and-server-components/switchable-runtime/pages/edge.js
vendored
Normal file
18
test/integration/react-streaming-and-server-components/switchable-runtime/pages/edge.js
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import getRuntime from '../utils/runtime'
|
||||||
|
import getTime from '../utils/time'
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
This is a SSR page.
|
||||||
|
<br />
|
||||||
|
{'Runtime: ' + getRuntime()}
|
||||||
|
<br />
|
||||||
|
{'Time: ' + getTime()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import getRuntime from '../utils/runtime'
|
||||||
|
import getTime from '../utils/time'
|
||||||
|
|
||||||
|
export default function Page({ type }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
This is a {type} RSC page.
|
||||||
|
<br />
|
||||||
|
{'Runtime: ' + getRuntime()}
|
||||||
|
<br />
|
||||||
|
{'Time: ' + getTime()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStaticProps() {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
type: 'SSG',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'nodejs',
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import getRuntime from '../utils/runtime'
|
||||||
|
import getTime from '../utils/time'
|
||||||
|
|
||||||
|
export default function Page({ type }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
This is a {type} RSC page.
|
||||||
|
<br />
|
||||||
|
{'Runtime: ' + getRuntime()}
|
||||||
|
<br />
|
||||||
|
{'Time: ' + getTime()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getServerSideProps() {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
type: 'SSR',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'nodejs',
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import getRuntime from '../utils/runtime'
|
||||||
|
import getTime from '../utils/time'
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
This is a static RSC page.
|
||||||
|
<br />
|
||||||
|
{'Runtime: ' + getRuntime()}
|
||||||
|
<br />
|
||||||
|
{'Time: ' + getTime()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'nodejs',
|
||||||
|
}
|
26
test/integration/react-streaming-and-server-components/switchable-runtime/pages/node-ssg.js
vendored
Normal file
26
test/integration/react-streaming-and-server-components/switchable-runtime/pages/node-ssg.js
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import getRuntime from '../utils/runtime'
|
||||||
|
import getTime from '../utils/time'
|
||||||
|
|
||||||
|
export default function Page({ type }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
This is a {type} page.
|
||||||
|
<br />
|
||||||
|
{'Runtime: ' + getRuntime()}
|
||||||
|
<br />
|
||||||
|
{'Time: ' + getTime()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStaticProps() {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
type: 'SSG',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'nodejs',
|
||||||
|
}
|
26
test/integration/react-streaming-and-server-components/switchable-runtime/pages/node-ssr.js
vendored
Normal file
26
test/integration/react-streaming-and-server-components/switchable-runtime/pages/node-ssr.js
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import getRuntime from '../utils/runtime'
|
||||||
|
import getTime from '../utils/time'
|
||||||
|
|
||||||
|
export default function Page({ type }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
This is a {type} page.
|
||||||
|
<br />
|
||||||
|
{'Runtime: ' + getRuntime()}
|
||||||
|
<br />
|
||||||
|
{'Time: ' + getTime()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getServerSideProps() {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
type: 'SSR',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'nodejs',
|
||||||
|
}
|
18
test/integration/react-streaming-and-server-components/switchable-runtime/pages/node.js
vendored
Normal file
18
test/integration/react-streaming-and-server-components/switchable-runtime/pages/node.js
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import getRuntime from '../utils/runtime'
|
||||||
|
import getTime from '../utils/time'
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
This is a static page.
|
||||||
|
<br />
|
||||||
|
{'Runtime: ' + getRuntime()}
|
||||||
|
<br />
|
||||||
|
{'Time: ' + getTime()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'nodejs',
|
||||||
|
}
|
14
test/integration/react-streaming-and-server-components/switchable-runtime/pages/static.js
vendored
Normal file
14
test/integration/react-streaming-and-server-components/switchable-runtime/pages/static.js
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import getRuntime from '../utils/runtime'
|
||||||
|
import getTime from '../utils/time'
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
This is a static page.
|
||||||
|
<br />
|
||||||
|
{'Runtime: ' + getRuntime()}
|
||||||
|
<br />
|
||||||
|
{'Time: ' + getTime()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
3
test/integration/react-streaming-and-server-components/switchable-runtime/utils/runtime.js
vendored
Normal file
3
test/integration/react-streaming-and-server-components/switchable-runtime/utils/runtime.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function getRuntime() {
|
||||||
|
return process.version ? `Node.js ${process.version}` : 'Edge/Browser'
|
||||||
|
}
|
3
test/integration/react-streaming-and-server-components/switchable-runtime/utils/time.js
vendored
Normal file
3
test/integration/react-streaming-and-server-components/switchable-runtime/utils/time.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function getTime() {
|
||||||
|
return Date.now()
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
/* eslint-env jest */
|
||||||
|
|
||||||
|
import { join } from 'path'
|
||||||
|
import {
|
||||||
|
// File,
|
||||||
|
nextBuild as _nextBuild,
|
||||||
|
nextStart as _nextStart,
|
||||||
|
} from 'next-test-utils'
|
||||||
|
|
||||||
|
import { findPort, killApp, renderViaHTTP } from 'next-test-utils'
|
||||||
|
|
||||||
|
const nodeArgs = ['-r', join(__dirname, '../../react-18/test/require-hook.js')]
|
||||||
|
|
||||||
|
const appDir = join(__dirname, '../switchable-runtime')
|
||||||
|
// const nextConfig = new File(join(appDir, 'next.config.js'))
|
||||||
|
|
||||||
|
async function nextBuild(dir, options) {
|
||||||
|
return await _nextBuild(dir, [], {
|
||||||
|
...options,
|
||||||
|
stdout: true,
|
||||||
|
stderr: true,
|
||||||
|
nodeArgs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function nextStart(dir, port) {
|
||||||
|
return await _nextStart(dir, port, {
|
||||||
|
stdout: true,
|
||||||
|
stderr: true,
|
||||||
|
nodeArgs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testRoute(appPort, url, { isStatic, isEdge }) {
|
||||||
|
const html1 = await renderViaHTTP(appPort, url)
|
||||||
|
const renderedAt1 = +html1.match(/Time: (\d+)/)[1]
|
||||||
|
expect(html1).toContain(`Runtime: ${isEdge ? 'Edge' : 'Node.js'}`)
|
||||||
|
|
||||||
|
const html2 = await renderViaHTTP(appPort, url)
|
||||||
|
const renderedAt2 = +html2.match(/Time: (\d+)/)[1]
|
||||||
|
expect(html2).toContain(`Runtime: ${isEdge ? 'Edge' : 'Node.js'}`)
|
||||||
|
|
||||||
|
if (isStatic) {
|
||||||
|
// Should not be re-rendered, some timestamp should be returned.
|
||||||
|
expect(renderedAt1).toBe(renderedAt2)
|
||||||
|
} else {
|
||||||
|
// Should be re-rendered.
|
||||||
|
expect(renderedAt1).toBeLessThan(renderedAt2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Without global runtime configuration', () => {
|
||||||
|
const context = { appDir }
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
context.appPort = await findPort()
|
||||||
|
const { stderr } = await nextBuild(context.appDir)
|
||||||
|
context.stderr = stderr
|
||||||
|
context.server = await nextStart(context.appDir, context.appPort)
|
||||||
|
})
|
||||||
|
afterAll(async () => {
|
||||||
|
await killApp(context.server)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should build /static as a static page with the nodejs runtime', async () => {
|
||||||
|
await testRoute(context.appPort, '/static', {
|
||||||
|
isStatic: true,
|
||||||
|
isEdge: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should build /node as a static page with the nodejs runtime', async () => {
|
||||||
|
await testRoute(context.appPort, '/node', {
|
||||||
|
isStatic: true,
|
||||||
|
isEdge: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should build /node-ssr as a dynamic page with the nodejs runtime', async () => {
|
||||||
|
await testRoute(context.appPort, '/node-ssr', {
|
||||||
|
isStatic: false,
|
||||||
|
isEdge: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should build /node-ssg as a static page with the nodejs runtime', async () => {
|
||||||
|
await testRoute(context.appPort, '/node-ssg', {
|
||||||
|
isStatic: true,
|
||||||
|
isEdge: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should build /node-rsc as a static page with the nodejs runtime', async () => {
|
||||||
|
await testRoute(context.appPort, '/node-rsc', {
|
||||||
|
isStatic: true,
|
||||||
|
isEdge: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should build /node-rsc-ssr as a dynamic page with the nodejs runtime', async () => {
|
||||||
|
await testRoute(context.appPort, '/node-rsc-ssr', {
|
||||||
|
isStatic: false,
|
||||||
|
isEdge: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should build /node-rsc-ssg as a static page with the nodejs runtime', async () => {
|
||||||
|
await testRoute(context.appPort, '/node-rsc-ssg', {
|
||||||
|
isStatic: true,
|
||||||
|
isEdge: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should build /edge as a dynamic page with the edge runtime', async () => {
|
||||||
|
await testRoute(context.appPort, '/edge', {
|
||||||
|
isStatic: false,
|
||||||
|
isEdge: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should build /edge-rsc as a dynamic page with the edge runtime', async () => {
|
||||||
|
await testRoute(context.appPort, '/edge-rsc', {
|
||||||
|
isStatic: false,
|
||||||
|
isEdge: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -3,7 +3,6 @@ const withReact18 = require('../../react-18/test/with-react-18')
|
||||||
module.exports = withReact18({
|
module.exports = withReact18({
|
||||||
experimental: {
|
experimental: {
|
||||||
reactRoot: true,
|
reactRoot: true,
|
||||||
runtime: 'edge',
|
|
||||||
serverComponents: true,
|
serverComponents: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,3 +8,7 @@ export default function Index() {
|
||||||
console.log(EOF)
|
console.log(EOF)
|
||||||
return 'Access Node.js native module dns'
|
return 'Access Node.js native module dns'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
runtime: 'edge',
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue