Skip to content

Commit f63bb83

Browse files
authored
fix(rsc): fix cjs transform to preserve module.exports on require side and allow exports assignment + expose cjsModuleRunnerPlugin (#833)
1 parent 6d6890e commit f63bb83

File tree

9 files changed

+110
-23
lines changed

9 files changed

+110
-23
lines changed

packages/plugin-rsc/src/transforms/cjs.test.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe(transformCjsToEsm, () => {
2222
exports.ok = true;
2323
`
2424
expect(await testTransform(input)).toMatchInlineSnapshot(`
25-
"const exports = {}; const module = { exports };
25+
"let exports = {}; const module = { exports };
2626
exports.ok = true;
2727
"
2828
`)
@@ -37,11 +37,11 @@ if (true) {
3737
}
3838
`
3939
expect(await testTransform(input)).toMatchInlineSnapshot(`
40-
"const exports = {}; const module = { exports };
40+
"let exports = {}; const module = { exports };
4141
if (true) {
42-
module.exports = (await import('./cjs/use-sync-external-store.production.js'));
42+
module.exports = ((await import('./cjs/use-sync-external-store.production.js')).default);
4343
} else {
44-
module.exports = (await import('./cjs/use-sync-external-store.development.js'));
44+
module.exports = ((await import('./cjs/use-sync-external-store.development.js')).default);
4545
}
4646
"
4747
`)
@@ -56,9 +56,9 @@ if (true) {
5656
})()
5757
`
5858
expect(await testTransform(input)).toMatchInlineSnapshot(`
59-
"const exports = {}; const module = { exports };
60-
const __cjs_to_esm_hoist_0 = await import("react");
61-
const __cjs_to_esm_hoist_1 = await import("react-dom");
59+
"let exports = {}; const module = { exports };
60+
const __cjs_to_esm_hoist_0 = (await import("react")).default;
61+
const __cjs_to_esm_hoist_1 = (await import("react-dom")).default;
6262
"production" !== process.env.NODE_ENV && (function() {
6363
var React = __cjs_to_esm_hoist_0;
6464
var ReactDOM = __cjs_to_esm_hoist_1;
@@ -81,13 +81,13 @@ function test() {
8181
}
8282
`
8383
expect(await testTransform(input)).toMatchInlineSnapshot(`
84-
"const exports = {}; const module = { exports };
85-
const __cjs_to_esm_hoist_0 = await import("te" + "st");
86-
const __cjs_to_esm_hoist_1 = await import("test");
87-
const __cjs_to_esm_hoist_2 = await import("test");
88-
const x1 = (await import("te" + "st"));
89-
const x2 = (await import("test"))().test;
90-
console.log((await import("test")))
84+
"let exports = {}; const module = { exports };
85+
const __cjs_to_esm_hoist_0 = (await import("te" + "st")).default;
86+
const __cjs_to_esm_hoist_1 = (await import("test")).default;
87+
const __cjs_to_esm_hoist_2 = (await import("test")).default;
88+
const x1 = ((await import("te" + "st")).default);
89+
const x2 = ((await import("test")).default)().test;
90+
console.log(((await import("test")).default))
9191
9292
function test() {
9393
const y1 = __cjs_to_esm_hoist_0;
@@ -106,7 +106,7 @@ function test() {
106106
}
107107
`
108108
expect(await testTransform(input)).toMatchInlineSnapshot(`
109-
"const exports = {}; const module = { exports };
109+
"let exports = {}; const module = { exports };
110110
{
111111
const require = () => {};
112112
require("test");
@@ -150,6 +150,11 @@ export default module.exports;
150150
"a": "a",
151151
"b": "b",
152152
},
153+
"depExports": {},
154+
"depFn": [Function],
155+
"depFnRequire": {
156+
"value": 3,
157+
},
153158
"depNamespace": {
154159
"a": "a",
155160
"b": "b",
@@ -158,6 +163,7 @@ export default module.exports;
158163
"b": "b",
159164
},
160165
},
166+
"depPrimitive": "[ok]",
161167
}
162168
`)
163169
})

packages/plugin-rsc/src/transforms/cjs.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,18 @@ export function transformCjsToEsm(
4141
if (isTopLevel) {
4242
// top-level scope `require` to dynamic import
4343
// (this allows handling react development/production re-export within top-level if branch)
44-
output.update(node.start, node.callee.end, '(await import')
45-
output.appendRight(node.end, ')')
44+
output.update(node.start, node.callee.end, '((await import')
45+
output.appendRight(node.end, ').default)')
4646
} else {
4747
// hoist non top-level `require` to top-level
4848
const hoisted = `__cjs_to_esm_hoist_${hoistIndex}`
4949
const importee = code.slice(
5050
node.arguments[0]!.start,
5151
node.arguments[0]!.end,
5252
)
53-
hoistedCodes.push(`const ${hoisted} = await import(${importee});\n`)
53+
hoistedCodes.push(
54+
`const ${hoisted} = (await import(${importee})).default;\n`,
55+
)
5456
output.update(node.start, node.end, hoisted)
5557
hoistIndex++
5658
}
@@ -63,6 +65,7 @@ export function transformCjsToEsm(
6365
for (const hoisted of hoistedCodes.reverse()) {
6466
output.prepend(hoisted)
6567
}
66-
output.prepend(`const exports = {}; const module = { exports };\n`)
68+
// https://nodejs.org/docs/v22.19.0/api/modules.html#exports-shortcut
69+
output.prepend(`let exports = {}; const module = { exports };\n`)
6770
return { output }
6871
}
Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
11
import depDefault from './dep1.cjs'
22
import * as depNamespace from './dep2.cjs'
3-
export { depDefault, depNamespace }
3+
import depFn from './function.cjs'
4+
import depPrimitive from './primitive.cjs'
5+
import depExports from './exports.cjs'
6+
import depFnRequire from './function-require.cjs'
7+
export {
8+
depDefault,
9+
depNamespace,
10+
depFn,
11+
depPrimitive,
12+
depExports,
13+
depFnRequire,
14+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exports = '[not-exports]'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
const lib = require('./function.cjs')
2+
exports.value = lib(1, 2)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = (x, y) => x + y
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = '[ok]'

packages/plugin-rsc/tsdown.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export default defineConfig({
1919
'src/extra/ssr.tsx',
2020
'src/extra/rsc.tsx',
2121
'src/transforms/index.ts',
22+
'src/plugins/cjs.ts',
2223
'src/rsc-html-stream/ssr.ts',
2324
'src/rsc-html-stream/browser.ts',
2425
'src/utils/rpc.ts',

pnpm-lock.yaml

Lines changed: 64 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)