Skip to content

Commit ef81b10

Browse files
shudingMaxLeiter
authored andcommitted
Fix Server Reference being double registered (#61244)
When we apply `createActionProxy` twice to the same value, it currently errors because we can't override the ID of it. This PR makes sure that 1) when the value already have an ID defined, we skip setting it again; 2) all the action IDs are being tracked even for duplicated ones. Note that it's technically impossible to "detect" the duplication here (via static analyzation) and only set it once. For example: ```ts 'use server' export async function foo () {} export const bar = a_value_we_dont_know_yet ? foo : async () => {} ``` So, the compiler will always wrap `createActionProxy` for every exported value and assume they're different Server Actions (hence different IDs). With this fix, if we find that it's already defined before, we just return the defined reference as they're strictly identical so the ID does't matter. Closes #54655, closes #61183. Closes NEXT-2264
1 parent bf6460e commit ef81b10

File tree

9 files changed

+49
-9
lines changed

9 files changed

+49
-9
lines changed

packages/next-swc/crates/next-custom-transforms/src/transforms/server_actions.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1021,11 +1021,11 @@ impl<C: Comments> VisitMut for ServerActions<C> {
10211021
}
10221022

10231023
if self.has_action {
1024-
let actions = if self.in_action_file {
1025-
self.exported_idents.iter().map(|e| e.1.clone()).collect()
1026-
} else {
1027-
self.export_actions.clone()
1024+
let mut actions = self.export_actions.clone();
1025+
if self.in_action_file {
1026+
actions.extend(self.exported_idents.iter().map(|e| e.1.clone()));
10281027
};
1028+
10291029
let actions = actions
10301030
.into_iter()
10311031
.map(|name| (generate_action_id(&self.file_name, &name), name))

packages/next-swc/crates/next-custom-transforms/tests/errors/server-actions/server-graph/6/output.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* __next_internal_action_entry_do_not_use__ {} */ import { createActionProxy } from "private-next-rsc-action-proxy";
1+
/* __next_internal_action_entry_do_not_use__ {"6d53ce510b2e36499b8f56038817b9bad86cabb4":"$$ACTION_0"} */ import { createActionProxy } from "private-next-rsc-action-proxy";
22
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
33
export default createActionProxy("6d53ce510b2e36499b8f56038817b9bad86cabb4", $$ACTION_0);
44
export async function $$ACTION_0() {}

packages/next-swc/crates/next-custom-transforms/tests/fixture/server-actions/server/15/output.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* __next_internal_action_entry_do_not_use__ {"c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default"} */ import { createActionProxy } from "private-next-rsc-action-proxy";
1+
/* __next_internal_action_entry_do_not_use__ {"188d5d945750dc32e2c842b93c75a65763d4a922":"$$ACTION_1","c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default"} */ import { createActionProxy } from "private-next-rsc-action-proxy";
22
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
33
export default $$ACTION_0 = createActionProxy("188d5d945750dc32e2c842b93c75a65763d4a922", $$ACTION_1);
44
var $$ACTION_0;

packages/next-swc/crates/next-custom-transforms/tests/fixture/server-actions/server/17/output.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* __next_internal_action_entry_do_not_use__ {"ab21efdafbe611287bc25c0462b1e0510d13e48b":"foo","ac840dcaf5e8197cb02b7f3a43c119b7a770b272":"bar"} */ import { createActionProxy } from "private-next-rsc-action-proxy";
1+
/* __next_internal_action_entry_do_not_use__ {"6d53ce510b2e36499b8f56038817b9bad86cabb4":"$$ACTION_0","ab21efdafbe611287bc25c0462b1e0510d13e48b":"foo","ac840dcaf5e8197cb02b7f3a43c119b7a770b272":"bar"} */ import { createActionProxy } from "private-next-rsc-action-proxy";
22
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
33
export const foo = createActionProxy("6d53ce510b2e36499b8f56038817b9bad86cabb4", $$ACTION_0);
44
export async function $$ACTION_0() {}

packages/next-swc/crates/next-custom-transforms/tests/fixture/server-actions/server/22/output.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* __next_internal_action_entry_do_not_use__ {"c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default","f14702b5a021dd117f7ec7a3c838f397c2046d3b":"action"} */ import { createActionProxy } from "private-next-rsc-action-proxy";
1+
/* __next_internal_action_entry_do_not_use__ {"6d53ce510b2e36499b8f56038817b9bad86cabb4":"$$ACTION_0","9878bfa39811ca7650992850a8751f9591b6a557":"$$ACTION_2","c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default","f14702b5a021dd117f7ec7a3c838f397c2046d3b":"action"} */ import { createActionProxy } from "private-next-rsc-action-proxy";
22
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
33
import { validator } from 'auth';
44
export const action = validator(createActionProxy("6d53ce510b2e36499b8f56038817b9bad86cabb4", $$ACTION_0));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use server'
2+
3+
export const dec = async (value) => {
4+
return value - 1
5+
}
6+
7+
// Test case for https://github.com/vercel/next.js/issues/54655
8+
export default dec
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* __next_internal_action_entry_do_not_use__ {"28baf972d345b86b747ad0df73d75a0088a42214":"dec","6d53ce510b2e36499b8f56038817b9bad86cabb4":"$$ACTION_0","c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default"} */ import { createActionProxy } from "private-next-rsc-action-proxy";
2+
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
3+
export const dec = createActionProxy("6d53ce510b2e36499b8f56038817b9bad86cabb4", $$ACTION_0);
4+
export async function $$ACTION_0(value) {
5+
return value - 1;
6+
}
7+
// Test case for https://github.com/vercel/next.js/issues/54655
8+
export default dec;
9+
import { ensureServerEntryExports } from "private-next-rsc-action-validate";
10+
ensureServerEntryExports([
11+
dec,
12+
dec
13+
]);
14+
createActionProxy("28baf972d345b86b747ad0df73d75a0088a42214", dec);
15+
createActionProxy("c18c215a6b7cdc64bf709f3a714ffdef1bf9651d", dec);
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
/* eslint-disable import/no-extraneous-dependencies */
22
import { registerServerReference } from 'react-server-dom-webpack/server.edge'
33

4+
const SERVER_REFERENCE_TAG = Symbol.for('react.server.reference')
5+
6+
function isServerReference(reference: any) {
7+
return reference && reference.$$typeof === SERVER_REFERENCE_TAG
8+
}
9+
410
export function createActionProxy(id: string, action: any) {
11+
// Avoid registering the same action twice
12+
if (isServerReference(action)) {
13+
return action
14+
}
15+
516
return registerServerReference(action, id, null)
617
}

test/e2e/app-dir/actions/app/server/actions.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ export async function slowInc(value) {
77
return value + 1
88
}
99

10-
export default async function dec(value) {
10+
export const dec = async (value) => {
1111
return value - 1
1212
}
1313

14+
// Test case for https://github.com/vercel/next.js/issues/54655
15+
export default dec
16+
1417
export async function redirectAction(formData) {
1518
'use server'
1619
redirect(
@@ -20,3 +23,6 @@ export async function redirectAction(formData) {
2023
formData.get('hidden-info')
2124
)
2225
}
26+
27+
// Test case for https://github.com/vercel/next.js/issues/61183
28+
export const dummyServerAction = () => new Promise((r) => setTimeout(r, 2000))

0 commit comments

Comments
 (0)