From a9549afac05c74bcb5d501568372609dba998bea Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Thu, 22 Aug 2024 23:07:05 +0200 Subject: [PATCH] Improve SWC transform ID generation (#69183) This adds an extra `hash_salt` option to the Server Actions transform's `generate_action_id` method which defaults to empty for now (so no actual behavior changes). When set, the salt will be used to prefix the payload. --- .../next_shared/transforms/server_actions.rs | 1 + .../src/transforms/server_actions.rs | 51 +++++++++++++------ .../next-custom-transforms/tests/errors.rs | 6 ++- .../next-custom-transforms/tests/fixture.rs | 6 ++- packages/next/src/build/swc/options.ts | 1 + 5 files changed, 46 insertions(+), 19 deletions(-) diff --git a/packages/next-swc/crates/next-core/src/next_shared/transforms/server_actions.rs b/packages/next-swc/crates/next-core/src/next_shared/transforms/server_actions.rs index 24f34ab87c7dd..3b9fe5d75e974 100644 --- a/packages/next-swc/crates/next-core/src/next_shared/transforms/server_actions.rs +++ b/packages/next-swc/crates/next-core/src/next_shared/transforms/server_actions.rs @@ -50,6 +50,7 @@ impl CustomTransformer for NextServerActions { Config { is_react_server_layer: matches!(self.transform, ActionsTransform::Server), enabled: true, + hash_salt: "".into(), }, ctx.comments.clone(), ); diff --git a/packages/next-swc/crates/next-custom-transforms/src/transforms/server_actions.rs b/packages/next-swc/crates/next-custom-transforms/src/transforms/server_actions.rs index a18d60a6bb79c..95128587724d4 100644 --- a/packages/next-swc/crates/next-custom-transforms/src/transforms/server_actions.rs +++ b/packages/next-swc/crates/next-custom-transforms/src/transforms/server_actions.rs @@ -28,6 +28,7 @@ use turbopack_binding::swc::core::{ pub struct Config { pub is_react_server_layer: bool, pub enabled: bool, + pub hash_salt: String, } /// A mapping of hashed action id to the action's exported function name. @@ -174,8 +175,11 @@ impl ServerActions { .cloned() .map(|id| Some(id.as_arg())) .collect(), - &self.file_name, - export_name.to_string(), + generate_action_id( + &self.config.hash_salt, + &self.file_name, + export_name.to_string().as_str(), + ), ); if let BlockStmtOrExpr::BlockStmt(block) = &mut *a.body { @@ -223,7 +227,12 @@ impl ServerActions { span: DUMMY_SP, callee: quote_ident!("decryptActionBoundArgs").as_callee(), args: vec![ - generate_action_id(&self.file_name, &export_name).as_arg(), + generate_action_id( + &self.config.hash_salt, + &self.file_name, + &export_name, + ) + .as_arg(), quote_ident!("$$ACTION_CLOSURE_BOUND").as_arg(), ], type_args: None, @@ -299,8 +308,7 @@ impl ServerActions { .cloned() .map(|id| Some(id.as_arg())) .collect(), - &self.file_name, - export_name.to_string(), + generate_action_id(&self.config.hash_salt, &self.file_name, &export_name), ); f.body.visit_mut_with(&mut ClosureReplacer { @@ -347,7 +355,12 @@ impl ServerActions { span: DUMMY_SP, callee: quote_ident!("decryptActionBoundArgs").as_callee(), args: vec![ - generate_action_id(&self.file_name, &export_name).as_arg(), + generate_action_id( + &self.config.hash_salt, + &self.file_name, + &export_name, + ) + .as_arg(), quote_ident!("$$ACTION_CLOSURE_BOUND").as_arg(), ], type_args: None, @@ -959,7 +972,8 @@ impl VisitMut for ServerActions { let ident = Ident::new(id.0.clone(), DUMMY_SP.with_ctxt(id.1)); if !self.config.is_react_server_layer { - let action_id = generate_action_id(&self.file_name, export_name); + let action_id = + generate_action_id(&self.config.hash_salt, &self.file_name, export_name); if export_name == "default" { let export_expr = ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr( @@ -1009,8 +1023,11 @@ impl VisitMut for ServerActions { expr: Box::new(annotate_ident_as_action( ident.clone(), Vec::new(), - &self.file_name, - export_name.to_string(), + generate_action_id( + &self.config.hash_salt, + &self.file_name, + export_name, + ), )), })); } @@ -1083,7 +1100,12 @@ impl VisitMut for ServerActions { let actions = actions .into_iter() - .map(|name| (generate_action_id(&self.file_name, &name), name)) + .map(|name| { + ( + generate_action_id(&self.config.hash_salt, &self.file_name, &name), + name, + ) + }) .collect::(); // Prepend a special comment to the top of the file. self.comments.add_leading( @@ -1230,10 +1252,11 @@ fn attach_name_to_expr(ident: Ident, expr: Expr, extra_items: &mut Vec String { +fn generate_action_id(hash_salt: &str, file_name: &str, export_name: &str) -> String { // Attach a checksum to the action using sha1: - // $$id = sha1('file_name' + ':' + 'export_name'); + // $$id = sha1('hash_salt' + 'file_name' + ':' + 'export_name'); let mut hasher = Sha1::new(); + hasher.update(hash_salt.as_bytes()); hasher.update(file_name.as_bytes()); hasher.update(b":"); hasher.update(export_name.as_bytes()); @@ -1245,12 +1268,10 @@ fn generate_action_id(file_name: &str, export_name: &str) -> String { fn annotate_ident_as_action( ident: Ident, bound: Vec>, - file_name: &str, - export_name: String, + action_id: String, ) -> Expr { // Add the proxy wrapper call `registerServerReference($$id, $$bound, myAction, // maybe_orig_action)`. - let action_id = generate_action_id(file_name, &export_name); let proxy_expr = Expr::Call(CallExpr { span: DUMMY_SP, diff --git a/packages/next-swc/crates/next-custom-transforms/tests/errors.rs b/packages/next-swc/crates/next-custom-transforms/tests/errors.rs index be74f9fc9a6b1..34698f34ed89b 100644 --- a/packages/next-swc/crates/next-custom-transforms/tests/errors.rs +++ b/packages/next-swc/crates/next-custom-transforms/tests/errors.rs @@ -178,7 +178,8 @@ fn react_server_actions_server_errors(input: PathBuf) { &FileName::Real("/app/item.js".into()), server_actions::Config { is_react_server_layer: true, - enabled: true + enabled: true, + hash_salt: "".into() }, tr.comments.as_ref().clone(), ) @@ -214,7 +215,8 @@ fn react_server_actions_client_errors(input: PathBuf) { &FileName::Real("/app/item.js".into()), server_actions::Config { is_react_server_layer: false, - enabled: true + enabled: true, + hash_salt: "".into() }, tr.comments.as_ref().clone(), ) diff --git a/packages/next-swc/crates/next-custom-transforms/tests/fixture.rs b/packages/next-swc/crates/next-custom-transforms/tests/fixture.rs index e14ed2fba985d..1851aa27bc928 100644 --- a/packages/next-swc/crates/next-custom-transforms/tests/fixture.rs +++ b/packages/next-swc/crates/next-custom-transforms/tests/fixture.rs @@ -381,7 +381,8 @@ fn server_actions_server_fixture(input: PathBuf) { &FileName::Real("/app/item.js".into()), server_actions::Config { is_react_server_layer: true, - enabled: true + enabled: true, + hash_salt: "".into() }, _tr.comments.as_ref().clone(), ) @@ -405,7 +406,8 @@ fn server_actions_client_fixture(input: PathBuf) { &FileName::Real("/app/item.js".into()), server_actions::Config { is_react_server_layer: false, - enabled: true + enabled: true, + hash_salt: "".into() }, _tr.comments.as_ref().clone(), ) diff --git a/packages/next/src/build/swc/options.ts b/packages/next/src/build/swc/options.ts index 3ea92e5bbdf9c..bb999f96621b9 100644 --- a/packages/next/src/build/swc/options.ts +++ b/packages/next/src/build/swc/options.ts @@ -206,6 +206,7 @@ function getBaseSWCOptions({ // TODO: remove this option enabled: true, isReactServerLayer, + hashSalt: '', } : undefined, // For app router we prefer to bundle ESM,