From 118a18711ec8fdb26e35ba7da308a24292ce7466 Mon Sep 17 00:00:00 2001 From: Robin Genz Date: Sat, 17 Oct 2026 17:46:26 +0200 Subject: [PATCH 1/4] [vitest-plugin] pass the import kind to Vite so require() picks the require condition --- .changeset/vitest-plugin-require-condition.md | 5 +++ .../vitest-plugin/src/pool/module-fallback.ts | 16 ++++--- .../vitest-plugin/test/cjs-require.test.ts | 45 +++++++++++++++++++ .../test/module-fallback.test.ts | 35 ++++++++++----- 4 files changed, 85 insertions(+), 16 deletions(-) create mode 100644 .changeset/vitest-plugin-require-condition.md diff --git a/.changeset/vitest-plugin-require-condition.md b/.changeset/vitest-plugin-require-condition.md new file mode 100644 index 00000000000..aeaea68f813 --- /dev/null +++ b/.changeset/vitest-plugin-require-condition.md @@ -0,0 +1,5 @@ +--- +"@cloudflare/vitest-plugin": patch +--- + +Pass the import kind to Vite in the module fallback so a `require()` picks the `require` export condition on Vite 8 diff --git a/packages/vitest-plugin/src/pool/module-fallback.ts b/packages/vitest-plugin/src/pool/module-fallback.ts index 641a286d9f4..002313436fa 100644 --- a/packages/vitest-plugin/src/pool/module-fallback.ts +++ b/packages/vitest-plugin/src/pool/module-fallback.ts @@ -328,11 +328,17 @@ async function viteResolve( referrer: string, isRequire: boolean ): Promise { - const resolved = await vite.pluginContainer.resolveId(specifier, referrer, { - ssr: true, - // https://github.com/vitejs/vite/blob/v5.1.4/packages/vite/src/node/plugins/resolve.ts#L178-L179 - custom: { "node-resolve": { isRequire } }, - }); + const resolved = await vite.environments.ssr.pluginContainer.resolveId( + specifier, + referrer, + { + // Vite 8 picks the `require` or `import` export condition from `kind` + kind: isRequire ? "require-call" : "import-statement", + // Vite 7 and earlier read `isRequire` from here instead + // https://github.com/vitejs/vite/blob/v5.1.4/packages/vite/src/node/plugins/resolve.ts#L178-L179 + custom: { "node-resolve": { isRequire } }, + } + ); if (resolved === null) { // Vite's resolution algorithm doesn't apply Node resolution to specifiers // starting with a dot. Unfortunately, the `@prisma/client` package includes diff --git a/packages/vitest-plugin/test/cjs-require.test.ts b/packages/vitest-plugin/test/cjs-require.test.ts index 6a9d50748a7..58593ca550b 100644 --- a/packages/vitest-plugin/test/cjs-require.test.ts +++ b/packages/vitest-plugin/test/cjs-require.test.ts @@ -32,3 +32,48 @@ test( expect(await result.exitCode).toBe(0); } ); + +test( + "resolves requires from CJS modules to the require export condition", + { timeout: 45_000 }, + async ({ expect, seed, vitestRun }) => { + await seed({ + "vitest.config.mts": vitestConfig(), + "node_modules/cjs-demo/package.json": JSON.stringify({ + name: "cjs-demo", + version: "1.0.0", + main: "index.cjs", + }), + "node_modules/cjs-demo/index.cjs": dedent` + module.exports = require("dual-demo"); + `, + "node_modules/dual-demo/package.json": JSON.stringify({ + name: "dual-demo", + version: "1.0.0", + exports: { + ".": { + import: "./index.mjs", + require: "./index.cjs", + }, + }, + }), + "node_modules/dual-demo/index.mjs": dedent` + export const format = "esm"; + `, + "node_modules/dual-demo/index.cjs": dedent` + module.exports = { format: "cjs" }; + `, + "index.test.ts": dedent` + import { expect, it } from "vitest"; + import demo from "cjs-demo"; + + it("loads the CJS entry of a dual-format package", () => { + expect(demo.format).toBe("cjs"); + }); + `, + }); + + const result = await vitestRun(); + expect(await result.exitCode).toBe(0); + } +); diff --git a/packages/vitest-plugin/test/module-fallback.test.ts b/packages/vitest-plugin/test/module-fallback.test.ts index 5e87ceb4837..29ef789a5ce 100644 --- a/packages/vitest-plugin/test/module-fallback.test.ts +++ b/packages/vitest-plugin/test/module-fallback.test.ts @@ -17,13 +17,18 @@ import { } from "../src/shared/module-path"; import type { Vite } from "vitest/node"; -// The fallback handler only reads `vite.pluginContainer.resolveId`, and only -// when a specifier can't be resolved directly from the filesystem. Returning -// `null` mimics Vite failing to resolve, exercising the 404 fall-through. +// The fallback handler only reads +// `vite.environments.ssr.pluginContainer.resolveId`, and only when a specifier +// can't be resolved directly from the filesystem. Returning `null` mimics Vite +// failing to resolve, exercising the 404 fall-through. function fakeVite(): Vite.ViteDevServer { return { - pluginContainer: { - resolveId: async () => null, + environments: { + ssr: { + pluginContainer: { + resolveId: async () => null, + }, + }, }, } as unknown as Vite.ViteDevServer; } @@ -37,8 +42,12 @@ function fakeVite(): Vite.ViteDevServer { // same rooted path that branch would have produced reaches the same place. function fakeViteResolvingTo(id: string): Vite.ViteDevServer { return { - pluginContainer: { - resolveId: async () => ({ id }), + environments: { + ssr: { + pluginContainer: { + resolveId: async () => ({ id }), + }, + }, }, } as unknown as Vite.ViteDevServer; } @@ -48,10 +57,14 @@ function fakeViteResolving( resolvedIds: Record ): Vite.ViteDevServer { return { - pluginContainer: { - resolveId: async (specifier: string) => { - const id = resolvedIds[specifier]; - return id === undefined ? null : { id }; + environments: { + ssr: { + pluginContainer: { + resolveId: async (specifier: string) => { + const id = resolvedIds[specifier]; + return id === undefined ? null : { id }; + }, + }, }, }, } as unknown as Vite.ViteDevServer; From 5e72806f5e0cb1fb95de9550a1c619027cde338d Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Fri, 23 Oct 2026 14:29:20 +0100 Subject: [PATCH 2/4] Apply suggestion from @devin-ai-integration[bot] Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .changeset/vitest-plugin-require-condition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/vitest-plugin-require-condition.md b/.changeset/vitest-plugin-require-condition.md index aeaea68f813..3f7760afb2a 100644 --- a/.changeset/vitest-plugin-require-condition.md +++ b/.changeset/vitest-plugin-require-condition.md @@ -2,4 +2,4 @@ "@cloudflare/vitest-plugin": patch --- -Pass the import kind to Vite in the module fallback so a `require()` picks the `require` export condition on Vite 8 +Fix Worker tests failing when CommonJS dependencies require packages with separate `import` and `require` entries on Vite 8. From 444c44b170110d56ce5be4033dad33ad70336e28 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Fri, 23 Oct 2026 18:34:07 +0100 Subject: [PATCH 3/4] Update packages/vitest-plugin/src/pool/module-fallback.ts --- packages/vitest-plugin/src/pool/module-fallback.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vitest-plugin/src/pool/module-fallback.ts b/packages/vitest-plugin/src/pool/module-fallback.ts index 002313436fa..e39875f7fda 100644 --- a/packages/vitest-plugin/src/pool/module-fallback.ts +++ b/packages/vitest-plugin/src/pool/module-fallback.ts @@ -332,7 +332,7 @@ async function viteResolve( specifier, referrer, { - // Vite 8 picks the `require` or `import` export condition from `kind` + // Vite 8.2.1, and later, picks the `require` or `import` export condition from `kind` kind: isRequire ? "require-call" : "import-statement", // Vite 7 and earlier read `isRequire` from here instead // https://github.com/vitejs/vite/blob/v5.1.4/packages/vite/src/node/plugins/resolve.ts#L178-L179 From 9c44bf8f84b0b02dd5926fd1c8a6a8e17dfee862 Mon Sep 17 00:00:00 2001 From: Robin Genz Date: Sat, 24 Oct 2026 09:19:34 +0200 Subject: [PATCH 4/4] Mention the Vite versions the fix needs in the changeset --- .changeset/vitest-plugin-require-condition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/vitest-plugin-require-condition.md b/.changeset/vitest-plugin-require-condition.md index 3f7760afb2a..d5f5c0ba3d7 100644 --- a/.changeset/vitest-plugin-require-condition.md +++ b/.changeset/vitest-plugin-require-condition.md @@ -2,4 +2,4 @@ "@cloudflare/vitest-plugin": patch --- -Fix Worker tests failing when CommonJS dependencies require packages with separate `import` and `require` entries on Vite 8. +Fix Worker tests failing when CommonJS dependencies require packages with separate `import` and `require` entries on Vite 8. The fix needs Vite 8.2.1 or later; Vite 8.0.x, 8.1.x and 8.2.0 stay affected.