mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
763736eae0
* refactor(app-rsc-entry): delegate pure helpers to typed server modules The generated RSC entry template contained five inline implementations of pure runtime helpers: post-middleware request context construction, RSC error handler creation, request context cleanup, RSC stream preload hint normalization, and hook warning suppression. These inline blocks repeated runtime behavior across multiple call sites and could not be unit-tested independently. Move each helper to a dedicated typed module under server/ so the generated entry imports and wires them instead of owning the logic: - buildPostMwRequestContext → server/app-post-middleware-context.ts - createRscOnErrorHandler → server/app-rsc-error-handler.ts - __clearRequestContext + setNavigationContext → server/app-request-context.ts - renderToReadableStream preload hints → server/rsc-stream-hints.ts (createRscRenderer) - hook warning console.error patch → server/app-hook-warning-suppression.ts Each helper module has focused unit tests. The entry template is 55 lines shorter and now describes the app shape; the typed modules own behaviour. * fix: remove redundant as cast and fix test module caching app-hook-warning-suppression.ts contained an `as string` cast that was redundant because the typeof check on the prior line already narrows the type. The hook warning suppression tests in tests 2-4 were passing vacuously because dynamic import() returned the cached module whose console.error patch was no longer active after test 1 restored the original. Use vi.resetModules() before each test to force fresh module evaluation. clearAppRequestContext now delegates to setAppNavigationContext(null) instead of calling the raw navigation and root-params setters directly, matching the DRY pattern from the original inline code. * fix: resolve entry helper rebase conflicts * ci: rerun checks (font-google flake) * Update packages/vinext/src/entries/app-rsc-entry.ts Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com> --------- Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
102 lines
3.0 KiB
TypeScript
102 lines
3.0 KiB
TypeScript
import { describe, expect, it, afterEach } from "vite-plus/test";
|
|
import {
|
|
clearAppRequestContext,
|
|
setAppNavigationContext,
|
|
} from "../packages/vinext/src/server/app-request-context.js";
|
|
import { setHeadersContext, getHeadersContext } from "../packages/vinext/src/shims/headers.js";
|
|
import { getNavigationContext } from "../packages/vinext/src/shims/navigation.js";
|
|
import { getRootParam } from "../packages/vinext/src/shims/root-params.js";
|
|
|
|
describe("clearAppRequestContext", () => {
|
|
afterEach(() => {
|
|
// Ensure clean state for next test.
|
|
clearAppRequestContext();
|
|
});
|
|
|
|
it("nulls out all three per-request stores: headers, navigation, root params", () => {
|
|
// Seed all stores with non-null values.
|
|
setHeadersContext({
|
|
headers: new Headers({ host: "example.com" }),
|
|
cookies: new Map(),
|
|
});
|
|
setAppNavigationContext({
|
|
pathname: "/test",
|
|
searchParams: new URLSearchParams("q=1"),
|
|
params: { id: "42" },
|
|
});
|
|
|
|
clearAppRequestContext();
|
|
|
|
expect(getHeadersContext()).toBeNull();
|
|
expect(getNavigationContext()).toBeNull();
|
|
// Root params are cleared synchronously, so getRootParam resolves to undefined.
|
|
return getRootParam("id").then((val) => {
|
|
expect(val).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("is idempotent — multiple calls do not throw", () => {
|
|
for (let i = 0; i < 3; i++) {
|
|
clearAppRequestContext();
|
|
}
|
|
expect(getHeadersContext()).toBeNull();
|
|
expect(getNavigationContext()).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("setAppNavigationContext", () => {
|
|
afterEach(() => {
|
|
clearAppRequestContext();
|
|
});
|
|
|
|
it("stores pathname, searchParams, and params so getNavigationContext can read them", () => {
|
|
const searchParams = new URLSearchParams("page=2");
|
|
setAppNavigationContext({
|
|
pathname: "/blog",
|
|
searchParams,
|
|
params: { slug: "hello", id: "42" },
|
|
});
|
|
|
|
const ctx = getNavigationContext();
|
|
expect(ctx).not.toBeNull();
|
|
expect(ctx!.pathname).toBe("/blog");
|
|
expect(ctx!.searchParams.get("page")).toBe("2");
|
|
expect(ctx!.params).toEqual({ slug: "hello", id: "42" });
|
|
});
|
|
|
|
it("clears both navigation context and root params when ctx is null", () => {
|
|
// Seed state.
|
|
setAppNavigationContext({
|
|
pathname: "/docs",
|
|
searchParams: new URLSearchParams(),
|
|
params: { slug: "readme" },
|
|
});
|
|
|
|
setAppNavigationContext(null);
|
|
|
|
expect(getNavigationContext()).toBeNull();
|
|
return getRootParam("slug").then((val) => {
|
|
expect(val).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("clearing navigation does not affect the headers context", () => {
|
|
setHeadersContext({
|
|
headers: new Headers({ host: "example.com" }),
|
|
cookies: new Map([["a", "1"]]),
|
|
});
|
|
setAppNavigationContext({
|
|
pathname: "/a",
|
|
searchParams: new URLSearchParams(),
|
|
params: {},
|
|
});
|
|
|
|
setAppNavigationContext(null);
|
|
|
|
// Headers should survive the navigation clear — they're independent stores.
|
|
const h = getHeadersContext();
|
|
expect(h).not.toBeNull();
|
|
expect(h!.headers.get("host")).toBe("example.com");
|
|
});
|
|
});
|