From 0b1feb183030bf86e768e738426fa1ffa2c08eb7 Mon Sep 17 00:00:00 2001 From: Erlei Xu Date: Mon, 2 Feb 2026 00:29:19 -0800 Subject: [PATCH 1/6] add a basic test to reproduce the issue #1087 reproduced call stack: ```text [examples/basic:dev] 11:58:49 PM [vite] Internal server error: Class extends value [object Module] is not a constructor or null at eval (...\vite-plugin-react\node_modules\.pnpm\@vitejs+test-dep-cjs-events_287fa3db95bd309409d973813ed1549a\node_modules\@vitejs\test-dep-cjs-events-extend\index.js:3:34) at async ESModulesEvaluator.runInlinedModule (.../vite-plugin-react/node_modules/.pnpm/vite@7.3.1_@types+node@24.1_8836aae8899929c20df59ea37bacc8d4/node_modules/vite/dist/node/module-runner.js:913:3) at async ModuleRunner.directRequest (.../vite-plugin-react/node_modules/.pnpm/vite@7.3.1_@types+node@24.1_8836aae8899929c20df59ea37bacc8d4/node_modules/vite/dist/node/module-runner.js:1146:59) at async ModuleRunner.cachedRequest (.../vite-plugin-react/node_modules/.pnpm/vite@7.3.1_@types+node@24.1_8836aae8899929c20df59ea37bacc8d4/node_modules/vite/dist/node/module-runner.js:1053:73) at async eval (...\vite-plugin-react\packages\plugin-rsc\examples\basic\src\routes\cjs-builtin-interop\server.tsx:1:1) at async ESModulesEvaluator.runInlinedModule (.../vite-plugin-react/node_modules/.pnpm/vite@7.3.1_@types+node@24.1_8836aae8899929c20df59ea37bacc8d4/node_modules/vite/dist/node/module-runner.js:913:3) at async ModuleRunner.directRequest (.../vite-plugin-react/node_modules/.pnpm/vite@7.3.1_@types+node@24.1_8836aae8899929c20df59ea37bacc8d4/node_modules/vite/dist/node/module-runner.js:1146:59) at async ModuleRunner.cachedRequest (.../vite-plugin-react/node_modules/.pnpm/vite@7.3.1_@types+node@24.1_8836aae8899929c20df59ea37bacc8d4/node_modules/vite/dist/node/module-runner.js:1053:73) at async eval (...\vite-plugin-react\packages\plugin-rsc\examples\basic\src\routes\root.tsx:59:1) at async ESModulesEvaluator.runInlinedModule (.../vite-plugin-react/node_modules/.pnpm/vite@7.3.1_@types+node@24.1_8836aae8899929c20df59ea37bacc8d4/node_modules/vite/dist/node/module-runner.js:913:3) ``` --- packages/plugin-rsc/e2e/basic.test.ts | 8 ++++++++ packages/plugin-rsc/examples/basic/package.json | 1 + .../src/routes/cjs-builtin-interop/server.tsx | 17 +++++++++++++++++ .../examples/basic/src/routes/root.tsx | 2 ++ .../basic/test-dep/cjs-events-extend/index.d.ts | 7 +++++++ .../basic/test-dep/cjs-events-extend/index.js | 15 +++++++++++++++ .../test-dep/cjs-events-extend/package.json | 8 ++++++++ .../plugin-rsc/examples/basic/vite.config.ts | 5 +++++ pnpm-lock.yaml | 8 ++++++++ 9 files changed, 71 insertions(+) create mode 100644 packages/plugin-rsc/examples/basic/src/routes/cjs-builtin-interop/server.tsx create mode 100644 packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.d.ts create mode 100644 packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.js create mode 100644 packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/package.json diff --git a/packages/plugin-rsc/e2e/basic.test.ts b/packages/plugin-rsc/e2e/basic.test.ts index 76493a80f..3c3163617 100644 --- a/packages/plugin-rsc/e2e/basic.test.ts +++ b/packages/plugin-rsc/e2e/basic.test.ts @@ -1523,6 +1523,14 @@ function defineTest(f: Fixture) { ).toHaveText('ok:browser') }) + test('cjs builtin interop', async ({ page }) => { + await page.goto(f.url()) + await waitForHydration(page) + await expect(page.getByTestId('cjs-builtin-interop')).toHaveText( + 'cjs-builtin-interop: working', + ) + }) + test('use cache function', async ({ page }) => { await page.goto(f.url()) await waitForHydration(page) diff --git a/packages/plugin-rsc/examples/basic/package.json b/packages/plugin-rsc/examples/basic/package.json index c8d432086..dbcada2df 100644 --- a/packages/plugin-rsc/examples/basic/package.json +++ b/packages/plugin-rsc/examples/basic/package.json @@ -21,6 +21,7 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "latest", "@vitejs/plugin-rsc": "latest", + "@vitejs/test-dep-cjs-events-extend": "file:./test-dep/cjs-events-extend", "@vitejs/test-dep-client-in-server": "file:./test-dep/client-in-server", "@vitejs/test-dep-client-in-server2": "file:./test-dep/client-in-server2", "@vitejs/test-dep-css-in-server": "file:./test-dep/css-in-server", diff --git a/packages/plugin-rsc/examples/basic/src/routes/cjs-builtin-interop/server.tsx b/packages/plugin-rsc/examples/basic/src/routes/cjs-builtin-interop/server.tsx new file mode 100644 index 000000000..dc15f86a5 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/cjs-builtin-interop/server.tsx @@ -0,0 +1,17 @@ +import { CustomEventEmitter } from '@vitejs/test-dep-cjs-events-extend' + +export function TestCjsBuiltinInterop() { + let result = 'not-working' + + try { + // console.trace('CjsBuiltinInterop component rendering'); + const emitter = new CustomEventEmitter() + result = emitter.getTestValue() + } catch (error) { + result = 'error: ' + (error as Error).message + } + + return ( +
cjs-builtin-interop: {result}
+ ) +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/root.tsx b/packages/plugin-rsc/examples/basic/src/routes/root.tsx index 47415dcb7..d1a00c305 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/root.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/root.tsx @@ -20,6 +20,7 @@ import { TestAssetsServer } from './assets/server' import { TestBrowserOnly } from './browser-only/client' import { TestClientChunkServer } from './chunk/server' import { TestChunk2 } from './chunk2/server' +import { TestCjsBuiltinInterop } from './cjs-builtin-interop/server' import { ClientCounter, Hydrated } from './client' import { TestClientError } from './client-error/client' import { TestCssQueries } from './css-queries/server' @@ -123,6 +124,7 @@ export function Root(props: { url: URL }) { + ) diff --git a/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.d.ts b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.d.ts new file mode 100644 index 000000000..0d9adf432 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.d.ts @@ -0,0 +1,7 @@ +import { EventEmitter } from 'events' + +export class CustomEventEmitter extends EventEmitter { + constructor() + testValue: string + getTestValue(): string +} diff --git a/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.js b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.js new file mode 100644 index 000000000..66332be6d --- /dev/null +++ b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.js @@ -0,0 +1,15 @@ +const EventEmitter = require('events') + +class CustomEventEmitter extends EventEmitter { + constructor() { + super() + this.testValue = 'working' + } + + getTestValue() { + // console.trace('getTestValue called'); + return this.testValue + } +} + +module.exports = { CustomEventEmitter } diff --git a/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/package.json b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/package.json new file mode 100644 index 000000000..252e66adb --- /dev/null +++ b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/package.json @@ -0,0 +1,8 @@ +{ + "name": "@vitejs/test-dep-cjs-events-extend", + "version": "0.0.0", + "private": true, + "type": "commonjs", + "main": "index.js", + "types": "index.d.ts" +} diff --git a/packages/plugin-rsc/examples/basic/vite.config.ts b/packages/plugin-rsc/examples/basic/vite.config.ts index 80d8464c7..c12357461 100644 --- a/packages/plugin-rsc/examples/basic/vite.config.ts +++ b/packages/plugin-rsc/examples/basic/vite.config.ts @@ -260,6 +260,11 @@ export default { fetch: handler }; include: ['@vitejs/test-dep-transitive-cjs > @vitejs/test-dep-cjs'], }, }, + rsc: { + resolve: { + noExternal: ['@vitejs/test-dep-cjs-events-extend'], + }, + }, }, }) as any diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7be4feae5..81a0454e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -531,6 +531,9 @@ importers: '@vitejs/plugin-rsc': specifier: latest version: link:../.. + '@vitejs/test-dep-cjs-events-extend': + specifier: file:./test-dep/cjs-events-extend + version: file:packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend '@vitejs/test-dep-client-in-server': specifier: file:./test-dep/client-in-server version: file:packages/plugin-rsc/examples/basic/test-dep/client-in-server(react@19.2.4) @@ -2622,6 +2625,9 @@ packages: '@vitejs/release-scripts@1.6.0': resolution: {integrity: sha512-XV+w22Fvn+wqDtEkz8nQIJzvmRVSh90c2xvOO7cX9fkX8+39ZJpYRiXDIRJG1JRnF8khm1rHjulid+l+khc7TQ==} + '@vitejs/test-dep-cjs-events-extend@file:packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend': + resolution: {directory: packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend, type: directory} + '@vitejs/test-dep-cjs@file:packages/plugin-rsc/examples/basic/test-dep/cjs': resolution: {directory: packages/plugin-rsc/examples/basic/test-dep/cjs, type: directory} peerDependencies: @@ -6056,6 +6062,8 @@ snapshots: transitivePeerDependencies: - conventional-commits-filter + '@vitejs/test-dep-cjs-events-extend@file:packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend': {} + '@vitejs/test-dep-cjs@file:packages/plugin-rsc/examples/basic/test-dep/cjs(react@19.2.4)': dependencies: react: 19.2.4 From 2e05d51d9f75b9230fd2734f2f1e262e160b850a Mon Sep 17 00:00:00 2001 From: Erlei Xu Date: Mon, 2 Feb 2026 00:55:59 -0800 Subject: [PATCH 2/6] fix(dev): cjs to esm interop helper doesn't handle native/external cjs import properly this is to fix issue #1087 This change enhances the cjs import check to cover the common Node cjs to esm wrapping behavior, where the esm wrapper namespace object is equivalent to the default export except the type differences and the default and named export of the default itself. the reproduce test now passes with this fix. --- packages/plugin-rsc/src/transforms/cjs.test.ts | 6 +++--- packages/plugin-rsc/src/transforms/cjs.ts | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/plugin-rsc/src/transforms/cjs.test.ts b/packages/plugin-rsc/src/transforms/cjs.test.ts index fa46caab8..26426463b 100644 --- a/packages/plugin-rsc/src/transforms/cjs.test.ts +++ b/packages/plugin-rsc/src/transforms/cjs.test.ts @@ -44,7 +44,7 @@ if (true) { expect(await testTransform(input)).toMatchInlineSnapshot(` "let __filename = "/test.js"; let __dirname = "/"; let exports = {}; const module = { exports }; - function __cjs_interop__(m) { return m.__cjs_module_runner_transform ? m.default : m; } + function __cjs_interop__(m) { return m?.__cjs_module_runner_transform ? m.default : m?.default && Object.keys(m).every(k => k === "default" || m[k] === m.default[k] || m[k] === m.default) ? m.default : m; } if (true) { module.exports = (__cjs_interop__(await import('./cjs/use-sync-external-store.production.js'))); } else { @@ -69,7 +69,7 @@ if (true) { expect(await testTransform(input)).toMatchInlineSnapshot(` "let __filename = "/test.js"; let __dirname = "/"; let exports = {}; const module = { exports }; - function __cjs_interop__(m) { return m.__cjs_module_runner_transform ? m.default : m; } + function __cjs_interop__(m) { return m?.__cjs_module_runner_transform ? m.default : m?.default && Object.keys(m).every(k => k === "default" || m[k] === m.default[k] || m[k] === m.default) ? m.default : m; } const __cjs_to_esm_hoist_0 = __cjs_interop__(await import("react")); const __cjs_to_esm_hoist_1 = __cjs_interop__(await import("react-dom")); "production" !== process.env.NODE_ENV && (function() { @@ -100,7 +100,7 @@ function test() { expect(await testTransform(input)).toMatchInlineSnapshot(` "let __filename = "/test.js"; let __dirname = "/"; let exports = {}; const module = { exports }; - function __cjs_interop__(m) { return m.__cjs_module_runner_transform ? m.default : m; } + function __cjs_interop__(m) { return m?.__cjs_module_runner_transform ? m.default : m?.default && Object.keys(m).every(k => k === "default" || m[k] === m.default[k] || m[k] === m.default) ? m.default : m; } const __cjs_to_esm_hoist_0 = __cjs_interop__(await import("te" + "st")); const __cjs_to_esm_hoist_1 = __cjs_interop__(await import("test")); const __cjs_to_esm_hoist_2 = __cjs_interop__(await import("test")); diff --git a/packages/plugin-rsc/src/transforms/cjs.ts b/packages/plugin-rsc/src/transforms/cjs.ts index e48b3920c..b95e581a0 100644 --- a/packages/plugin-rsc/src/transforms/cjs.ts +++ b/packages/plugin-rsc/src/transforms/cjs.ts @@ -9,9 +9,15 @@ import { analyze } from 'periscopic' // replacing require("xxx") into import("xxx") affects Vite's resolution. // Runtime helper to handle CJS/ESM interop when transforming require() to import() -// Only unwrap .default for modules that were transformed by this plugin (marked with __cjs_module_runner_transform) +// Unwrap .default for modules +// 1. if it was transformed by this plugin (marked with __cjs_module_runner_transform) +// 2. if all named exports point to .default properties (common CJS pattern) +// this is particularly important for Node built-in modules consumptions; +// where the built-in modules are not transformed by this plugin but still follow the CJS export pattern +// see [getESMFacade](https://github.com/nodejs/node/blob/f200685d9930404d610a52d9e06513bf0a821ed4/lib/internal/bootstrap/realm.js#L347-L360) +// // This ensures we don't incorrectly unwrap .default on genuine ESM modules -const CJS_INTEROP_HELPER = `function __cjs_interop__(m) { return m.__cjs_module_runner_transform ? m.default : m; }` +const CJS_INTEROP_HELPER = `function __cjs_interop__(m) { return m?.__cjs_module_runner_transform ? m.default : m?.default && Object.keys(m).every(k => k === "default" || m[k] === m.default[k] || m[k] === m.default) ? m.default : m; }` export function transformCjsToEsm( code: string, From 67dfdad779bdaf0274046eb0f6caef8b71c0acae Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 3 Feb 2026 12:37:55 +0900 Subject: [PATCH 3/6] refactor: remove redundant check --- packages/plugin-rsc/src/transforms/cjs.test.ts | 9 ++++++--- packages/plugin-rsc/src/transforms/cjs.ts | 10 +++++++++- .../plugin-rsc/src/transforms/fixtures/cjs/entry.mjs | 3 +++ .../src/transforms/fixtures/cjs/node-builtins.cjs | 10 ++++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 packages/plugin-rsc/src/transforms/fixtures/cjs/node-builtins.cjs diff --git a/packages/plugin-rsc/src/transforms/cjs.test.ts b/packages/plugin-rsc/src/transforms/cjs.test.ts index 26426463b..a25809718 100644 --- a/packages/plugin-rsc/src/transforms/cjs.test.ts +++ b/packages/plugin-rsc/src/transforms/cjs.test.ts @@ -44,7 +44,7 @@ if (true) { expect(await testTransform(input)).toMatchInlineSnapshot(` "let __filename = "/test.js"; let __dirname = "/"; let exports = {}; const module = { exports }; - function __cjs_interop__(m) { return m?.__cjs_module_runner_transform ? m.default : m?.default && Object.keys(m).every(k => k === "default" || m[k] === m.default[k] || m[k] === m.default) ? m.default : m; } + function __cjs_interop__(m) {return m.__cjs_module_runner_transform || "default" in m && Object.keys(m).every((k) => k === "default" || m[k] === m.default[k]) ? m.default : m;} if (true) { module.exports = (__cjs_interop__(await import('./cjs/use-sync-external-store.production.js'))); } else { @@ -69,7 +69,7 @@ if (true) { expect(await testTransform(input)).toMatchInlineSnapshot(` "let __filename = "/test.js"; let __dirname = "/"; let exports = {}; const module = { exports }; - function __cjs_interop__(m) { return m?.__cjs_module_runner_transform ? m.default : m?.default && Object.keys(m).every(k => k === "default" || m[k] === m.default[k] || m[k] === m.default) ? m.default : m; } + function __cjs_interop__(m) {return m.__cjs_module_runner_transform || "default" in m && Object.keys(m).every((k) => k === "default" || m[k] === m.default[k]) ? m.default : m;} const __cjs_to_esm_hoist_0 = __cjs_interop__(await import("react")); const __cjs_to_esm_hoist_1 = __cjs_interop__(await import("react-dom")); "production" !== process.env.NODE_ENV && (function() { @@ -100,7 +100,7 @@ function test() { expect(await testTransform(input)).toMatchInlineSnapshot(` "let __filename = "/test.js"; let __dirname = "/"; let exports = {}; const module = { exports }; - function __cjs_interop__(m) { return m?.__cjs_module_runner_transform ? m.default : m?.default && Object.keys(m).every(k => k === "default" || m[k] === m.default[k] || m[k] === m.default) ? m.default : m; } + function __cjs_interop__(m) {return m.__cjs_module_runner_transform || "default" in m && Object.keys(m).every((k) => k === "default" || m[k] === m.default[k]) ? m.default : m;} const __cjs_to_esm_hoist_0 = __cjs_interop__(await import("te" + "st")); const __cjs_to_esm_hoist_1 = __cjs_interop__(await import("test")); const __cjs_to_esm_hoist_2 = __cjs_interop__(await import("test")); @@ -196,6 +196,9 @@ function test() { }, "depPrimitive": "[ok]", "dualLib": "ok", + "testNodeBuiltins": { + "nodeEventsOk": true, + }, } `) }) diff --git a/packages/plugin-rsc/src/transforms/cjs.ts b/packages/plugin-rsc/src/transforms/cjs.ts index b95e581a0..766ab13a0 100644 --- a/packages/plugin-rsc/src/transforms/cjs.ts +++ b/packages/plugin-rsc/src/transforms/cjs.ts @@ -17,7 +17,15 @@ import { analyze } from 'periscopic' // see [getESMFacade](https://github.com/nodejs/node/blob/f200685d9930404d610a52d9e06513bf0a821ed4/lib/internal/bootstrap/realm.js#L347-L360) // // This ensures we don't incorrectly unwrap .default on genuine ESM modules -const CJS_INTEROP_HELPER = `function __cjs_interop__(m) { return m?.__cjs_module_runner_transform ? m.default : m?.default && Object.keys(m).every(k => k === "default" || m[k] === m.default[k] || m[k] === m.default) ? m.default : m; }` +function __cjs_interop__(m: any) { + return m.__cjs_module_runner_transform || + ('default' in m && + Object.keys(m).every((k) => k === 'default' || m[k] === m.default[k])) + ? m.default + : m +} + +const CJS_INTEROP_HELPER = __cjs_interop__.toString().replace(/\n\s*/g, '') export function transformCjsToEsm( code: string, diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs index 191b45b89..6849534cb 100644 --- a/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs @@ -5,7 +5,9 @@ import depExports from './exports.cjs' import depFnRequire from './function-require.cjs' import depFn from './function.cjs' import cjsGlobals from './globals.cjs' +import testNodeBuiltins from './node-builtins.cjs' import depPrimitive from './primitive.cjs' + export { depDefault, depNamespace, @@ -15,4 +17,5 @@ export { depFnRequire, dualLib, cjsGlobals, + testNodeBuiltins, } diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/node-builtins.cjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/node-builtins.cjs new file mode 100644 index 000000000..ab7677b36 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/node-builtins.cjs @@ -0,0 +1,10 @@ +const EventEmitter = require('node:events') + +class CustomEmitter extends EventEmitter { + constructor() { + super() + this.custom = true + } +} + +exports.nodeEventsOk = new CustomEmitter().custom From 2e5279aa49be98d8ef085750f3b4d1522dbb0dd3 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 3 Feb 2026 12:44:04 +0900 Subject: [PATCH 4/6] test: test primitive module.exports --- packages/plugin-rsc/package.json | 1 + packages/plugin-rsc/src/transforms/cjs.test.ts | 3 +++ packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs | 2 ++ .../src/transforms/fixtures/cjs/external-falsy-primitive.cjs | 3 +++ packages/plugin-rsc/test-dep/cjs-falsy-primitive/index.cjs | 1 + .../plugin-rsc/test-dep/cjs-falsy-primitive/package.json | 5 +++++ pnpm-lock.yaml | 3 +++ 7 files changed, 18 insertions(+) create mode 100644 packages/plugin-rsc/src/transforms/fixtures/cjs/external-falsy-primitive.cjs create mode 100644 packages/plugin-rsc/test-dep/cjs-falsy-primitive/index.cjs create mode 100644 packages/plugin-rsc/test-dep/cjs-falsy-primitive/package.json diff --git a/packages/plugin-rsc/package.json b/packages/plugin-rsc/package.json index 267fab317..f411c2e13 100644 --- a/packages/plugin-rsc/package.json +++ b/packages/plugin-rsc/package.json @@ -58,6 +58,7 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "workspace:*", "@vitejs/test-dep-cjs-and-esm": "./test-dep/cjs-and-esm", + "@vitejs/test-dep-cjs-falsy-primitive": "./test-dep/cjs-falsy-primitive", "picocolors": "^1.1.1", "react": "^19.2.4", "react-dom": "^19.2.4", diff --git a/packages/plugin-rsc/src/transforms/cjs.test.ts b/packages/plugin-rsc/src/transforms/cjs.test.ts index a25809718..8a71f2c53 100644 --- a/packages/plugin-rsc/src/transforms/cjs.test.ts +++ b/packages/plugin-rsc/src/transforms/cjs.test.ts @@ -196,6 +196,9 @@ function test() { }, "depPrimitive": "[ok]", "dualLib": "ok", + "testExternalFalsyPrimitive": { + "ok": true, + }, "testNodeBuiltins": { "nodeEventsOk": true, }, diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs index 6849534cb..997d67b05 100644 --- a/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs @@ -2,6 +2,7 @@ import depDefault from './dep1.cjs' import * as depNamespace from './dep2.cjs' import dualLib from './dual-lib.cjs' import depExports from './exports.cjs' +import testExternalFalsyPrimitive from './external-falsy-primitive.cjs' import depFnRequire from './function-require.cjs' import depFn from './function.cjs' import cjsGlobals from './globals.cjs' @@ -18,4 +19,5 @@ export { dualLib, cjsGlobals, testNodeBuiltins, + testExternalFalsyPrimitive, } diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/external-falsy-primitive.cjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/external-falsy-primitive.cjs new file mode 100644 index 000000000..6343cbf56 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/external-falsy-primitive.cjs @@ -0,0 +1,3 @@ +const lib = require('@vitejs/test-dep-cjs-falsy-primitive') + +exports.ok = lib === false diff --git a/packages/plugin-rsc/test-dep/cjs-falsy-primitive/index.cjs b/packages/plugin-rsc/test-dep/cjs-falsy-primitive/index.cjs new file mode 100644 index 000000000..f6b439dd6 --- /dev/null +++ b/packages/plugin-rsc/test-dep/cjs-falsy-primitive/index.cjs @@ -0,0 +1 @@ +module.exports = false diff --git a/packages/plugin-rsc/test-dep/cjs-falsy-primitive/package.json b/packages/plugin-rsc/test-dep/cjs-falsy-primitive/package.json new file mode 100644 index 000000000..e83c19bc4 --- /dev/null +++ b/packages/plugin-rsc/test-dep/cjs-falsy-primitive/package.json @@ -0,0 +1,5 @@ +{ + "name": "@vitejs/test-dep-cjs-falsy-primitive", + "private": true, + "main": "./index.cjs" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7845d11c9..163ca3b88 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -488,6 +488,9 @@ importers: '@vitejs/test-dep-cjs-and-esm': specifier: ./test-dep/cjs-and-esm version: link:test-dep/cjs-and-esm + '@vitejs/test-dep-cjs-falsy-primitive': + specifier: ./test-dep/cjs-falsy-primitive + version: link:test-dep/cjs-falsy-primitive picocolors: specifier: ^1.1.1 version: 1.1.1 From d9727d8723cda997fe85d72e7c2f3dbb512dbbaa Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 3 Feb 2026 12:53:54 +0900 Subject: [PATCH 5/6] test: simplify --- .../src/routes/cjs-builtin-interop/server.tsx | 17 ----------------- .../routes/deps/cjs-builtin-interop/server.tsx | 10 ++++++++++ .../examples/basic/src/routes/root.tsx | 2 +- .../basic/test-dep/cjs-events-extend/index.d.ts | 7 ------- .../basic/test-dep/cjs-events-extend/index.js | 11 +++-------- .../test-dep/cjs-events-extend/package.json | 6 ++++-- .../plugin-rsc/examples/basic/vite.config.ts | 5 ----- pnpm-lock.yaml | 8 ++++++-- 8 files changed, 24 insertions(+), 42 deletions(-) delete mode 100644 packages/plugin-rsc/examples/basic/src/routes/cjs-builtin-interop/server.tsx create mode 100644 packages/plugin-rsc/examples/basic/src/routes/deps/cjs-builtin-interop/server.tsx delete mode 100644 packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.d.ts diff --git a/packages/plugin-rsc/examples/basic/src/routes/cjs-builtin-interop/server.tsx b/packages/plugin-rsc/examples/basic/src/routes/cjs-builtin-interop/server.tsx deleted file mode 100644 index dc15f86a5..000000000 --- a/packages/plugin-rsc/examples/basic/src/routes/cjs-builtin-interop/server.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { CustomEventEmitter } from '@vitejs/test-dep-cjs-events-extend' - -export function TestCjsBuiltinInterop() { - let result = 'not-working' - - try { - // console.trace('CjsBuiltinInterop component rendering'); - const emitter = new CustomEventEmitter() - result = emitter.getTestValue() - } catch (error) { - result = 'error: ' + (error as Error).message - } - - return ( -
cjs-builtin-interop: {result}
- ) -} diff --git a/packages/plugin-rsc/examples/basic/src/routes/deps/cjs-builtin-interop/server.tsx b/packages/plugin-rsc/examples/basic/src/routes/deps/cjs-builtin-interop/server.tsx new file mode 100644 index 000000000..9e0c6ddf4 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/deps/cjs-builtin-interop/server.tsx @@ -0,0 +1,10 @@ +// @ts-ignore +import * as testDep from '@vitejs/test-dep-cjs-events-extend' + +export function TestCjsBuiltinInterop() { + return ( +
+ cjs-builtin-interop: {testDep.test} +
+ ) +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/root.tsx b/packages/plugin-rsc/examples/basic/src/routes/root.tsx index d1a00c305..2e6ef5e71 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/root.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/root.tsx @@ -20,10 +20,10 @@ import { TestAssetsServer } from './assets/server' import { TestBrowserOnly } from './browser-only/client' import { TestClientChunkServer } from './chunk/server' import { TestChunk2 } from './chunk2/server' -import { TestCjsBuiltinInterop } from './cjs-builtin-interop/server' import { ClientCounter, Hydrated } from './client' import { TestClientError } from './client-error/client' import { TestCssQueries } from './css-queries/server' +import { TestCjsBuiltinInterop } from './deps/cjs-builtin-interop/server' import { TestClientInServer } from './deps/client-in-server/server' import { TestServerInClient } from './deps/server-in-client/client' import { TestServerInServer } from './deps/server-in-server/server' diff --git a/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.d.ts b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.d.ts deleted file mode 100644 index 0d9adf432..000000000 --- a/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { EventEmitter } from 'events' - -export class CustomEventEmitter extends EventEmitter { - constructor() - testValue: string - getTestValue(): string -} diff --git a/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.js b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.js index 66332be6d..01676475f 100644 --- a/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.js +++ b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/index.js @@ -1,15 +1,10 @@ -const EventEmitter = require('events') +const EventEmitter = require('node:events') class CustomEventEmitter extends EventEmitter { constructor() { super() - this.testValue = 'working' - } - - getTestValue() { - // console.trace('getTestValue called'); - return this.testValue + this.testValue = 'ok' } } -module.exports = { CustomEventEmitter } +module.exports.test = new CustomEventEmitter().testValue || 'ko' diff --git a/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/package.json b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/package.json index 252e66adb..e4a1b4e06 100644 --- a/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/package.json +++ b/packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend/package.json @@ -3,6 +3,8 @@ "version": "0.0.0", "private": true, "type": "commonjs", - "main": "index.js", - "types": "index.d.ts" + "exports": "./index.js", + "peerDependencies": { + "react": "*" + } } diff --git a/packages/plugin-rsc/examples/basic/vite.config.ts b/packages/plugin-rsc/examples/basic/vite.config.ts index c12357461..80d8464c7 100644 --- a/packages/plugin-rsc/examples/basic/vite.config.ts +++ b/packages/plugin-rsc/examples/basic/vite.config.ts @@ -260,11 +260,6 @@ export default { fetch: handler }; include: ['@vitejs/test-dep-transitive-cjs > @vitejs/test-dep-cjs'], }, }, - rsc: { - resolve: { - noExternal: ['@vitejs/test-dep-cjs-events-extend'], - }, - }, }, }) as any diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 163ca3b88..fa25fcb8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -536,7 +536,7 @@ importers: version: link:../.. '@vitejs/test-dep-cjs-events-extend': specifier: file:./test-dep/cjs-events-extend - version: file:packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend + version: file:packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend(react@19.2.4) '@vitejs/test-dep-client-in-server': specifier: file:./test-dep/client-in-server version: file:packages/plugin-rsc/examples/basic/test-dep/client-in-server(react@19.2.4) @@ -2781,6 +2781,8 @@ packages: '@vitejs/test-dep-cjs-events-extend@file:packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend': resolution: {directory: packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend, type: directory} + peerDependencies: + react: '*' '@vitejs/test-dep-cjs@file:packages/plugin-rsc/examples/basic/test-dep/cjs': resolution: {directory: packages/plugin-rsc/examples/basic/test-dep/cjs, type: directory} @@ -6278,7 +6280,9 @@ snapshots: transitivePeerDependencies: - conventional-commits-filter - '@vitejs/test-dep-cjs-events-extend@file:packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend': {} + '@vitejs/test-dep-cjs-events-extend@file:packages/plugin-rsc/examples/basic/test-dep/cjs-events-extend(react@19.2.4)': + dependencies: + react: 19.2.4 '@vitejs/test-dep-cjs@file:packages/plugin-rsc/examples/basic/test-dep/cjs(react@19.2.4)': dependencies: From 942df8ea3fe678587243b87e722a3c29506c4b71 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 3 Feb 2026 12:54:32 +0900 Subject: [PATCH 6/6] test: update --- packages/plugin-rsc/e2e/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-rsc/e2e/basic.test.ts b/packages/plugin-rsc/e2e/basic.test.ts index 3c3163617..bf96dcb6c 100644 --- a/packages/plugin-rsc/e2e/basic.test.ts +++ b/packages/plugin-rsc/e2e/basic.test.ts @@ -1527,7 +1527,7 @@ function defineTest(f: Fixture) { await page.goto(f.url()) await waitForHydration(page) await expect(page.getByTestId('cjs-builtin-interop')).toHaveText( - 'cjs-builtin-interop: working', + 'cjs-builtin-interop: ok', ) })