diff --git a/packages/next-swc/crates/core/src/server_actions.rs b/packages/next-swc/crates/core/src/server_actions.rs index 0c66f8734fa53..9a41200cd9513 100644 --- a/packages/next-swc/crates/core/src/server_actions.rs +++ b/packages/next-swc/crates/core/src/server_actions.rs @@ -77,7 +77,9 @@ struct ServerActions { closure_idents: Vec, action_idents: Vec, async_fn_idents: Vec, - exported_idents: Vec, + + // (ident, is default export) + exported_idents: Vec<(Id, bool)>, annotations: Vec, extra_items: Vec, @@ -85,9 +87,14 @@ struct ServerActions { } impl ServerActions { - fn get_action_info(&mut self, ident: &mut Ident, function: &mut Box) -> (bool, bool) { + fn get_action_info( + &mut self, + ident: &mut Ident, + function: &mut Box, + ) -> (bool, bool, bool) { let mut is_action_fn = false; - let mut is_exported = false; + let mut is_exported = self.in_export_decl; + let mut is_default_export = self.in_default_export_decl; if self.in_action_file && self.in_export_decl { // All export functions in a server file are actions @@ -103,13 +110,18 @@ impl ServerActions { } // If it's exported via named export, it's a valid action. - if !is_action_fn && self.exported_idents.contains(&ident.to_id()) { + let exported_ident = self + .exported_idents + .iter() + .find(|(id, _)| id == &ident.to_id()); + if let Some((_, is_default)) = exported_ident { is_action_fn = true; is_exported = true; + is_default_export = *is_default; } } - (is_action_fn, is_exported) + (is_action_fn, is_exported, is_default_export) } fn add_action_annotations( @@ -117,6 +129,7 @@ impl ServerActions { ident: &mut Ident, function: &mut Box, is_exported: bool, + is_default_export: bool, ) -> Option> { if !function.is_async { HANDLER.with(|handler| { @@ -134,7 +147,7 @@ impl ServerActions { }; let action_ident = private_ident!(action_name.clone()); - let export_name: JsWord = if self.in_default_export_decl { + let export_name: JsWord = if is_default_export { "default".into() } else { action_name @@ -315,7 +328,7 @@ impl VisitMut for ServerActions { } } - let (is_action_fn, is_exported) = + let (is_action_fn, is_exported, is_default_export) = self.get_action_info(f.ident.as_mut().unwrap(), &mut f.function); { @@ -336,8 +349,12 @@ impl VisitMut for ServerActions { return; } - let maybe_new_fn = - self.add_action_annotations(f.ident.as_mut().unwrap(), &mut f.function, is_exported); + let maybe_new_fn = self.add_action_annotations( + f.ident.as_mut().unwrap(), + &mut f.function, + is_exported, + is_default_export, + ); if let Some(new_fn) = maybe_new_fn { f.function = new_fn; @@ -354,7 +371,8 @@ impl VisitMut for ServerActions { return; } - let (is_action_fn, is_exported) = self.get_action_info(&mut f.ident, &mut f.function); + let (is_action_fn, is_exported, is_default_export) = + self.get_action_info(&mut f.ident, &mut f.function); { // Visit children @@ -374,7 +392,12 @@ impl VisitMut for ServerActions { return; } - let maybe_new_fn = self.add_action_annotations(&mut f.ident, &mut f.function, is_exported); + let maybe_new_fn = self.add_action_annotations( + &mut f.ident, + &mut f.function, + is_exported, + is_default_export, + ); if let Some(new_fn) = maybe_new_fn { f.function = new_fn; @@ -468,7 +491,8 @@ impl VisitMut for ServerActions { .. })) => { let ids: Vec = collect_idents_in_var_decls(&var.decls); - self.exported_idents.extend(ids); + self.exported_idents + .extend(ids.into_iter().map(|id| (id, false))); } ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(named)) => { for spec in &named.specifiers { @@ -478,10 +502,19 @@ impl VisitMut for ServerActions { }) = spec { // export { foo, foo as bar } - self.exported_idents.push(ident.to_id()); + self.exported_idents.push((ident.to_id(), false)); } } } + ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(ExportDefaultExpr { + expr, + .. + })) => { + if let Expr::Ident(ident) = &**expr { + // export default foo + self.exported_idents.push((ident.to_id(), true)); + } + } _ => {} } @@ -555,6 +588,11 @@ impl VisitMut for ServerActions { .. })) => match &**expr { Expr::Fn(_f) => {} + Expr::Ident(ident) => { + if !self.async_fn_idents.contains(&ident.to_id()) { + disallowed_export_span = *span; + } + } _ => { disallowed_export_span = *span; } diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/12/input.js b/packages/next-swc/crates/core/tests/fixture/server-actions/12/input.js new file mode 100644 index 0000000000000..379c0151a3218 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/12/input.js @@ -0,0 +1,3 @@ +'use server' +async function foo () {} +export default foo \ No newline at end of file diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/12/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/12/output.js new file mode 100644 index 0000000000000..adb6819b17db0 --- /dev/null +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/12/output.js @@ -0,0 +1,5 @@ +/* __next_internal_action_entry_do_not_use__ default */ async function foo() {} +foo.$$typeof = Symbol.for("react.server.reference"); +foo.$$id = "c18c215a6b7cdc64bf709f3a714ffdef1bf9651d"; +foo.$$bound = []; +export default foo; \ No newline at end of file