mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
e953bdaaaa
### What? Fix a dev-server startup race that could temporarily publish an empty Pages Router route set when Watchpack's first filesystem aggregation arrived before its route-directory scan completed. The dev bundler now snapshots the initial `pages/` and `app/` files before starting the watcher. During startup it ignores Watchpack aggregations that are missing an existing snapshot page file, so it does not publish route definitions or errors from a partial filesystem view. Empty route directories are detected from the same snapshot rather than from a separate callback that could race or mistake a read error for an empty directory. The `next-head` e2e fixture now delays Watchpack's initial `pages/` scan in development mode, deterministically covering the failing ordering while preserving every existing head-tag assertion and non-dev test mode. ### Why? PR #97905's build-and-test run 32911367895, job 98006962799 (`test cache components dev (6/6) / build`) exposed this as a flaky `test/e2e/next-head/index.test.ts` failure. In the raw job log around lines 3135–3305, retry 0 reported `Ready in 420ms` and then served `/` as a 404 four times. The returned document contained the `_error` page title (`404: This page could not be found`) and `pages__error` chunks, so four head-tag assertions failed downstream. Retry 1 passed about nine seconds later. The first Watchpack aggregation could run route discovery before `pages/index.js` appeared in `getTimeInfoEntries()`. That published no page route definitions and released the request handler, causing `/` to fall through to `_error` until a later aggregation repaired the route table. This change waits to publish any initial route state until Watchpack has a complete view, rather than adding request retries, sleeps, or broader timeouts. ### How? - Snapshot route directories once before watcher startup, tolerating a directory removed during boot so Watchpack can report the removal normally. - Ignore incomplete startup aggregations until Watchpack observes every snapshot page that still exists, with a 30-second safety deadline so an unwatchable file cannot stall startup. - Drive the empty-directory startup path from the snapshot, removing the error-swallowing `fs.readdir` race. - Delay the first Watchpack `pages/` scan in a separate dev-only route-discovery case and assert that the injection fired. The focused test reproduced `GET / 404` before the fix and returns `GET / 200` with the fix. - Preserve the original five `next/head` test cases unchanged; validate the app/pages conflict path separately. ### Verification - Exact CI environment: `__NEXT_CACHE_COMPONENTS=true __NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS=true __NEXT_EXPERIMENTAL_SERVER_COMPONENTS_HMR_CANCELLATION=true NEXT_EXTERNAL_TESTS_FILTERS=test/cache-components-tests-manifest.json NEXT_TEST_MODE=dev IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 pnpm test-dev-turbo test/e2e/next-head/index.test.ts` — 5/5 passed - `IS_WEBPACK_TEST=1 NEXT_TEST_MODE=dev pnpm test-dev-webpack test/e2e/next-head/index.test.ts` — 5/5 passed - `pnpm --filter=next types` - `pnpm --filter=next build` - Prettier, ESLint, and `git diff --check` <!-- NEXT_JS_LLM --> <!-- fleet 4ac891b6-2d24-4103-95bf-9daf04359e3f --> --------- Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com> Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>