fix(pages): emit canonical __NEXT_DATA__ JSON - #2043
Conversation
|
|
/bigbonk review for issues |
There was a problem hiding this comment.
Review: PR #2043 — canonical __NEXT_DATA__ JSON
The core change is correct and matches Next.js: emitting <script id="__NEXT_DATA__" type="application/json">...</script> with HTML-escaped JSON (via safeJsonStringify, which escapes </>/&), parsing it client-side, and omitting gsp when false. Next.js serializes the same canonical markup, and gsp: undefined is correctly stripped by both the production payload filter (pages-page-response.ts:273) and JSON.stringify in dev.
However, this PR will fail the required Vitest CI check. It changed the emitted markup but only updated stale assertions in 3 test files, leaving ~25 tests across at least 5 files that still extract __NEXT_DATA__ with the old executable-assignment pattern (window.__NEXT_DATA__ = {...}) against live server HTML.
Blocking — stale test assertions not updated (CI red)
Verified failing locally on this branch (pass on main):
tests/pages-router.test.ts— 14 failures. Lines1552,1659,1670,1682,1696,1712,1867,1927,1947,1956,4773,4925,4935,4953all use/<script>window\.__NEXT_DATA__\s*=\s*({.*?})<\/script>/or/__NEXT_DATA__\s*=\s*(\{.*?\})\s*[;<]/against fetched HTML. e.g.includes isFallback: false in __NEXT_DATA__(tests/pages-router.test.ts:1956) →expected null to be truthy. Note the newreadNextData()helper added attests/pages-router.test.ts:6355was only applied to therouter __NEXT_DATA__ correctnessblock, not these.tests/features.test.ts— 6 failures. Lines680,1216,1227,4721,4734,5751— incl.i18n routing > includes locale info in __NEXT_DATA__ script/...for default locale,__NEXT_DATA__ query contains dynamic params for static pages,__NEXT_DATA__ contains nested GSSP props.tests/pages-router-concurrency.test.ts— 3 failures. Lines78,159,174→no __NEXT_DATA__ found in response.tests/static-export.test.ts— 2 failures. Lines183,205→expected null to be truthy.tests/pages-page-response.test.ts— 2 failures.tests/pages-page-response.test.ts:181/:182(asserts"window.__NEXT_DATA__"and"__VINEXT_LOCALE__"in server HTML) and:706(window.__NEXT_DATA__). The PR updated the assertion at:310in this same file but missed these.
The PR description says "Local validation: Passed" and lists tests/pages-router.test.ts: 8 targeted integration tests" and "tests/features.test.ts is not listed — these full files are red. All of these need to be migrated to extract the canonical id="__NEXT_DATA__" type="application/json" element (the readNextData() helper, or extractVinextNextDataJson, is the natural fit). Note :182's __VINEXT_LOCALE__ assertion is now legitimately gone from server HTML (locale globals moved client-side) and should be dropped, not just reformatted.
The test inputs at tests/pages-page-response.test.ts:840–1213 and tests/pages-page-data.test.ts:88–1246 are mock cached HTML feeding the legacy-fallback path in rewritePagesCachedHtml — those are fine to leave as legacy markup.
Non-blocking — duplicated locale-globals logic
The new initial-hydration code in entries/pages-client-entry.ts:164-169 and server/dev-server.ts:1572-1578 reimplements locale-global assignment inline and unconditionally:
window.__VINEXT_LOCALE__ = window.__NEXT_DATA__.locale; // undefined on non-i18n pages
window.__VINEXT_LOCALES__ = window.__NEXT_DATA__.locales;
window.__VINEXT_DEFAULT_LOCALE__ = window.__NEXT_DATA__.defaultLocale;The repo already has applyVinextLocaleGlobals() (client/vinext-next-data.ts:133) which sets each global only when defined and is used during client navigation (shims/router.ts:1696,1874). For initial load the unconditional undefined assignment is functionally equivalent, but reusing applyVinextLocaleGlobals would keep the initial-load and navigation paths in lockstep and avoid two more copies of this logic drifting. Consider routing both initialization sites through that helper.
Summary
Source change: looks correct and Next.js-aligned. Block on the test regressions — please update all the stale __NEXT_DATA__ extractors above so the full Vitest suite is green, and consider consolidating the locale-globals initialization onto applyVinextLocaleGlobals.
|
Review posted to PR #2043. SummaryThe source change is correct and Next.js-aligned — canonical The blocking issue: the PR will fail the required Vitest CI check. It changed the emitted markup but only updated stale assertions in 3 test files, leaving ~27 test failures across 5 files that still extract
I distinguished these live-response assertions from the legitimate legacy-markup test inputs (e.g. One non-blocking note: the new initial-hydration code duplicates locale-global assignment inline and unconditionally in two places, instead of reusing the existing |
|
/bigbonk review for issues |
commit: |
There was a problem hiding this comment.
Review: PR #2043 — canonical __NEXT_DATA__ JSON
I re-ran the affected files on the current branch HEAD (48798ac). The core source change is correct and Next.js-aligned (canonical <script id="__NEXT_DATA__" type="application/json">, HTML-escaped JSON, client-side parse, gsp omitted when false). But the PR will fail the required Vitest check, and the prior review's failure inventory is now partly stale and missed a real regression. Corrected findings below.
Blocking #1 — NEW regression: static next/router import removed (missed by prior review)
tests/pages-router.test.ts has exactly 1 failure, and it is not a __NEXT_DATA__ extractor — it's Virtual server entry generation > client entry statically imports next/router so window.next.router is set before hydration (tests/pages-router.test.ts:2434). Both assertions fail:
:2451expects the entry to staticallyimport Router, { wrapWithRouterContext } from "next/router"at module top:2458expects the lazyawait import("next/router")to NOT be present
The diff in packages/vinext/src/entries/pages-client-entry.ts deleted the top-level static import and replaced it with:
const { default: Router, wrapWithRouterContext, _initializePagesRouterReadyFromNextData } =
await import("next/router");This is a behavioral regression, not just a stale test. Importing next/router runs installPagesRouterRuntime() + installWindowNext() as a module-eval side effect (shims/router.ts:3037-3056) — that stamps the initial history entry, attaches the popstate listener, and exposes window.next.router. The static import was deliberately the first vinext shim specifically to run that side effect before hydration and close the popstate race window (the deleted comment and Next.js client/next.ts reference document this). Deferring it via await import reopens that race and removes window.next.router availability before async work — which is exactly what the test guards against.
The motivation (parse __NEXT_DATA__ before the router reads readiness) is legitimate, but it shouldn't require dropping the static import. Keep the static import Router, { wrapWithRouterContext, _initializePagesRouterReadyFromNextData } from "next/router" at the top, parse __NEXT_DATA__ from the JSON element, then call _initializePagesRouterReadyFromNextData(window.__NEXT_DATA__). The readiness initializer is an explicit call already, so it does not depend on a dynamic import.
Blocking #2 — stale live-response __NEXT_DATA__ extractors
Verified failing on the branch:
tests/features.test.ts— 4 failures.:680(window.__NEXT_DATA__ = (\{...\})),:1217/:1228(i18n locale info),:4735(dynamic params for static pages),:5751(nested GSSP props). All still matchwindow.__NEXT_DATA__ = {...}against live HTML.tests/pages-router-concurrency.test.ts— 3 failures.:78,:159,:174use/__NEXT_DATA__\s*=\s*(\{[^<]+\})/→no __NEXT_DATA__ found in response.tests/static-export.test.ts— 2 failures.:183,:205use/window\.__NEXT_DATA__\s*=\s*({[^<]+})/→expected null to be truthy.
Migrate these to extract the canonical id="__NEXT_DATA__" type="application/json" element (reuse the readNextData() helper added at tests/pages-router.test.ts:6383, or extractVinextNextDataJson).
Corrections to the prior review
tests/pages-router.test.tsis not 14 failures — its__NEXT_DATA__extractors are already migrated to the canonical regex (e.g.:1552,:1660,:1962). It fails only on the static-import test above.tests/pages-page-response.test.tspasses now (40/40 — verified). The:181/:182/:706and__VINEXT_LOCALE__concerns were already addressed; the:182block now asserts the canonical JSON element and parses locale fields out of it.
Non-blocking — duplicated locale-globals logic (confirmed)
entries/pages-client-entry.ts:154-156 and server/dev-server.ts reimplement locale-global assignment inline and unconditionally (window.__VINEXT_LOCALE__ = window.__NEXT_DATA__.locale, etc.), assigning undefined on non-i18n pages. applyVinextLocaleGlobals() (client/vinext-next-data.ts:133) already sets each only when defined and is used by the navigation path (shims/router.ts). Route both init sites through it to avoid drift.
Summary
Source change is correct. Block on: (1) restore the static next/router import (pages-router.test.ts:2434 regression — reopens the popstate race), and (2) migrate the remaining 9 stale live-response __NEXT_DATA__ extractors in features.test.ts, pages-router-concurrency.test.ts, and static-export.test.ts. Then the full Vitest suite should be green.
|
Review posted to PR #2043. SummaryThe source change is correct and Next.js-aligned, but the PR will fail the required Vitest check. I re-ran the affected files on the current branch HEAD and the prior review's inventory was partly stale and missed a real regression: Blocking #1 — NEW regression the prior review missed. Blocking #2 — 9 stale live-response extractors (verified failing): Corrections to prior review: Non-blocking: duplicated unconditional locale-globals assignment should reuse |
|
/bigbonk review for issues |
|
/bigbonk review for issues |
|
@james-elicx Bonk workflow was cancelled. View workflow run · To retry, trigger Bonk again. |
|
/bigbonk review for issues |
There was a problem hiding this comment.
Review: PR #2043 — canonical __NEXT_DATA__ JSON
I re-ran the affected files on the current branch HEAD (75311fb). The source change is correct and Next.js-aligned, and the two previously-blocking findings are now resolved — but there is one new blocking test regression that all three prior reviews missed, introduced by the latest commit (75311fb fix(pages): preserve router bootstrap ordering).
Blocking — tests/entry-templates.test.ts:994 fails (required Vitest check)
Verified failing on this branch, passing on main:
tests/entry-templates.test.ts > Pages Router entry template > hydrates _app with the full Pages props envelope
AssertionError: expected '…' to contain 'import Router, { wrapWithRouterContext } from "next/router";'
tests/entry-templates.test.ts:994 asserts the generated client entry contains the exact single-line string:
expect(code).toContain('import Router, { wrapWithRouterContext } from "next/router";');Commit 75311fb restored the static next/router import (good — that fixes the prior Blocking #1), but emitted it in a multi-line form and added a third symbol:
import Router, {
wrapWithRouterContext,
_initializePagesRouterReadyFromNextData,
} from "next/router";The exact .toContain() no longer matches, so the test fails. This assertion existed and passed on main (a3d2f92), so it is a regression introduced by this PR, not a pre-existing failure.
Note the sibling test tests/pages-router.test.ts:2451 passes because it uses a tolerant regex (/^import\s+Router,\s*\{[^}]*\bwrapWithRouterContext\b[^}]*\}\s+from\s+["']next\/router["']/m). The fix is to bring entry-templates.test.ts:994 in line — either match the multi-line/multi-symbol import exactly, or switch to a regex like the one at pages-router.test.ts:2451.
Resolved since the prior reviews
- Prior Blocking #1 (static
next/routerimport removed): fixed by75311fb. The staticimport Router, { wrapWithRouterContext, _initializePagesRouterReadyFromNextData } from "next/router"is back at module top in bothentries/pages-client-entry.tsandserver/dev-server.ts, andpages-router.test.ts:2434("client entry statically imports next/router…") now passes. The popstate-race concern is addressed. - Prior Blocking #2 (stale live-response
__NEXT_DATA__extractors): resolved. I rantests/features.test.ts,tests/pages-router-concurrency.test.ts, andtests/static-export.test.tstogether — all 347 tests pass. The extractors were migrated to the canonical<script id="__NEXT_DATA__" type="application/json">…</script>element. I also confirmed the remainingwindow.__NEXT_DATA__ =matches in the test suite are legitimate legacy inputs (mock cached HTML for therewritePagesCachedHtmlfallback path inpages-page-data.test.ts/pages-page-response.test.ts, theextractVinextNextDataJsonlegacy-fallback test inshims.test.ts:45, andsafe-json.test.tsXSS-escaping inputs), not live-response assertions.
Source change — correct and Next.js-aligned
- Canonical markup
<script id="__NEXT_DATA__" type="application/json"…>with HTML-escaped JSON viasafeJsonStringify(escapes</>/&/\u2028/\u2029) — matches Next.js and is XSS-safe in the JSON-script context. gspomission:pages-readiness.ts:45now emitsgsp: hasPageGsp ? true : undefined;undefinedis stripped by both the production payload filter andJSON.stringify. The only consumer (shims/router.ts:1171) checksnextData.gsp !== true, so absent vs.falseis behaviorally identical. Type-clean (vp checkpasses).- Client navigation parses both canonical and legacy markup via
extractVinextNextDataJson+parseVinextNextDataJson; ISR cached-HTML rewrite (pages-page-data.ts) handles both canonical and legacy__NEXT_DATA__blocks. Nonced canonical element is handled by lookahead-based regexes that tolerate attribute order. vp checkis clean on all 7 changed source files.
Non-blocking — duplicated locale-globals logic (still present)
entries/pages-client-entry.ts:164-169 and server/dev-server.ts:1573-1577 reimplement locale-global assignment inline and unconditionally (window.__VINEXT_LOCALE__ = window.__NEXT_DATA__.locale, etc., assigning undefined on non-i18n pages). The repo already has applyVinextLocaleGlobals() (client/vinext-next-data.ts:133), which assigns each global only when defined and is used by the navigation path (shims/router.ts:1705). Routing both init sites through that helper would keep initial-load and navigation in lockstep and avoid further drift. Functionally equivalent today, so non-blocking.
Summary
Block on the single regression: update tests/entry-templates.test.ts:994 to match the new multi-line next/router import (the latest commit changed the generated import shape but left this exact-string assertion stale). Everything else — source correctness, the static-import restoration, and the migrated live-response extractors — is in good shape and the rest of the affected suite is green.
|
Review posted to PR #2043. SummaryThe source change is correct and Next.js-aligned, and the two previously-blocking findings are now resolved by commit Blocking — Resolved since prior reviews:
Non-blocking: the duplicated unconditional locale-globals assignment in The core implementation (canonical JSON markup, XSS-safe escaping, |
|
/bigbonk review for issues |
There was a problem hiding this comment.
Review: PR #2043 — canonical __NEXT_DATA__ JSON
Re-reviewed at branch HEAD (f0b2434 test(pages): expect readiness bootstrap import). The prior blocking finding is resolved and the affected Vitest suite is now green. I verified all changed source files lint/type-clean and ran the full set of affected test files — 1826 tests pass across entry-templates, pages-router, features, pages-router-concurrency, static-export, pages-page-response, pages-page-data, and shims. No blocking issues found.
Prior blocking finding — resolved
- Prior Blocking (
tests/entry-templates.test.ts:994exact-stringimport Router, { wrapWithRouterContext } from "next/router";): fixed byf0b2434. The assertion now matches the multi-line/three-symbol import shape (import Router, {+wrapWithRouterContext,+_initializePagesRouterReadyFromNextData,+} from "next/router";) and additionally asserts_initializePagesRouterReadyFromNextData(window.__NEXT_DATA__);. Verified passing on the branch; was failing before. The staticnext/routerimport restoration (prior Blocking #1) and the migrated live-response extractors (prior Blocking #2) remain in good shape.
Source change — correct and Next.js-aligned
- Canonical markup
<script id="__NEXT_DATA__" type="application/json"…>with HTML-escaped JSON viasafeJsonStringify(escapes</>/&/\u2028/\u2029), so an embedded</script>serializes as\u003c/script\u003e— XSS-safe in the JSON-script context and matches Next.js'shtmlEscapeJsonStringbehavior. - Because the element is
type="application/json"(non-executable) and the hydration entry is an ES module (deferred),document.getElementById("__NEXT_DATA__")resolves it regardless of DOM order relative to the module script. Dev (dev-server.ts) and prod (pages-page-response.ts→buildPagesShellHtml) both inject it, and both client entries (generatedpages-client-entry.ts+ dev inline) parse it and call_initializePagesRouterReadyFromNextData. Dev/prod parity holds. - The new readiness seam is necessary:
routerRuntimeStateis computed at module-eval ofnext/router(shims/router.ts:464→:439shouldDeferInitialPagesRouterReady()), which readswindow.__NEXT_DATA__. Since the canonical element is no longer executable,__NEXT_DATA__is undefined at that point, so the explicit_initializePagesRouterReadyFromNextData(window.__NEXT_DATA__)after parsing correctly re-derives the ready bit. Verified bypages-router.test.tsreadiness tests passing. gspomission (pages-readiness.ts:45→gsp: hasPageGsp ? true : undefined):undefinedis stripped by both the production payload filter andJSON.stringify; the only consumer (shims/router.ts:1171) checksnextData.gsp !== true, so absent vs.falseis behaviorally identical. Correct.- Legacy + canonical dual-parse:
extractVinextNextDataJson(vinext-next-data.ts) tries the canonical lookahead regex first, then the legacy assignment;rewritePagesCachedHtml(pages-page-data.ts:553-558) replaces whichever block is present. Attribute-order-tolerant lookaheads handle the nonced element.
Non-blocking — duplicated locale-globals logic (still present)
entries/pages-client-entry.ts:158-160 and server/dev-server.ts:1575-1577 reimplement locale-global assignment inline and unconditionally (window.__VINEXT_LOCALE__ = window.__NEXT_DATA__.locale, etc.), assigning undefined on non-i18n pages. applyVinextLocaleGlobals() (client/vinext-next-data.ts:133) already assigns each global only when defined and defensively copies locales ([...nextData.locales]); it is used by the navigation path (shims/router.ts:1705, :1882). Routing both init sites through it would keep initial-load and navigation in lockstep and avoid further drift. Functionally equivalent today.
Non-blocking — unconditional readiness call when __NEXT_DATA__ is absent
entries/pages-client-entry.ts:163 calls _initializePagesRouterReadyFromNextData(window.__NEXT_DATA__) unconditionally, even when nextDataElement?.textContent is falsy and window.__NEXT_DATA__ is therefore undefined. initializePagesRouterReadyFromNextData (shims/router.ts:1217) dereferences nextData.page with no guard, so it would throw Cannot read properties of undefined (reading 'page'). The very next function, hydrate(), explicitly guards if (!nextData) { console.error("[vinext] No __NEXT_DATA__ found"); return; } (:167) — so the codebase already anticipates a missing __NEXT_DATA__, but the new readiness call now crashes before that graceful path is reached. This is new code (on main, __NEXT_DATA__ was set by the executable inline script before next/router ran, so no separate call existed). Low severity since a Pages page should always carry __NEXT_DATA__, but consider moving the call inside the if (nextDataElement?.textContent) block (or guarding it) to preserve the existing graceful-degradation behavior. (The dev-server copy at :1581 already dereferences nextData.props unguarded, so it degrades the same way regardless.)
Summary
No blocking issues — the previously-failing entry-templates.test.ts:994 is fixed and the affected suite is green. Source change is correct, XSS-safe, and Next.js-aligned. Two non-blocking nits: consolidate the initial-load locale-globals onto applyVinextLocaleGlobals(), and guard the unconditional _initializePagesRouterReadyFromNextData call against a missing __NEXT_DATA__ so the existing graceful-degradation path in hydrate() is reachable.
|
Review posted to PR #2043. SummaryNo blocking issues. The prior blocking finding ( Source change is correct and Next.js-aligned:
Two non-blocking notes:
|
|
/bigbonk review for issues |
There was a problem hiding this comment.
Review: PR #2043 — canonical __NEXT_DATA__ JSON
Re-reviewed at branch HEAD (748325f fix(pages): guard readiness bootstrap without next data). No blocking issues. The one remaining non-blocking concern from the prior review (unconditional _initializePagesRouterReadyFromNextData call when __NEXT_DATA__ is absent) is now fixed. The affected Vitest suite is green and vp check is clean on all 7 changed source files.
Verification
- Affected suite green — 1854 tests pass. I ran
entry-templates,pages-router(322), plusfeatures,pages-router-concurrency,static-export,pages-page-response,pages-page-data,shims(1532). All pass on this branch. vp checkclean on all 7 changed source files (format, lint, types).
Prior blocking findings — all resolved
entry-templates.test.ts:994stale exact-string import (resolved earlier byf0b2434): the assertion now matches the multi-line three-symbolnext/routerimport shape and asserts_initializePagesRouterReadyFromNextData(nextData);. Passing.- Static
next/routerimport restoration and migrated live-response__NEXT_DATA__extractors: both still in good shape; the relevant tests pass.
Prior non-blocking #2 — now fixed by 748325f
entries/pages-client-entry.ts previously called _initializePagesRouterReadyFromNextData(window.__NEXT_DATA__) unconditionally (would throw Cannot read properties of undefined (reading 'page') when __NEXT_DATA__ is absent, before the graceful if (!nextData) return in hydrate()). The latest commit moves the call inside hydrate() after that guard (pages-client-entry.ts:165-170) and wraps the locale-globals assignment in if (nextDataElement?.textContent) (:156-161). A new focused test (entry-templates.test.ts:1015 "gracefully skips Pages Router initialization without NEXT_DATA") asserts the readiness initializer is not called and only the [vinext] No __NEXT_DATA__ found error is logged. Verified passing. Good fix with coverage.
Source change — correct and Next.js-aligned
- Canonical markup
<script id="__NEXT_DATA__" type="application/json"…>with HTML-escaped JSON viasafeJsonStringify(escapes</>/&/\u2028/\u2029→ embedded</script>serializes as\u003c/script\u003e). XSS-safe in the JSON-script context and matches Next.jshtmlEscapeJsonString(server/html.ts:21). - The readiness seam (
shims/router.ts:1217initializePagesRouterReadyFromNextData) is necessary: the canonical element is non-executable, sowindow.__NEXT_DATA__is undefined atnext/routermodule-eval; the explicit post-parse call re-derives the ready bit. Staticnext/routerimport preserved at module top in both entries, so the popstate-race concern stays closed. gspomission (pages-readiness.ts:45→gsp: hasPageGsp ? true : undefined):undefinedis stripped by both the production payload filter andJSON.stringify; the only consumer (shims/router.ts:1171) checksnextData.gsp !== true, so absent vs.falseis behaviorally identical. Matches Next.js (which does not serializegsp: false).- Dual canonical/legacy parse:
extractVinextNextDataJson(client/vinext-next-data.ts:57) tries the attribute-order-tolerant canonical lookahead first, then a balanced-brace scan of the legacy assignment;rewritePagesCachedHtml(pages-page-data.ts:553-558) replaces whichever block is present. Nonce handled viacreateNonceAttribute. - Dev (
dev-server.ts) and prod (pages-page-response.ts:293→buildPagesShellHtml) both emit the canonical element and both client entries parse it before hydration. Dev/prod parity holds.
Remaining non-blocking note (carried over)
The initial-load locale-globals are still assigned inline and unconditionally in entries/pages-client-entry.ts:158-160 and server/dev-server.ts:1575-1577 (window.__VINEXT_LOCALE__ = window.__NEXT_DATA__.locale, etc., assigning undefined on non-i18n pages), rather than reusing applyVinextLocaleGlobals() (client/vinext-next-data.ts:133), which sets each only when defined and defensively copies locales. The navigation path already uses the helper (shims/router.ts:1705, :1882). Functionally equivalent today, but routing both init sites through the helper would keep initial-load and navigation in lockstep and avoid further drift. Optional cleanup, not a blocker.
(Note: the dev-server inline script at dev-server.ts:1580-1581 still dereferences nextData unguarded, but the dev path always injects __NEXT_DATA__ immediately above the hydration script, so it is not reachable in practice — same as before this PR.)
Summary
No blocking issues. The previously-noted unconditional readiness call is fixed with a dedicated test; source change is correct, XSS-safe, Next.js-aligned, and the affected Vitest suite + vp check are green. The single remaining nit (consolidate initial-load locale-globals onto applyVinextLocaleGlobals) is optional.
|
Review posted to PR #2043. SummaryNo blocking issues. I re-reviewed at branch HEAD ( Verification:
Prior non-blocking finding now fixed by Source change is correct and Next.js-aligned: canonical One optional non-blocking nit carried over: initial-load locale-globals are still assigned inline/unconditionally in |
Prerender canonical
__NEXT_DATA__paritySource and scope
27514800656,test/e2e/prerender.test.ts(20 failed records).origin/mainata3d2f921520ff140a826224616df5e0db4ed0186.ee6e79b1792a4d401ddf2480f40a83549fe8e722.<script>window.__NEXT_DATA__ = ...</script>rather than Next.js-compatible<script id="__NEXT_DATA__" type="application/json">...</script>.gspwhen the page does not exportgetStaticProps; Next.js does not serializegsp: false._next/data, fallback rewrites, caching headers, invalid JSON behavior, navigation failures, no-revalidate behavior, preview/on-demand ISR, and all PR fix(pages): align on-demand ISR regeneration semantics #2027 scope.Original failure classification
The cohesive cluster contained eight assertions:
should SSR incremental page correctlyshould SSR blocking path correctly (blocking)should SSR blocking path correctly (pre-rendered)should have gsp in __NEXT_DATA__should not have gsp in __NEXT_DATA__ for non-GSP pageshould support prerendered catchall routeshould support prerendered catchall-explicit route (nested)should support prerendered catchall-explicit route (single)Seven initially failed with
Unexpected end of JSON inputbecause the upstream tests readscript#__NEXT_DATA__.text(). After canonical markup, the eighth reached its real payload assertion and showed that vinext serializedgsp: falseinstead of omitting the field.The other 12 records were classified as separate product gaps or cascade symptoms and were not changed in this work.
Implementation
__NEXT_DATA__markup in Pages dev and production responses, including nonce support.window.__NEXT_DATA__and locale globals explicitly from the JSON element before hydration.__NEXT_DATA__blocks during cached Pages regeneration.gspwhen false while preservinggsp: truereadiness behavior.Local validation
Passed:
tests/shims.test.ts: 3 targeted tests.tests/pages-page-response.test.ts: 3 targeted tests.tests/pages-page-data.test.ts: 9 targeted tests.tests/entry-templates.test.ts: 27 tests.tests/pages-router.test.ts: 8 targeted integration tests.vp check: formatting, lint, and type checks passed for all nine changed source/test files.git diff --checkpassed.Independent review found four stale CSP integration assertions that still expected the former executable assignment markup. They now assert the canonical nonced JSON element while preserving the existing cache, preload, and content checks.
Review-fix validation passed:
tests/pages-router.test.ts: 4 exact CSP/nonced__NEXT_DATA__tests.tests/shims.test.ts: 2vinext next data client helperstests.tests/pages-page-response.test.ts: 3 targeted nonce/cache/HTML recording tests.tests/pages-page-data.test.ts: 43 tests.tests/entry-templates.test.ts: 27 tests.vp check tests/pages-router.test.tspassed.git diff --checkpassed.The initial frozen
vp installpopulated dependencies and built the core packages; an unrelated Fumadocs example postinstall failed once. A subsequent wrapper install completed successfully.Exact Next.js deploy validation
Command:
NEXTJS_PREPARE=0 NEXT_TEST_CONCURRENCY=1 \ vp env exec --node 24 \ ./scripts/run-nextjs-deploy-suite.sh \ /Users/jamesanderson/Developer/vinext/.nextjs-ref \ --retries 0 -c 1 --debug \ test/e2e/prerender.test.tsThe wrapper does not forward a Jest name filter, so the single targeted file ran in full. Deployment succeeded and assertions were reached.
Scoped final results:
should SSR incremental page correctlyshould SSR blocking path correctly (blocking)should SSR blocking path correctly (pre-rendered)should have gsp in __NEXT_DATA__should not have gsp in __NEXT_DATA__ for non-GSP pageshould support prerendered catchall routeshould support prerendered catchall-explicit route (nested)should support prerendered catchall-explicit route (single)The full file remains red only for out-of-scope query, rewrite,
_next/data, caching, navigation, invalid-JSON, and ISR assertions.