Turbopack: exclude metadata routes from server HMR#92034
Merged
wbinnssmith merged 2 commits intocanaryfrom Mar 31, 2026
Merged
Conversation
Contributor
Tests Passed |
lukesandberg
approved these changes
Mar 28, 2026
sokra
approved these changes
Mar 28, 2026
Contributor
Stats from current PR✅ No significant changes detected📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📎 Tarball URL |
Adds a fixture and test case to verify that changes to a metadata route (manifest.ts) are reflected on the next fetch after a hot reload. Co-Authored-By: Claude <noreply@anthropic.com>
…responses Metadata routes (manifest.ts, robots.ts, sitemap.ts, icon.tsx, etc.) were incorrectly getting usesServerHmr=true after PR #91466 broadened it from app-page only to all app entries. This caused clearRequireCache() to skip deleting their chunks from require.cache and skip __next__clear_chunk_cache__(), so edits to manifest.ts (and other metadata routes) would not be reflected on the next request in Turbopack dev mode. Fix by adding an !isMetadataRoute(entryPage) guard so metadata routes always take the full cache-clearing path, while regular route handlers continue to benefit from server HMR. Co-Authored-By: Claude <noreply@anthropic.com>
502f23e to
052a5e0
Compare
wbinnssmith
added a commit
that referenced
this pull request
Mar 31, 2026
### What? Metadata routes (`manifest.ts`, `robots.ts`, `sitemap.ts`, `icon.tsx`, `apple-icon.tsx`, etc.) were not being hot-reloaded in Turbopack dev mode — changes to those files would not be reflected on subsequent requests until a full server restart. ### Why? PR #91466 extended `usesServerHmr = true` in `clearRequireCache()` (in `hot-reloader-turbopack.ts`) from `app-page` entries only to **all** `app`-type entries (pages + route handlers). The motivation was correct: regular route handlers like `app/api/hello/route.ts` use Turbopack's in-place module update model and benefit from server HMR. However, metadata routes (`/manifest.webmanifest/route`, `/robots.txt/route`, etc.) are also `app`-type entries but they are **not** suitable for in-place server HMR. When `usesServerHmr = true` for a metadata route, `clearRequireCache()` skips two critical invalidation steps: 1. Deleting the compiled chunk from `require.cache` 2. Calling `__next__clear_chunk_cache__()` Without those steps, the old module stays in-memory and all subsequent requests to `/manifest.webmanifest` (etc.) return the stale content. ### How? Added an `!isMetadataRoute(entryPage)` guard to the `usesServerHmr` expression in `clearRequireCache()`. This restores full cache invalidation for metadata routes on every rebuild while leaving regular route handler server HMR (added in #91466) intact. ```ts // Before const usesServerHmr = serverFastRefresh && entryType === 'app' && writtenEndpoint.type !== 'edge' // After const usesServerHmr = serverFastRefresh && entryType === 'app' && writtenEndpoint.type !== 'edge' && !isMetadataRoute(entryPage) // ← metadata routes always clear the cache ``` `isMetadataRoute('/manifest.webmanifest/route')` → `true` (excluded from server HMR) `isMetadataRoute('/api/hello/route')` → `false` (keeps server HMR, no regression) Also added a regression test: `metadata route hmr > reflects manifest.ts changes on fetch/refresh` in the `server-hmr` test suite, with a `manifest.ts` fixture that starts at `name: 'Version 0'`. The test patches the file and asserts the updated JSON is returned on the next fetch. Fixes #91981 --------- Co-authored-by: Will Binns-Smith <wbinnssmith@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or 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
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
What?
Metadata routes (
manifest.ts,robots.ts,sitemap.ts,icon.tsx,apple-icon.tsx, etc.) were not being hot-reloaded in Turbopack dev mode — changes to those files would not be reflected on subsequent requests until a full server restart.Why?
PR #91466 extended
usesServerHmr = trueinclearRequireCache()(inhot-reloader-turbopack.ts) fromapp-pageentries only to allapp-type entries (pages + route handlers). The motivation was correct: regular route handlers likeapp/api/hello/route.tsuse Turbopack's in-place module update model and benefit from server HMR.However, metadata routes (
/manifest.webmanifest/route,/robots.txt/route, etc.) are alsoapp-type entries but they are not suitable for in-place server HMR. WhenusesServerHmr = truefor a metadata route,clearRequireCache()skips two critical invalidation steps:require.cache__next__clear_chunk_cache__()Without those steps, the old module stays in-memory and all subsequent requests to
/manifest.webmanifest(etc.) return the stale content.How?
Added an
!isMetadataRoute(entryPage)guard to theusesServerHmrexpression inclearRequireCache(). This restores full cache invalidation for metadata routes on every rebuild while leaving regular route handler server HMR (added in #91466) intact.isMetadataRoute('/manifest.webmanifest/route')→true(excluded from server HMR)isMetadataRoute('/api/hello/route')→false(keeps server HMR, no regression)Also added a regression test:
metadata route hmr > reflects manifest.ts changes on fetch/refreshin theserver-hmrtest suite, with amanifest.tsfixture that starts atname: 'Version 0'. The test patches the file and asserts the updated JSON is returned on the next fetch.Fixes #91981