-
Notifications
You must be signed in to change notification settings - Fork 27.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add partial support for "use cache"
in metadata route handlers
#74835
Merged
+610
−352
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
52f25ed
Add a failing test for a metadata route handler with `"use cache"`
unstubbable 04297fa
Revert "Do not create client reference manifest for metadata route ha…
unstubbable 9993433
Generate manifests for metadata routes (Webpack)
unstubbable 3781ceb
Generate manifests for metadata routes (Turbopack)
unstubbable fc20b37
Fix `bundlePath` for other metadata routes, and add more tests
unstubbable f746b4e
Revert accidental change in unrelated test file
unstubbable e9e182d
Fix expectations for deploy tests
unstubbable e848a90
Add clarifying comment
unstubbable File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ import type { | |
} from '../loaders/next-flight-client-entry-loader' | ||
|
||
import { webpack } from 'next/dist/compiled/webpack/webpack' | ||
import { stringify } from 'querystring' | ||
import { parse, stringify } from 'querystring' | ||
import path from 'path' | ||
import { sources } from 'next/dist/compiled/webpack/webpack' | ||
import { | ||
|
@@ -13,7 +13,10 @@ import { | |
EntryTypes, | ||
getEntryKey, | ||
} from '../../../server/dev/on-demand-entry-handler' | ||
import { WEBPACK_LAYERS } from '../../../lib/constants' | ||
import { | ||
WEBPACK_LAYERS, | ||
WEBPACK_RESOURCE_QUERIES, | ||
} from '../../../lib/constants' | ||
import { | ||
APP_CLIENT_INTERNALS, | ||
BARREL_OPTIMIZATION_PREFIX, | ||
|
@@ -41,6 +44,7 @@ import { PAGE_TYPES } from '../../../lib/page-types' | |
import { getModuleBuildInfo } from '../loaders/get-module-build-info' | ||
import { getAssumedSourceType } from '../loaders/next-flight-loader' | ||
import { isAppRouteRoute } from '../../../lib/is-app-route-route' | ||
import { isMetadataRoute } from '../../../lib/metadata/is-metadata-route' | ||
|
||
interface Options { | ||
dev: boolean | ||
|
@@ -296,10 +300,14 @@ export class FlightClientEntryPlugin { | |
compilation.moduleGraph | ||
)) { | ||
// Entry can be any user defined entry files such as layout, page, error, loading, etc. | ||
const entryRequest = ( | ||
let entryRequest = ( | ||
connection.dependency as unknown as webpack.NormalModule | ||
).request | ||
|
||
if (entryRequest.endsWith(WEBPACK_RESOURCE_QUERIES.metadataRoute)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not blocking: would be nice to have a helper to extract entryRequest and handle all cases, using |
||
entryRequest = getMetadataRouteResource(entryRequest) | ||
} | ||
|
||
const { clientComponentImports, actionImports, cssImports } = | ||
this.collectComponentInfoFromServerEntryDependency({ | ||
entryRequest, | ||
|
@@ -332,10 +340,16 @@ export class FlightClientEntryPlugin { | |
: entryRequest | ||
|
||
// Replace file suffix as `.js` will be added. | ||
const bundlePath = normalizePathSep( | ||
let bundlePath = normalizePathSep( | ||
relativeRequest.replace(/\.[^.\\/]+$/, '').replace(/^src[\\/]/, '') | ||
) | ||
|
||
// For metadata routes, the entry name can be used as the bundle path, | ||
// as it has been normalized already. | ||
if (isMetadataRoute(bundlePath)) { | ||
bundlePath = name | ||
} | ||
|
||
Object.assign(mergedCSSimports, cssImports) | ||
clientEntriesToInject.push({ | ||
compiler, | ||
|
@@ -1094,5 +1108,16 @@ function getModuleResource(mod: webpack.NormalModule): string { | |
if (mod.matchResource?.startsWith(BARREL_OPTIMIZATION_PREFIX)) { | ||
modResource = mod.matchResource + ':' + modResource | ||
} | ||
|
||
if (mod.resource === `?${WEBPACK_RESOURCE_QUERIES.metadataRoute}`) { | ||
return getMetadataRouteResource(mod.rawRequest) | ||
} | ||
|
||
return modResource | ||
} | ||
|
||
function getMetadataRouteResource(request: string): string { | ||
const query = request.split('next-metadata-route-loader?')[1] | ||
|
||
return parse(query).filePath as string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
test/e2e/app-dir/use-cache-metadata-route-handler/app/icon.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { ImageResponse } from 'next/og' | ||
import { setTimeout } from 'timers/promises' | ||
|
||
export const size = { width: 32, height: 32 } | ||
export const contentType = 'image/png' | ||
|
||
async function fetchIconLetter() { | ||
'use cache' | ||
|
||
// Simulate I/O | ||
await setTimeout(100) | ||
|
||
return 'N' | ||
} | ||
|
||
export default async function Icon() { | ||
const letter = await fetchIconLetter() | ||
|
||
return new ImageResponse( | ||
( | ||
<div | ||
style={{ | ||
fontSize: 24, | ||
background: 'black', | ||
width: '100%', | ||
height: '100%', | ||
display: 'flex', | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
color: 'white', | ||
}} | ||
> | ||
{letter} | ||
</div> | ||
), | ||
{ ...size } | ||
) | ||
} |
12 changes: 12 additions & 0 deletions
12
test/e2e/app-dir/use-cache-metadata-route-handler/app/manifest.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type { MetadataRoute } from 'next' | ||
import { getSentinelValue } from './sentinel' | ||
import { setTimeout } from 'timers/promises' | ||
|
||
export default async function manifest(): Promise<MetadataRoute.Manifest> { | ||
'use cache' | ||
|
||
// Simulate I/O | ||
await setTimeout(100) | ||
|
||
return { name: getSentinelValue() } | ||
} |
38 changes: 38 additions & 0 deletions
38
test/e2e/app-dir/use-cache-metadata-route-handler/app/opengraph-image.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { ImageResponse } from 'next/og' | ||
|
||
export const alt = 'About Acme' | ||
export const size = { width: 1200, height: 630 } | ||
export const contentType = 'image/png' | ||
|
||
async function fetchPostData() { | ||
'use cache' | ||
|
||
return { title: 'Test', created: Date.now() } | ||
} | ||
|
||
export default async function Image() { | ||
const post = await fetchPostData() | ||
|
||
return new ImageResponse( | ||
( | ||
<div | ||
style={{ | ||
fontSize: 48, | ||
background: 'white', | ||
width: '100%', | ||
height: '100%', | ||
display: 'flex', | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
flexDirection: 'column', | ||
}} | ||
> | ||
<h1>{post.title}</h1> | ||
<p style={{ fontSize: 32 }}> | ||
{new Date(post.created).toLocaleTimeString()} | ||
</p> | ||
</div> | ||
), | ||
size | ||
) | ||
} |
20 changes: 20 additions & 0 deletions
20
test/e2e/app-dir/use-cache-metadata-route-handler/app/products/sitemap.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import type { MetadataRoute } from 'next' | ||
import { getSentinelValue } from '../sentinel' | ||
import { setTimeout } from 'timers/promises' | ||
|
||
export async function generateSitemaps() { | ||
return [{ id: 0 }, { id: 1 }] | ||
} | ||
|
||
export default async function sitemap({ | ||
id, | ||
}: { | ||
id: number | ||
}): Promise<MetadataRoute.Sitemap> { | ||
'use cache' | ||
|
||
// Simulate I/O | ||
await setTimeout(100) | ||
|
||
return [{ url: `https://acme.com/${id}?sentinel=${getSentinelValue()}` }] | ||
} |
14 changes: 14 additions & 0 deletions
14
test/e2e/app-dir/use-cache-metadata-route-handler/app/robots.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import type { MetadataRoute } from 'next' | ||
import { getSentinelValue } from './sentinel' | ||
import { setTimeout } from 'timers/promises' | ||
|
||
export default async function robots(): Promise<MetadataRoute.Robots> { | ||
'use cache' | ||
|
||
// Simulate I/O | ||
await setTimeout(100) | ||
|
||
return { | ||
rules: { userAgent: '*', allow: `/${getSentinelValue()}` }, | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
test/e2e/app-dir/use-cache-metadata-route-handler/app/sentinel.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
const { PHASE_PRODUCTION_BUILD } = require('next/constants') | ||
|
||
export function getSentinelValue() { | ||
return process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD | ||
? 'buildtime' | ||
: 'runtime' | ||
} |
12 changes: 12 additions & 0 deletions
12
test/e2e/app-dir/use-cache-metadata-route-handler/app/sitemap.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type { MetadataRoute } from 'next' | ||
import { getSentinelValue } from './sentinel' | ||
import { setTimeout } from 'timers/promises' | ||
|
||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> { | ||
'use cache' | ||
|
||
// Simulate I/O | ||
await setTimeout(100) | ||
|
||
return [{ url: `https://acme.com?sentinel=${getSentinelValue()}` }] | ||
} |
10 changes: 10 additions & 0 deletions
10
test/e2e/app-dir/use-cache-metadata-route-handler/next.config.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** | ||
* @type {import('next').NextConfig} | ||
*/ | ||
const nextConfig = { | ||
experimental: { | ||
dynamicIO: true, | ||
}, | ||
} | ||
|
||
module.exports = nextConfig |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These 2 sounds fine to keep as RSC like layers, curious why we need to change them here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They will still be in the RSC layer (via issuer layer), but we don't want to apply the
next-flight-loader
to them, as that would generate additional and conflicting entries, which are already covered by thenext-metadata-route-loader
entries.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without this exclusion, we would generate duplicate export statements like these:
I've pushed a (hopefully) clarifying comment.