diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index 9cfbb276b8d4c59..cde116766a2f08f 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -1,4 +1,8 @@ -import type { PageRuntime, NextConfigComplete } from '../server/config-shared' +import type { + PageRuntime, + NextConfigComplete, + NextConfig, +} from '../server/config-shared' import type { webpack5 } from 'next/dist/compiled/webpack/webpack' import fs from 'fs' import chalk from 'next/dist/compiled/chalk' @@ -105,8 +109,11 @@ const cachedPageRuntimeConfig = new Map() // could be thousands of pages existing. export async function getPageRuntime( pageFilePath: string, - globalRuntimeFallback?: 'nodejs' | 'edge' + nextConfig: Partial ): Promise { + if (!nextConfig.experimental?.reactRoot) return undefined + + const globalRuntime = nextConfig.experimental?.runtime const cached = cachedPageRuntimeConfig.get(pageFilePath) if (cached) { return cached[1] @@ -118,6 +125,7 @@ export async function getPageRuntime( encoding: 'utf8', }) } catch (err) { + if (process.env.NODE_ENV === 'production') throw err return undefined } @@ -183,7 +191,7 @@ export async function getPageRuntime( if (!pageRuntime) { if (isRuntimeRequired) { - pageRuntime = globalRuntimeFallback + pageRuntime = globalRuntime } } @@ -217,6 +225,7 @@ export async function createEntrypoints( const hasRuntimeConfig = Object.keys(config.publicRuntimeConfig).length > 0 || Object.keys(config.serverRuntimeConfig).length > 0 + const hasReactRoot = !!config.experimental.reactRoot const defaultServerlessOptions = { absoluteAppPath: pages['/_app'], @@ -242,11 +251,9 @@ export async function createEntrypoints( 'base64' ), i18n: config.i18n ? JSON.stringify(config.i18n) : '', - reactRoot: config.experimental.reactRoot ? 'true' : '', + reactRoot: hasReactRoot ? 'true' : '', } - const globalRuntime = config.experimental.runtime - await Promise.all( Object.keys(pages).map(async (page) => { const absolutePagePath = pages[page] @@ -260,11 +267,12 @@ export async function createEntrypoints( const isReserved = isReservedPage(page) const isCustomError = isCustomErrorPage(page) const isFlight = isFlightPage(config, absolutePagePath) - const isEdgeRuntime = - (await getPageRuntime( - join(pagesDir, absolutePagePath.slice(PAGES_DIR_ALIAS.length + 1)), - globalRuntime - )) === 'edge' + const isInternalPages = !absolutePagePath.startsWith(PAGES_DIR_ALIAS) + const pageFilePath = isInternalPages + ? require.resolve(absolutePagePath) + : join(pagesDir, absolutePagePath.replace(PAGES_DIR_ALIAS, '')) + const pageRuntime = await getPageRuntime(pageFilePath, config) + const isEdgeRuntime = pageRuntime === 'edge' if (page.match(MIDDLEWARE_ROUTE)) { const loaderOpts: MiddlewareLoaderOptions = { diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index db60484e0745507..ee7666d33949563 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -971,13 +971,9 @@ export default async function build( p.startsWith(actualPage + '.') || p.startsWith(actualPage + '/index.') ) - const pageRuntime = - hasConcurrentFeatures && pagePath - ? await getPageRuntime( - join(pagesDir, pagePath), - config.experimental.runtime - ) - : undefined + const pageRuntime = pagePath + ? await getPageRuntime(join(pagesDir, pagePath), config) + : undefined if ( !isMiddlewareRoute && diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index ed5d7ee5aea3426..e4240446806f2c3 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -509,7 +509,7 @@ export default class HotReloader { const pageRuntimeConfig = await getPageRuntime( absolutePagePath, - this.runtime + this.config ) const isEdgeSSRPage = pageRuntimeConfig === 'edge' && !isApiRoute diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 9f0fd64aba56157..1a8534b0d464d54 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -285,7 +285,7 @@ export default class DevServer extends Server { invalidatePageRuntimeCache(fileName, safeTime) const pageRuntimeConfig = await getPageRuntime( fileName, - this.nextConfig.experimental.runtime + this.nextConfig ) const isEdgeRuntime = pageRuntimeConfig === 'edge' diff --git a/packages/next/server/dev/on-demand-entry-handler.ts b/packages/next/server/dev/on-demand-entry-handler.ts index edb06c39c7e6cb1..6df016d7e59e3b4 100644 --- a/packages/next/server/dev/on-demand-entry-handler.ts +++ b/packages/next/server/dev/on-demand-entry-handler.ts @@ -207,7 +207,7 @@ export default function onDemandEntryHandler( const isApiRoute = normalizedPage.match(API_ROUTE) && !isMiddleware const pageRuntimeConfig = await getPageRuntime( absolutePagePath, - nextConfig.experimental.runtime + nextConfig ) const isEdgeServer = pageRuntimeConfig === 'edge' diff --git a/test/unit/parse-page-runtime.test.ts b/test/unit/parse-page-runtime.test.ts index 11b4946ec1a2db0..f37490d6b0c29f4 100644 --- a/test/unit/parse-page-runtime.test.ts +++ b/test/unit/parse-page-runtime.test.ts @@ -1,26 +1,36 @@ import { getPageRuntime } from 'next/dist/build/entries' +import type { PageRuntime } from 'next/dist/server/config-shared' import { join } from 'path' const fixtureDir = join(__dirname, 'fixtures') +function createNextConfig(runtime?: PageRuntime) { + return { + experimental: { reactRoot: true, runtime }, + } +} + describe('parse page runtime config', () => { it('should parse nodejs runtime correctly', async () => { const runtime = await getPageRuntime( - join(fixtureDir, 'page-runtime/nodejs.js') + join(fixtureDir, 'page-runtime/nodejs.js'), + createNextConfig() ) expect(runtime).toBe('nodejs') }) it('should parse edge runtime correctly', async () => { const runtime = await getPageRuntime( - join(fixtureDir, 'page-runtime/edge.js') + join(fixtureDir, 'page-runtime/edge.js'), + createNextConfig() ) expect(runtime).toBe('edge') }) it('should return undefined if no runtime is specified', async () => { const runtime = await getPageRuntime( - join(fixtureDir, 'page-runtime/static.js') + join(fixtureDir, 'page-runtime/static.js'), + createNextConfig() ) expect(runtime).toBe(undefined) }) @@ -30,7 +40,7 @@ describe('fallback to the global runtime configuration', () => { it('should fallback when gSP is defined and exported', async () => { const runtime = await getPageRuntime( join(fixtureDir, 'page-runtime/fallback-with-gsp.js'), - 'edge' + createNextConfig('edge') ) expect(runtime).toBe('edge') }) @@ -38,7 +48,7 @@ describe('fallback to the global runtime configuration', () => { it('should fallback when gSP is re-exported from other module', async () => { const runtime = await getPageRuntime( join(fixtureDir, 'page-runtime/fallback-re-export-gsp.js'), - 'edge' + createNextConfig('edge') ) expect(runtime).toBe('edge') })