refactor: extract pages page response helpers#657
Conversation
|
/bigbonk |
commit: |
|
There was a problem hiding this comment.
Good extraction — the generated entry is noticeably thinner and the new helper is well-typed. A few things to address before merging:
Behavioral change in applyGsspHeaders — non-primitive header values silently dropped
pages-page-response.ts:200-202
The old inline code accepted any non-null header value via String(hv):
} else if (hv != null) {
responseHeaders.set(hk, String(hv));
}The new code only accepts string | number | boolean:
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
headers.set(key, String(value));
}This silently drops non-set-cookie array values. Node.js res.setHeader() accepts readonly string[] for multi-value headers — e.g., res.setHeader("Vary", ["Accept", "Accept-Encoding"]) is idiomatic Node. The res facade's getHeaders() returns these as-is (raw arrays), so the old code would have set "Accept,Accept-Encoding" (via String()), while the new code drops it entirely.
I agree the old behavior was sloppy (especially the "[object Object]" case for plain objects), but the string[] case is legitimate Node.js API usage. Consider adding array handling:
if (Array.isArray(value)) {
headers.set(key, value.map(String).join(", "));
} else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
headers.set(key, String(value));
}Or at minimum, match the old String() coercion behavior (which handles arrays via Array.prototype.toString() → join(",")) to avoid a silent parity break.
Test coverage gap: no test for array header values from gSSP
The new test at tests/pages-page-response.test.ts:81 only covers scalar gSSP header values ("content-type": "application/json", "x-test": "1"). Add a test case with an array-valued non-set-cookie header to cover the gap mentioned above and lock in whatever behavior you choose:
gsspRes: {
statusCode: 200,
getHeaders() {
return {
"vary": ["Accept", "Accept-Encoding"],
"set-cookie": ["a=1", "b=2"],
"x-custom": 42,
};
},
},Nit: PagesGsspResponse.getHeaders return type is too loose
pages-page-response.ts:17-18
export interface PagesGsspResponse {
statusCode: number;
getHeaders(): Record<string, unknown>;
}The unknown value type means applyGsspHeaders can't safely iterate values without narrowing. Consider Record<string, string | number | boolean | string[]> to match the actual possible values from the res facade, which would also make the type guard in applyGsspHeaders feel less arbitrary.
Overall
The extraction itself is clean and well-structured. The helper boundary (options bag with callbacks for rendering, ISR, fonts) is a good seam. The test file covers the key paths (document shell, ISR cache fill, gSSP header merge, streamed response marking). Just need to fix the header coercion behavioral change and add the corresponding test case.
|
Review posted on PR #657. Here's a summary of the findings: One behavioral change that needs fixing:
One test gap:
One typing nit:
Otherwise, the extraction is clean — the helper boundary is well-designed with an options bag of callbacks, the generated entry is substantially thinner, and the existing test coverage for document shell, ISR cache fill, and streamed response marking is solid. |
|
Addressed the requested changes in What changed:
Local verification:
/bigbonk |
There was a problem hiding this comment.
All three items from the previous review are addressed:
-
Array header coercion fixed —
applyGsspHeadersnow handlesArray.isArray(value)withvalue.join(", ")before the primitive check, matching Node.jsres.setHeader("Vary", ["Accept", "Accept-Encoding"])semantics correctly. -
Test added — "preserves array-valued non-set-cookie headers from gSSP responses" covers the array-valued Vary, numeric x-custom, and array set-cookie cases.
-
Type tightened —
PagesGsspResponse.getHeaders()returnsRecord<string, string | number | boolean | string[]>instead ofRecord<string, unknown>.
The extraction itself is clean. The helper boundary (options bag with callbacks for rendering, ISR, fonts) is a good seam that makes the generated entry substantially thinner while keeping all behavior testable in isolation. The three test cases cover the key paths: document shell with gSSP headers, array header coercion, and ISR cache fill with query-string stripping.
One minor observation (not blocking): applyGsspHeaders uses key.toLowerCase() only for the set-cookie check but passes the original key to headers.set() for array values. This is fine because Headers.set() is case-insensitive per the Fetch spec, but it's a slight asymmetry with the set-cookie branch. Not worth changing.
LGTM.
|
Review posted. All three items from the prior review (array header coercion, test coverage for array headers, tighter |
* refactor: extract pages page response helpers * fix: preserve array-valued pages gssp headers
Summary
This PR starts the Pages Router side of the generated-entry refactor by extracting the page HTML shell / stream / response construction out of
pages-server-entry.tsand into a typed runtime helper:packages/vinext/src/server/pages-page-response.ts__NEXT_DATA__serialization, streamed HTML response shaping, and ISR cache fill HTML assembly into that helperpackages/vinext/src/entries/pages-server-entry.tsas thinner route/data wiringtests/pages-page-response.test.tsThis PR was written by Codex.
Why
After the App Router extraction series landed,
pages-server-entry.tsis the next major generated module that still owns too much real runtime behavior. This is the first Pages Router slice: pull the HTML render/response lifecycle into normal typed code, then follow with the data/ISR and API/runtime seams on top of that.Testing
vp check packages/vinext/src/server/pages-page-response.ts packages/vinext/src/entries/pages-server-entry.ts tests/pages-page-response.test.ts tests/entry-templates.test.tsvp test run tests/pages-page-response.test.ts tests/entry-templates.test.ts -t 'pages page response|server entry delegates Pages HTML stream/response shaping to a typed helper'vp test run tests/pages-router.test.ts -t 'getServerSideProps headers and status are applied to the response|getServerSideProps calling res.end\(\) short-circuits the response|renders Suspense \+ React.lazy content via streaming SSR|Production Pages Router SSR streaming'vp test run tests/entry-templates.test.ts -uvp run vinext#build