mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
bafbcd4d75
Server-rendered user modules can currently observe a partially populated browser global when the runtime provides one. That violates Next.js SSR semantics, where server code must see browser globals as absent, and it breaks common typeof window guards during module evaluation. Add a shared server globals bootstrap that removes window and document before user modules load, preserves the existing AsyncLocalStorage edge-runtime global, and import it ahead of generated App and Pages Router user imports. Cover the runtime behavior and generated import order with focused regression tests.
51 lines
1.7 KiB
TypeScript
51 lines
1.7 KiB
TypeScript
import { afterAll, beforeAll, describe, expect, it } from "vite-plus/test";
|
|
import type { ViteDevServer } from "vite-plus";
|
|
|
|
import { APP_FIXTURE_DIR, fetchHtml, startFixtureServer } from "./helpers.js";
|
|
|
|
type BrowserGlobalName = "window" | "document";
|
|
|
|
function definePartialBrowserGlobal(name: BrowserGlobalName, value: unknown): void {
|
|
Object.defineProperty(globalThis, name, {
|
|
configurable: true,
|
|
value,
|
|
writable: true,
|
|
});
|
|
}
|
|
|
|
describe("server globals", () => {
|
|
let server: ViteDevServer;
|
|
let baseUrl: string;
|
|
let originalWindow: PropertyDescriptor | undefined;
|
|
let originalDocument: PropertyDescriptor | undefined;
|
|
|
|
beforeAll(async () => {
|
|
originalWindow = Object.getOwnPropertyDescriptor(globalThis, "window");
|
|
originalDocument = Object.getOwnPropertyDescriptor(globalThis, "document");
|
|
|
|
definePartialBrowserGlobal("window", { getComputedStyle: undefined, history: undefined });
|
|
definePartialBrowserGlobal("document", { documentElement: {} });
|
|
|
|
({ server, baseUrl } = await startFixtureServer(APP_FIXTURE_DIR, {
|
|
appRouter: true,
|
|
}));
|
|
}, 60_000);
|
|
|
|
afterAll(async () => {
|
|
await server?.close();
|
|
|
|
if (originalWindow) Object.defineProperty(globalThis, "window", originalWindow);
|
|
else Reflect.deleteProperty(globalThis, "window");
|
|
|
|
if (originalDocument) Object.defineProperty(globalThis, "document", originalDocument);
|
|
else Reflect.deleteProperty(globalThis, "document");
|
|
});
|
|
|
|
it("clears browser globals before App Router user modules evaluate in dev", async () => {
|
|
const { res, html } = await fetchHtml(baseUrl, "/nextjs-compat/typeof-window-ssr");
|
|
|
|
expect(res.status).toBe(200);
|
|
expect(html).toContain("server globals ok");
|
|
}, 30_000);
|
|
});
|