From 1face6e6e08769f56a85bef79591abf1477f8550 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 +++++++++++++------ crates/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/crates/next-core/src/next_shared/transforms/server_actions.rs b/crates/next-core/src/next_shared/transforms/server_actions.rs index c7eabdc1fa279..17ef57e3994c0 100644 --- a/crates/next-core/src/next_shared/transforms/server_actions.rs +++ b/crates/next-core/src/next_shared/transforms/server_actions.rs @@ -47,6 +47,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/crates/next-custom-transforms/src/transforms/server_actions.rs b/crates/next-custom-transforms/src/transforms/server_actions.rs index 906e0b4232567..3c3f4dc2a4ec1 100644 --- a/crates/next-custom-transforms/src/transforms/server_actions.rs +++ b/crates/next-custom-transforms/src/transforms/server_actions.rs @@ -27,6 +27,7 @@ use 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. @@ -173,8 +174,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 { @@ -224,7 +228,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(), ], ..Default::default() @@ -302,8 +311,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 { @@ -351,7 +359,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(), ], ..Default::default() @@ -980,7 +993,8 @@ impl VisitMut for ServerActions { let ident = Ident::new(id.0.clone(), DUMMY_SP, 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( @@ -1031,8 +1045,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, + ), )), })); } @@ -1106,7 +1123,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( @@ -1280,10 +1302,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()); @@ -1295,12 +1318,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/crates/next-custom-transforms/tests/errors.rs b/crates/next-custom-transforms/tests/errors.rs index 5ea9d364c31a2..3ac7d21d27d59 100644 --- a/crates/next-custom-transforms/tests/errors.rs +++ b/crates/next-custom-transforms/tests/errors.rs @@ -176,7 +176,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(), ) @@ -212,7 +213,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/crates/next-custom-transforms/tests/fixture.rs b/crates/next-custom-transforms/tests/fixture.rs index 5a03eb6bede44..029b542f45a1d 100644 --- a/crates/next-custom-transforms/tests/fixture.rs +++ b/crates/next-custom-transforms/tests/fixture.rs @@ -405,7 +405,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(), ) @@ -429,7 +430,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 9f4391c801908..0cd2efba8c44e 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,