From 652765d2d59ad7927f886a53b2c67b0b87c34f47 Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Sun, 13 Oct 2024 20:19:29 +0200 Subject: [PATCH] Do not create client reference manifest for metadata route handlers Follow-up fix from #70897 --- .../src/build/flying-shuttle/stitch-builds.ts | 3 ++- .../webpack/plugins/flight-manifest-plugin.ts | 7 +++--- .../plugins/next-trace-entrypoints-plugin.ts | 22 +++++++++++-------- packages/next/src/build/webpack/utils.ts | 7 +++++- packages/next/src/server/load-components.ts | 6 ++++- .../app-dir/hello-world/hello-world.test.ts | 4 ++-- 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/packages/next/src/build/flying-shuttle/stitch-builds.ts b/packages/next/src/build/flying-shuttle/stitch-builds.ts index 3329ed9466bd2..224ea8cffb92b 100644 --- a/packages/next/src/build/flying-shuttle/stitch-builds.ts +++ b/packages/next/src/build/flying-shuttle/stitch-builds.ts @@ -31,6 +31,7 @@ import { } from '../../shared/lib/constants' import { normalizeAppPath } from '../../shared/lib/router/utils/app-paths' import type { NextConfigComplete } from '../../server/config-shared' +import { isMetadataRoute } from '../../lib/metadata/is-metadata-route' export async function stitchBuilds( { @@ -106,7 +107,7 @@ export async function stitchBuilds( path.join(distDir, entryFile + '.nft.json') ) - if (type === 'app') { + if (type === 'app' && !isMetadataRoute(entry)) { const clientRefManifestFile = path.join( 'server', type, diff --git a/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts index bcafc9153a230..594bef23030a7 100644 --- a/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/src/build/webpack/plugins/flight-manifest-plugin.ts @@ -26,6 +26,7 @@ import { } from '../utils' import type { ChunkGroup } from 'webpack' import { encodeURIPath } from '../../../shared/lib/encode-uri-path' +import { isMetadataRoute } from '../../../lib/metadata/is-metadata-route' interface Options { dev: boolean @@ -519,9 +520,9 @@ export class ClientReferenceManifestPlugin { manifestEntryFiles.push(entryName.replace(/\/page(\.[^/]+)?$/, '/page')) } - // We also need to create manifests for route handler entrypoints to - // enable `'use cache'`. - if (/\/route$/.test(entryName)) { + // We also need to create manifests for route handler entrypoints + // (excluding metadata route handlers) to enable `'use cache'`. + if (/\/route$/.test(entryName) && !isMetadataRoute(entryName)) { manifestEntryFiles.push(entryName) } diff --git a/packages/next/src/build/webpack/plugins/next-trace-entrypoints-plugin.ts b/packages/next/src/build/webpack/plugins/next-trace-entrypoints-plugin.ts index dc02489789d45..9b2bfc35a9f9e 100644 --- a/packages/next/src/build/webpack/plugins/next-trace-entrypoints-plugin.ts +++ b/packages/next/src/build/webpack/plugins/next-trace-entrypoints-plugin.ts @@ -23,6 +23,7 @@ import { getModuleBuildInfo } from '../loaders/get-module-build-info' import { getPageFilePath } from '../../entries' import { resolveExternal } from '../../handle-externals' import swcLoader from '../loaders/next-swc-loader' +import { isMetadataRoute } from '../../../lib/metadata/is-metadata-route' const PLUGIN_NAME = 'TraceEntryPointsPlugin' export const TRACE_IGNORES = [ @@ -277,15 +278,18 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance { ) if (entrypoint.name.startsWith('app/')) { - // include the client reference manifest - const clientManifestsForEntrypoint = nodePath.join( - outputPath, - outputPrefix, - entrypoint.name.replace(/%5F/g, '_') + - '_' + - CLIENT_REFERENCE_MANIFEST + - '.js' - ) + // Include the client reference manifest for pages and route handlers, + // excluding metadata route handlers. + const clientManifestsForEntrypoint = isMetadataRoute(entrypoint.name) + ? null + : nodePath.join( + outputPath, + outputPrefix, + entrypoint.name.replace(/%5F/g, '_') + + '_' + + CLIENT_REFERENCE_MANIFEST + + '.js' + ) if (clientManifestsForEntrypoint !== null) { entryFiles.add(clientManifestsForEntrypoint) diff --git a/packages/next/src/build/webpack/utils.ts b/packages/next/src/build/webpack/utils.ts index 01afb14e48380..912a8c884b1f1 100644 --- a/packages/next/src/build/webpack/utils.ts +++ b/packages/next/src/build/webpack/utils.ts @@ -7,6 +7,7 @@ import type { ModuleGraph, } from 'webpack' import type { ModuleGraphConnection } from 'webpack' +import { isMetadataRoute } from '../../lib/metadata/is-metadata-route' export function traverseModules( compilation: Compilation, @@ -47,7 +48,11 @@ export function forEachEntryModule( ) { for (const [name, entry] of compilation.entries.entries()) { // Skip for entries under pages/ - if (name.startsWith('pages/')) { + if ( + name.startsWith('pages/') || + // Skip for metadata route handlers + (name.startsWith('app/') && isMetadataRoute(name)) + ) { continue } diff --git a/packages/next/src/server/load-components.ts b/packages/next/src/server/load-components.ts index 388ac4f1ec80c..887039d06ad5a 100644 --- a/packages/next/src/server/load-components.ts +++ b/packages/next/src/server/load-components.ts @@ -30,6 +30,7 @@ import { wait } from '../lib/wait' import { setReferenceManifestsSingleton } from './app-render/encryption-utils' import { createServerModuleMap } from './app-render/action-utils' import type { DeepReadonly } from '../shared/lib/deep-readonly' +import { isMetadataRoute } from '../lib/metadata/is-metadata-route' export type ManifestItem = { id: number | string @@ -139,6 +140,9 @@ async function loadComponentsImpl({ ]) } + // Make sure to avoid loading the manifest for metadata route handlers. + const hasClientManifest = isAppPath && !isMetadataRoute(page) + // Load the manifest files first const [ buildManifest, @@ -150,7 +154,7 @@ async function loadComponentsImpl({ loadManifestWithRetries( join(distDir, REACT_LOADABLE_MANIFEST) ), - isAppPath + hasClientManifest ? loadClientReferenceManifest( join( distDir, diff --git a/test/e2e/app-dir/hello-world/hello-world.test.ts b/test/e2e/app-dir/hello-world/hello-world.test.ts index 9f9aeae6fcc10..f7a390baacb65 100644 --- a/test/e2e/app-dir/hello-world/hello-world.test.ts +++ b/test/e2e/app-dir/hello-world/hello-world.test.ts @@ -11,7 +11,7 @@ describe('hello-world', () => { expect($('p').text()).toBe('hello world') }) - // Recommended for tests that need a full browser + // Recommended for tests that need a full browser. it('should work using browser', async () => { const browser = await next.browser('/') expect(await browser.elementByCss('p').text()).toBe('hello world') @@ -23,7 +23,7 @@ describe('hello-world', () => { expect(html).toContain('hello world') }) - // In case you need to test the response object + // In case you need to test the response object. it('should work with fetch', async () => { const res = await next.fetch('/') const html = await res.text()