fix(app-router): emit streamed not-found/redirect meta tags in Next.js byte-exact format (#1491)#1651
Conversation
) When notFound() or redirect() is called from inside a Suspense boundary the error surfaces after the shell has already streamed, so the not-found / redirect intent is communicated to the client via inline <meta> tags injected into the head section of the stream. vinext was emitting these as `<meta name="robots" content="noindex" />` (with a space before the self-closing slash), but Next.js serializes them via React's HTML renderer, which produces `<meta ... content="..."/>` without the space. The Next.js navigation tests assert on that exact substring, so this mismatch caused ~5 deploy-suite failures. Drop the extra space so the streamed meta tags match Next.js byte-for-byte, and add ported regression tests against the suspense fixtures. Ref: packages/next/src/server/app-render/make-get-server-inserted-html.tsx Ref: test/e2e/app-dir/navigation/navigation.test.ts (SEO describe block)
|
/bigbonk review |
commit: |
There was a problem hiding this comment.
Verified against the upstream Next.js test file (test/e2e/app-dir/navigation/navigation.test.ts, SEO describe block, lines ~732-772). Next.js asserts the exact substrings:
'<meta name="robots" content="noindex"/>'
'<meta id="__next-page-redirect" http-equiv="refresh" content="1;url=/redirect/result"/>'
'<meta id="__next-page-redirect" http-equiv="refresh" content="0;url=/redirect/result"/>'
The fix is correct — React's void-element serialization uses no space before />, and this PR matches that format across all three emission sites (noindex, dev next-error, and redirect refresh).
Code changes look clean:
app-ssr-error-meta.ts: All three meta tag templates updated consistently. The comment explaining why the format matters and linking to the Next.js source is a good addition.app-ssr-error-meta.test.ts: All 12 assertion strings updated to match — mechanically correct, nothing missed.rsc-streaming.test.ts: The one redirect meta assertion in the stream-ordering test updated to match.navigation.test.ts: Two new ported tests with good block comment explaining the streaming meta tag scenario. Removing the "N/A: Redirect refresh meta tag" entry makes sense since it's now covered.
Fixtures (suspense-notfound-test/page.tsx, suspense-redirect-test/page.tsx) already exist and correctly exercise the Suspense-boundary streaming path.
No concerns.
|
Reviewed and approved PR #1651. The fix is correct — I verified against the upstream Next.js test file ( |
|
The 'Deploy benchmarks' check failure is unrelated: wrangler asset upload returned 401 Unauthorized from the Cloudflare API ( |
|
Summary
notFound()orredirect()is called from inside a Suspense boundary the error fires after the shell has streamed; the not-found / redirect intent is communicated via inline<meta>tags injected into<head>(<meta name="robots" content="noindex"/>,<meta id="__next-page-redirect" http-equiv="refresh" content="...">).<meta ... content="..." />(a space before/>), but Next.js serializes them via React's HTML renderer which produces<meta ... content="..."/>(no space). The Next.js navigation deploy suite asserts on that exact substring, so the mismatch caused ~5 failures.app-ssr-error-meta.tsand update the focused unit tests / stream-injection test to match. Added two ported regression tests (tests/nextjs-compat/navigation.test.ts) that assert the exact streamed substrings against the existingsuspense-notfound-test/suspense-redirect-testfixtures.Closes #1491
Test plan
pnpm test tests/app-ssr-error-meta.test.ts— unit tests for the rendererpnpm test tests/rsc-streaming.test.ts— covers the streaming injection ordering testpnpm test tests/nextjs-compat/navigation.test.ts— new exact-substring suspense assertionspnpm test tests/nextjs-compat/not-found.test.ts tests/nextjs-compat/metadata.test.ts tests/app-page-boundary-render.test.ts— adjacent paths unaffectedpnpm run check— format + lint + types greenReferences
packages/next/src/server/app-render/make-get-server-inserted-html.tsxtest/e2e/app-dir/navigation/navigation.test.ts(SEO describe block, lines 732-772)