* fix(app-router): emit pages client entry for hybrid builds
* fix pages router navigation params readiness
* fix(pages-router): track hashed fallback client entry
* fix(pages-router): honor navigation readiness in hooks
* fix(pages-router): derive server router.isReady from SSR readiness
Address review feedback on #1741:
- getRouterSnapshot() now reads navigationIsReady from the SSR context on
the server instead of unconditionally returning true. For pre-ready Pages
routes (auto-export dynamic / query-string / rewrite-capable builds) this
keeps useRouter().isReady consistent between SSR and client hydration,
avoiding a hydration mismatch for components that read it in JSX. Mirrors
Next.js render.tsx's server readiness rule.
- Drop the redundant guard branch in getClientParamsSnapshot.
- Add a regression test asserting server useRouter().isReady reflects the
SSR navigationIsReady context.
* fix(pages-router): align param resolution and dev/prod readiness parity
Address review feedback (ask-bonk + self-review) on #1741:
- Align client Pages navigation params to query-first, matching the server
snapshot and Next.js's adaptForPathParams (derives path params from
router.query). __NEXT_DATA__.query is kept current on client navigation, so
the two snapshots are now provably identical and avoid a latent post-
hydration mismatch.
- Populate window.__VINEXT_PAGE_PATTERNS__ in the dev server (own script tag)
so the next/navigation compat hooks can resolve a dynamic pattern from a
resolved path in dev exactly as in production. Closes a dev/prod parity gap
(prod set this via the client entry; dev never did).
- Drop the redundant pre-readiness setSSRContext in dev; the single post-
module-load call now publishes navigationIsReady before rendering, matching
the prod handler.
- Document that installPagesClientAssetGlobals is intentionally inert for
App-only builds, and that getReadonlyPagesSearchParams' string-keyed cache
is safe to share across concurrent SSR requests (immutable wrappers).
- Add a dev regression test asserting __VINEXT_PAGE_PATTERNS__ exposure.
* fix(pages-router): prefer URL params for client snapshots
* fix(app-router): gate pages asset globals
* test(app-router): add production test for hybrid basePath + assetPrefix
* fix(navigation): widen usePathname return type to string | null
* refactor(pages-router): dedupe navigation readiness modeling and cache pattern scan
Addresses code-review cleanup findings on the Pages Router navigation
readiness work:
- Extract buildPagesReadinessNextData() so the dev SSR handler and the
production Pages page handler derive the gssp/gsp/gip/appGip/autoExport
__NEXT_DATA__ readiness flags from one place, instead of two hand-kept
copies that could drift and produce a dev/prod hydration mismatch.
- Extract resolvePagesNavigationPathname() shared by the client and SSR
branches of getPagesNavigationContext, so the pathname null-ness rule
(fallback / pre-ready auto-export dynamic) cannot diverge between them.
- Memoize resolvePagesRoutePatternForPath()'s pattern scan, which ran an
uncached O(routes) loop on every useSyncExternalStore snapshot read.
- In getServerSearchParamsSnapshot, serialize the source only when the
object-identity fast path misses, avoiding a toString() on every read.
* refactor(pages-router): consistent ISR readiness data + robustness cleanups
Addresses further code-review findings:
- Thread the readiness flags (gssp/gsp/gip/appGip/autoExport) through the ISR
background-regeneration path: renderPagesIsrHtml now serializes the same
__NEXT_DATA__ readiness state the initial render emits, so a regenerated,
re-cached page hydrates with the identical initial router.isReady the server
computed. Previously these flags were dropped on that path — benign today
(ISR-eligible getStaticProps routes never trigger the flag-gated early-ready
branches) but a latent dev/prod hydration-readiness drift. Adds a shared
PagesNextDataExtras type so every render path stays in lockstep.
- readSsrManifest: degrade a malformed/unparseable SSR manifest to "no SSR
manifest" with a warning instead of throwing and aborting server startup,
restoring the previous tolerant behavior.
- pages-readiness: project PagesReadinessNextData from the canonical
VinextNextData and accept PagesPageModule, instead of bespoke types that
could drift from the __NEXT_DATA__ shape.
- getReadonlyPagesSearchParams: drop the redundant string-keyed cache slot; the
per-object WeakMap is authoritative and concurrency-safe on its own.
- isPagesRouterReady: drop the redundant server-side window check.
* fix(pages-router): restore string-keyed readonly search-params cache
Reverts the cache simplification from the previous commit. The string-keyed
slot is load-bearing, not redundant: across the Pages Router pre-ready → ready
transition the navigation context swaps in a new URLSearchParams instance even
when the query string is unchanged, so the per-object WeakMap alone returns a
fresh ReadonlyURLSearchParams wrapper and useSearchParams() loses Object.is
stability — re-firing a [searchParams] effect. Caught by the rewrite-driven
pre-ready static page e2e (search-params-snapshots saw ['',''] not ['']).
Keeps the improved comment explaining why both cache levels are needed.
---------
Co-authored-by: James <james@eli.cx>
* refactor(server): extract pages fallback bridge into typed helper
* test(app-router): update pages fallback codegen assertion
* refactor(server): tighten buildRequestHeaders type to Headers | null
The injected buildRequestHeaders dependency wires in
buildRequestHeadersFromMiddlewareResponse, which returns Headers | null,
but the dependency type declared a non-nullable Headers return. Tighten
the type to match reality and add a test exercising the null path where
the original request is forwarded unchanged.
---------
Co-authored-by: James <james@eli.cx>
Vitest integration CI shards by test file, so the 340-test App Router integration file becomes one oversized scheduling unit. That makes the slowest shard depend on one monolithic file even after adding native shards.
Split the App Router integration coverage into focused files and increase the integration matrix to four native Vitest shards. This keeps fileParallelism disabled for shared-fixture safety while giving CI smaller scheduling units to distribute.
Validation: vp check tests/app-router-*.test.ts vite.config.ts .github/workflows/ci.yml; vp test run --project integration tests/app-router-client-preloading.test.ts tests/app-router-dev-server.test.ts tests/app-router-external-rewrite.test.ts tests/app-router-font-google-prod.test.ts tests/app-router-isr-codegen.test.ts tests/app-router-malformed-url.test.ts tests/app-router-metadata-routes.test.ts tests/app-router-middleware-next-request.test.ts tests/app-router-next-config-codegen.test.ts tests/app-router-next-config-dev.test.ts tests/app-router-origin-check.test.ts tests/app-router-production-build.test.ts tests/app-router-production-server.test.ts tests/app-router-rsc-flight-hint.test.ts tests/app-router-rsc-plugin.test.ts tests/app-router-static-export.test.ts tests/app-router-worker-entry.test.ts