Skip to content

Commit

Permalink
Differentiate error messages in server function transforms
Browse files Browse the repository at this point in the history
  • Loading branch information
unstubbable committed Oct 18, 2024
1 parent 8d29d4b commit 0d28d70
Show file tree
Hide file tree
Showing 15 changed files with 139 additions and 56 deletions.
82 changes: 54 additions & 28 deletions crates/next-custom-transforms/src/transforms/server_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,10 +917,21 @@ impl<C: Comments> VisitMut for ServerActions<C> {

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 {
Expand Down Expand Up @@ -1041,9 +1052,14 @@ impl<C: Comments> VisitMut for ServerActions<C> {
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
Expand Down Expand Up @@ -1177,16 +1193,20 @@ impl<C: Comments> VisitMut for ServerActions<C> {
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 {
Expand Down Expand Up @@ -1424,28 +1444,24 @@ impl<C: Comments> VisitMut for ServerActions<C> {
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
Expand Down Expand Up @@ -1484,14 +1500,24 @@ impl<C: Comments> VisitMut for ServerActions<C> {

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;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use cache'

export default function () {}
export function foo() {}
export const bar = () => {}
export const baz = 42
Empty file.
Original file line number Diff line number Diff line change
@@ -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
: ^^^^^^^^^^^^^^^^^^^^^
`----
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function () {
'use cache'
}

export function foo() {
'use cache'
}

export const bar = () => {
'use cache'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function() {}
export function foo() {}
export const bar = ()=>{};
Original file line number Diff line number Diff line change
@@ -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 | `-> }
`----
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -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 () => {}
: ^^^^^^^^^^^^^^^^^^^^^^^
: ^^^^^^^^
`----
Original file line number Diff line number Diff line change
@@ -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 = ()=>{};
Original file line number Diff line number Diff line change
@@ -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'
Expand Down
3 changes: 2 additions & 1 deletion test/e2e/app-dir/actions/app/server/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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))

0 comments on commit 0d28d70

Please sign in to comment.