Skip to content

Separate server HMR entries from VersionedContentMap - #97903

Draft
wbinnssmith wants to merge 1 commit into
wbinnssmith/lazy-server-hmrfrom
wbinnssmith/server-hmr-entry-map-refactor
Draft

Separate server HMR entries from VersionedContentMap#97903
wbinnssmith wants to merge 1 commit into
wbinnssmith/lazy-server-hmrfrom
wbinnssmith/server-hmr-entry-map-refactor

Conversation

@wbinnssmith

@wbinnssmith wbinnssmith commented Aug 25, 2026

Copy link
Copy Markdown
Member

Summary

Move server HMR chunk-list ownership out of VersionedContentMap and into a dedicated ServerHmrEntryMap.

VersionedContentMap no longer scans all emitted assets to discover server HMR chunk lists or filters them for each pull. Instead, endpoints register their chunk entry lists under the route entry key when they are written. Server HMR snapshots then read only the chunk lists owned by that route.

This keeps VersionedContentMap focused on asset lookup and emission, while server HMR state is compartmentalized by route. It also removes the project-wide chunk-list scan and the associated path filtering from the update path.

The endpoint and native binding changes pass the route entry key through to the server HMR pull without changing the update wire format.

Verification

  • cargo check -p next-api -p next-napi-bindings
  • cargo fmt --all -- --check

@wbinnssmith wbinnssmith changed the title Track server HMR entries by route Scope lazy server HMR updates to route entries Aug 25, 2026
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Stats from current PR

🟢 3 improvements

Metric Canary PR Change Trend
Webpack Build Time (cached) 25.623s 24.502s 🟢 1.121s (-4%) ▁▁█▁▁
Webpack Cold (First Request) 3.478s 3.308s 🟢 170ms (-5%) ▁▂█▁▂
Webpack Warm (First Request) 3.439s 3.286s 🟢 153ms (-4%) ▁▂█▁▁
📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions

⚡ Dev Server

Metric Canary PR Change Trend
Cold (Listen) 813ms 814ms ██▁██
Cold (Ready in log) 806ms 805ms ▃▃▁▅█
Cold (First Request) 1.291s 1.287s ▁▁▁▂█
Warm (Listen) 815ms 815ms ██▁██
Warm (Ready in log) 801ms 809ms ▃▃▁▆█
Warm (First Request) 1.274s 1.294s ▁▂▁▂█
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 816ms 812ms ▂▂▇▂▂
Cold (Ready in log) 816ms 801ms ▁▁█▁▂
Cold (First Request) 3.478s 3.308s 🟢 170ms (-5%) ▁▂█▁▂
Warm (Listen) 813ms 812ms ▂▂█▂▂
Warm (Ready in log) 808ms 788ms ▁▂█▁▁
Warm (First Request) 3.439s 3.286s 🟢 153ms (-4%) ▁▂█▁▁

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 5.948s 5.819s ▂▁▁▁▆
Cached Build 2.934s 3.058s ▁▁▁▁▁
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 25.090s 25.385s ▁▁█▁▁
Cached Build 25.623s 24.502s 🟢 1.121s (-4%) ▁▁█▁▁
node_modules Size 553 MB 553 MB █████
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
017_4moyv5m34.js gzip 117 B 117 B
05jocc85-b73_.js gzip 341 B N/A -
096twxhe86hwd.js gzip 7.53 kB N/A -
0cz1d0mv5g_q7.js gzip 39.4 kB 39.4 kB
0e5lgz-xcf6ln.js gzip 273 B 273 B
0enhddgqs2dl-.js gzip 265 B 265 B
0h34mejwa_7pd.js gzip 354 B 354 B
0w853zxni3ro0.js gzip 1.36 kB N/A -
13lq2aw3_0bgw.js gzip 3.52 kB N/A -
1drifx6-oqf3q.js gzip 5.7 kB N/A -
1elt1qium-r2m.css gzip 115 B 115 B
1m5vdnlf0deiv.js gzip 411 B 411 B
1nigpy_mr0xvj.js gzip 339 B N/A -
1p0l8mn2wz5f2.js gzip 1.97 kB N/A -
1ph_nbsm967ot.js gzip 152 B N/A -
1sqoyy39jpgkq.js gzip 121 B 121 B
1tf1phijqlx9j.js gzip 220 B 220 B
1vusicjyl-6kb.js gzip 71.5 kB N/A -
2-54v5vj7orwp.js gzip 46.8 kB N/A -
2b05_424u1zq-.js gzip 120 B N/A -
2ca40xittx95r.js gzip 8.75 kB N/A -
2kci0vnt-49o-.js gzip 113 B 113 B
2rtriq9lc6zj-.js gzip 116 B N/A -
2sxgl_rcrrn_p.js gzip 399 B N/A -
2vt62i17bhbdu.js gzip 2.35 kB N/A -
342di-crrg9kq.js gzip 119 B N/A -
36hfc2_979aw2.js gzip 119 B N/A -
38wfp6jjrrdfm.js gzip 131 B 131 B
3dl1ilk37-30g.js gzip 117 B N/A -
3h0us7yy7bd_o.js gzip 13.1 kB N/A -
3kvxagoobts7h.js gzip 117 B N/A -
3p99hd9vwe6ae.js gzip 13 kB N/A -
3r0475panhk6m.js gzip 123 B N/A -
3s0i-weykgzrt.js gzip 125 B N/A -
3vjosdbddykpw.js gzip 5.88 kB N/A -
3wijk2ywqgsv9.js gzip 2.52 kB N/A -
3xuguv9lsoqw7.js gzip 65.5 kB N/A -
41u5s3oe2-erp.js gzip 2.29 kB N/A -
turbopack-0d..9ejy.js gzip 3.74 kB 3.74 kB
02zc7sqlpqxn3.js gzip N/A 119 B -
06pzxqp5qiozm.js gzip N/A 5.7 kB -
0dw-iyxdctoxo.js gzip N/A 65.5 kB -
0e28eh3gbwmp0.js gzip N/A 3.51 kB -
0jwho9fkrb_t3.js gzip N/A 2.29 kB -
0longconn5mlm.js gzip N/A 123 B -
0p4l3ozbez2cz.js gzip N/A 2.35 kB -
0sbh_15ejop4o.js gzip N/A 13 kB -
0skdm-9rpid8g.js gzip N/A 117 B -
0wv-h56sz83wn.js gzip N/A 120 B -
11hbt-mw4jxsv.js gzip N/A 71.5 kB -
137xup2t69ovc.js gzip N/A 1.97 kB -
1ezuqvubf86h1.js gzip N/A 5.88 kB -
1fk10s3m1oecx.js gzip N/A 400 B -
1hut5u-lz2yif.js gzip N/A 117 B -
20qhkyyj2_tla.js gzip N/A 116 B -
2imyufe18bsb7.js gzip N/A 1.36 kB -
2p4n3-8-xixh_.js gzip N/A 341 B -
2vqdfieehz5do.js gzip N/A 8.75 kB -
2x99jdcpr5n8x.js gzip N/A 7.53 kB -
2xn85ykzw2v1e.js gzip N/A 13.1 kB -
34knadd6wu9qg.js gzip N/A 119 B -
37b0_t16yn97f.js gzip N/A 153 B -
39jq2b6t6z6up.js gzip N/A 339 B -
3a9a9tvkag_-j.js gzip N/A 2.52 kB -
3d61tgick8g6b.js gzip N/A 46.8 kB -
3t121tj-h0kbm.js gzip N/A 125 B -
Total 299 kB 299 kB ⚠️ +4 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 1.02 kB 1.02 kB
Total 1.02 kB 1.02 kB ⚠️ +2 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 846 B 847 B
Total 846 B 847 B ⚠️ +1 B
Build Cache
Canary PR Change
00000001.sst gzip 13.4 MB 13.6 MB 🔴 +182 kB (+1%)
00000002.sst gzip 13.6 MB 13.2 MB 🟢 354 kB (-3%)
00000003.sst gzip 13.4 MB 13.3 MB 🟢 59.1 kB (0%)
00000004.sst gzip 14.5 MB 14.7 MB 🔴 +206 kB (+1%)
00000005.sst gzip 10.1 MB 10.1 MB 🟢 51.2 kB (-1%)
00000006.sst gzip 2.75 MB 2.75 MB
00000007.sst gzip 59 B 59 B
00000008.meta gzip 89 B 89 B
00000009.meta gzip 297 kB 297 kB
00000010.meta gzip 297 kB 297 kB
00000011.meta gzip 297 kB 297 kB
00000012.sst gzip 53.1 kB 52.5 kB 🟢 578 B (-1%)
00000013.sst gzip 1.77 MB 1.71 MB 🟢 57.3 kB (-3%)
00000014.sst gzip 59 B 59 B
00000015.meta gzip 116 B 116 B
00000016.meta gzip 327 kB 327 kB
00000017.meta gzip 403 kB 403 kB
00000018.sst gzip 53.8 kB 53.4 kB
00000019.sst gzip 1.35 MB 1.35 MB 🟢 5.6 kB (0%)
00000020.sst gzip 59 B 59 B
00000021.meta gzip 116 B 116 B
00000022.meta gzip 327 kB 327 kB
00000023.meta gzip 372 kB 372 kB
00000024.sst gzip 54 kB 53.4 kB 🟢 575 B (-1%)
00000025.sst gzip 1.35 MB 1.35 MB 🟢 6.17 kB (0%)
00000026.sst gzip 59 B 59 B
00000027.meta gzip 116 B 116 B
00000028.meta gzip 327 kB 327 kB
00000029.meta gzip 372 kB 372 kB
00000030.sst gzip 53.3 kB 52.8 kB 🟢 573 B (-1%)
00000031.sst gzip 1.35 MB 1.35 MB 🟢 6.15 kB (0%)
00000032.sst gzip 59 B 59 B
00000033.meta gzip 116 B 116 B
00000034.meta gzip 327 kB 327 kB
00000035.meta gzip 372 kB 372 kB
00000036.sst gzip 53.9 kB 53.4 kB
00000037.sst gzip 1.35 MB 1.35 MB 🟢 6.11 kB (0%)
00000038.sst gzip 59 B 59 B
00000039.meta gzip 116 B 116 B
00000040.meta gzip 327 kB 327 kB
00000041.meta gzip 372 kB 372 kB
CURRENT gzip 94 B 93 B 🟢 1 B (-1%)
LOG gzip 694 B 672 B 🟢 22 B (-3%)
Total 79.5 MB 79.4 MB ✅ -160 kB

📦 Webpack

Client

Main Bundles
Canary PR Change
3322-HASH.js gzip 65.7 kB N/A -
4191.HASH.js gzip 169 B N/A -
9784-HASH.js gzip 5.6 kB N/A -
b1ad9f4c-HASH.js gzip 63.5 kB N/A -
framework-HASH.js gzip 59.7 kB 59.7 kB
main-app-HASH.js gzip 253 B 253 B
main-HASH.js gzip 39.8 kB 39.7 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
3577.HASH.js gzip N/A 168 B -
578-HASH.js gzip N/A 65.8 kB -
8590-HASH.js gzip N/A 5.57 kB -
a8984546-HASH.js gzip N/A 63.5 kB -
Total 236 kB 236 kB ⚠️ +106 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Total 39.4 kB 39.4 kB
Pages
Canary PR Change
_app-HASH.js gzip 194 B 193 B
_error-HASH.js gzip 181 B 182 B
css-HASH.js gzip 334 B 331 B
dynamic-HASH.js gzip 1.81 kB 1.81 kB
edge-ssr-HASH.js gzip 255 B 253 B
head-HASH.js gzip 349 B 351 B
hooks-HASH.js gzip 382 B 384 B
image-HASH.js gzip 5.05 kB 5.05 kB
index-HASH.js gzip 260 B 259 B
link-HASH.js gzip 2.48 kB 2.48 kB
routerDirect..HASH.js gzip 317 B 318 B
script-HASH.js gzip 384 B 386 B
withRouter-HASH.js gzip 316 B 315 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 12.4 kB 12.4 kB ✅ -6 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 128 kB 128 kB
page.js gzip 293 kB 293 kB
Total 421 kB 421 kB ✅ -404 B
Middleware
Canary PR Change
middleware-b..fest.js gzip 600 B 602 B
middleware-r..fest.js gzip 156 B 156 B
middleware.js gzip 45.6 kB 45.3 kB
edge-runtime..pack.js gzip 842 B 842 B
Total 47.2 kB 46.9 kB ✅ -225 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 699 B 699 B
Total 699 B 699 B
Build Cache
Canary PR Change
0.pack gzip 4.8 MB 4.79 MB 🟢 9.07 kB (0%)
index.pack gzip 123 kB 124 kB
index.pack.old gzip 124 kB 124 kB
Total 5.04 MB 5.04 MB ✅ -8.42 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 375 kB 375 kB
app-page-exp..prod.js gzip 206 kB 206 kB
app-page-tur...dev.js gzip 374 kB 374 kB
app-page-tur..prod.js gzip 206 kB 206 kB
app-page-tur...dev.js gzip 370 kB 370 kB
app-page-tur..prod.js gzip 204 kB 204 kB
app-page.run...dev.js gzip 371 kB 371 kB
app-page.run..prod.js gzip 204 kB 204 kB
app-route-ex...dev.js gzip 82.6 kB 82.6 kB
app-route-ex..prod.js gzip 55.7 kB 55.7 kB
app-route-tu...dev.js gzip 82.6 kB 82.6 kB
app-route-tu..prod.js gzip 55.7 kB 55.7 kB
app-route-tu...dev.js gzip 82.2 kB 82.2 kB
app-route-tu..prod.js gzip 55.4 kB 55.4 kB
app-route.ru...dev.js gzip 82.2 kB 82.2 kB
app-route.ru..prod.js gzip 55.4 kB 55.4 kB
dev-validati...dev.js gzip 134 kB 134 kB
dev-validati...dev.js gzip 134 kB 134 kB
dev-validati...dev.js gzip 132 kB 132 kB
dev-validati...dev.js gzip 132 kB 132 kB
dist_client_...dev.js gzip 324 B 324 B
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 318 B 318 B
dist_client_...dev.js gzip 317 B 317 B
pages-api-tu...dev.js gzip 46.3 kB 46.3 kB
pages-api-tu..prod.js gzip 34.3 kB 34.3 kB
pages-api.ru...dev.js gzip 46.2 kB 46.2 kB
pages-api.ru..prod.js gzip 34.3 kB 34.3 kB
pages-turbo....dev.js gzip 54.7 kB 54.7 kB
pages-turbo...prod.js gzip 39.7 kB 39.7 kB
pages.runtim...dev.js gzip 54.7 kB 54.7 kB
pages.runtim..prod.js gzip 39.6 kB 39.6 kB
server.runti..prod.js gzip 65.9 kB 65.9 kB
use-cache-pr...dev.js gzip 73.3 kB 73.3 kB
use-cache-pr...dev.js gzip 73.3 kB 73.3 kB
use-cache-pr...dev.js gzip 71.5 kB 71.5 kB
use-cache-pr...dev.js gzip 71.5 kB 71.5 kB
Total 4.1 MB 4.1 MB
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/a3016622c6a20698be7982f4af90a2558c4901cb/next

Commit: a301662

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Failing test suites

Commit: a301662 | About building and testing Next.js

pnpm test-dev-experimental-turbo test/development/app-dir/cache-indicator-partial-prefetching/cache-indicator-partial-prefetching.test.ts (turbopack) (Experimental) (job)

  • cache-indicator-partial-prefetching > cache after session data triggers cold cache indicator (DD)
  • cache-indicator-partial-prefetching > shows the Cold cache badge on a cold runtime-prefetch navigation to a private-cache route, not on a warm one (DD)
  • cache-indicator-partial-prefetching > cache after link data > does not trigger cold cache indicator without prefetch={true} (DD)
Expand output

● cache-indicator-partial-prefetching › cache after session data triggers cold cache indicator

page.waitForSelector: Timeout 5000ms exceeded.
Call log:
  - waiting for locator('#cookies') to be visible
    - locator resolved to hidden <p id="cookies">Yum yum, delicious</p>
    - waiting for" http://localhost:45851/" navigation to finish...

  583 |
  584 |     return this.startChain(async () => {
> 585 |       const el = await page.waitForSelector(selector, {
      |                             ^
  586 |         timeout,
  587 |         state,
  588 |       })

  at waitForSelector (lib/browsers/playwright.ts:585:29)
  at Playwright._chain (lib/browsers/playwright.ts:715:23)
  at Playwright._chain [as startChain] (lib/browsers/playwright.ts:696:17)
  at Playwright.startChain [as waitForElementByCss] (lib/browsers/playwright.ts:584:17)
  at Playwright.waitForElementByCss [as elementByCss] (lib/browsers/playwright.ts:469:17)
  at Playwright.elementByCss [as elementById] (lib/browsers/playwright.ts:489:17)
  at Object.elementById (development/app-dir/cache-indicator-partial-prefetching/cache-indicator-partial-prefetching.test.ts:48:19)

● cache-indicator-partial-prefetching › cache after link data › does not trigger cold cache indicator without prefetch={true}

No child process available

  53 |   describe('cache after link data', () => {
  54 |     it('does not trigger cold cache indicator without prefetch={true}', async () => {
> 55 |       const browser = await next.browser('/')
     |                                  ^
  56 |
  57 |       // Cold navigation: the cache read sits after `await params`, so it resolves
  58 |       // in the runtime stage, part of the runtime shell for this navigation, so a

  at Object.browser (development/app-dir/cache-indicator-partial-prefetching/cache-indicator-partial-prefetching.test.ts:55:34)

● cache-indicator-partial-prefetching › shows the Cold cache badge on a cold runtime-prefetch navigation to a private-cache route, not on a warm one

No child process available

  106 |
  107 |   it('shows the Cold cache badge on a cold runtime-prefetch navigation to a private-cache route, not on a warm one', async () => {
> 108 |     const browser = await next.browser('/')
      |                                ^
  109 |
  110 |     // Cold navigation: the private cache resolves in the app shell, so a
  111 |     // cold cache there shows the badge.

  at Object.browser (development/app-dir/cache-indicator-partial-prefetching/cache-indicator-partial-prefetching.test.ts:108:32)

pnpm test-dev-experimental-turbo test/development/app-dir/hmr-shared-css/hmr-shared-css.test.ts (turbopack) (Experimental) (job)

  • hmr-app-and-pages > should do HMR when app router and pages router have shared CSS (DD)
Expand output

● hmr-app-and-pages › should do HMR when app router and pages router have shared CSS

page.goto: net::ERR_EMPTY_RESPONSE at http://localhost:45561/pages-router
Call log:
  - navigating to "http://localhost:45561/pages-router", waiting until "load"

  392 |     await opts?.beforePageLoad?.(page)
  393 |
> 394 |     await page.goto(url, { waitUntil: opts?.waitUntil ?? 'load' })
      |                ^
  395 |   }
  396 |
  397 |   back(options?: Parameters<Page['goBack']>[0]) {

  at Playwright.goto (lib/browsers/playwright.ts:394:16)
  at webdriver (lib/next-webdriver.ts:151:3)
  at Object.<anonymous> (development/app-dir/hmr-shared-css/hmr-shared-css.test.ts:30:15)

pnpm test-dev-experimental-turbo test/development/app-dir/server-hmr/server-hmr.test.ts (turbopack) (Experimental) (job)

  • server-hmr > metadata route hmr > reflects manifest dep changes on fetch/refresh (DD)
  • server-hmr > metadata route hmr > does not re-evaluate an unmodified dep when manifest changes (DD)
  • server-hmr > route handler hmr > reflects route handler changes on fetch/refresh (DD)
  • server-hmr > route handler hmr > does not re-evaluate an unmodified dependency when route changes (DD)
Expand output

● server-hmr › metadata route hmr › reflects manifest dep changes on fetch/refresh

expect(received).toBe(expected) // Object.is equality

Expected: "Version 1"
Received: "Version 0"

  665 |           .fetch('/manifest.webmanifest')
  666 |           .then((res) => res.json())
> 667 |         expect(updated.name).toBe('Version 1')
      |                              ^
  668 |       })
  669 |     })
  670 |

  at toBe (development/app-dir/server-hmr/server-hmr.test.ts:667:30)
  at retry (lib/next-test-utils.ts:868:14)
  at Object.<anonymous> (development/app-dir/server-hmr/server-hmr.test.ts:663:7)

● server-hmr › metadata route hmr › does not re-evaluate an unmodified dep when manifest changes

expect(received).not.toBe(expected) // Object.is equality

Expected: not 1787703813007

  687 |             .then((res) => res.json())
  688 |           // manifest.ts should have been re-evaluated (new timestamp)
> 689 |           expect(updated.manifestEvaluatedAt).not.toBe(
      |                                                   ^
  690 |             initial.manifestEvaluatedAt
  691 |           )
  692 |           // manifest-dep.ts should NOT have been re-evaluated

  at toBe (development/app-dir/server-hmr/server-hmr.test.ts:689:51)
  at retry (lib/next-test-utils.ts:868:14)
  at Object.<anonymous> (development/app-dir/server-hmr/server-hmr.test.ts:684:9)

● server-hmr › route handler hmr › reflects route handler changes on fetch/refresh

expect(received).toBe(expected) // Object.is equality

Expected: "version: 1"
Received: "version: 0"

  716 |       await retry(async () => {
  717 |         const updated = await next.fetch('/api/hello').then(getText)
> 718 |         expect(updated).toBe('version: 1')
      |                         ^
  719 |       })
  720 |     })
  721 |

  at toBe (development/app-dir/server-hmr/server-hmr.test.ts:718:25)
  at retry (lib/next-test-utils.ts:868:14)
  at Object.<anonymous> (development/app-dir/server-hmr/server-hmr.test.ts:716:7)

● server-hmr › route handler hmr › does not re-evaluate an unmodified dependency when route changes

expect(received).toBe(expected) // Object.is equality

Expected: "v2"
Received: "v1"

  740 |
  741 |           // The route change should be reflected in the response
> 742 |           expect(updated.routeVersion).toBe('v2')
      |                                        ^
  743 |
  744 |           // The unmodified dependency should NOT have been re-evaluated
  745 |           expect(updated.depEvaluatedAt).toBe(initialDepEvaluatedAt)

  at toBe (development/app-dir/server-hmr/server-hmr.test.ts:742:40)
  at retry (lib/next-test-utils.ts:868:14)
  at Object.<anonymous> (development/app-dir/server-hmr/server-hmr.test.ts:736:9)

pnpm test-dev-experimental-turbo test/development/app-dir/turbopack-ignore-issue/turbopack-ignore-issue.test.ts (turbopack) (Experimental) (job)

  • turbopack-ignore-issue > with turbopack.ignoreIssue config > should suppress ignored server component warning from cli output (DD)
  • turbopack-ignore-issue > with turbopack.ignoreIssue config > should suppress ignored server component error from error overlay (DD)
  • turbopack-ignore-issue > with turbopack.ignoreIssue config > should suppress ignored route handler warning from cli output (DD)
  • turbopack-ignore-issue > with turbopack.ignoreIssue config > should suppress ignored route handler error from cli output (DD)
  • turbopack-ignore-issue > with turbopack.ignoreIssue config > should still show issues for pages without ignore rules (DD)
Expand output

● turbopack-ignore-issue › with turbopack.ignoreIssue config › should suppress ignored server component warning from cli output

FetchError: request to http://localhost:43361/server-with-warning failed, reason: socket hang up

  at ClientRequest.<anonymous> (../node_modules/.pnpm/node-fetch@2.6.7_encoding@0.1.13/node_modules/node-fetch/lib/index.js:1491:11)

● turbopack-ignore-issue › with turbopack.ignoreIssue config › should suppress ignored server component error from error overlay

page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:43361/server-with-error
Call log:
  - navigating to "http://localhost:43361/server-with-error", waiting until "load"

  392 |     await opts?.beforePageLoad?.(page)
  393 |
> 394 |     await page.goto(url, { waitUntil: opts?.waitUntil ?? 'load' })
      |                ^
  395 |   }
  396 |
  397 |   back(options?: Parameters<Page['goBack']>[0]) {

  at Playwright.goto (lib/browsers/playwright.ts:394:16)
  at webdriver (lib/next-webdriver.ts:151:3)
  at Object.<anonymous> (development/app-dir/turbopack-ignore-issue/turbopack-ignore-issue.test.ts:85:23)

● turbopack-ignore-issue › with turbopack.ignoreIssue config › should suppress ignored route handler warning from cli output

No child process available

  89 |     it('should suppress ignored route handler warning from cli output', async () => {
  90 |       const outputIndex = next.cliOutput.length
> 91 |       await next.fetch('/route-with-warning')
     |                  ^
  92 |
  93 |       await retry(async () => {
  94 |         const output = stripAnsi(next.cliOutput.slice(outputIndex))

  at Object.fetch (development/app-dir/turbopack-ignore-issue/turbopack-ignore-issue.test.ts:91:18)

● turbopack-ignore-issue › with turbopack.ignoreIssue config › should suppress ignored route handler error from cli output

No child process available

  102 |     it('should suppress ignored route handler error from cli output', async () => {
  103 |       const outputIndex = next.cliOutput.length
> 104 |       await next.fetch('/route-with-error')
      |                  ^
  105 |
  106 |       await retry(async () => {
  107 |         const output = stripAnsi(next.cliOutput.slice(outputIndex))

  at Object.fetch (development/app-dir/turbopack-ignore-issue/turbopack-ignore-issue.test.ts:104:18)

● turbopack-ignore-issue › with turbopack.ignoreIssue config › should still show issues for pages without ignore rules

No child process available

  115 |     it('should still show issues for pages without ignore rules', async () => {
  116 |       // The home page should compile normally without issues
> 117 |       const res = await next.fetch('/')
      |                              ^
  118 |       expect(res.status).toBe(200)
  119 |       const html = await res.text()
  120 |       expect(html).toContain('hello world')

  at Object.fetch (development/app-dir/turbopack-ignore-issue/turbopack-ignore-issue.test.ts:117:30)

pnpm test-dev-experimental-turbo test/development/app-dir/turbopack-worker-thread-error-cleanup/turbopack-worker-thread-error-cleanup.test.ts (turbopack) (Experimental) (job)

  • turbopack worker thread error cleanup > terminates a loader worker after an evaluation error (DD)
Expand output

● turbopack worker thread error cleanup › terminates a loader worker after an evaluation error

No child process available

  28 |       await next.patchFile('input.probe', 'recovered')
  29 |       await retry(async () => {
> 30 |         expect((await next.render$('/'))('p').text()).toBe('recovered')
     |                            ^
  31 |       })
  32 |     }
  33 |   )

  at render$ (development/app-dir/turbopack-worker-thread-error-cleanup/turbopack-worker-thread-error-cleanup.test.ts:30:28)
  at fn (lib/next-test-utils.ts:868:20)
  at Object.<anonymous> (development/app-dir/turbopack-worker-thread-error-cleanup/turbopack-worker-thread-error-cleanup.test.ts:29:7)

pnpm test-dev-experimental-turbo test/e2e/app-dir/global-not-found/basic/global-not-found-basic.test.ts (turbopack) (Experimental) (job)

  • global-not-found - basic > should ssr global-not-found for 404 (DD)
  • global-not-found - basic > should render not-found boundary when calling notFound() in a page (DD)
Expand output

● global-not-found - basic › should ssr global-not-found for 404

FetchError: request to http://localhost:43189/does-not-exist failed, reason: socket hang up

  at ClientRequest.<anonymous> (../node_modules/.pnpm/node-fetch@2.6.7_encoding@0.1.13/node_modules/node-fetch/lib/index.js:1491:11)

● global-not-found - basic › should render not-found boundary when calling notFound() in a page

page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:43189/call-not-found
Call log:
  - navigating to "http://localhost:43189/call-not-found", waiting until "load"

  392 |     await opts?.beforePageLoad?.(page)
  393 |
> 394 |     await page.goto(url, { waitUntil: opts?.waitUntil ?? 'load' })
      |                ^
  395 |   }
  396 |
  397 |   back(options?: Parameters<Page['goBack']>[0]) {

  at Playwright.goto (lib/browsers/playwright.ts:394:16)
  at webdriver (lib/next-webdriver.ts:151:3)
  at Object.<anonymous> (e2e/app-dir/global-not-found/basic/global-not-found-basic.test.ts:32:21)

pnpm test-dev-experimental-turbo test/e2e/app-dir/global-not-found/no-root-layout/no-root-layout.test.ts (turbopack) (Experimental) (job)

  • global-not-found - no-root-layout > should ssr global-not-found for 404 (DD)
Expand output

● global-not-found - no-root-layout › should ssr global-not-found for 404

FetchError: request to http://localhost:37285/does-not-exist failed, reason: socket hang up

  at ClientRequest.<anonymous> (../node_modules/.pnpm/node-fetch@2.6.7_encoding@0.1.13/node_modules/node-fetch/lib/index.js:1491:11)

pnpm test-dev-experimental-turbo test/e2e/app-dir/navigation/navigation.test.ts (turbopack) (Experimental) (job)

  • app dir - navigation > SEO > should emit noindex meta tag for not found page when streaming (DD)
  • app dir - navigation > SEO > should emit refresh meta tag for redirect page when streaming (DD)
  • app dir - navigation > SEO > should emit refresh meta tag (permanent) for redirect page when streaming (DD)
  • app dir - navigation > SEO > should contain default meta tags in error page (DD)
  • app dir - navigation > SEO > should not log 404 errors in ipc server (DD)
  • app dir - navigation > browser back after a push followed by a refresh > should load the previous page (DD)
  • app dir - navigation > browser back to a revalidated page > should load the page (DD)
  • app dir - navigation > cross-pathname Link then same-pathname hash change > should replace (not concatenate) the hash when triggers the same-pathname hash change (DD)
  • app dir - navigation > external push > should push external url without affecting hooks (DD)
  • app dir - navigation > hash > should scroll to the specified hash (DD)
  • app dir - navigation > hash > should not scroll to hash when scroll={false} is set (DD)
  • app dir - navigation > hash-link-back-to-same-page > should scroll to the specified hash (DD)
  • app dir - navigation > hash-with-scroll-offset > should scroll to the specified hash (DD)
  • app dir - navigation > locale warnings > should warn about using the locale prop with next/link in app router (DD)
  • app dir - navigation > locale warnings > should have no warnings in pages router (DD)
  • app dir - navigation > middleware redirect > should change browser location when router.refresh() gets a redirect response (DD)
  • app dir - navigation > navigating to a page with async metadata > shows a fallback when prefetch was pending (DD)
  • app dir - navigation > navigating to a page with async metadata > shows a fallback when prefetch completed (DD)
  • app dir - navigation > navigating to dynamic params & changing the casing > should load the page correctly (DD)
  • app dir - navigation > navigation between pages and app > should not contain _rsc query while navigating from app to pages (DD)
  • app dir - navigation > navigation between pages and app > should not contain _rsc query while navigating from pages to app (DD)
  • app dir - navigation > navigation between pages and app > should not omit the hash while navigating from app to pages (DD)
  • app dir - navigation > navigations when attaching a Proxy to window.Promise > should navigate without issue (DD)
  • app dir - navigation > nested navigation > should navigate to nested pages (DD)
  • app dir - navigation > nested navigation > should load chunks correctly without double encoding of url (DD)
  • app dir - navigation > not-found > should trigger not-found in a server component (DD)
  • app dir - navigation > not-found > should trigger not-found in a client component (DD)
  • app dir - navigation > not-found > should trigger not-found client-side (DD)
  • app dir - navigation > not-found > should trigger not-found while streaming (DD)
  • app dir - navigation > redirect > components > should redirect in a server component (DD)
  • app dir - navigation > redirect > components > should redirect in a client component (DD)
  • app dir - navigation > redirect > components > should redirect client-side (DD)
  • app dir - navigation > redirect > components > should redirect to external url (DD)
  • app dir - navigation > redirect > components > should redirect to external url, initiating only once (DD)
  • app dir - navigation > redirect > components > should only trigger the redirect once (/redirect/servercomponent) (DD)
  • app dir - navigation > redirect > components > should only trigger the redirect once (redirect/redirect-with-loading) (DD)
  • app dir - navigation > redirect > middleware redirects > should redirect from middleware (DD)
  • app dir - navigation > redirect > middleware redirects > should redirect from middleware with link navigation (DD)
  • app dir - navigation > redirect > next.config.js redirects > should redirect from next.config.js (DD)
  • app dir - navigation > redirect > next.config.js redirects > should redirect from next.config.js with link navigation (DD)
  • app dir - navigation > relative hashes and queries > should work with a hash-only href (DD)
  • app dir - navigation > relative hashes and queries > should work with a hash-only router.push(...) (DD)
  • app dir - navigation > relative hashes and queries > should work with a query-only href (DD)
  • app dir - navigation > relative hashes and queries > should work with both relative hashes and queries (DD)
  • app dir - navigation > replace during a push that has not committed > should supersede the pending push (DD)
  • app dir - navigation > scroll restoration > should restore original scroll position when navigating back (DD)
  • app dir - navigation > useRouter identity between navigations > should preserve identity when navigating to the same page (DD)
  • app dir - navigation > useRouter identity between navigations > should preserve identity when navigating between different pages (DD)
Expand output

● app dir - navigation › hash › should scroll to the specified hash

expect(received).resolves.toEqual(expected) // deep equality

Expected: 128
Received: 0

  178 |         await retry(
  179 |           () =>
> 180 |             expect(browser.eval('window.pageYOffset')).resolves.toEqual(
      |                                                                 ^
  181 |               expectedScroll
  182 |             ),
  183 |           10_000

  at Object.toEqual (../node_modules/.pnpm/expect@29.7.0/node_modules/expect/build/index.js:174:22)
  at toEqual (e2e/app-dir/navigation/navigation.test.ts:180:65)
  at fn (lib/next-test-utils.ts:868:20)
  at checkLink (e2e/app-dir/navigation/navigation.test.ts:178:9)
  at Object.<anonymous> (e2e/app-dir/navigation/navigation.test.ts:195:7)

● app dir - navigation › hash › should not scroll to hash when scroll={false} is set

No child process available

  217 |
  218 |     it('should not scroll to hash when scroll={false} is set', async () => {
> 219 |       const browser = await next.browser('/hash-changes')
      |                                  ^
  220 |       const curScroll = await browser.eval('document.documentElement.scrollTop')
  221 |       await browser.elementByCss('#scroll-to-name-item-400-no-scroll').click()
  222 |       expect(curScroll).toBe(

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:219:34)

● app dir - navigation › hash-with-scroll-offset › should scroll to the specified hash

No child process available

  228 |   describe('hash-with-scroll-offset', () => {
  229 |     it('should scroll to the specified hash', async () => {
> 230 |       const browser = await next.browser('/hash-with-scroll-offset')
      |                                  ^
  231 |
  232 |       const checkLink = async (
  233 |         val: number | string,

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:230:34)

● app dir - navigation › hash-link-back-to-same-page › should scroll to the specified hash

No child process available

  256 |   describe('hash-link-back-to-same-page', () => {
  257 |     it('should scroll to the specified hash', async () => {
> 258 |       const browser = await next.browser('/hash-link-back-to-same-page')
      |                                  ^
  259 |
  260 |       const checkLink = async (
  261 |         val: number | string,

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:258:34)

● app dir - navigation › relative hashes and queries › should work with a hash-only href

No child process available

  297 |
  298 |     it('should work with a hash-only href', async () => {
> 299 |       const browser = await next.browser(pathname)
      |                                  ^
  300 |       await browser.elementByCss('#link-to-h1-hash-only').click()
  301 |
  302 |       await retry(() =>

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:299:34)

● app dir - navigation › relative hashes and queries › should work with a hash-only router.push(...)

No child process available

  306 |
  307 |     it('should work with a hash-only `router.push(...)`', async () => {
> 308 |       const browser = await next.browser(pathname)
      |                                  ^
  309 |       await browser.elementByCss('#button-to-h3-hash-only').click()
  310 |
  311 |       await retry(() =>

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:308:34)

● app dir - navigation › relative hashes and queries › should work with a query-only href

No child process available

  315 |
  316 |     it('should work with a query-only href', async () => {
> 317 |       const browser = await next.browser(pathname)
      |                                  ^
  318 |       await browser.elementByCss('#link-to-dummy-query').click()
  319 |
  320 |       await retry(() =>

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:317:34)

● app dir - navigation › relative hashes and queries › should work with both relative hashes and queries

No child process available

  326 |
  327 |     it('should work with both relative hashes and queries', async () => {
> 328 |       const browser = await next.browser(pathname)
      |                                  ^
  329 |       await browser.elementByCss('#link-to-h2-with-hash-and-query').click()
  330 |
  331 |       await retry(() =>

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:328:34)

● app dir - navigation › cross-pathname Link then same-pathname hash change › should replace (not concatenate) the hash when triggers the same-pathname hash change

No child process available

  374 |
  375 |     it('should replace (not concatenate) the hash when <Link> triggers the same-pathname hash change', async () => {
> 376 |       const browser = await next.browser(startPath)
      |                                  ^
  377 |
  378 |       await browser.elementByCss('#link-to-target-foo').click()
  379 |       await retry(() =>

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:376:34)

● app dir - navigation › not-found › should trigger not-found in a server component

No child process available

  394 |   describe('not-found', () => {
  395 |     it('should trigger not-found in a server component', async () => {
> 396 |       const browser = await next.browser('/not-found/servercomponent')
      |                                  ^
  397 |
  398 |       expect(
  399 |         await browser.waitForElementByCss('#not-found-component').text()

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:396:34)

● app dir - navigation › not-found › should trigger not-found in a client component

No child process available

  407 |
  408 |     it('should trigger not-found in a client component', async () => {
> 409 |       const browser = await next.browser('/not-found/clientcomponent')
      |                                  ^
  410 |       expect(
  411 |         await browser.waitForElementByCss('#not-found-component').text()
  412 |       ).toBe('Not Found!')

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:409:34)

● app dir - navigation › not-found › should trigger not-found client-side

No child process available

  418 |     })
  419 |     it('should trigger not-found client-side', async () => {
> 420 |       const browser = await next.browser('/not-found/client-side')
      |                                  ^
  421 |       await browser
  422 |         .elementByCss('button')
  423 |         .click()

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:420:34)

● app dir - navigation › not-found › should trigger not-found while streaming

No child process available

  433 |     })
  434 |     it('should trigger not-found while streaming', async () => {
> 435 |       const browser = await next.browser('/not-found/suspense')
      |                                  ^
  436 |       expect(
  437 |         await browser.waitForElementByCss('#not-found-component').text()
  438 |       ).toBe('Not Found!')

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:435:34)

● app dir - navigation › redirect › components › should redirect in a server component

No child process available

  448 |     describe('components', () => {
  449 |       it('should redirect in a server component', async () => {
> 450 |         const browser = await next.browser('/redirect/servercomponent')
      |                                    ^
  451 |         await browser.waitForElementByCss('#result-page')
  452 |         expect(await browser.elementByCss('#result-page').text()).toBe(
  453 |           'Result Page'

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:450:36)

● app dir - navigation › redirect › components › should redirect in a client component

No child process available

  456 |
  457 |       it('should redirect in a client component', async () => {
> 458 |         const browser = await next.browser('/redirect/clientcomponent')
      |                                    ^
  459 |         await browser.waitForElementByCss('#result-page')
  460 |         expect(await browser.elementByCss('#result-page').text()).toBe(
  461 |           'Result Page'

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:458:36)

● app dir - navigation › redirect › components › should redirect client-side

No child process available

  464 |
  465 |       it('should redirect client-side', async () => {
> 466 |         const browser = await next.browser('/redirect/client-side')
      |                                    ^
  467 |         await browser
  468 |           .elementByCss('button')
  469 |           .click()

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:466:36)

● app dir - navigation › redirect › components › should redirect to external url

No child process available

  475 |
  476 |       it('should redirect to external url', async () => {
> 477 |         const browser = await next.browser('/redirect/external')
      |                                    ^
  478 |         expect(await browser.waitForElementByCss('h1').text()).toBe(
  479 |           'Example Domain'
  480 |         )

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:477:36)

● app dir - navigation › redirect › components › should redirect to external url, initiating only once

No child process available

  483 |       it('should redirect to external url, initiating only once', async () => {
  484 |         const storageKey = Math.random()
> 485 |         const browser = await next.browser(
      |                                    ^
  486 |           `/redirect/external-log/${storageKey}`
  487 |         )
  488 |         expect(await browser.waitForElementByCss('h1').text()).toBe(

  at Object.browser (e2e/app-dir/navigation/navigation.test.ts:485:36)

● app dir - navigation › redirect › components › should only trigger the redirect once (/redirect/servercomponent)

No child process available

  516 |           const requestedPathnames: string[] = []
  517 |
> 518 |           const browser = await next.browser(path, {
      |                                      ^
  519 |             beforePageLoad(page) {
  520 |               page.on('request', async (req) => {
  521 |                 requestedPathnames.push(new URL(req.url()).pathname)

  at browser (e2e/app-dir/navigation/navigation.test.ts:518:38)

● app dir - navigation › redirect › components › should only trigger the redirect once (redirect/redirect-with-loading)

No child process available

  516 |           const requestedPathnames: string[] = []
  517 |
> 518 |           const browser = await next.browser(path, {
      |                                      ^
  519 |             beforePageLoad(page) {
  520 |               page.on('request', async (req) => {
  521 |                 requestedPathnames.push(new URL(req.url()).pathname)

  at browser (e2e/app-dir/navigation/navigation.test.ts:518:38)

● app dir - navigation › redirect › next.config.js redirects › should redirect from next.config.js

No child process available

  573 |     describe('next.config.js redirects', () => {
  574 |       it('

... truncated ...

pnpm test-dev-experimental-turbo test/e2e/app-dir/searchparams-reuse-loading/searchparams-reuse-loading.test.ts (turbopack) (Experimental) (job)

  • searchparams-reuse-loading > should re-use the prefetched loading state when navigating to a new searchParam value (DD)
  • searchparams-reuse-loading > should properly render root page with searchParams when prefetch is aliased (DD)
  • searchparams-reuse-loading > should reflect the correct searchParams when re-using the same page segment (DD)
  • searchparams-reuse-loading > should reflect the correct searchParams when the root page is prefetched first (DD)
  • searchparams-reuse-loading > when aliasing is skipped > should work for not found pages (DD)
  • searchparams-reuse-loading > when aliasing is skipped > should work for route handlers (DD)
  • searchparams-reuse-loading > when aliasing is skipped > should work for navigating to pages dir (DD)
Expand output

● searchparams-reuse-loading › should re-use the prefetched loading state when navigating to a new searchParam value

page.waitForSelector: Timeout 10000ms exceeded.
Call log:
  - waiting for locator('#loading') to be visible
    - waiting for navigation to finish...

  583 |
  584 |     return this.startChain(async () => {
> 585 |       const el = await page.waitForSelector(selector, {
      |                             ^
  586 |         timeout,
  587 |         state,
  588 |       })

  at waitForSelector (lib/browsers/playwright.ts:585:29)
  at Playwright._chain (lib/browsers/playwright.ts:715:23)
  at Playwright._chain [as startChain] (lib/browsers/playwright.ts:696:17)
  at Playwright.startChain [as waitForElementByCss] (lib/browsers/playwright.ts:584:17)
  at Object.waitForElementByCss (e2e/app-dir/searchparams-reuse-loading/searchparams-reuse-loading.test.ts:17:35)

● searchparams-reuse-loading › should properly render root page with searchParams when prefetch is aliased

No child process available

  33 |
  34 |   it('should properly render root page with searchParams when prefetch is aliased', async () => {
> 35 |     const browser = await next.browser('/search-params')
     |                                ^
  36 |     await browser.elementByCss("[href='/?id=1']").click()
  37 |     const params = await browser.waitForElementByCss('#root-params').text()
  38 |     expect(param

... truncated to fit in one GitHub comment ...

@wbinnssmith wbinnssmith changed the title Scope lazy server HMR updates to route entries Separate server HMR entries from VersionedContentMap Aug 25, 2026

@vercel vercel Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Additional Suggestion:

app-route writeToDisk() is called without the entry key, so its server HMR chunks are never registered, making every server HMR pull for app-route a silent no-op.

Fix on Vercel

@wbinnssmith
wbinnssmith force-pushed the wbinnssmith/server-hmr-entry-map-refactor branch from ea61a37 to fb9e7c3 Compare August 25, 2026 23:33
@wbinnssmith
wbinnssmith force-pushed the wbinnssmith/server-hmr-entry-map-refactor branch from fb9e7c3 to a301662 Compare August 26, 2026 00:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant