Files
cloudflare__vinext/tests/cjs.test.ts
T
Zeb Piasecki bcd4bb14cf feat: add CJS support via vite-plugin-commonjs (#198)
* feat: add CJS support via vite-plugin-commonjs

Add vite-plugin-commonjs to handle require() and module.exports in
both App Router and Pages Router. This enables CJS interop in server
components and pages, including CJS require('server-only') boundary
enforcement by @vitejs/plugin-rsc.

- Add vite-plugin-commonjs as a dependency and register it in the plugin array
- Add App Router fixtures: CJS basic, CJS server-only, CJS server-only
  violation (client component importing server-only module)
- Add Pages Router fixture: CJS basic (require/module.exports)
- Add integration tests for both routers (cjs.test.ts)
- Add App Router e2e tests for CJS rendering and server-only boundary
  enforcement (tests/e2e/app-router/cjs.spec.ts)
- Add Pages Router e2e test for CJS rendering
  (tests/e2e/pages-router/cjs.spec.ts)

* fix: use buildViteConfig() for Pages Router production builds

The Pages Router build path hardcoded plugins: [vinext()] in inline
config, duplicating plugins when a vite.config.ts also registers
vinext(). This caused double-transformation errors during production
builds. Use buildViteConfig() instead, matching the App Router path
which already handles this correctly.

* fix: move server-only-violation fixture to isolated directory

The server-only-violation fixture causes a fatal build error during
production builds of app-basic (the RSC plugin rejects the intentional
violation). This breaks unrelated tests in app-router.test.ts.

Move the fixture to tests/fixtures/app-cjs-violation/ so it doesn't
poison the shared app-basic production build. Remove the E2E test case
since it requires the app-basic dev server; the server-only boundary
enforcement for CJS require() is verified by the RSC plugin's build
validation.

* fix: address review feedback from bonk

- Remove unused components/random.tsx (dead code, Pages Router CJS test
  uses pages/cjs/random.ts instead)
- Add trailing newlines to fixture files
- Move commonjs() plugin earlier in array (after tsconfigPaths, before
  vinext:config) so CJS->ESM transform runs before directive scanning
- Tighten test assertions: use regex for 'Random:.*4' to be specific
  while handling React SSR comment nodes between text and expressions

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
2026-02-28 15:23:21 -06:00

60 lines
1.8 KiB
TypeScript

import { describe, it, expect, beforeAll, afterAll } from "vitest";
import type { ViteDevServer } from "vite";
import {
APP_FIXTURE_DIR,
PAGES_FIXTURE_DIR,
startFixtureServer,
fetchHtml,
} from "./helpers.js";
describe("CJS interop (App Router)", () => {
let server: ViteDevServer;
let baseUrl: string;
beforeAll(async () => {
({ server, baseUrl } = await startFixtureServer(APP_FIXTURE_DIR, { appRouter: true }));
}, 30000);
afterAll(async () => {
await server?.close();
});
it("renders page that uses CJS require() and module.exports", async () => {
const { res, html } = await fetchHtml(baseUrl, "/cjs/basic");
expect(res.status).toBe(200);
expect(html).toContain("cjs-basic");
// React SSR may insert comment nodes between text and expressions
// (e.g. "Random: <!-- -->4"), so use a regex.
expect(html).toMatch(/Random:.*4/);
});
it("renders page that uses CJS require('server-only')", async () => {
const { res, html } = await fetchHtml(baseUrl, "/cjs/server-only");
expect(res.status).toBe(200);
expect(html).toContain("cjs-server-only");
expect(html).toContain("This page uses CJS require");
});
});
describe("CJS interop (Pages Router)", () => {
let server: ViteDevServer;
let baseUrl: string;
beforeAll(async () => {
({ server, baseUrl } = await startFixtureServer(PAGES_FIXTURE_DIR));
}, 30000);
afterAll(async () => {
await server?.close();
});
it("renders page that uses CJS require() and module.exports", async () => {
const { res, html } = await fetchHtml(baseUrl, "/cjs/basic");
expect(res.status).toBe(200);
expect(html).toContain("cjs-basic");
// Pages Router SSR inserts React comment nodes between text and
// expressions (e.g. "Random: <!-- -->4"), so use a regex.
expect(html).toMatch(/Random:.*4/);
});
});