Skip to content
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

Forward headers from React to static output and dynamic render #58162

Merged
merged 3 commits into from
Nov 8, 2023

Conversation

sebmarkbage
Copy link
Contributor

React can emit a Link: header for preloads instead of <link rel="preload"> in certain scenarios when that can be useful. This works by listening to the onHeaders event.

In particular it's interesting for PPR because if you have something dynamic outside a Suspense boundary it generates an empty payload without any preloads in it. That's because when we do render the real shell we don't know what the document will look like. However, we can emit the Link header for CSS, images and font preloads that we've already discovered. In effect, even a dynamic page gets PPR benefits by early fetching resources.

Custom headers is supported for static a ROUTE but not a PAGE. So I had to add similar wiring to forward headers when it's a page being rendered.

It's important that this works every where, including dynamic routes, because otherwise we might miss out on preloads that we previously would've had.

@@ -184,9 +185,10 @@ export async function exportAppPage(
)
}

let headers: OutgoingHttpHeaders | undefined
const headers = toNodeOutgoingHttpHeaders(res.headers)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the case where a static build is generated.

It mirrors what we already do for custom routes:

https://github.com/vercel/next.js/blob/canary/packages/next/src/export/routes/app-route.ts#L92

@ijjk
Copy link
Member

ijjk commented Nov 8, 2023

Failing test suites

Commit: 1787df9

pnpm test-dev test/development/acceptance/ReactRefreshLogBox-builtins.test.ts

  • ReactRefreshLogBox default > Module not found (missing global CSS)
Expand output

● ReactRefreshLogBox default › Module not found (missing global CSS)

expect(received).toContain(expected) // indexOf

Expected substring: "index page"
Received string:    "<head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><meta name=\"next-head-count\" content=\"2\"><noscript data-n-css=\"\"></noscript><script defer=\"\" nomodule=\"\" src=\"/_next/static/chunks/polyfills.js?ts=1699421234502\"></script><script src=\"/_next/static/chunks/fallback/webpack.js?ts=1699421234502\" defer=\"\"></script><script src=\"/_next/static/chunks/fallback/main.js?ts=1699421234502\" defer=\"\"></script><script src=\"/_next/static/chunks/fallback/pages/_app.js?ts=1699421234502\" defer=\"\"></script><script src=\"/_next/static/chunks/fallback/pages/_error.js?ts=1699421234502\" defer=\"\"></script><noscript id=\"__next_css__DO_NOT_USE__\"></noscript></head><body style=\"\"><div id=\"__next\"></div><script src=\"/_next/static/chunks/fallback/react-refresh.js?ts=1699421234502\"></script><script id=\"__NEXT_DATA__\" type=\"application/json\">{\"props\":{\"pageProps\":{\"statusCode\":500}},\"page\":\"/_error\",\"query\":{},\"buildId\":\"development\",\"isFallback\":false,\"err\":{\"name\":\"Error\",\"source\":\"server\",\"message\":\"Module not found: Can't resolve './non-existent.css'\\n\\u003e 1 | import './non-existent.css'\\n  2 |\\n  3 | export default function App({ Component, pageProps }) {\\n  4 |   return \\u003cComponent {...pageProps} /\\u003e\\n\\nhttps://nextjs.org/docs/messages/module-not-found\\n\",\"stack\":\"Error: Module not found: Can't resolve './non-existent.css'\\n\\u001b[0m\\u001b[31m\\u001b[1m\\u003e\\u001b[22m\\u001b[39m\\u001b[90m 1 |\\u001b[39m \\u001b[36mimport\\u001b[39m \\u001b[32m'./non-existent.css'\\u001b[39m\\u001b[0m\\n\\u001b[0m \\u001b[90m 2 |\\u001b[39m\\u001b[0m\\n\\u001b[0m \\u001b[90m 3 |\\u001b[39m \\u001b[36mexport\\u001b[39m \\u001b[36mdefault\\u001b[39m \\u001b[36mfunction\\u001b[39m \\u001b[33mApp\\u001b[39m({ \\u001b[33mComponent\\u001b[39m\\u001b[33m,\\u001b[39m pageProps }) {\\u001b[0m\\n\\u001b[0m \\u001b[90m 4 |\\u001b[39m   \\u001b[36mreturn\\u001b[39m \\u001b[33m\\u003c\\u001b[39m\\u001b[33mComponent\\u001b[39m {\\u001b[33m...\\u001b[39mpageProps} \\u001b[33m/\\u001b[39m\\u001b[33m\\u003e\\u001b[39m\\u001b[0m\\n\\nhttps://nextjs.org/docs/messages/module-not-found\\n\\n    at getNotFoundError (/tmp/next-install-60c648200b7523f8fc7fa1eb6bc1e8f251fc1dc3dde1c1c0ddab06c0e34ad753/node_modules/.pnpm/file+..+next-repo-a59282c95ac9aa88b35646ae357926049532361c4353944b9cd2b6a60dd976cd+packages+n_ambwevwtu435xtu3pobxutfqxa/node_modules/next/dist/build/webpack/plugins/wellknown-errors-plugin/parseNotFoundError.js:120:16)\\n    at async getModuleBuildError (/tmp/next-install-60c648200b7523f8fc7fa1eb6bc1e8f251fc1dc3dde1c1c0ddab06c0e34ad753/node_modules/.pnpm/file+..+next-repo-a59282c95ac9aa88b35646ae357926049532361c4353944b9cd2b6a60dd976cd+packages+n_ambwevwtu435xtu3pobxutfqxa/node_modules/next/dist/build/webpack/plugins/wellknown-errors-plugin/webpackModuleError.js:102:27)\\n    at async /tmp/next-install-60c648200b7523f8fc7fa1eb6bc1e8f251fc1dc3dde1c1c0ddab06c0e34ad753/node_modules/.pnpm/file+..+next-repo-a59282c95ac9aa88b35646ae357926049532361c4353944b9cd2b6a60dd976cd+packages+n_ambwevwtu435xtu3pobxutfqxa/node_modules/next/dist/build/webpack/plugins/wellknown-errors-plugin/index.js:29:49\\n    at async Promise.all (index 0)\\n    at async /tmp/next-install-60c648200b7523f8fc7fa1eb6bc1e8f251fc1dc3dde1c1c0ddab06c0e34ad753/node_modules/.pnpm/file+..+next-repo-a59282c95ac9aa88b35646ae357926049532361c4353944b9cd2b6a60dd976cd+packages+n_ambwevwtu435xtu3pobxutfqxa/node_modules/next/dist/build/webpack/plugins/wellknown-errors-plugin/index.js:27:21\"},\"gip\":true,\"scriptLoader\":[]}</script><div id=\"__next-build-watcher\" style=\"position: fixed; bottom: 10px; right: 20px; width: 0px; height: 0px; z-index: 99999;\"></div><next-route-announcer><p aria-live=\"assertive\" id=\"__next-route-announcer__\" role=\"alert\" style=\"border: 0px; clip: rect(0px, 0px, 0px, 0px); height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; top: 0px; width: 1px; white-space: nowrap; overflow-wrap: normal;\"></p></next-route-announcer><nextjs-portal></nextjs-portal></body>"

  182 |     expect(
  183 |       await session.evaluate(() => document.documentElement.innerHTML)
> 184 |     ).toContain('index page')
      |       ^
  185 |
  186 |     await cleanup()
  187 |   })

  at Object.toContain (development/acceptance/ReactRefreshLogBox-builtins.test.ts:184:7)

Read more about building and testing Next.js in contributing.md.

pnpm test-start test/e2e/app-dir/app-static/app-static-ppr.test.ts

  • app-dir static/dynamic handling > should output HTML/RSC files for static paths
  • app-dir static/dynamic handling > should have correct prerender-manifest entries
Expand output

● app-dir static/dynamic handling › should output HTML/RSC files for static paths

expect(received).toMatchInlineSnapshot(properties)

Snapshot name: `app-dir static/dynamic handling should output HTML/RSC files for static paths 1`

- Expected properties  - 45
+ Received value       + 59

@@ -1,186 +1,198 @@
  Array [
    "(new)/custom/page.js",
    "(new)/custom/page_client-reference-manifest.js",
    "_not-found.html",
    "_not-found.js",
-   "_not-found.rsc",
+   "_not-found.prefetch.rsc",
    "_not-found_client-reference-manifest.js",
    "api/draft-mode/route.js",
    "api/revalidate-path-edge/route.js",
    "api/revalidate-path-node/route.js",
    "api/revalidate-tag-edge/route.js",
    "api/revalidate-tag-node/route.js",
    "articles/[slug]/page.js",
    "articles/[slug]/page_client-reference-manifest.js",
    "articles/works.html",
-   "articles/works.rsc",
+   "articles/works.prefetch.rsc",
    "blog/[author]/[slug]/page.js",
    "blog/[author]/[slug]/page_client-reference-manifest.js",
    "blog/[author]/page.js",
    "blog/[author]/page_client-reference-manifest.js",
    "blog/seb.html",
-   "blog/seb.rsc",
+   "blog/seb.prefetch.rsc",
    "blog/seb/second-post.html",
-   "blog/seb/second-post.rsc",
+   "blog/seb/second-post.prefetch.rsc",
    "blog/styfle.html",
-   "blog/styfle.rsc",
+   "blog/styfle.prefetch.rsc",
    "blog/styfle/first-post.html",
-   "blog/styfle/first-post.rsc",
+   "blog/styfle/first-post.prefetch.rsc",
    "blog/styfle/second-post.html",
-   "blog/styfle/second-post.rsc",
+   "blog/styfle/second-post.prefetch.rsc",
    "blog/tim.html",
-   "blog/tim.rsc",
+   "blog/tim.prefetch.rsc",
    "blog/tim/first-post.html",
-   "blog/tim/first-post.rsc",
-   "custom.prefetch.rsc",
+   "blog/tim/first-post.prefetch.rsc",
+   "default-cache.html",
    "default-cache.prefetch.rsc",
    "default-cache/page.js",
    "default-cache/page_client-reference-manifest.js",
    "dynamic-error/[id]/page.js",
    "dynamic-error/[id]/page_client-reference-manifest.js",
    "dynamic-no-gen-params-ssr/[slug]/page.js",
    "dynamic-no-gen-params-ssr/[slug]/page_client-reference-manifest.js",
    "dynamic-no-gen-params/[slug]/page.js",
    "dynamic-no-gen-params/[slug]/page_client-reference-manifest.js",
+   "fetch-no-cache.html",
    "fetch-no-cache.prefetch.rsc",
    "fetch-no-cache/page.js",
    "fetch-no-cache/page_client-reference-manifest.js",
    "flight/[slug]/[slug2]/page.js",
    "flight/[slug]/[slug2]/page_client-reference-manifest.js",
    "force-cache.html",
-   "force-cache.rsc",
+   "force-cache.prefetch.rsc",
    "force-cache/page.js",
    "force-cache/page_client-reference-manifest.js",
    "force-dynamic-catch-all/[slug]/[[...id]]/page.js",
    "force-dynamic-catch-all/[slug]/[[...id]]/page_client-reference-manifest.js",
+   "force-dynamic-catch-all/slug.html",
    "force-dynamic-catch-all/slug.prefetch.rsc",
    "force-dynamic-no-prerender/[id]/page.js",
    "force-dynamic-no-prerender/[id]/page_client-reference-manifest.js",
    "force-dynamic-prerender/[slug]/page.js",
    "force-dynamic-prerender/[slug]/page_client-reference-manifest.js",
+   "force-dynamic-prerender/frameworks.html",
    "force-dynamic-prerender/frameworks.prefetch.rsc",
+   "force-no-store.html",
    "force-no-store.prefetch.rsc",
    "force-no-store/page.js",
    "force-no-store/page_client-reference-manifest.js",
+   "force-static.html",
+   "force-static.prefetch.rsc",
    "force-static/[slug]/page.js",
    "force-static/[slug]/page_client-reference-manifest.js",
    "force-static/first.html",
-   "force-static/first.rsc",
+   "force-static/first.prefetch.rsc",
    "force-static/page.js",
    "force-static/page_client-reference-manifest.js",
    "force-static/second.html",
-   "force-static/second.rsc",
+   "force-static/second.prefetch.rsc",
    "gen-params-dynamic-revalidate/[slug]/page.js",
    "gen-params-dynamic-revalidate/[slug]/page_client-reference-manifest.js",
    "gen-params-dynamic-revalidate/one.html",
-   "gen-params-dynamic-revalidate/one.rsc",
+   "gen-params-dynamic-revalidate/one.prefetch.rsc",
    "gen-params-dynamic/[slug]/page.js",
    "gen-params-dynamic/[slug]/page_client-reference-manifest.js",
+   "gen-params-dynamic/one.html",
    "gen-params-dynamic/one.prefetch.rsc",
    "hooks/use-pathname/[slug]/page.js",
    "hooks/use-pathname/[slug]/page_client-reference-manifest.js",
    "hooks/use-pathname/slug.html",
-   "hooks/use-pathname/slug.rsc",
+   "hooks/use-pathname/slug.prefetch.rsc",
    "hooks/use-search-params.html",
-   "hooks/use-search-params.rsc",
+   "hooks/use-search-params.prefetch.rsc",
    "hooks/use-search-params/force-static.html",
-   "hooks/use-search-params/force-static.rsc",
+   "hooks/use-search-params/force-static.prefetch.rsc",
    "hooks/use-search-params/force-static/page.js",
    "hooks/use-search-params/force-static/page_client-reference-manifest.js",
    "hooks/use-search-params/page.js",
    "hooks/use-search-params/page_client-reference-manifest.js",
    "hooks/use-search-params/with-suspense.html",
-   "hooks/use-search-params/with-suspense.rsc",
+   "hooks/use-search-params/with-suspense.prefetch.rsc",
    "hooks/use-search-params/with-suspense/page.js",
    "hooks/use-search-params/with-suspense/page_client-reference-manifest.js",
    "index.html",
-   "index.rsc",
+   "index.prefetch.rsc",
    "isr-error-handling.html",
-   "isr-error-handling.rsc",
+   "isr-error-handling.prefetch.rsc",
    "isr-error-handling/page.js",
    "isr-error-handling/page_client-reference-manifest.js",
+   "no-store/dynamic.html",
+   "no-store/dynamic.prefetch.rsc",
    "no-store/dynamic/page.js",
    "no-store/dynamic/page_client-reference-manifest.js",
    "no-store/static.html",
-   "no-store/static.rsc",
+   "no-store/static.prefetch.rsc",
    "no-store/static/page.js",
    "no-store/static/page_client-reference-manifest.js",
    "page.js",
    "page_client-reference-manifest.js",
    "partial-gen-params-no-additional-lang/[lang]/[slug]/page.js",
    "partial-gen-params-no-additional-lang/[lang]/[slug]/page_client-reference-manifest.js",
    "partial-gen-params-no-additional-lang/en/RAND.html",
-   "partial-gen-params-no-additional-lang/en/RAND.rsc",
+   "partial-gen-params-no-additional-lang/en/RAND.prefetch.rsc",
    "partial-gen-params-no-additional-lang/en/first.html",
-   "partial-gen-params-no-additional-lang/en/first.rsc",
+   "partial-gen-params-no-additional-lang/en/first.prefetch.rsc",
    "partial-gen-params-no-additional-lang/en/second.html",
-   "partial-gen-params-no-additional-lang/en/second.rsc",
+   "partial-gen-params-no-additional-lang/en/second.prefetch.rsc",
    "partial-gen-params-no-additional-lang/fr/RAND.html",
-   "partial-gen-params-no-additional-lang/fr/RAND.rsc",
+   "partial-gen-params-no-additional-lang/fr/RAND.prefetch.rsc",
    "partial-gen-params-no-additional-lang/fr/first.html",
-   "partial-gen-params-no-additional-lang/fr/first.rsc",
+   "partial-gen-params-no-additional-lang/fr/first.prefetch.rsc",
    "partial-gen-params-no-additional-lang/fr/second.html",
-   "partial-gen-params-no-additional-lang/fr/second.rsc",
+   "partial-gen-params-no-additional-lang/fr/second.prefetch.rsc",
    "partial-gen-params-no-additional-slug/[lang]/[slug]/page.js",
    "partial-gen-params-no-additional-slug/[lang]/[slug]/page_client-reference-manifest.js",
    "partial-gen-params-no-additional-slug/en/RAND.html",
-   "partial-gen-params-no-additional-slug/en/RAND.rsc",
+   "partial-gen-params-no-additional-slug/en/RAND.prefetch.rsc",
    "partial-gen-params-no-additional-slug/en/first.html",
-   "partial-gen-params-no-additional-slug/en/first.rsc",
+   "partial-gen-params-no-additional-slug/en/first.prefetch.rsc",
    "partial-gen-params-no-additional-slug/en/second.html",
-   "partial-gen-params-no-additional-slug/en/second.rsc",
+   "partial-gen-params-no-additional-slug/en/second.prefetch.rsc",
    "partial-gen-params-no-additional-slug/fr/RAND.html",
-   "partial-gen-params-no-additional-slug/fr/RAND.rsc",
+   "partial-gen-params-no-additional-slug/fr/RAND.prefetch.rsc",
    "partial-gen-params-no-additional-slug/fr/first.html",
-   "partial-gen-params-no-additional-slug/fr/first.rsc",
+   "partial-gen-params-no-additional-slug/fr/first.prefetch.rsc",
    "partial-gen-params-no-additional-slug/fr/second.html",
-   "partial-gen-params-no-additional-slug/fr/second.rsc",
+   "partial-gen-params-no-additional-slug/fr/second.prefetch.rsc",
    "partial-gen-params/[lang]/[slug]/page.js",
    "partial-gen-params/[lang]/[slug]/page_client-reference-manifest.js",
    "react-fetch-deduping-edge/page.js",
    "react-fetch-deduping-edge/page_client-reference-manifest.js",
+   "react-fetch-deduping-node.html",
    "react-fetch-deduping-node.prefetch.rsc",
    "react-fetch-deduping-node/page.js",
    "react-fetch-deduping-node/page_client-reference-manifest.js",
+   "response-url.html",
    "response-url.prefetch.rsc",
    "response-url/page.js",
    "response-url/page_client-reference-manifest.js",
    "route-handler-edge/revalidate-360/route.js",
    "route-handler/post/route.js",
    "route-handler/revalidate-360-isr/route.js",
    "route-handler/revalidate-360/route.js",
    "route-handler/static-cookies/route.js",
    "ssg-draft-mode.html",
-   "ssg-draft-mode.rsc",
+   "ssg-draft-mode.prefetch.rsc",
    "ssg-draft-mode/[[...route]]/page.js",
    "ssg-draft-mode/[[...route]]/page_client-reference-manifest.js",
    "ssg-draft-mode/test-2.html",
-   "ssg-draft-mode/test-2.rsc",
+   "ssg-draft-mode/test-2.prefetch.rsc",
    "ssg-draft-mode/test.html",
-   "ssg-draft-mode/test.rsc",
+   "ssg-draft-mode/test.prefetch.rsc",
+   "ssr-auto/cache-no-store.html",
    "ssr-auto/cache-no-store.prefetch.rsc",
    "ssr-auto/cache-no-store/page.js",
    "ssr-auto/cache-no-store/page_client-reference-manifest.js",
+   "ssr-auto/fetch-revalidate-zero.html",
    "ssr-auto/fetch-revalidate-zero.prefetch.rsc",
    "ssr-auto/fetch-revalidate-zero/page.js",
    "ssr-auto/fetch-revalidate-zero/page_client-reference-manifest.js",
-   "ssr-forced.prefetch.rsc",
    "ssr-forced/page.js",
    "ssr-forced/page_client-reference-manifest.js",
    "stale-cache-serving-edge/app-page/page.js",
    "stale-cache-serving-edge/app-page/page_client-reference-manifest.js",
    "stale-cache-serving-edge/route-handler/route.js",
-   "stale-cache-serving/app-page.prefetch.rsc",
    "stale-cache-serving/app-page/page.js",
    "stale-cache-serving/app-page/page_client-reference-manifest.js",
    "stale-cache-serving/route-handler/route.js",
    "static-to-dynamic-error-forced/[id]/page.js",
    "static-to-dynamic-error-forced/[id]/page_client-reference-manifest.js",
    "static-to-dynamic-error/[id]/page.js",
    "static-to-dynamic-error/[id]/page_client-reference-manifest.js",
    "variable-config-revalidate/revalidate-3.html",
+   "variable-config-revalidate/revalidate-3.prefetch.rsc",
    "variable-config-revalidate/revalidate-3.rsc",
    "variable-config-revalidate/revalidate-3/page.js",
    "variable-config-revalidate/revalidate-3/page_client-reference-manifest.js",
    "variable-revalidate-edge/body/page.js",
    "variable-revalidate-edge/body/page_client-reference-manifest.js",
@@ -193,45 +205,47 @@
    "variable-revalidate-edge/post-method/page.js",
    "variable-revalidate-edge/post-method/page_client-reference-manifest.js",
    "variable-revalidate-edge/revalidate-3/page.js",
    "variable-revalidate-edge/revalidate-3/page_client-reference-manifest.js",
    "variable-revalidate/authorization.html",
-   "variable-revalidate/authorization.rsc",
+   "variable-revalidate/authorization.prefetch.rsc",
    "variable-revalidate/authorization/page.js",
    "variable-revalidate/authorization/page_client-reference-manifest.js",
    "variable-revalidate/cookie.html",
-   "variable-revalidate/cookie.rsc",
+   "variable-revalidate/cookie.prefetch.rsc",
    "variable-revalidate/cookie/page.js",
    "variable-revalidate/cookie/page_client-reference-manifest.js",
    "variable-revalidate/encoding.html",
-   "variable-revalidate/encoding.rsc",
+   "variable-revalidate/encoding.prefetch.rsc",
    "variable-revalidate/encoding/page.js",
    "variable-revalidate/encoding/page_client-reference-manifest.js",
    "variable-revalidate/headers-instance.html",
-   "variable-revalidate/headers-instance.rsc",
+   "variable-revalidate/headers-instance.prefetch.rsc",
    "variable-revalidate/headers-instance/page.js",
    "variable-revalidate/headers-instance/page_client-reference-manifest.js",
+   "variable-revalidate/no-store.html",
    "variable-revalidate/no-store.prefetch.rsc",
    "variable-revalidate/no-store/page.js",
    "variable-revalidate/no-store/page_client-reference-manifest.js",
+   "variable-revalidate/post-method-request.html",
    "variable-revalidate/post-method-request.prefetch.rsc",
    "variable-revalidate/post-method-request/page.js",
    "variable-revalidate/post-method-request/page_client-reference-manifest.js",
    "variable-revalidate/post-method.html",
-   "variable-revalidate/post-method.rsc",
+   "variable-revalidate/post-method.prefetch.rsc",
    "variable-revalidate/post-method/page.js",
    "variable-revalidate/post-method/page_client-reference-manifest.js",
    "variable-revalidate/revalidate-3.html",
+   "variable-revalidate/revalidate-3.prefetch.rsc",
    "variable-revalidate/revalidate-3.rsc",
    "variable-revalidate/revalidate-3/page.js",
    "variable-revalidate/revalidate-3/page_client-reference-manifest.js",
    "variable-revalidate/revalidate-360-isr.html",
+   "variable-revalidate/revalidate-360-isr.prefetch.rsc",
    "variable-revalidate/revalidate-360-isr.rsc",
    "variable-revalidate/revalidate-360-isr/page.js",
    "variable-revalidate/revalidate-360-isr/page_client-reference-manifest.js",
-   "variable-revalidate/revalidate-360.prefetch.rsc",
    "variable-revalidate/revalidate-360/page.js",
    "variable-revalidate/revalidate-360/page_client-reference-manifest.js",
-   "variable-revalidate/status-code.prefetch.rsc",
    "variable-revalidate/status-code/page.js",
    "variable-revalidate/status-code/page_client-reference-manifest.js",
  ]

  488 |             })
  489 |
> 490 |           expect(files.sort()).toMatchInlineSnapshot(
      |                                ^
  491 |             [
  492 |               'page.js',
  493 |               'index.rsc',

  at Object.toMatchInlineSnapshot (e2e/app-dir/app-static/app-static.test.ts:490:32)

● app-dir static/dynamic handling › should have correct prerender-manifest entries

expect(received).toMatchInlineSnapshot(snapshot)

Snapshot name: `app-dir static/dynamic handling should have correct prerender-manifest entries 1`

- Snapshot  - 4
+ Received  + 4

@@ -498,11 +498,11 @@
          "value": "multipart/form-data",
        },
      ],
      "experimentalPPR": true,
      "initialRevalidateSeconds": false,
-     "prefetchDataRoute": "/partial-gen-params-no-additional-lang/en/45.prefetch.rsc",
+     "prefetchDataRoute": "/partial-gen-params-no-additional-lang/en/19.prefetch.rsc",
      "srcRoute": "/partial-gen-params-no-additional-lang/[lang]/[slug]",
    },
    "/partial-gen-params-no-additional-lang/en/first": {
      "dataRoute": "/partial-gen-params-no-additional-lang/en/first.rsc",
      "experimentalBypassFor": [
@@ -552,11 +552,11 @@
          "value": "multipart/form-data",
        },
      ],
      "experimentalPPR": true,
      "initialRevalidateSeconds": false,
-     "prefetchDataRoute": "/partial-gen-params-no-additional-lang/fr/45.prefetch.rsc",
+     "prefetchDataRoute": "/partial-gen-params-no-additional-lang/fr/19.prefetch.rsc",
      "srcRoute": "/partial-gen-params-no-additional-lang/[lang]/[slug]",
    },
    "/partial-gen-params-no-additional-lang/fr/first": {
      "dataRoute": "/partial-gen-params-no-additional-lang/fr/first.rsc",
      "experimentalBypassFor": [
@@ -606,11 +606,11 @@
          "value": "multipart/form-data",
        },
      ],
      "experimentalPPR": true,
      "initialRevalidateSeconds": false,
-     "prefetchDataRoute": "/partial-gen-params-no-additional-slug/en/90.prefetch.rsc",
+     "prefetchDataRoute": "/partial-gen-params-no-additional-slug/en/15.prefetch.rsc",
      "srcRoute": "/partial-gen-params-no-additional-slug/[lang]/[slug]",
    },
    "/partial-gen-params-no-additional-slug/en/first": {
      "dataRoute": "/partial-gen-params-no-additional-slug/en/first.rsc",
      "experimentalBypassFor": [
@@ -660,11 +660,11 @@
          "value": "multipart/form-data",
        },
      ],
      "experimentalPPR": true,
      "initialRevalidateSeconds": false,
-     "prefetchDataRoute": "/partial-gen-params-no-additional-slug/fr/90.prefetch.rsc",
+     "prefetchDataRoute": "/partial-gen-params-no-additional-slug/fr/15.prefetch.rsc",
      "srcRoute": "/partial-gen-params-no-additional-slug/[lang]/[slug]",
    },
    "/partial-gen-params-no-additional-slug/fr/first": {
      "dataRoute": "/partial-gen-params-no-additional-slug/fr/first.rsc",
      "experimentalBypassFor": [

  759 |
  760 |           expect(curManifest.version).toBe(4)
> 761 |           expect(curManifest.routes).toMatchInlineSnapshot(`
      |                                      ^
  762 |             {
  763 |               "/": {
  764 |                 "dataRoute": "/index.rsc",

  at Object.toMatchInlineSnapshot (e2e/app-dir/app-static/app-static.test.ts:761:38)

Read more about building and testing Next.js in contributing.md.

@@ -737,9 +737,17 @@ async function renderToHTMLOrFlightImpl(
hasPostponed,
})

function onHeaders(headers: Headers): void {
// Copy headers created by React into the response object.
headers.forEach((value: string, key: string) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will only actually be the Link: header so far but we could expand it later.

@ijjk
Copy link
Member

ijjk commented Nov 8, 2023

Stats from current PR

Default Build (Increase detected ⚠️)
General Overall increase ⚠️
vercel/next.js canary sebmarkbage/next.js onheaders Change
buildDuration 10.4s 10.5s N/A
buildDurationCached 6s 6.5s ⚠️ +516ms
nodeModulesSize 175 MB 175 MB ⚠️ +24.6 kB
nextStartRea..uration (ms) 418ms 421ms N/A
Client Bundles (main, webpack)
vercel/next.js canary sebmarkbage/next.js onheaders Change
199-HASH.js gzip 29.2 kB 29.3 kB N/A
3f784ff6-HASH.js gzip 53.2 kB 53.2 kB
494.HASH.js gzip 180 B 181 B N/A
framework-HASH.js gzip 45.2 kB 45.2 kB
main-app-HASH.js gzip 241 B 239 B N/A
main-HASH.js gzip 31.7 kB 31.7 kB N/A
webpack-HASH.js gzip 1.7 kB 1.7 kB
Overall change 100 kB 100 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary sebmarkbage/next.js onheaders Change
polyfills-HASH.js gzip 31 kB 31 kB
Overall change 31 kB 31 kB
Client Pages
vercel/next.js canary sebmarkbage/next.js onheaders Change
_app-HASH.js gzip 194 B 195 B N/A
_error-HASH.js gzip 182 B 181 B N/A
amp-HASH.js gzip 504 B 506 B N/A
css-HASH.js gzip 322 B 323 B N/A
dynamic-HASH.js gzip 2.5 kB 2.5 kB
edge-ssr-HASH.js gzip 253 B 255 B N/A
head-HASH.js gzip 348 B 347 B N/A
hooks-HASH.js gzip 369 B 368 B N/A
image-HASH.js gzip 4.3 kB 4.3 kB N/A
index-HASH.js gzip 256 B 256 B
link-HASH.js gzip 2.65 kB 2.65 kB N/A
routerDirect..HASH.js gzip 311 B 311 B
script-HASH.js gzip 384 B 383 B N/A
withRouter-HASH.js gzip 307 B 308 B N/A
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 3.17 kB 3.17 kB
Client Build Manifests
vercel/next.js canary sebmarkbage/next.js onheaders Change
_buildManifest.js gzip 483 B 484 B N/A
Overall change 0 B 0 B
Rendered Page Sizes
vercel/next.js canary sebmarkbage/next.js onheaders Change
index.html gzip 529 B 527 B N/A
link.html gzip 541 B 540 B N/A
withRouter.html gzip 524 B 523 B N/A
Overall change 0 B 0 B
Edge SSR bundle Size Overall increase ⚠️
vercel/next.js canary sebmarkbage/next.js onheaders Change
edge-ssr.js gzip 92.1 kB 92.2 kB N/A
page.js gzip 145 kB 145 kB ⚠️ +151 B
Overall change 145 kB 145 kB ⚠️ +151 B
Middleware size
vercel/next.js canary sebmarkbage/next.js onheaders Change
middleware-b..fest.js gzip 626 B 627 B N/A
middleware-r..fest.js gzip 150 B 151 B N/A
middleware.js gzip 24.8 kB 24.8 kB N/A
edge-runtime..pack.js gzip 1.92 kB 1.92 kB
Overall change 1.92 kB 1.92 kB
Next Runtimes
vercel/next.js canary sebmarkbage/next.js onheaders Change
app-page-exp...dev.js gzip 166 kB 166 kB N/A
app-page-exp..prod.js gzip 93.3 kB 93.4 kB N/A
app-page-tur..prod.js gzip 94.1 kB 94.1 kB N/A
app-page-tur..prod.js gzip 88.7 kB 88.8 kB N/A
app-page.run...dev.js gzip 136 kB 136 kB N/A
app-page.run..prod.js gzip 88 kB 88.1 kB N/A
app-route-ex...dev.js gzip 23.5 kB 23.5 kB
app-route-ex..prod.js gzip 16.4 kB 16.4 kB
app-route-tu..prod.js gzip 16.4 kB 16.4 kB
app-route-tu..prod.js gzip 16 kB 16 kB
app-route.ru...dev.js gzip 22.9 kB 22.9 kB
app-route.ru..prod.js gzip 16 kB 16 kB
pages-api-tu..prod.js gzip 9.37 kB 9.37 kB
pages-api.ru...dev.js gzip 9.64 kB 9.64 kB
pages-api.ru..prod.js gzip 9.37 kB 9.37 kB
pages-turbo...prod.js gzip 21.5 kB 21.5 kB
pages.runtim...dev.js gzip 22.2 kB 22.2 kB
pages.runtim..prod.js gzip 21.5 kB 21.5 kB
server.runti..prod.js gzip 48.5 kB 48.6 kB N/A
Overall change 205 kB 205 kB
Diff details
Diff for page.js

Diff too large to display

Diff for edge-ssr.js

Diff too large to display

Diff for app-page-exp..ntime.dev.js

Diff too large to display

Diff for app-page-exp..time.prod.js

Diff too large to display

Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page.runtime.dev.js

Diff too large to display

Diff for app-page.runtime.prod.js

Diff too large to display

Diff for server.runtime.prod.js

Diff too large to display

Commit: 762f10a

@@ -2373,10 +2373,12 @@ export default abstract class Server<ServerOptions extends Options = Options> {

// Add any fetch tags that were on the page to the response headers.
const cacheTags = metadata.fetchTags

// Copy the headers from the response.
headers = { ...res.getHeaders() }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the case where we lazily on-demand render a static page or the static part of a PPR page. This one is a bit sketchy. I'm not sure if this is the best way and if this res is actually always going to be the same response as what we expect here.

I'm trying to read back the headers we've already added in the render but I'm not sure this is 100% safe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the cause of the test failure because this response will have too many headers that we're not actually interested in writing to the meta data in it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went with another approach: f501fce

const resHeaders = cachedData.headers
for (const key of Object.keys(resHeaders)) {
if (key === NEXT_CACHE_TAGS_HEADER) {
// Not sure if needs to be special.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@ijjk ijjk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error seems related:

● app dir rendering › ISR › should revalidate the page when revalidate is configured

    FetchError: Invalid response body while trying to fetch http://localhost:34825/isr-multiple/nested: incorrect header check

https://github.com/vercel/next.js/actions/runs/6793370291/job/18468109272?pr=58162#step:27:237

Maybe we should also copy this test and add it to a suite with the flag enabled

"initialHeaders": {
"content-type": "application/json",
"x-next-cache-tags": "thankyounext,_N_T_/layout,_N_T_/route-handler/layout,_N_T_/route-handler/revalidate-360-isr/layout,_N_T_/route-handler/revalidate-360-isr/route,_N_T_/route-handler/revalidate-360-isr",
},

@sebmarkbage sebmarkbage force-pushed the onheaders branch 3 times, most recently from f93e582 to 762f10a Compare November 8, 2023 05:35
@sebmarkbage
Copy link
Contributor Author

@ijjk This should be good to go now. I broke out tests into a separate PR because I had some issues with it not being consistent in its file name generation which breaks the snapshots. #58167

Comment on lines +2781 to +2786
if (typeof v !== 'undefined') {
if (typeof v === 'number') {
v = v.toString()
}
res.setHeader(key, v)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally prefer to perform an early continue/return rather than add more nesting, but not required!

@sebmarkbage sebmarkbage merged commit 1063021 into vercel:canary Nov 8, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 22, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants