From 4228f1c37e6236a115f42a6fa016cba0842dc14e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Oct 2025 13:33:03 +0000 Subject: [PATCH 1/6] Initial plan From 5f18644b32a9737fe76e30ff72f8ac057f6de0dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Oct 2025 13:43:07 +0000 Subject: [PATCH 2/6] fix(plugin-rsc): add CJS/ESM interop helper for require() transformation - Add __cjs_interop__ runtime helper to handle ESM modules correctly - Check for __esModule flag and default export before accessing .default - Update all tests to reflect new transformation output - Fixes issue where require("esm-package") would fail when transformed Co-authored-by: hi-ogawa <4232207+hi-ogawa@users.noreply.github.com> --- .../plugin-rsc/src/transforms/cjs.test.ts | 23 +++++++++------- packages/plugin-rsc/src/transforms/cjs.ts | 27 ++++++++++++++++--- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/packages/plugin-rsc/src/transforms/cjs.test.ts b/packages/plugin-rsc/src/transforms/cjs.test.ts index 11cc6d22..1da0ca31 100644 --- a/packages/plugin-rsc/src/transforms/cjs.test.ts +++ b/packages/plugin-rsc/src/transforms/cjs.test.ts @@ -38,10 +38,11 @@ if (true) { ` expect(await testTransform(input)).toMatchInlineSnapshot(` "let exports = {}; const module = { exports }; + function __cjs_interop__(m) { return m && m.__esModule ? m.default : (m.default !== undefined ? m.default : m); } if (true) { - module.exports = ((await import('./cjs/use-sync-external-store.production.js')).default); + module.exports = (__cjs_interop__(await import('./cjs/use-sync-external-store.production.js'))); } else { - module.exports = ((await import('./cjs/use-sync-external-store.development.js')).default); + module.exports = (__cjs_interop__(await import('./cjs/use-sync-external-store.development.js'))); } " `) @@ -57,8 +58,9 @@ if (true) { ` expect(await testTransform(input)).toMatchInlineSnapshot(` "let exports = {}; const module = { exports }; - const __cjs_to_esm_hoist_0 = (await import("react")).default; - const __cjs_to_esm_hoist_1 = (await import("react-dom")).default; + function __cjs_interop__(m) { return m && m.__esModule ? m.default : (m.default !== undefined ? 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() { var React = __cjs_to_esm_hoist_0; var ReactDOM = __cjs_to_esm_hoist_1; @@ -82,12 +84,13 @@ function test() { ` expect(await testTransform(input)).toMatchInlineSnapshot(` "let exports = {}; const module = { exports }; - const __cjs_to_esm_hoist_0 = (await import("te" + "st")).default; - const __cjs_to_esm_hoist_1 = (await import("test")).default; - const __cjs_to_esm_hoist_2 = (await import("test")).default; - const x1 = ((await import("te" + "st")).default); - const x2 = ((await import("test")).default)().test; - console.log(((await import("test")).default)) + function __cjs_interop__(m) { return m && m.__esModule ? m.default : (m.default !== undefined ? 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")); + const x1 = (__cjs_interop__(await import("te" + "st"))); + const x2 = (__cjs_interop__(await import("test")))().test; + console.log((__cjs_interop__(await import("test")))) function test() { const y1 = __cjs_to_esm_hoist_0; diff --git a/packages/plugin-rsc/src/transforms/cjs.ts b/packages/plugin-rsc/src/transforms/cjs.ts index de89e892..e4dfd66b 100644 --- a/packages/plugin-rsc/src/transforms/cjs.ts +++ b/packages/plugin-rsc/src/transforms/cjs.ts @@ -3,6 +3,13 @@ import MagicString from 'magic-string' import { analyze } from 'periscopic' import { walk } from 'estree-walker' +// Runtime helper to handle CJS/ESM interop when transforming require() to import() +// This is needed because when CJS code does require("pkg"), it expects: +// - For CJS modules: the module.exports value directly +// - For ESM modules: Node.js actually returns the namespace object +// Since we're transforming to dynamic import(), we need to handle both cases +const CJS_INTEROP_HELPER = `function __cjs_interop__(m) { return m && m.__esModule ? m.default : (m.default !== undefined ? m.default : m); }` + export function transformCjsToEsm( code: string, ast: Program, @@ -13,6 +20,8 @@ export function transformCjsToEsm( const parentNodes: Node[] = [] const hoistedCodes: string[] = [] let hoistIndex = 0 + let needsInteropHelper = false + walk(ast, { enter(node) { parentNodes.push(node) @@ -38,11 +47,17 @@ export function transformCjsToEsm( } } + needsInteropHelper = true + if (isTopLevel) { - // top-level scope `require` to dynamic import + // top-level scope `require` to dynamic import with interop // (this allows handling react development/production re-export within top-level if branch) - output.update(node.start, node.callee.end, '((await import') - output.appendRight(node.end, ').default)') + output.update( + node.start, + node.callee.end, + '(__cjs_interop__(await import', + ) + output.appendRight(node.end, '))') } else { // hoist non top-level `require` to top-level const hoisted = `__cjs_to_esm_hoist_${hoistIndex}` @@ -51,7 +66,7 @@ export function transformCjsToEsm( node.arguments[0]!.end, ) hoistedCodes.push( - `const ${hoisted} = (await import(${importee})).default;\n`, + `const ${hoisted} = __cjs_interop__(await import(${importee}));\n`, ) output.update(node.start, node.end, hoisted) hoistIndex++ @@ -65,6 +80,10 @@ export function transformCjsToEsm( for (const hoisted of hoistedCodes.reverse()) { output.prepend(hoisted) } + // Prepend interop helper if needed + if (needsInteropHelper) { + output.prepend(`${CJS_INTEROP_HELPER}\n`) + } // https://nodejs.org/docs/v22.19.0/api/modules.html#exports-shortcut output.prepend(`let exports = {}; const module = { exports };\n`) return { output } From d0ee6b5ffbf2ef0f98e2cd789b76b5271949f49d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Oct 2025 13:57:43 +0000 Subject: [PATCH 3/6] fix(plugin-rsc): use explicit marker for CJS module detection in interop helper - Change interop helper to check for __cjs_module_runner_transform marker - Add marker export to all transformed CJS modules - Update tests to include marker in transformed modules - More explicit approach than heuristic-based detection Co-authored-by: hi-ogawa <4232207+hi-ogawa@users.noreply.github.com> --- packages/plugin-rsc/src/plugins/cjs.ts | 1 + packages/plugin-rsc/src/transforms/cjs.test.ts | 8 +++++--- packages/plugin-rsc/src/transforms/cjs.ts | 8 +++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/plugin-rsc/src/plugins/cjs.ts b/packages/plugin-rsc/src/plugins/cjs.ts index dc0e6216..60af1e22 100644 --- a/packages/plugin-rsc/src/plugins/cjs.ts +++ b/packages/plugin-rsc/src/plugins/cjs.ts @@ -60,6 +60,7 @@ export function cjsModuleRunnerPlugin(): Plugin[] { output.append(` ;__vite_ssr_exportAll__(module.exports); export default module.exports; +export const __cjs_module_runner_transform = true; `) return { code: output.toString(), diff --git a/packages/plugin-rsc/src/transforms/cjs.test.ts b/packages/plugin-rsc/src/transforms/cjs.test.ts index 1da0ca31..9030f623 100644 --- a/packages/plugin-rsc/src/transforms/cjs.test.ts +++ b/packages/plugin-rsc/src/transforms/cjs.test.ts @@ -38,7 +38,7 @@ if (true) { ` expect(await testTransform(input)).toMatchInlineSnapshot(` "let exports = {}; const module = { exports }; - function __cjs_interop__(m) { return m && m.__esModule ? m.default : (m.default !== undefined ? m.default : m); } + function __cjs_interop__(m) { return m.__cjs_module_runner_transform ? m.default : m; } if (true) { module.exports = (__cjs_interop__(await import('./cjs/use-sync-external-store.production.js'))); } else { @@ -58,7 +58,7 @@ if (true) { ` expect(await testTransform(input)).toMatchInlineSnapshot(` "let exports = {}; const module = { exports }; - function __cjs_interop__(m) { return m && m.__esModule ? m.default : (m.default !== undefined ? m.default : m); } + function __cjs_interop__(m) { return m.__cjs_module_runner_transform ? 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() { @@ -84,7 +84,7 @@ function test() { ` expect(await testTransform(input)).toMatchInlineSnapshot(` "let exports = {}; const module = { exports }; - function __cjs_interop__(m) { return m && m.__esModule ? m.default : (m.default !== undefined ? m.default : m); } + function __cjs_interop__(m) { return m.__cjs_module_runner_transform ? 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")); @@ -133,6 +133,7 @@ function test() { output.append(` ;__vite_ssr_exportAll__(module.exports); export default module.exports; +export const __cjs_module_runner_transform = true; `) return { code: output.toString(), @@ -159,6 +160,7 @@ export default module.exports; "value": 3, }, "depNamespace": { + "__cjs_module_runner_transform": true, "a": "a", "b": "b", "default": { diff --git a/packages/plugin-rsc/src/transforms/cjs.ts b/packages/plugin-rsc/src/transforms/cjs.ts index e4dfd66b..33982428 100644 --- a/packages/plugin-rsc/src/transforms/cjs.ts +++ b/packages/plugin-rsc/src/transforms/cjs.ts @@ -4,11 +4,9 @@ import { analyze } from 'periscopic' import { walk } from 'estree-walker' // Runtime helper to handle CJS/ESM interop when transforming require() to import() -// This is needed because when CJS code does require("pkg"), it expects: -// - For CJS modules: the module.exports value directly -// - For ESM modules: Node.js actually returns the namespace object -// Since we're transforming to dynamic import(), we need to handle both cases -const CJS_INTEROP_HELPER = `function __cjs_interop__(m) { return m && m.__esModule ? m.default : (m.default !== undefined ? m.default : m); }` +// Only unwrap .default for modules that were transformed by this plugin (marked with __cjs_module_runner_transform) +// 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; }` export function transformCjsToEsm( code: string, From b592df4b3594380b9a429548ded534aa5f1ddb39 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 8 Oct 2025 09:09:27 +0900 Subject: [PATCH 4/6] refactor: remove state --- packages/plugin-rsc/src/transforms/cjs.ts | 6 +- pnpm-lock.yaml | 181 +++++++++++++++++++++- 2 files changed, 178 insertions(+), 9 deletions(-) diff --git a/packages/plugin-rsc/src/transforms/cjs.ts b/packages/plugin-rsc/src/transforms/cjs.ts index 33982428..bb98b075 100644 --- a/packages/plugin-rsc/src/transforms/cjs.ts +++ b/packages/plugin-rsc/src/transforms/cjs.ts @@ -18,7 +18,6 @@ export function transformCjsToEsm( const parentNodes: Node[] = [] const hoistedCodes: string[] = [] let hoistIndex = 0 - let needsInteropHelper = false walk(ast, { enter(node) { @@ -45,8 +44,6 @@ export function transformCjsToEsm( } } - needsInteropHelper = true - if (isTopLevel) { // top-level scope `require` to dynamic import with interop // (this allows handling react development/production re-export within top-level if branch) @@ -78,8 +75,7 @@ export function transformCjsToEsm( for (const hoisted of hoistedCodes.reverse()) { output.prepend(hoisted) } - // Prepend interop helper if needed - if (needsInteropHelper) { + if (output.hasChanged()) { output.prepend(`${CJS_INTEROP_HELPER}\n`) } // https://nodejs.org/docs/v22.19.0/api/modules.html#exports-shortcut diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 191b360e..ab2d7c36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1692,6 +1692,9 @@ packages: '@napi-rs/wasm-runtime@1.0.5': resolution: {integrity: sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==} + '@napi-rs/wasm-runtime@1.0.6': + resolution: {integrity: sha512-DXj75ewm11LIWUk198QSKUTxjyRjsBwk09MuMk5DGK+GDUtyPhhEHOGP/Xwwj3DjQXXkivoBirmOnKrLfc0+9g==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1711,6 +1714,9 @@ packages: '@oxc-project/types@0.93.0': resolution: {integrity: sha512-yNtwmWZIBtJsMr5TEfoZFDxIWV6OdScOpza/f5YxbqUMJk+j6QX3Cf3jgZShGEFYWQJ5j9mJ6jM0tZHu2J9Yrg==} + '@oxc-project/types@0.94.0': + resolution: {integrity: sha512-+UgQT/4o59cZfH6Cp7G0hwmqEQ0wE+AdIwhikdwnhWI9Dp8CgSY081+Q3O67/wq3VJu8mgUEB93J9EHHn70fOw==} + '@playwright/test@1.55.1': resolution: {integrity: sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==} engines: {node: '>=18'} @@ -1747,86 +1753,172 @@ packages: cpu: [arm64] os: [android] + '@rolldown/binding-android-arm64@1.0.0-beta.42': + resolution: {integrity: sha512-W5ZKF3TP3bOWuBfotAGp+UGjxOkGV7jRmIRbBA7NFjggx7Oi6vOmGDqpHEIX7kDCiry1cnIsWQaxNvWbMdkvzQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@rolldown/binding-darwin-arm64@1.0.0-beta.41': resolution: {integrity: sha512-XGCzqfjdk7550PlyZRTBKbypXrB7ATtXhw/+bjtxnklLQs0mKP/XkQVOKyn9qGKSlvH8I56JLYryVxl0PCvSNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@rolldown/binding-darwin-arm64@1.0.0-beta.42': + resolution: {integrity: sha512-abw/wtgJA8OCgaTlL+xJxnN/Z01BwV1rfzIp5Hh9x+IIO6xOBfPsQ0nzi0+rWx3TyZ9FZXyC7bbC+5NpQ9EaXQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@rolldown/binding-darwin-x64@1.0.0-beta.41': resolution: {integrity: sha512-Ho6lIwGJed98zub7n0xcRKuEtnZgbxevAmO4x3zn3C3N4GVXZD5xvCvTVxSMoeBJwTcIYzkVDRTIhylQNsTgLQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@rolldown/binding-darwin-x64@1.0.0-beta.42': + resolution: {integrity: sha512-Y/UrZIRVr8CvXVEB88t6PeC46r1K9/QdPEo2ASE/b/KBEyXIx+QbM6kv9QfQVWU2Atly2+SVsQzxQsIvuk3lZQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@rolldown/binding-freebsd-x64@1.0.0-beta.41': resolution: {integrity: sha512-ijAZETywvL+gACjbT4zBnCp5ez1JhTRs6OxRN4J+D6AzDRbU2zb01Esl51RP5/8ZOlvB37xxsRQ3X4YRVyYb3g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@rolldown/binding-freebsd-x64@1.0.0-beta.42': + resolution: {integrity: sha512-zRM0oOk7BZiy6DoWBvdV4hyEg+j6+WcBZIMHVirMEZRu8hd18kZdJkg+bjVMfCEhwpWeFUfBfZ1qcaZ5UdYzlQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.41': resolution: {integrity: sha512-EgIOZt7UildXKFEFvaiLNBXm+4ggQyGe3E5Z1QP9uRcJJs9omihOnm897FwOBQdCuMvI49iBgjFrkhH+wMJ2MA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.42': + resolution: {integrity: sha512-6RjFaC52QNwo7ilU8C5H7swbGlgfTkG9pudXwzr3VYyT18s0C9gLg3mvc7OMPIGqNxnQ0M5lU8j6aQCk2DTRVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.41': resolution: {integrity: sha512-F8bUwJq8v/JAU8HSwgF4dztoqJ+FjdyjuvX4//3+Fbe2we9UktFeZ27U4lRMXF1vxWtdV4ey6oCSqI7yUrSEeg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.42': + resolution: {integrity: sha512-LMYHM5Sf6ROq+VUwHMDVX2IAuEsWTv4SnlFEedBnMGpvRuQ14lCmD4m5Q8sjyAQCgyha9oghdGoK8AEg1sXZKg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.41': resolution: {integrity: sha512-MioXcCIX/wB1pBnBoJx8q4OGucUAfC1+/X1ilKFsjDK05VwbLZGRgOVD5OJJpUQPK86DhQciNBrfOKDiatxNmg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.42': + resolution: {integrity: sha512-/bNTYb9aKNhzdbPn3O4MK2aLv55AlrkUKPE4KNfBYjkoZUfDr4jWp7gsSlvTc5A/99V1RCm9axvt616ZzeXGyA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.41': resolution: {integrity: sha512-m66M61fizvRCwt5pOEiZQMiwBL9/y0bwU/+Kc4Ce/Pef6YfoEkR28y+DzN9rMdjo8Z28NXjsDPq9nH4mXnAP0g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.42': + resolution: {integrity: sha512-n/SLa4h342oyeGykZdch7Y3GNCNliRPL4k5wkeZ/5eQZs+c6/ZG1SHCJQoy7bZcmxiMyaXs9HoFmv1PEKrZgWg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + '@rolldown/binding-linux-x64-musl@1.0.0-beta.41': resolution: {integrity: sha512-yRxlSfBvWnnfrdtJfvi9lg8xfG5mPuyoSHm0X01oiE8ArmLRvoJGHUTJydCYz+wbK2esbq5J4B4Tq9WAsOlP1Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + '@rolldown/binding-linux-x64-musl@1.0.0-beta.42': + resolution: {integrity: sha512-4PSd46sFzqpLHSGdaSViAb1mk55sCUMpJg+X8ittXaVocQsV3QLG/uydSH8RyL0ngHX5fy3D70LcCzlB15AgHw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + '@rolldown/binding-openharmony-arm64@1.0.0-beta.41': resolution: {integrity: sha512-PHVxYhBpi8UViS3/hcvQQb9RFqCtvFmFU1PvUoTRiUdBtgHA6fONNHU4x796lgzNlVSD3DO/MZNk1s5/ozSMQg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@rolldown/binding-openharmony-arm64@1.0.0-beta.42': + resolution: {integrity: sha512-BmWoeJJyeZXmZBcfoxG6J9+rl2G7eO47qdTkAzEegj4n3aC6CBIHOuDcbE8BvhZaEjQR0nh0nJrtEDlt65Q7Sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@rolldown/binding-wasm32-wasi@1.0.0-beta.41': resolution: {integrity: sha512-OAfcO37ME6GGWmj9qTaDT7jY4rM0T2z0/8ujdQIJQ2x2nl+ztO32EIwURfmXOK0U1tzkyuaKYvE34Pug/ucXlQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] + '@rolldown/binding-wasm32-wasi@1.0.0-beta.42': + resolution: {integrity: sha512-2Ft32F7uiDTrGZUKws6CLNTlvTWHC33l4vpXrzUucf9rYtUThAdPCOt89Pmn13tNX6AulxjGEP2R0nZjTSW3eQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.41': resolution: {integrity: sha512-NIYGuCcuXaq5BC4Q3upbiMBvmZsTsEPG9k/8QKQdmrch+ocSy5Jv9tdpdmXJyighKqm182nh/zBt+tSJkYoNlg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.42': + resolution: {integrity: sha512-hC1kShXW/z221eG+WzQMN06KepvPbMBknF0iGR3VMYJLOe9gwnSTfGxFT5hf8XrPv7CEZqTWRd0GQpkSHRbGsw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.41': resolution: {integrity: sha512-kANdsDbE5FkEOb5NrCGBJBCaZ2Sabp3D7d4PRqMYJqyLljwh9mDyYyYSv5+QNvdAmifj+f3lviNEUUuUZPEFPw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.42': + resolution: {integrity: sha512-AICBYromawouGjj+GS33369E8Vwhy6UwhQEhQ5evfS8jPCsyVvoICJatbDGDGH01dwtVGLD5eDFzPicUOVpe4g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.41': resolution: {integrity: sha512-UlpxKmFdik0Y2VjZrgUCgoYArZJiZllXgIipdBRV1hw6uK45UbQabSTW6Kp6enuOu7vouYWftwhuxfpE8J2JAg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.42': + resolution: {integrity: sha512-XpZ0M+tjoEiSc9c+uZR7FCnOI0uxDRNs1elGOMjeB0pUP1QmvVbZGYNsyLbLoP4u7e3VQN8rie1OQ8/mB6rcJg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.0-beta.41': resolution: {integrity: sha512-ycMEPrS3StOIeb87BT3/+bu+blEtyvwQ4zmo2IcJQy0Rd1DAAhKksA0iUZ3MYSpJtjlPhg0Eo6mvVS6ggPhRbw==} + '@rolldown/pluginutils@1.0.0-beta.42': + resolution: {integrity: sha512-N7pQzk9CyE7q0bBN/q0J8s6Db279r5kUZc6d7/wWRe9/zXqC52HQovVyu6iXPIDY4BEzzgbVLhVFXrOuGJ22ZQ==} + '@rollup/pluginutils@5.1.4': resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} engines: {node: '>=14.0.0'} @@ -4074,6 +4166,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rolldown@1.0.0-beta.42: + resolution: {integrity: sha512-xaPcckj+BbJhYLsv8gOqezc8EdMcKKe/gk8v47B0KPvgABDrQ0qmNPAiT/gh9n9Foe0bUkEv2qzj42uU5q1WRg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup@4.52.2: resolution: {integrity: sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -5305,6 +5402,13 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@napi-rs/wasm-runtime@1.0.6': + dependencies: + '@emnapi/core': 1.5.0 + '@emnapi/runtime': 1.5.0 + '@tybys/wasm-util': 0.10.1 + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -5321,6 +5425,8 @@ snapshots: '@oxc-project/types@0.93.0': {} + '@oxc-project/types@0.94.0': {} + '@playwright/test@1.55.1': dependencies: playwright: 1.55.1 @@ -5352,49 +5458,95 @@ snapshots: '@rolldown/binding-android-arm64@1.0.0-beta.41': optional: true + '@rolldown/binding-android-arm64@1.0.0-beta.42': + optional: true + '@rolldown/binding-darwin-arm64@1.0.0-beta.41': optional: true + '@rolldown/binding-darwin-arm64@1.0.0-beta.42': + optional: true + '@rolldown/binding-darwin-x64@1.0.0-beta.41': optional: true + '@rolldown/binding-darwin-x64@1.0.0-beta.42': + optional: true + '@rolldown/binding-freebsd-x64@1.0.0-beta.41': optional: true + '@rolldown/binding-freebsd-x64@1.0.0-beta.42': + optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.41': optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.42': + optional: true + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.41': optional: true + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.42': + optional: true + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.41': optional: true + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.42': + optional: true + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.41': optional: true + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.42': + optional: true + '@rolldown/binding-linux-x64-musl@1.0.0-beta.41': optional: true + '@rolldown/binding-linux-x64-musl@1.0.0-beta.42': + optional: true + '@rolldown/binding-openharmony-arm64@1.0.0-beta.41': optional: true + '@rolldown/binding-openharmony-arm64@1.0.0-beta.42': + optional: true + '@rolldown/binding-wasm32-wasi@1.0.0-beta.41': dependencies: '@napi-rs/wasm-runtime': 1.0.5 optional: true + '@rolldown/binding-wasm32-wasi@1.0.0-beta.42': + dependencies: + '@napi-rs/wasm-runtime': 1.0.6 + optional: true + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.41': optional: true + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.42': + optional: true + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.41': optional: true + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.42': + optional: true + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.41': optional: true + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.42': + optional: true + '@rolldown/pluginutils@1.0.0-beta.41': {} + '@rolldown/pluginutils@1.0.0-beta.42': {} + '@rollup/pluginutils@5.1.4(rollup@4.52.2)': dependencies: '@types/estree': 1.0.8 @@ -7648,7 +7800,7 @@ snapshots: rfdc@1.4.1: {} - rolldown-plugin-dts@0.16.11(rolldown@1.0.0-beta.41)(typescript@5.9.3): + rolldown-plugin-dts@0.16.11(rolldown@1.0.0-beta.42)(typescript@5.9.3): dependencies: '@babel/generator': 7.28.3 '@babel/parser': 7.28.4 @@ -7659,7 +7811,7 @@ snapshots: dts-resolver: 2.1.2 get-tsconfig: 4.10.1 magic-string: 0.30.19 - rolldown: 1.0.0-beta.41 + rolldown: 1.0.0-beta.42 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -7703,6 +7855,27 @@ snapshots: '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.41 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.41 + rolldown@1.0.0-beta.42: + dependencies: + '@oxc-project/types': 0.94.0 + '@rolldown/pluginutils': 1.0.0-beta.42 + ansis: 4.2.0 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-beta.42 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.42 + '@rolldown/binding-darwin-x64': 1.0.0-beta.42 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.42 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.42 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.42 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.42 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.42 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.42 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.42 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.42 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.42 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.42 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.42 + rollup@4.52.2: dependencies: '@types/estree': 1.0.8 @@ -7979,8 +8152,8 @@ snapshots: diff: 8.0.2 empathic: 2.0.0 hookable: 5.5.3 - rolldown: 1.0.0-beta.41 - rolldown-plugin-dts: 0.16.11(rolldown@1.0.0-beta.41)(typescript@5.9.3) + rolldown: 1.0.0-beta.42 + rolldown-plugin-dts: 0.16.11(rolldown@1.0.0-beta.42)(typescript@5.9.3) semver: 7.7.2 tinyexec: 1.0.1 tinyglobby: 0.2.15 From e80ef8ea18ed0684fffebd3da1c8dba2632929c8 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 8 Oct 2025 09:13:18 +0900 Subject: [PATCH 5/6] chore: comment --- packages/plugin-rsc/src/transforms/cjs.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/plugin-rsc/src/transforms/cjs.ts b/packages/plugin-rsc/src/transforms/cjs.ts index bb98b075..fa80ec4b 100644 --- a/packages/plugin-rsc/src/transforms/cjs.ts +++ b/packages/plugin-rsc/src/transforms/cjs.ts @@ -3,6 +3,9 @@ import MagicString from 'magic-string' import { analyze } from 'periscopic' import { walk } from 'estree-walker' +// TODO: +// 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) // This ensures we don't incorrectly unwrap .default on genuine ESM modules From 3b57374068ba9843afd5d9f803e45970b9d0b40b Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 8 Oct 2025 09:37:13 +0900 Subject: [PATCH 6/6] test: add test --- packages/plugin-rsc/package.json | 1 + packages/plugin-rsc/src/transforms/cjs.test.ts | 1 + .../src/transforms/fixtures/cjs/dual-lib.cjs | 2 ++ .../plugin-rsc/src/transforms/fixtures/cjs/entry.mjs | 2 ++ packages/plugin-rsc/test-dep/cjs-and-esm/index.cjs | 1 + packages/plugin-rsc/test-dep/cjs-and-esm/index.mjs | 1 + packages/plugin-rsc/test-dep/cjs-and-esm/package.json | 11 +++++++++++ pnpm-lock.yaml | 3 +++ 8 files changed, 22 insertions(+) create mode 100644 packages/plugin-rsc/src/transforms/fixtures/cjs/dual-lib.cjs create mode 100644 packages/plugin-rsc/test-dep/cjs-and-esm/index.cjs create mode 100644 packages/plugin-rsc/test-dep/cjs-and-esm/index.mjs create mode 100644 packages/plugin-rsc/test-dep/cjs-and-esm/package.json diff --git a/packages/plugin-rsc/package.json b/packages/plugin-rsc/package.json index ba4145a9..3a7d4150 100644 --- a/packages/plugin-rsc/package.json +++ b/packages/plugin-rsc/package.json @@ -56,6 +56,7 @@ "@types/react": "^19.1.16", "@types/react-dom": "^19.1.9", "@vitejs/plugin-react": "workspace:*", + "@vitejs/test-dep-cjs-and-esm": "./test-dep/cjs-and-esm", "react": "^19.1.1", "react-dom": "^19.1.1", "react-server-dom-webpack": "^19.1.1", diff --git a/packages/plugin-rsc/src/transforms/cjs.test.ts b/packages/plugin-rsc/src/transforms/cjs.test.ts index 9030f623..b4342448 100644 --- a/packages/plugin-rsc/src/transforms/cjs.test.ts +++ b/packages/plugin-rsc/src/transforms/cjs.test.ts @@ -169,6 +169,7 @@ export const __cjs_module_runner_transform = true; }, }, "depPrimitive": "[ok]", + "dualLib": "ok", } `) }) diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/dual-lib.cjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/dual-lib.cjs new file mode 100644 index 00000000..2ed97779 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/dual-lib.cjs @@ -0,0 +1,2 @@ +const lib = require('@vitejs/test-dep-cjs-and-esm') +module.exports = lib.ok diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs index 0fb91817..191d29e4 100644 --- a/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs @@ -4,6 +4,7 @@ import depFn from './function.cjs' import depPrimitive from './primitive.cjs' import depExports from './exports.cjs' import depFnRequire from './function-require.cjs' +import dualLib from './dual-lib.cjs' export { depDefault, depNamespace, @@ -11,4 +12,5 @@ export { depPrimitive, depExports, depFnRequire, + dualLib, } diff --git a/packages/plugin-rsc/test-dep/cjs-and-esm/index.cjs b/packages/plugin-rsc/test-dep/cjs-and-esm/index.cjs new file mode 100644 index 00000000..94dc9b3a --- /dev/null +++ b/packages/plugin-rsc/test-dep/cjs-and-esm/index.cjs @@ -0,0 +1 @@ +exports.ok = 'ok' diff --git a/packages/plugin-rsc/test-dep/cjs-and-esm/index.mjs b/packages/plugin-rsc/test-dep/cjs-and-esm/index.mjs new file mode 100644 index 00000000..4e2b7106 --- /dev/null +++ b/packages/plugin-rsc/test-dep/cjs-and-esm/index.mjs @@ -0,0 +1 @@ +export const ok = 'ok' diff --git a/packages/plugin-rsc/test-dep/cjs-and-esm/package.json b/packages/plugin-rsc/test-dep/cjs-and-esm/package.json new file mode 100644 index 00000000..d31fd3f5 --- /dev/null +++ b/packages/plugin-rsc/test-dep/cjs-and-esm/package.json @@ -0,0 +1,11 @@ +{ + "name": "@vitejs/test-dep-cjs-and-esm", + "private": true, + "type": "module", + "exports": { + ".": { + "require": "./index.cjs", + "default": "./index.mjs" + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab2d7c36..24541584 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -481,6 +481,9 @@ importers: '@vitejs/plugin-react': specifier: workspace:* version: link:../plugin-react + '@vitejs/test-dep-cjs-and-esm': + specifier: ./test-dep/cjs-and-esm + version: link:test-dep/cjs-and-esm react: specifier: ^19.1.1 version: 19.1.1