@@ -4,6 +4,51 @@ import {
44} from '../_testing/utils'
55
66// TODO: revisit when we have a better side-effect free transform approach for server action
7+ // These tests are checking that importing a single server action from a file that has multiple
8+ // server actions, only includes the used server action in the manifest. This has both security
9+ // benefits (not exposing unused server actions, though we also have the unguessable IDs anyway)
10+ // and bundle size benefits (not bundling unused server actions and their dependencies).
11+ //
12+ // https://github.com/vercel/next.js/pull/76877 originally implemented this optimization, but
13+ // only when importing server action modules from client components. This only happened
14+ // accidentally because the main goal was to not include the unused server action hashes in the
15+ // client chunk, see test/production/app-dir/actions-tree-shaking/client-actions-tree-shaking.
16+ //
17+ // Importing server action modules from server components did not have this optimization, so if
18+ // it would still bundle all server actions.
19+ //
20+ // https://github.com/vercel/next.js/pull/91212 then reworked the server action implementation
21+ // and now both use situation unfortunately cause all server actions to be bundled.
22+ //
23+ // The solution here is to rework the giant
24+ // crates/next-custom-transforms/src/transforms/server_actions.rs transform and perform the
25+ // dataurl reexports trick also when importing from server components.
26+ //
27+ // So when in the client layer (already implemented):
28+ // ```
29+ // export {foo} from "data:text/javascript,export const foo = createServerReference('HASH'); __turbopack_emit__('./actions.ts?server_actions_impl', {data: 'foo|HASH', exports: 'foo', with: {turbopackTransition: 'next-rsc'}})";
30+ // export {bar} from "data:...";
31+ // ```
32+ //
33+ // When in the server layer:
34+ // ```
35+ // export {foo} from "data:text/javascript,export {foo} from './actions.ts?server_actions_impl'; __turbopack_emit__('./actions.ts?server_actions_impl', {data: 'foo|HASH', exports: 'foo'})";
36+ // export {bar} from "data:...";
37+ // ```
38+ //
39+ // And `actions.ts?server_actions_impl` would be transformed to:
40+ // ```
41+ // export const foo = registerServerReference(async function foo() { ... })
42+ //
43+ // const $$RSC_SERVER_CACHE_0_INNER = async function my_fn() { return 'data'; };
44+ // export var $$RSC_SERVER_CACHE_0 = $$reactCache__(function my_fn() {
45+ // return $$cache__("default", "803128060c414d59f8552e4788b846c0d2b7f74743", 0, $$RSC_SERVER_CACHE_0_INNER, []);
46+ // });
47+ // export const my_fn = registerServerReference($$RSC_SERVER_CACHE_0);
48+ // ```
49+ //
50+ // This way, the actual actions.ts?server_actions_impl file has accurate used exports, so unused
51+ // code can be removed by inner graph tree shaking.
752; ( process . env . IS_TURBOPACK_TEST ? describe : describe . skip ) (
853 'actions-tree-shaking - basic' ,
954 ( ) => {
@@ -17,6 +62,8 @@ import {
1762 {
1863 "app/client/page": [
1964 "app/actions.js#clientComponentAction",
65+ "app/actions.js#serverComponentAction",
66+ "app/actions.js#unusedExportedAction",
2067 ],
2168 "app/inline/page": [
2269 "app/inline/page.js#$$RSC_SERVER_ACTION_0",
0 commit comments