diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 4e480e73f107..813dbfbdae56 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1439,8 +1439,11 @@ export default async function getBaseWebpackConfig( }), ((isServerless && isServer) || isEdgeRuntime) && new ServerlessPlugin(), isServer && - !isEdgeRuntime && - new PagesManifestPlugin({ serverless: isLikeServerless, dev }), + new PagesManifestPlugin({ + serverless: isLikeServerless, + dev, + isEdgeRuntime, + }), // MiddlewarePlugin should be after DefinePlugin so NEXT_PUBLIC_* // replacement is done before its process.env.* handling (!isServer || isEdgeRuntime) && diff --git a/packages/next/build/webpack/plugins/pages-manifest-plugin.ts b/packages/next/build/webpack/plugins/pages-manifest-plugin.ts index 75a308efc6b8..e562239c5295 100644 --- a/packages/next/build/webpack/plugins/pages-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/pages-manifest-plugin.ts @@ -4,16 +4,29 @@ import getRouteFromEntrypoint from '../../../server/get-route-from-entrypoint' export type PagesManifest = { [page: string]: string } +let edgeServerPages = {} +let nodeServerPages = {} + // This plugin creates a pages-manifest.json from page entrypoints. // This is used for mapping paths like `/` to `.next/server/static//pages/index.js` when doing SSR // It's also used by next export to provide defaultPathMap export default class PagesManifestPlugin implements webpack.Plugin { serverless: boolean dev: boolean + isEdgeRuntime: boolean - constructor({ serverless, dev }: { serverless: boolean; dev: boolean }) { + constructor({ + serverless, + dev, + isEdgeRuntime, + }: { + serverless: boolean + dev: boolean + isEdgeRuntime: boolean + }) { this.serverless = serverless this.dev = dev + this.isEdgeRuntime = isEdgeRuntime } createAssets(compilation: any, assets: any) { @@ -40,13 +53,33 @@ export default class PagesManifestPlugin implements webpack.Plugin { pages[pagePath] = files[files.length - 1] if (!this.dev) { - pages[pagePath] = pages[pagePath].slice(3) + if (!this.isEdgeRuntime) { + pages[pagePath] = pages[pagePath].slice(3) + } } pages[pagePath] = pages[pagePath].replace(/\\/g, '/') } - assets[`${!this.dev ? '../' : ''}` + PAGES_MANIFEST] = - new sources.RawSource(JSON.stringify(pages, null, 2)) + // This plugin is used by both the Node server and Edge server compilers, + // we need to merge both pages to generate the full manifest. + if (this.isEdgeRuntime) { + edgeServerPages = pages + } else { + nodeServerPages = pages + } + + assets[ + `${!this.dev && !this.isEdgeRuntime ? '../' : ''}` + PAGES_MANIFEST + ] = new sources.RawSource( + JSON.stringify( + { + ...edgeServerPages, + ...nodeServerPages, + }, + null, + 2 + ) + ) } apply(compiler: webpack.Compiler): void { diff --git a/test/integration/react-streaming-and-server-components/test/runtime.js b/test/integration/react-streaming-and-server-components/test/runtime.js index 9258833bc4c4..b166fdb4ded3 100644 --- a/test/integration/react-streaming-and-server-components/test/runtime.js +++ b/test/integration/react-streaming-and-server-components/test/runtime.js @@ -1,6 +1,10 @@ import { renderViaHTTP } from 'next-test-utils' +import { join } from 'path' +import fs from 'fs-extra' -export default async function runtime(context, { runtime }) { +import { distDir } from './utils' + +export default async function runtime(context, { runtime, env }) { if (runtime === 'edge') { it('should support per-page runtime configuration', async () => { const html1 = await renderViaHTTP(context.appPort, '/runtime') @@ -9,4 +13,31 @@ export default async function runtime(context, { runtime }) { expect(html2).toContain('Runtime: Node.js') }) } + if (runtime === 'edge' && env === 'prod') { + it('should include entrypoints from both runtimes in pages manifest', async () => { + const distServerDir = join(distDir, 'server') + const pagesManifest = await fs.readJSON( + join(distServerDir, 'pages-manifest.json') + ) + + for (const key of [ + // Defaults: + '/_app', + '/_error', + '/_document', + // Special: + '/404', + // API routes: + '/api/ping', + // Edge runtime pages: + '/streaming', + '/streaming-rsc', + // Node runtime pages: + '/runtime', + '/runtime-rsc', + ]) { + expect(key in pagesManifest).toBeTruthy() + } + }) + } }