Skip to content

Commit d2f2e71

Browse files
authored
fix(rsc): expose only "use server" as server functions (#752)
1 parent 9988f54 commit d2f2e71

File tree

7 files changed

+74
-5
lines changed

7 files changed

+74
-5
lines changed

packages/plugin-rsc/examples/basic/src/routes/root.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { TestImportMetaGlob } from './import-meta-glob/server'
4040
import { TestAssetsServer } from './assets/server'
4141
import { TestHmrSwitchServer } from './hmr-switch/server'
4242
import { TestHmrSwitchClient } from './hmr-switch/client'
43+
import { TestTreeShakeServer } from './tree-shake/server'
4344

4445
export function Root(props: { url: URL }) {
4546
return (
@@ -93,6 +94,7 @@ export function Root(props: { url: URL }) {
9394
<TestCssQueries />
9495
<TestImportMetaGlob />
9596
<TestAssetsServer />
97+
<TestTreeShakeServer />
9698
</body>
9799
</html>
98100
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export function TestTreeShakeServer() {
2+
return (
3+
<form
4+
action={async () => {
5+
'use server'
6+
console.log('test-tree-shake-server')
7+
}}
8+
>
9+
<button>test-tree-shake-server</button>
10+
</form>
11+
)
12+
}
13+
14+
// this should not be exported as server functions
15+
export function __unused_server_export__() {
16+
console.log('__unused_server_export__')
17+
}

packages/plugin-rsc/examples/basic/vite.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ export default defineConfig({
2525
fileName !== '__server_secret.txt',
2626
}),
2727
{
28-
name: 'test-client-reference-tree-shaking',
28+
name: 'test-tree-shake',
2929
enforce: 'post',
3030
writeBundle(_options, bundle) {
3131
for (const chunk of Object.values(bundle)) {
3232
if (chunk.type === 'chunk') {
3333
assert(!chunk.code.includes('__unused_client_reference__'))
34+
assert(!chunk.code.includes('__unused_server_export__'))
3435
}
3536
}
3637
},

packages/plugin-rsc/src/plugin.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,8 @@ type ClientReferenceMeta = {
5959
type ServerRerferenceMeta = {
6060
importId: string
6161
referenceKey: string
62-
// TODO: expose only "use server" exports
63-
// TODO: tree shake unused exports
64-
// exportNames: string[]
62+
// TODO: tree shake unused server functions
63+
exportNames: string[]
6564
}
6665

6766
const PKG_NAME = '@vitejs/plugin-rsc'
@@ -1458,6 +1457,7 @@ function vitePluginUseServer(
14581457
manager.serverReferenceMetaMap[id] = {
14591458
importId: id,
14601459
referenceKey: getNormalizedId(),
1460+
exportNames: 'names' in result ? result.names : result.exportNames,
14611461
}
14621462
const importSource = resolvePackage(`${PKG_NAME}/react/rsc`)
14631463
output.prepend(`import * as $$ReactServer from "${importSource}";\n`)
@@ -1499,6 +1499,7 @@ function vitePluginUseServer(
14991499
manager.serverReferenceMetaMap[id] = {
15001500
importId: id,
15011501
referenceKey: getNormalizedId(),
1502+
exportNames: result.exportNames,
15021503
}
15031504
const name =
15041505
this.environment.name === browserEnvironmentName ? 'browser' : 'ssr'
@@ -1519,7 +1520,15 @@ function vitePluginUseServer(
15191520
for (const meta of Object.values(manager.serverReferenceMetaMap)) {
15201521
const key = JSON.stringify(meta.referenceKey)
15211522
const id = JSON.stringify(meta.importId)
1522-
code += `${key}: () => import(${id}),`
1523+
const exports = meta.exportNames
1524+
.map((name) => (name === 'default' ? 'default: _default' : name))
1525+
.sort()
1526+
code += `
1527+
${key}: async () => {
1528+
const {${exports}} = await import(${id});
1529+
return {${exports}};
1530+
},
1531+
`
15231532
}
15241533
code = `export default {${code}};\n`
15251534
return { code, map: null }

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ describe(transformHoistInlineDirective, () => {
3535
return output.toString()
3636
}
3737

38+
async function testTransformNames(input: string) {
39+
const ast = await parseAstAsync(input)
40+
const result = transformHoistInlineDirective(input, ast, {
41+
runtime: (value, name) =>
42+
`$$register(${value}, "<id>", ${JSON.stringify(name)})`,
43+
directive: 'use server',
44+
})
45+
return result.names
46+
}
47+
3848
it('none', async () => {
3949
const input = `
4050
const x = "x";
@@ -104,6 +114,14 @@ export default function w() {
104114
expect(await testTransform(input, { encode: true })).toBe(
105115
await testTransform(input),
106116
)
117+
118+
expect(await testTransformNames(input)).toMatchInlineSnapshot(`
119+
[
120+
"$$hoist_0_f",
121+
"$$hoist_1_h",
122+
"$$hoist_2_w",
123+
]
124+
`)
107125
})
108126

109127
it('closure', async () => {

packages/plugin-rsc/src/transforms/wrap-export.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ async function testTransform(
2323
return output.hasChanged() && output.toString()
2424
}
2525

26+
async function testTransformNames(input: string) {
27+
const ast = await parseAstAsync(input)
28+
const result = transformWrapExport(input, ast, {
29+
runtime: (value, name) =>
30+
`$$wrap(${value}, "<id>", ${JSON.stringify(name)})`,
31+
ignoreExportAllDeclaration: true,
32+
})
33+
return result.exportNames
34+
}
35+
2636
describe(transformWrapExport, () => {
2737
test('basic', async () => {
2838
const input = `
@@ -52,6 +62,16 @@ export class Cls {};
5262
export { $$wrap_$$default as default };
5363
"
5464
`)
65+
66+
expect(await testTransformNames(input)).toMatchInlineSnapshot(`
67+
[
68+
"Arrow",
69+
"default",
70+
"Fn",
71+
"AsyncFn",
72+
"Cls",
73+
]
74+
`)
5575
})
5676

5777
test('preserve reference', async () => {

packages/plugin-rsc/src/transforms/wrap-export.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export function transformWrapExport(
3737
end: number,
3838
exports: { name: string; meta: ExportMeta }[],
3939
) {
40+
exportNames.push(...exports.map((e) => e.name))
4041
// update code and move to preserve `registerServerReference` position
4142
// e.g.
4243
// input
@@ -64,6 +65,7 @@ export function transformWrapExport(
6465
}
6566

6667
function wrapExport(name: string, exportName: string, meta: ExportMeta = {}) {
68+
exportNames.push(exportName)
6769
if (!filter(exportName, meta)) {
6870
toAppend.push(`export { ${name} as ${exportName} }`)
6971
return

0 commit comments

Comments
 (0)