mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-10 12:57:47 +00:00
[next] fix content-type for RSC prefetches (#10487)
This ensures that the `.prefetch.rsc` requests respond with the correct `content-type` since this is used by Next.js to determine if a request is valid or not (and in the case it's invalid, an mpa navigation will occur) Fixes: https://github.com/vercel/next.js/issues/54934
This commit is contained in:
5
.changeset/popular-donuts-jump.md
Normal file
5
.changeset/popular-donuts-jump.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@vercel/next": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix content-type for RSC prefetches
|
||||||
@@ -45,6 +45,8 @@ import {
|
|||||||
UnwrapPromise,
|
UnwrapPromise,
|
||||||
getOperationType,
|
getOperationType,
|
||||||
FunctionsConfigManifestV1,
|
FunctionsConfigManifestV1,
|
||||||
|
RSC_CONTENT_TYPE,
|
||||||
|
RSC_PREFETCH_SUFFIX,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import {
|
import {
|
||||||
nodeFileTrace,
|
nodeFileTrace,
|
||||||
@@ -168,7 +170,6 @@ export async function serverBuild({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const APP_PREFETCH_SUFFIX = '.prefetch.rsc';
|
|
||||||
let appRscPrefetches: UnwrapPromise<ReturnType<typeof glob>> = {};
|
let appRscPrefetches: UnwrapPromise<ReturnType<typeof glob>> = {};
|
||||||
let appBuildTraces: UnwrapPromise<ReturnType<typeof glob>> = {};
|
let appBuildTraces: UnwrapPromise<ReturnType<typeof glob>> = {};
|
||||||
let appDir: string | null = null;
|
let appDir: string | null = null;
|
||||||
@@ -176,7 +177,18 @@ export async function serverBuild({
|
|||||||
if (appPathRoutesManifest) {
|
if (appPathRoutesManifest) {
|
||||||
appDir = path.join(pagesDir, '../app');
|
appDir = path.join(pagesDir, '../app');
|
||||||
appBuildTraces = await glob('**/*.js.nft.json', appDir);
|
appBuildTraces = await glob('**/*.js.nft.json', appDir);
|
||||||
appRscPrefetches = await glob(`**/*${APP_PREFETCH_SUFFIX}`, appDir);
|
appRscPrefetches = await glob(`**/*${RSC_PREFETCH_SUFFIX}`, appDir);
|
||||||
|
|
||||||
|
const rscContentTypeHeader =
|
||||||
|
routesManifest?.rsc?.contentTypeHeader || RSC_CONTENT_TYPE;
|
||||||
|
|
||||||
|
// ensure all appRscPrefetches have a contentType since this is used by Next.js
|
||||||
|
// to determine if it's a valid response
|
||||||
|
for (const value of Object.values(appRscPrefetches)) {
|
||||||
|
if (!value.contentType) {
|
||||||
|
value.contentType = rscContentTypeHeader;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isCorrectNotFoundRoutes = semver.gte(
|
const isCorrectNotFoundRoutes = semver.gte(
|
||||||
@@ -1526,7 +1538,7 @@ export async function serverBuild({
|
|||||||
dest: path.posix.join(
|
dest: path.posix.join(
|
||||||
'/',
|
'/',
|
||||||
entryDirectory,
|
entryDirectory,
|
||||||
'/index.prefetch.rsc'
|
`/index${RSC_PREFETCH_SUFFIX}`
|
||||||
),
|
),
|
||||||
headers: { vary: rscVaryHeader },
|
headers: { vary: rscVaryHeader },
|
||||||
continue: true,
|
continue: true,
|
||||||
@@ -1547,7 +1559,7 @@ export async function serverBuild({
|
|||||||
dest: path.posix.join(
|
dest: path.posix.join(
|
||||||
'/',
|
'/',
|
||||||
entryDirectory,
|
entryDirectory,
|
||||||
`/$1${APP_PREFETCH_SUFFIX}`
|
`/$1${RSC_PREFETCH_SUFFIX}`
|
||||||
),
|
),
|
||||||
headers: { vary: rscVaryHeader },
|
headers: { vary: rscVaryHeader },
|
||||||
continue: true,
|
continue: true,
|
||||||
@@ -1626,7 +1638,7 @@ export async function serverBuild({
|
|||||||
src: path.posix.join(
|
src: path.posix.join(
|
||||||
'/',
|
'/',
|
||||||
entryDirectory,
|
entryDirectory,
|
||||||
`/index${APP_PREFETCH_SUFFIX}`
|
`/index${RSC_PREFETCH_SUFFIX}`
|
||||||
),
|
),
|
||||||
dest: path.posix.join('/', entryDirectory, '/index.rsc'),
|
dest: path.posix.join('/', entryDirectory, '/index.rsc'),
|
||||||
has: [
|
has: [
|
||||||
@@ -1642,7 +1654,7 @@ export async function serverBuild({
|
|||||||
src: `^${path.posix.join(
|
src: `^${path.posix.join(
|
||||||
'/',
|
'/',
|
||||||
entryDirectory,
|
entryDirectory,
|
||||||
`/(.+?)${APP_PREFETCH_SUFFIX}(?:/)?$`
|
`/(.+?)${RSC_PREFETCH_SUFFIX}(?:/)?$`
|
||||||
)}`,
|
)}`,
|
||||||
dest: path.posix.join('/', entryDirectory, '/$1.rsc'),
|
dest: path.posix.join('/', entryDirectory, '/$1.rsc'),
|
||||||
has: [
|
has: [
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ export const MIB = 1024 * KIB;
|
|||||||
|
|
||||||
export const prettyBytes = (n: number) => bytes(n, { unitSeparator: ' ' });
|
export const prettyBytes = (n: number) => bytes(n, { unitSeparator: ' ' });
|
||||||
|
|
||||||
|
export const RSC_CONTENT_TYPE = 'x-component';
|
||||||
|
export const RSC_PREFETCH_SUFFIX = '.prefetch.rsc';
|
||||||
|
|
||||||
// Identify /[param]/ in route string
|
// Identify /[param]/ in route string
|
||||||
// eslint-disable-next-line no-useless-escape
|
// eslint-disable-next-line no-useless-escape
|
||||||
const TEST_DYNAMIC_ROUTE = /\/\[[^\/]+?\](?=\/|$)/;
|
const TEST_DYNAMIC_ROUTE = /\/\[[^\/]+?\](?=\/|$)/;
|
||||||
@@ -2158,7 +2161,7 @@ export const onPrerenderRoute =
|
|||||||
routesManifest?.rsc?.varyHeader ||
|
routesManifest?.rsc?.varyHeader ||
|
||||||
'RSC, Next-Router-State-Tree, Next-Router-Prefetch';
|
'RSC, Next-Router-State-Tree, Next-Router-Prefetch';
|
||||||
const rscContentTypeHeader =
|
const rscContentTypeHeader =
|
||||||
routesManifest?.rsc?.contentTypeHeader || 'text/x-component';
|
routesManifest?.rsc?.contentTypeHeader || RSC_CONTENT_TYPE;
|
||||||
|
|
||||||
let sourcePath: string | undefined;
|
let sourcePath: string | undefined;
|
||||||
if (`/${outputPathPage}` !== srcRoute && srcRoute) {
|
if (`/${outputPathPage}` !== srcRoute && srcRoute) {
|
||||||
|
|||||||
@@ -44,6 +44,9 @@
|
|||||||
"RSC": "1",
|
"RSC": "1",
|
||||||
"Next-Router-Prefetch": "1"
|
"Next-Router-Prefetch": "1"
|
||||||
},
|
},
|
||||||
|
"responseHeaders": {
|
||||||
|
"content-type":"text/x-component"
|
||||||
|
},
|
||||||
"mustContain": ":",
|
"mustContain": ":",
|
||||||
"mustNotContain": "<html"
|
"mustNotContain": "<html"
|
||||||
},
|
},
|
||||||
@@ -72,6 +75,9 @@
|
|||||||
"headers": {
|
"headers": {
|
||||||
"Next-Router-Prefetch": "1",
|
"Next-Router-Prefetch": "1",
|
||||||
"RSC": "1"
|
"RSC": "1"
|
||||||
|
},
|
||||||
|
"responseHeaders": {
|
||||||
|
"content-type":"text/x-component"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -99,7 +105,8 @@
|
|||||||
"path": "/ssg",
|
"path": "/ssg",
|
||||||
"status": 200,
|
"status": 200,
|
||||||
"responseHeaders": {
|
"responseHeaders": {
|
||||||
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url"
|
"vary": "RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Url",
|
||||||
|
"content-type":"text/x-component"
|
||||||
},
|
},
|
||||||
"headers": {
|
"headers": {
|
||||||
"RSC": "1",
|
"RSC": "1",
|
||||||
|
|||||||
Reference in New Issue
Block a user