From ac0b6d2a719fd6656b555dc398e73c01c71feae4 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Wed, 25 Jan 2023 10:44:13 +0100 Subject: [PATCH] Update transform exports and tests (#45251) ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] [e2e](https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md) ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md) --- packages/next-swc/crates/core/src/lib.rs | 9 +- .../crates/core/src/server_actions.rs | 91 ++++++++++++------- .../next-swc/crates/core/tests/fixture.rs | 3 +- .../tests/fixture/server-actions/1/output.js | 2 +- .../tests/fixture/server-actions/2/output.js | 2 +- .../tests/fixture/server-actions/3/output.js | 3 +- 6 files changed, 69 insertions(+), 41 deletions(-) diff --git a/packages/next-swc/crates/core/src/lib.rs b/packages/next-swc/crates/core/src/lib.rs index ee003cc304fb..4618f42fe479 100644 --- a/packages/next-swc/crates/core/src/lib.rs +++ b/packages/next-swc/crates/core/src/lib.rs @@ -239,7 +239,7 @@ where config.clone(), path, cm, - comments, + comments.clone(), ), ) }) @@ -261,8 +261,11 @@ where None => Either::Right(noop()), }, match &opts.server_actions { - Some(config) => - Either::Left(server_actions::server_actions(&file.name, config.clone())), + Some(config) => Either::Left(server_actions::server_actions( + &file.name, + config.clone(), + comments, + )), None => Either::Right(noop()), }, ) diff --git a/packages/next-swc/crates/core/src/server_actions.rs b/packages/next-swc/crates/core/src/server_actions.rs index 43f7ae728164..b547e8c6b579 100644 --- a/packages/next-swc/crates/core/src/server_actions.rs +++ b/packages/next-swc/crates/core/src/server_actions.rs @@ -1,11 +1,16 @@ use next_binding::swc::core::{ - common::{errors::HANDLER, util::take::Take, FileName, DUMMY_SP}, + common::{ + comments::{Comment, CommentKind, Comments}, + errors::HANDLER, + util::take::Take, + BytePos, FileName, DUMMY_SP, + }, ecma::{ ast::{ op, ArrayLit, AssignExpr, BlockStmt, CallExpr, ComputedPropName, Decl, ExportDecl, Expr, ExprStmt, FnDecl, Function, Id, Ident, KeyValueProp, Lit, MemberExpr, MemberProp, - ModuleDecl, ModuleItem, PatOrExpr, Prop, PropName, ReturnStmt, Stmt, Str, VarDecl, - VarDeclKind, VarDeclarator, + Module, ModuleDecl, ModuleItem, PatOrExpr, Prop, PropName, ReturnStmt, Stmt, Str, + VarDecl, VarDeclKind, VarDeclarator, }, atoms::JsWord, utils::{find_pat_ids, private_ident, quote_ident, ExprFactory}, @@ -19,14 +24,23 @@ use serde::Deserialize; #[derive(Clone, Debug, Deserialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] -pub struct Config {} +pub struct Config { + pub is_server: bool, +} -pub fn server_actions(file_name: &FileName, config: Config) -> impl VisitMut + Fold { +pub fn server_actions( + file_name: &FileName, + config: Config, + comments: C, +) -> impl VisitMut + Fold { as_folder(ServerActions { config, + comments, file_name: file_name.clone(), + start_pos: BytePos(0), in_action_file: false, in_export_decl: false, + has_action: false, top_level: false, closure_candidates: Default::default(), annotations: Default::default(), @@ -34,13 +48,16 @@ pub fn server_actions(file_name: &FileName, config: Config) -> impl VisitMut + F }) } -struct ServerActions { +struct ServerActions { #[allow(unused)] config: Config, file_name: FileName, + comments: C, + start_pos: BytePos, in_action_file: bool, in_export_decl: bool, + has_action: bool, top_level: bool, closure_candidates: Vec, @@ -49,7 +66,7 @@ struct ServerActions { extra_items: Vec, } -impl VisitMut for ServerActions { +impl VisitMut for ServerActions { fn visit_mut_export_decl(&mut self, decl: &mut ExportDecl) { let old = self.in_export_decl; self.in_export_decl = true; @@ -97,6 +114,8 @@ impl VisitMut for ServerActions { let action_name: JsWord = format!("$ACTION_{}", f.ident.sym).into(); let action_ident = private_ident!(action_name.clone()); + self.has_action = true; + // myAction.$$typeof = Symbol.for('react.action.reference'); self.annotations.push(annotate( &f.ident, @@ -124,22 +143,24 @@ impl VisitMut for ServerActions { .push(annotate(&f.ident, "$$name", action_name.into())); if self.top_level { - // export const $ACTION_myAction = myAction; - self.extra_items - .push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { - span: DUMMY_SP, - decl: Decl::Var(Box::new(VarDecl { + if !(self.in_action_file && self.in_export_decl) { + // export const $ACTION_myAction = myAction; + self.extra_items + .push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { span: DUMMY_SP, - kind: VarDeclKind::Const, - declare: Default::default(), - decls: vec![VarDeclarator { + decl: Decl::Var(Box::new(VarDecl { span: DUMMY_SP, - name: action_ident.into(), - init: Some(f.ident.clone().into()), - definite: Default::default(), - }], - })), - }))); + kind: VarDeclKind::Const, + declare: Default::default(), + decls: vec![VarDeclarator { + span: DUMMY_SP, + name: action_ident.into(), + init: Some(f.ident.clone().into()), + definite: Default::default(), + }], + })), + }))); + } } else { // Hoist the function to the top level. @@ -215,18 +236,9 @@ impl VisitMut for ServerActions { } } - fn visit_mut_module_item(&mut self, s: &mut ModuleItem) { - s.visit_mut_children_with(self); - - if self.in_action_file { - if let ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { - decl: decl @ Decl::Fn(..), - .. - })) = s - { - *s = ModuleItem::Stmt(Stmt::Decl(decl.take())); - } - } + fn visit_mut_module(&mut self, m: &mut Module) { + self.start_pos = m.span.lo; + m.visit_mut_children_with(self); } fn visit_mut_module_items(&mut self, stmts: &mut Vec) { @@ -234,6 +246,7 @@ impl VisitMut for ServerActions { match &*first.expr { Expr::Lit(Lit::Str(Str { value, .. })) if value == "use action" => { self.in_action_file = true; + self.has_action = true; } _ => {} } @@ -258,6 +271,18 @@ impl VisitMut for ServerActions { *stmts = new; self.annotations = old_annotations; + + if self.has_action { + // Prepend a special comment to the top of the file. + self.comments.add_leading( + self.start_pos, + Comment { + span: DUMMY_SP, + kind: CommentKind::Block, + text: " __next_internal_action_entry_do_not_use__ ".into(), + }, + ); + } } fn visit_mut_stmts(&mut self, stmts: &mut Vec) { diff --git a/packages/next-swc/crates/core/tests/fixture.rs b/packages/next-swc/crates/core/tests/fixture.rs index e231096050a4..f02def70fa31 100644 --- a/packages/next-swc/crates/core/tests/fixture.rs +++ b/packages/next-swc/crates/core/tests/fixture.rs @@ -307,7 +307,8 @@ fn server_actions_fixture(input: PathBuf) { &|_tr| { server_actions( &FileName::Real("/app/item.js".into()), - server_actions::Config {}, + server_actions::Config { is_server: true }, + _tr.comments.as_ref().clone(), ) }, &input, diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/1/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/1/output.js index 2a8001145e2c..96ce0f0fc5da 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/1/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/1/output.js @@ -1,4 +1,4 @@ -export function Item({ id1 , id2 }) { +/* __next_internal_action_entry_do_not_use__ */ export function Item({ id1 , id2 }) { async function deleteItem() { return $ACTION_deleteItem(deleteItem.$$closure); } diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/2/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/2/output.js index ef6346f84df0..088716ed393b 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/2/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/2/output.js @@ -1,4 +1,4 @@ -async function myAction(a, b, c) { +/* __next_internal_action_entry_do_not_use__ */ async function myAction(a, b, c) { console.log('a'); } myAction.$$typeof = Symbol.for("react.action.reference"); diff --git a/packages/next-swc/crates/core/tests/fixture/server-actions/3/output.js b/packages/next-swc/crates/core/tests/fixture/server-actions/3/output.js index 2c5868240273..abb8f1fc5107 100644 --- a/packages/next-swc/crates/core/tests/fixture/server-actions/3/output.js +++ b/packages/next-swc/crates/core/tests/fixture/server-actions/3/output.js @@ -1,8 +1,7 @@ // app/send.ts -async function myAction(a, b, c) { +/* __next_internal_action_entry_do_not_use__ */ export async function myAction(a, b, c) { console.log('a'); } myAction.$$typeof = Symbol.for("react.action.reference"); myAction.$$filepath = "/app/item.js"; myAction.$$name = "$ACTION_myAction"; -export const $ACTION_myAction = myAction;