From d53dfd58bb7423ccca583ab60d0b4695f332dc31 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 10 Sep 2025 16:49:00 +0900 Subject: [PATCH 1/3] feat(rsc): expose `cjsModuleRunnerPlugin` --- packages/plugin-rsc/tsdown.config.ts | 1 + pnpm-lock.yaml | 67 ++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/packages/plugin-rsc/tsdown.config.ts b/packages/plugin-rsc/tsdown.config.ts index 7a79d5e7..a385c837 100644 --- a/packages/plugin-rsc/tsdown.config.ts +++ b/packages/plugin-rsc/tsdown.config.ts @@ -19,6 +19,7 @@ export default defineConfig({ 'src/extra/ssr.tsx', 'src/extra/rsc.tsx', 'src/transforms/index.ts', + 'src/plugins/cjs.ts', 'src/rsc-html-stream/ssr.ts', 'src/rsc-html-stream/browser.ts', 'src/utils/rpc.ts', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94240346..988e2b76 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,12 @@ settings: autoInstallPeers: false excludeLinksFromLockfile: false +catalogs: + rolldown-vite: + vite: + specifier: npm:rolldown-vite@^7.1.6 + version: 7.1.8 + overrides: '@types/estree': ^1.0.8 @@ -131,7 +137,7 @@ importers: version: 0.14.2(publint@0.3.12)(typescript@5.9.2) vite: specifier: catalog:rolldown-vite - version: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1) + version: rolldown-vite@7.1.8(@types/node@22.18.1)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1) packages/plugin-react-swc: dependencies: @@ -4448,6 +4454,46 @@ packages: vue-tsc: optional: true + rolldown-vite@7.1.8: + resolution: {integrity: sha512-AfI/iNNsTjJv6E3nUSAra8bP4j30MZTt8JSB6iBZP1dblxnF6+3EE6TXQc75M69aH/Cr5p6N1Sk/1JyTDKFgOg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + esbuild: ^0.25.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + rolldown@1.0.0-beta.35: resolution: {integrity: sha512-gJATyqcsJe0Cs8RMFO8XgFjfTc0lK1jcSvirDQDSIfsJE+vt53QH/Ob+OBSJsXb98YtZXHfP/bHpELpPwCprow==} hasBin: true @@ -8379,6 +8425,21 @@ snapshots: - oxc-resolver - supports-color + rolldown-vite@7.1.8(@types/node@22.18.1)(esbuild@0.25.9)(jiti@2.5.1)(yaml@2.8.1): + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + lightningcss: 1.30.1 + picomatch: 4.0.3 + postcss: 8.5.6 + rolldown: 1.0.0-beta.36 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.18.1 + esbuild: 0.25.9 + fsevents: 2.3.3 + jiti: 2.5.1 + yaml: 2.8.1 + rolldown@1.0.0-beta.35: dependencies: '@oxc-project/runtime': 0.82.3 @@ -8686,7 +8747,7 @@ snapshots: tinyglobby@0.2.14: dependencies: - fdir: 6.4.6(picomatch@4.0.3) + fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 tinyglobby@0.2.15: @@ -8919,7 +8980,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.2(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1) + vite: 7.1.5(@types/node@22.18.1)(jiti@2.5.1)(lightningcss@1.30.1)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti From 8da8356eb814ad4ff759344fc618d8511e940dee Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 10 Sep 2025 17:16:11 +0900 Subject: [PATCH 2/3] fix: fix exports reassign --- packages/plugin-rsc/src/transforms/cjs.test.ts | 13 ++++++++----- packages/plugin-rsc/src/transforms/cjs.ts | 3 ++- .../src/transforms/fixtures/cjs/entry.mjs | 5 ++++- .../src/transforms/fixtures/cjs/exports.cjs | 1 + .../src/transforms/fixtures/cjs/function.cjs | 1 + .../src/transforms/fixtures/cjs/primitive.cjs | 1 + 6 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 packages/plugin-rsc/src/transforms/fixtures/cjs/exports.cjs create mode 100644 packages/plugin-rsc/src/transforms/fixtures/cjs/function.cjs create mode 100644 packages/plugin-rsc/src/transforms/fixtures/cjs/primitive.cjs diff --git a/packages/plugin-rsc/src/transforms/cjs.test.ts b/packages/plugin-rsc/src/transforms/cjs.test.ts index c1c3d1d3..a30b2cac 100644 --- a/packages/plugin-rsc/src/transforms/cjs.test.ts +++ b/packages/plugin-rsc/src/transforms/cjs.test.ts @@ -22,7 +22,7 @@ describe(transformCjsToEsm, () => { exports.ok = true; ` expect(await testTransform(input)).toMatchInlineSnapshot(` - "const exports = {}; const module = { exports }; + "let exports = {}; const module = { exports }; exports.ok = true; " `) @@ -37,7 +37,7 @@ if (true) { } ` expect(await testTransform(input)).toMatchInlineSnapshot(` - "const exports = {}; const module = { exports }; + "let exports = {}; const module = { exports }; if (true) { module.exports = (await import('./cjs/use-sync-external-store.production.js')); } else { @@ -56,7 +56,7 @@ if (true) { })() ` expect(await testTransform(input)).toMatchInlineSnapshot(` - "const exports = {}; const module = { exports }; + "let exports = {}; const module = { exports }; const __cjs_to_esm_hoist_0 = await import("react"); const __cjs_to_esm_hoist_1 = await import("react-dom"); "production" !== process.env.NODE_ENV && (function() { @@ -81,7 +81,7 @@ function test() { } ` expect(await testTransform(input)).toMatchInlineSnapshot(` - "const exports = {}; const module = { exports }; + "let exports = {}; const module = { exports }; const __cjs_to_esm_hoist_0 = await import("te" + "st"); const __cjs_to_esm_hoist_1 = await import("test"); const __cjs_to_esm_hoist_2 = await import("test"); @@ -106,7 +106,7 @@ function test() { } ` expect(await testTransform(input)).toMatchInlineSnapshot(` - "const exports = {}; const module = { exports }; + "let exports = {}; const module = { exports }; { const require = () => {}; require("test"); @@ -150,6 +150,8 @@ export default module.exports; "a": "a", "b": "b", }, + "depExports": {}, + "depFn": [Function], "depNamespace": { "a": "a", "b": "b", @@ -158,6 +160,7 @@ export default module.exports; "b": "b", }, }, + "depPrimitive": "[ok]", } `) }) diff --git a/packages/plugin-rsc/src/transforms/cjs.ts b/packages/plugin-rsc/src/transforms/cjs.ts index ef5ce338..7fda8803 100644 --- a/packages/plugin-rsc/src/transforms/cjs.ts +++ b/packages/plugin-rsc/src/transforms/cjs.ts @@ -63,6 +63,7 @@ export function transformCjsToEsm( for (const hoisted of hoistedCodes.reverse()) { output.prepend(hoisted) } - output.prepend(`const exports = {}; const module = { exports };\n`) + // https://nodejs.org/docs/v22.19.0/api/modules.html#exports-shortcut + output.prepend(`let exports = {}; const module = { exports };\n`) return { output } } diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs index bdcffd80..44d64a59 100644 --- a/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs @@ -1,3 +1,6 @@ import depDefault from './dep1.cjs' import * as depNamespace from './dep2.cjs' -export { depDefault, depNamespace } +import depFn from './function.cjs' +import depPrimitive from './primitive.cjs' +import depExports from './exports.cjs' +export { depDefault, depNamespace, depFn, depPrimitive, depExports } diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/exports.cjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/exports.cjs new file mode 100644 index 00000000..c3305d7f --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/exports.cjs @@ -0,0 +1 @@ +exports = '[not-exports]' diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/function.cjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/function.cjs new file mode 100644 index 00000000..a023e0e4 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/function.cjs @@ -0,0 +1 @@ +module.exports = (x, y) => x + y diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/primitive.cjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/primitive.cjs new file mode 100644 index 00000000..f800a20a --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/primitive.cjs @@ -0,0 +1 @@ +module.exports = '[ok]' From e444f98c81e61cfc1f3885ed0b9b4986a0247ca8 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 10 Sep 2025 17:33:47 +0900 Subject: [PATCH 3/3] fix: rewrite `require` with `import + default` --- .../plugin-rsc/src/transforms/cjs.test.ts | 23 +++++++++++-------- packages/plugin-rsc/src/transforms/cjs.ts | 8 ++++--- .../src/transforms/fixtures/cjs/entry.mjs | 10 +++++++- .../fixtures/cjs/function-require.cjs | 2 ++ 4 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 packages/plugin-rsc/src/transforms/fixtures/cjs/function-require.cjs diff --git a/packages/plugin-rsc/src/transforms/cjs.test.ts b/packages/plugin-rsc/src/transforms/cjs.test.ts index a30b2cac..11cc6d22 100644 --- a/packages/plugin-rsc/src/transforms/cjs.test.ts +++ b/packages/plugin-rsc/src/transforms/cjs.test.ts @@ -39,9 +39,9 @@ if (true) { expect(await testTransform(input)).toMatchInlineSnapshot(` "let exports = {}; const module = { exports }; if (true) { - module.exports = (await import('./cjs/use-sync-external-store.production.js')); + module.exports = ((await import('./cjs/use-sync-external-store.production.js')).default); } else { - module.exports = (await import('./cjs/use-sync-external-store.development.js')); + module.exports = ((await import('./cjs/use-sync-external-store.development.js')).default); } " `) @@ -57,8 +57,8 @@ if (true) { ` expect(await testTransform(input)).toMatchInlineSnapshot(` "let exports = {}; const module = { exports }; - const __cjs_to_esm_hoist_0 = await import("react"); - const __cjs_to_esm_hoist_1 = await import("react-dom"); + const __cjs_to_esm_hoist_0 = (await import("react")).default; + const __cjs_to_esm_hoist_1 = (await import("react-dom")).default; "production" !== process.env.NODE_ENV && (function() { var React = __cjs_to_esm_hoist_0; var ReactDOM = __cjs_to_esm_hoist_1; @@ -82,12 +82,12 @@ function test() { ` expect(await testTransform(input)).toMatchInlineSnapshot(` "let exports = {}; const module = { exports }; - const __cjs_to_esm_hoist_0 = await import("te" + "st"); - const __cjs_to_esm_hoist_1 = await import("test"); - const __cjs_to_esm_hoist_2 = await import("test"); - const x1 = (await import("te" + "st")); - const x2 = (await import("test"))().test; - console.log((await import("test"))) + 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 test() { const y1 = __cjs_to_esm_hoist_0; @@ -152,6 +152,9 @@ export default module.exports; }, "depExports": {}, "depFn": [Function], + "depFnRequire": { + "value": 3, + }, "depNamespace": { "a": "a", "b": "b", diff --git a/packages/plugin-rsc/src/transforms/cjs.ts b/packages/plugin-rsc/src/transforms/cjs.ts index 7fda8803..de89e892 100644 --- a/packages/plugin-rsc/src/transforms/cjs.ts +++ b/packages/plugin-rsc/src/transforms/cjs.ts @@ -41,8 +41,8 @@ export function transformCjsToEsm( if (isTopLevel) { // top-level scope `require` to dynamic import // (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, ')') + output.update(node.start, node.callee.end, '((await import') + output.appendRight(node.end, ').default)') } else { // hoist non top-level `require` to top-level const hoisted = `__cjs_to_esm_hoist_${hoistIndex}` @@ -50,7 +50,9 @@ export function transformCjsToEsm( node.arguments[0]!.start, node.arguments[0]!.end, ) - hoistedCodes.push(`const ${hoisted} = await import(${importee});\n`) + hoistedCodes.push( + `const ${hoisted} = (await import(${importee})).default;\n`, + ) output.update(node.start, node.end, hoisted) hoistIndex++ } diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs index 44d64a59..0fb91817 100644 --- a/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/entry.mjs @@ -3,4 +3,12 @@ import * as depNamespace from './dep2.cjs' import depFn from './function.cjs' import depPrimitive from './primitive.cjs' import depExports from './exports.cjs' -export { depDefault, depNamespace, depFn, depPrimitive, depExports } +import depFnRequire from './function-require.cjs' +export { + depDefault, + depNamespace, + depFn, + depPrimitive, + depExports, + depFnRequire, +} diff --git a/packages/plugin-rsc/src/transforms/fixtures/cjs/function-require.cjs b/packages/plugin-rsc/src/transforms/fixtures/cjs/function-require.cjs new file mode 100644 index 00000000..c69d4152 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/cjs/function-require.cjs @@ -0,0 +1,2 @@ +const lib = require('./function.cjs') +exports.value = lib(1, 2)