Skip to content

Commit 72a0ad1

Browse files
authored
[perf] skip loading client manifest for static metadata routes (#77260)
### What During development we had a retry logic to load the client manifest for App Router routes, which is the requirement since #74835. But for static metadata routes like `favicon.ico` it doesn't generate client manifest, loading it will cause extra loading time. Refactoring the metadata helpers to determine the static metadata routes more strictly, mostly only cover the image one as their pathname and entry filename is pretty consistent. For sitemap or robots.txt it's still fine to leave as it in dev for now. The delay is mostly waiting for manifest. Long term goal is to build the static metadata routes as static output rather than compile each time.
1 parent 61e7fdb commit 72a0ad1

File tree

4 files changed

+23
-9
lines changed

4 files changed

+23
-9
lines changed

packages/next/src/build/webpack/plugins/next-trace-entrypoints-plugin.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import picomatch from 'next/dist/compiled/picomatch'
1818
import { getModuleBuildInfo } from '../loaders/get-module-build-info'
1919
import { getPageFilePath } from '../../entries'
2020
import { resolveExternal } from '../../handle-externals'
21-
import { isStaticMetadataRoute } from '../../../lib/metadata/is-metadata-route'
21+
import { isStaticMetadataRoutePage } from '../../../lib/metadata/is-metadata-route'
2222
import { getCompilationSpan } from '../utils'
2323

2424
const PLUGIN_NAME = 'TraceEntryPointsPlugin'
@@ -243,7 +243,7 @@ export class TraceEntryPointsPlugin implements webpack.WebpackPluginInstance {
243243

244244
const entryIsStaticMetadataRoute =
245245
appDirRelativeEntryPath &&
246-
isStaticMetadataRoute(appDirRelativeEntryPath)
246+
isStaticMetadataRoutePage(appDirRelativeEntryPath)
247247

248248
// Include the client reference manifest in the trace, but not for
249249
// static metadata routes, for which we don't generate those.

packages/next/src/lib/metadata/is-metadata-route.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { PageExtensions } from '../../build/page-extensions-type'
22
import { normalizePathSep } from '../../shared/lib/page-path/normalize-path-sep'
3+
import { isAppRouteRoute } from '../is-app-route-route'
34

45
export const STATIC_METADATA_IMAGES = {
56
icon: {
@@ -127,15 +128,24 @@ export function isMetadataRouteFile(
127128
)
128129
}
129130

130-
export function isStaticMetadataRouteFile(appDirRelativePath: string) {
131+
export function isStaticMetadataRoutePage(appDirRelativePath: string) {
131132
return isMetadataRouteFile(appDirRelativePath, [], true)
132133
}
133134

134-
export function isStaticMetadataRoute(page: string) {
135+
// Check if the route is a static metadata route, with /route suffix
136+
// e.g. /favicon.ico/route, /icon.png/route, etc.
137+
// But skip the text routes like robots.txt since they might also be dynamic.
138+
// Checking route path is not enough to determine if text routes is dynamic.
139+
export function isStaticMetadataRoute(route: string) {
140+
const pathname = route.slice(0, -'/route'.length)
135141
return (
136-
page === '/robots' ||
137-
page === '/manifest' ||
138-
isStaticMetadataRouteFile(page)
142+
isAppRouteRoute(route) &&
143+
isStaticMetadataRoutePage(pathname) &&
144+
// These routes can either be built by static or dynamic entrypoints,
145+
// so we assume they're dynamic
146+
pathname !== '/robots.txt' &&
147+
pathname !== '/manifest.webmanifest' &&
148+
!pathname.endsWith('/sitemap.xml')
139149
)
140150
}
141151

packages/next/src/server/load-components.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { setReferenceManifestsSingleton } from './app-render/encryption-utils'
3333
import { createServerModuleMap } from './app-render/action-utils'
3434
import type { DeepReadonly } from '../shared/lib/deep-readonly'
3535
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
36+
import { isStaticMetadataRoute } from '../lib/metadata/is-metadata-route'
3637

3738
export type ManifestItem = {
3839
id: number | string
@@ -195,6 +196,9 @@ async function loadComponentsImpl<N = any>({
195196
)
196197
}
197198

199+
// Make sure to avoid loading the manifest for static metadata routes for better performance.
200+
const hasClientManifest = !isStaticMetadataRoute(page)
201+
198202
// Load the manifest files first
199203
//
200204
// Loading page-specific manifests shouldn't throw an error if the manifest couldn't be found, so
@@ -223,7 +227,7 @@ async function loadComponentsImpl<N = any>({
223227
join(distDir, `${DYNAMIC_CSS_MANIFEST}.json`),
224228
manifestLoadAttempts
225229
).catch(() => undefined),
226-
isAppPath
230+
isAppPath && hasClientManifest
227231
? tryLoadClientReferenceManifest(
228232
join(
229233
distDir,

packages/next/src/server/route-matcher-providers/dev/dev-app-route-route-matcher-provider.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class DevAppRouteRouteMatcherProvider extends FileCacheRouteMatcherProvid
5353
true
5454
)
5555

56-
if (!isStaticMetadataRoute(page) && isEntryMetadataRouteFile) {
56+
if (isEntryMetadataRouteFile && !isStaticMetadataRoute(page)) {
5757
// Matching dynamic metadata routes.
5858
// Add 2 possibilities for both single and multiple routes:
5959
{

0 commit comments

Comments
 (0)