diff --git a/.changeset/clean-buttons-hunt.md b/.changeset/clean-buttons-hunt.md new file mode 100644 index 00000000000..8da5b178b4d --- /dev/null +++ b/.changeset/clean-buttons-hunt.md @@ -0,0 +1,5 @@ +--- +"@vercel/next": patch +--- + +Fix missing .rsc outputs for pages prerenders diff --git a/packages/next/src/utils.ts b/packages/next/src/utils.ts index 43e5d77d32d..b445291d12e 100644 --- a/packages/next/src/utils.ts +++ b/packages/next/src/utils.ts @@ -2477,6 +2477,22 @@ export const onPrerenderRoute = }); } + // we need to ensure all prerenders have a matching .rsc output + // otherwise routing could fall through unexpectedly for the + // fallback: false case as it doesn't have a dynamic route + // to catch the `.rsc` request for app -> pages routing + if (outputPrerenderPathData?.endsWith('.json') && appDir) { + const dummyOutput = new FileBlob({ + data: '{}', + contentType: 'application/json', + }); + const rscKey = `${outputPathPage}.rsc`; + const prefetchRscKey = `${outputPathPage}${RSC_PREFETCH_SUFFIX}`; + + prerenders[rscKey] = dummyOutput; + prerenders[prefetchRscKey] = dummyOutput; + } + ++prerenderGroup; if (routesManifest?.i18n && isBlocking) { diff --git a/packages/next/test/fixtures/00-app-dir-no-ppr/app/nested/[...rest]/page.js b/packages/next/test/fixtures/00-app-dir-no-ppr/app/nested/[...rest]/page.js new file mode 100644 index 00000000000..62806a9ce13 --- /dev/null +++ b/packages/next/test/fixtures/00-app-dir-no-ppr/app/nested/[...rest]/page.js @@ -0,0 +1,5 @@ +export default function Page() { + return ( +

nested app router catch-all

+ ) +} diff --git a/packages/next/test/fixtures/00-app-dir-no-ppr/pages/nested/blog-fallback-false/[slug].js b/packages/next/test/fixtures/00-app-dir-no-ppr/pages/nested/blog-fallback-false/[slug].js new file mode 100644 index 00000000000..4d87599e62a --- /dev/null +++ b/packages/next/test/fixtures/00-app-dir-no-ppr/pages/nested/blog-fallback-false/[slug].js @@ -0,0 +1,24 @@ +export default function Page(props) { + return ( + <> +

hello from /nested/blog-fallback-false/[slug]

+ + ); +} + +export function getStaticProps() { + return { + props: { + now: Date.now() + } + } +} + +export function getStaticPaths() { + return { + paths: [ + { params: { slug: 'first' } }, + ], + fallback: false + } +} diff --git a/packages/next/test/fixtures/00-app-dir-no-ppr/vercel.json b/packages/next/test/fixtures/00-app-dir-no-ppr/vercel.json index 4ea81c3d86e..cca661aaa61 100644 --- a/packages/next/test/fixtures/00-app-dir-no-ppr/vercel.json +++ b/packages/next/test/fixtures/00-app-dir-no-ppr/vercel.json @@ -18,6 +18,50 @@ } ], "probes": [ + { + "path": "/nested/blog-fallback-false/first", + "status": 200, + "mustContain": "hello from /nested/blog-fallback-false" + }, + { + "path": "/nested/blog-fallback-false/first", + "status": 200, + "mustContain": "{}", + "mustNotContain": ":", + "responseHeaders": { + "x-matched-path": "/nested/blog-fallback-false/first.prefetch.rsc" + }, + "headers": { + "RSC": 1, + "Next-Router-Prefetch": 1 + } + }, + { + "path": "/nested/blog-fallback-false/first", + "status": 200, + "mustContain": "{}", + "mustNotContain": ":", + "responseHeaders": { + "x-matched-path": "/nested/blog-fallback-false/first.rsc" + }, + "headers": { + "RSC": 1 + } + }, + { + "path": "/nested/blog-fallback-false/non-existent", + "status": 200, + "mustContain": "nested app router catch-all" + }, + { + "path": "/nested/blog-fallback-false/non-existent", + "status": 200, + "mustContain": ":", + "headers": { + "RSC": 1, + "Next-Router-Prefetch": 1 + } + }, { "path": "/dynamic-index/hello/index", "status": 200,