From 0d28d70fa59772042349cd02996c1ac87a7aa101 Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Fri, 18 Oct 2024 19:36:47 +0200 Subject: [PATCH] Differentiate error messages in server function transforms --- .../src/transforms/server_actions.rs | 82 ++++++++++++------- .../server-actions/server-graph/14/input.js | 6 ++ .../server-actions/server-graph/14/output.js | 0 .../server-graph/14/output.stderr | 27 ++++++ .../server-actions/server-graph/15/input.js | 11 +++ .../server-actions/server-graph/15/output.js | 3 + .../server-graph/15/output.stderr | 20 +++++ .../server-actions/server-graph/3/output.js | 8 -- .../server-actions/server-graph/4/output.js | 7 -- .../server-actions/server-graph/5/output.js | 3 - .../server-actions/server-graph/6/output.js | 14 +++- .../server-graph/6/output.stderr | 4 +- .../server-actions/server-graph/7/output.js | 5 +- .../server-graph/7/output.stderr | 2 +- .../e2e/app-dir/actions/app/server/actions.js | 3 +- 15 files changed, 139 insertions(+), 56 deletions(-) create mode 100644 crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/input.js create mode 100644 crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/output.js create mode 100644 crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/output.stderr create mode 100644 crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/input.js create mode 100644 crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/output.js create mode 100644 crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/output.stderr diff --git a/crates/next-custom-transforms/src/transforms/server_actions.rs b/crates/next-custom-transforms/src/transforms/server_actions.rs index a7092d8c75cb36..f6f8de78e3d9e9 100644 --- a/crates/next-custom-transforms/src/transforms/server_actions.rs +++ b/crates/next-custom-transforms/src/transforms/server_actions.rs @@ -917,10 +917,21 @@ impl VisitMut for ServerActions { if (is_action_fn || cache_type.is_some()) && !f.function.is_async { HANDLER.with(|handler| { + let subject = if is_action_fn { + "Server Actions" + } else { + "\"use cache\" functions" + }; + handler - .struct_span_err(f.function.span, "Server Actions must be async functions") + .struct_span_err( + f.function.span, + &format!("{subject} must be async functions."), + ) .emit(); }); + + return; } if !is_action_fn && cache_type.is_none() || !self.config.is_react_server_layer { @@ -1041,9 +1052,14 @@ impl VisitMut for ServerActions { if !f.function.is_async { HANDLER.with(|handler| { handler - .struct_span_err(f.ident.span, "Cache functions must be async functions") + .struct_span_err( + f.ident.span, + "\"use cache\" functions must be async functions.", + ) .emit(); }); + + return; } // Collect all the identifiers defined inside the closure and used @@ -1177,16 +1193,20 @@ impl VisitMut for ServerActions { take(&mut self.names) }; - if !a.is_async - // Errors for in_action_file/in_cache_file are handled in `visit_mut_module_items`. - && (is_action_fn && !self.in_action_file - || cache_type.is_some() && self.in_cache_file.is_none()) - { + if !a.is_async && (is_action_fn || cache_type.is_some()) { HANDLER.with(|handler| { + let subject = if is_action_fn { + "Server Actions" + } else { + "\"use cache\" functions" + }; + handler - .struct_span_err(a.span, "Server Actions must be async functions") + .struct_span_err(a.span, &format!("{subject} must be async functions.")) .emit(); }); + + return; } if !is_action_fn && cache_type.is_none() || !self.config.is_react_server_layer { @@ -1424,28 +1444,24 @@ impl VisitMut for ServerActions { match &mut *default_expr.expr { Expr::Fn(_f) => {} Expr::Arrow(arrow) => { - if !arrow.is_async { - disallowed_export_span = default_expr.span; - } else { - // export default async () => {} - // Use the span of the arrow function - let span = arrow.span; + // export default async () => {} + // Use the span of the arrow function + let span = arrow.span; - let new_ident = Ident::new( - gen_action_ident(&mut self.reference_index), - span, - self.private_ctxt, - ); + let new_ident = Ident::new( + gen_action_ident(&mut self.reference_index), + span, + self.private_ctxt, + ); - self.exported_idents - .push((new_ident.clone(), "default".into())); + self.exported_idents + .push((new_ident.clone(), "default".into())); - create_var_declarator(&new_ident, &mut self.extra_items); - attach_name_to_default_expr(&new_ident, &mut self.extra_items); + create_var_declarator(&new_ident, &mut self.extra_items); + attach_name_to_default_expr(&new_ident, &mut self.extra_items); - *default_expr.expr = - assign_arrow_expr(&new_ident, Expr::Arrow(arrow.clone())); - } + *default_expr.expr = + assign_arrow_expr(&new_ident, Expr::Arrow(arrow.clone())); } Expr::Ident(ident) => { // export default foo @@ -1484,14 +1500,24 @@ impl VisitMut for ServerActions { if disallowed_export_span != DUMMY_SP { HANDLER.with(|handler| { + let directive = if self.in_action_file { + "\"use server\"" + } else { + "\"use cache\"" + }; + handler .struct_span_err( disallowed_export_span, - "Only async functions are allowed to be exported in a \"use \ - server\" file.", + &format!( + "Only async functions are allowed to be exported in a \ + {directive} file." + ), ) .emit(); }); + + return; } } diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/input.js b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/input.js new file mode 100644 index 00000000000000..2b9960f8e4f247 --- /dev/null +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/input.js @@ -0,0 +1,6 @@ +'use cache' + +export default function () {} +export function foo() {} +export const bar = () => {} +export const baz = 42 diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/output.js b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/output.js new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/output.stderr b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/output.stderr new file mode 100644 index 00000000000000..b9a34293ef1cad --- /dev/null +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/14/output.stderr @@ -0,0 +1,27 @@ + x "use cache" functions must be async functions. + ,-[input.js:3:1] + 2 | + 3 | export default function () {} + : ^^^^^^^^^^^^^^ + 4 | export function foo() {} + `---- + x "use cache" functions must be async functions. + ,-[input.js:4:1] + 3 | export default function () {} + 4 | export function foo() {} + : ^^^ + 5 | export const bar = () => {} + `---- + x "use cache" functions must be async functions. + ,-[input.js:5:1] + 4 | export function foo() {} + 5 | export const bar = () => {} + : ^^^^^^^^ + 6 | export const baz = 42 + `---- + x Only async functions are allowed to be exported in a "use cache" file. + ,-[input.js:6:1] + 5 | export const bar = () => {} + 6 | export const baz = 42 + : ^^^^^^^^^^^^^^^^^^^^^ + `---- diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/input.js b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/input.js new file mode 100644 index 00000000000000..b8a7c6b0e4a1c2 --- /dev/null +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/input.js @@ -0,0 +1,11 @@ +export default function () { + 'use cache' +} + +export function foo() { + 'use cache' +} + +export const bar = () => { + 'use cache' +} diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/output.js b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/output.js new file mode 100644 index 00000000000000..d39c6544feea56 --- /dev/null +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/output.js @@ -0,0 +1,3 @@ +export default function() {} +export function foo() {} +export const bar = ()=>{}; diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/output.stderr b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/output.stderr new file mode 100644 index 00000000000000..5e8da60a868d62 --- /dev/null +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/15/output.stderr @@ -0,0 +1,20 @@ + x "use cache" functions must be async functions. + ,-[input.js:1:1] + 1 | ,-> export default function () { + 2 | | 'use cache' + 3 | `-> } + `---- + x "use cache" functions must be async functions. + ,-[input.js:5:1] + 4 | + 5 | export function foo() { + : ^^^ + 6 | 'use cache' + `---- + x "use cache" functions must be async functions. + ,-[input.js:9:1] + 8 | + 9 | ,-> export const bar = () => { + 10 | | 'use cache' + 11 | `-> } + `---- diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/3/output.js b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/3/output.js index 720e5a317895c4..e69de29bb2d1d6 100644 --- a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/3/output.js +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/3/output.js @@ -1,8 +0,0 @@ -/* __next_internal_action_entry_do_not_use__ {"b78c261f135a7a852508c2920bd7228020ff4bd7":"x"} */ import { registerServerReference } from "private-next-rsc-server-reference"; -import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; -export const x = 1; -import { ensureServerEntryExports } from "private-next-rsc-action-validate"; -ensureServerEntryExports([ - x -]); -registerServerReference(x, "b78c261f135a7a852508c2920bd7228020ff4bd7", null); diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/4/output.js b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/4/output.js index 3fcb6a481c21e2..e69de29bb2d1d6 100644 --- a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/4/output.js +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/4/output.js @@ -1,7 +0,0 @@ -/* __next_internal_action_entry_do_not_use__ {} */ import { registerServerReference } from "private-next-rsc-server-reference"; -import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; -export default class Component { - render() { - return null; - } -} diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/5/output.js b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/5/output.js index fa6276e8e1c20f..e69de29bb2d1d6 100644 --- a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/5/output.js +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/5/output.js @@ -1,3 +0,0 @@ -/* __next_internal_action_entry_do_not_use__ {} */ import { registerServerReference } from "private-next-rsc-server-reference"; -import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; -export * from 'foo'; diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/6/output.js b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/6/output.js index 6f9598ffebb425..281ae22a8f035e 100644 --- a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/6/output.js +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/6/output.js @@ -1,3 +1,13 @@ -/* __next_internal_action_entry_do_not_use__ {} */ import { registerServerReference } from "private-next-rsc-server-reference"; +/* __next_internal_action_entry_do_not_use__ {"c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default"} */ import { registerServerReference } from "private-next-rsc-server-reference"; import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; -export default (()=>{}); +export default $$RSC_SERVER_ACTION_0 = ()=>{}; +var $$RSC_SERVER_ACTION_0; +Object.defineProperty($$RSC_SERVER_ACTION_0, "name", { + "value": "default", + "writable": false +}); +import { ensureServerEntryExports } from "private-next-rsc-action-validate"; +ensureServerEntryExports([ + $$RSC_SERVER_ACTION_0 +]); +registerServerReference($$RSC_SERVER_ACTION_0, "c18c215a6b7cdc64bf709f3a714ffdef1bf9651d", null); diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/6/output.stderr b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/6/output.stderr index cef97723cbc3d1..49a11a6843e017 100644 --- a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/6/output.stderr +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/6/output.stderr @@ -1,6 +1,6 @@ - x Only async functions are allowed to be exported in a "use server" file. + x Server Actions must be async functions. ,-[input.js:3:1] 2 | 3 | export default () => {} - : ^^^^^^^^^^^^^^^^^^^^^^^ + : ^^^^^^^^ `---- diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/7/output.js b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/7/output.js index e9be23d26a79a5..5d628dc268f943 100644 --- a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/7/output.js +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/7/output.js @@ -1,4 +1 @@ -/* __next_internal_action_entry_do_not_use__ {"6a88810ecce4a4e8b59d53b8327d7e98bbf251d7":"$$RSC_SERVER_ACTION_0"} */ import { registerServerReference } from "private-next-rsc-server-reference"; -import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; -export const $$RSC_SERVER_ACTION_0 = async function foo() {}; -const foo = registerServerReference($$RSC_SERVER_ACTION_0, "6a88810ecce4a4e8b59d53b8327d7e98bbf251d7", null); +const foo = ()=>{}; diff --git a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/7/output.stderr b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/7/output.stderr index 0d277c53ec0093..65af4cd13d05b4 100644 --- a/crates/next-custom-transforms/tests/errors/server-actions/server-graph/7/output.stderr +++ b/crates/next-custom-transforms/tests/errors/server-actions/server-graph/7/output.stderr @@ -1,4 +1,4 @@ - x Server Actions must be async functions + x Server Actions must be async functions. ,-[input.js:1:1] 1 | ,-> const foo = () => { 2 | | 'use server' diff --git a/test/e2e/app-dir/actions/app/server/actions.js b/test/e2e/app-dir/actions/app/server/actions.js index 4b350ab41eebd1..2a7a47ebc388ce 100644 --- a/test/e2e/app-dir/actions/app/server/actions.js +++ b/test/e2e/app-dir/actions/app/server/actions.js @@ -25,4 +25,5 @@ export async function redirectAction(formData) { } // Test case for https://github.com/vercel/next.js/issues/61183 -export const dummyServerAction = () => new Promise((r) => setTimeout(r, 2000)) +export const dummyServerAction = async () => + new Promise((r) => setTimeout(r, 2000))