Skip to content

Commit a0a18a2

Browse files
Turbopack: restrict server HMR to app pages (#90663)
App router types like routes go through the traditional `require` route bypassing the Turbopack runtime, so they aren't easily solved with server HMR right now. Test Plan: Added an e2e test. --------- Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
1 parent 9651230 commit a0a18a2

3 files changed

Lines changed: 42 additions & 5 deletions

File tree

packages/next/src/server/dev/hot-reloader-turbopack.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -592,13 +592,22 @@ export async function createHotReloaderTurbopack(
592592
join(distDir, p)
593593
)
594594

595-
const { type: entryType } = splitEntryKey(key)
596-
// Server HMR only applies to App Router.
597-
// Pages Router uses Node's require(), root entries (middleware/instrumentation)
598-
// use the edge runtime.
595+
const { type: entryType, page: entryPage } = splitEntryKey(key)
596+
const isAppPage =
597+
entryType === 'app' &&
598+
currentEntrypoints.app.get(entryPage)?.type === 'app-page'
599+
600+
// Server HMR only applies to app router pages since these use the Turbopack runtime.
601+
// Currently, this is only app router pages.
602+
//
603+
// This excludes:
604+
// - Pages Router pages
605+
// - Edge routes
606+
// - Middleware
607+
// - App Router route handlers (route.ts)
599608
const usesServerHmr =
600609
experimentalServerFastRefresh &&
601-
entryType === 'app' &&
610+
isAppPage &&
602611
writtenEndpoint.type !== 'edge'
603612

604613
for (const file of serverPaths) {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export async function GET() {
2+
return new Response('version: 0')
3+
}

test/development/app-dir/server-hmr/server-hmr.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Response } from 'node-fetch'
12
import { nextTestSetup } from 'e2e-utils'
23
import { retry } from 'next-test-utils'
34

@@ -154,4 +155,28 @@ describe('server-hmr', () => {
154155
}
155156
)
156157
})
158+
159+
describe('route handler hmr', () => {
160+
function getText(res: Response) {
161+
return res.ok
162+
? res.text()
163+
: Promise.reject(
164+
new Error('Failed to fetch route handler: ' + res.status)
165+
)
166+
}
167+
168+
it('reflects route handler changes on fetch/refresh', async () => {
169+
const initial = await next.fetch('/api/hello').then(getText)
170+
expect(initial).toBe('version: 0')
171+
172+
await next.patchFile('app/api/hello/route.ts', (content) =>
173+
content.replace('version: 0', 'version: 1')
174+
)
175+
176+
await retry(async () => {
177+
const updated = await next.fetch('/api/hello').then(getText)
178+
expect(updated).toBe('version: 1')
179+
})
180+
})
181+
})
157182
})

0 commit comments

Comments
 (0)