Skip to content

Commit

Permalink
Improve SWC transform ID generation (#69183)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
shuding authored Aug 22, 2024
1 parent a9a3098 commit 1face6e
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
);
Expand Down
51 changes: 36 additions & 15 deletions crates/next-custom-transforms/src/transforms/server_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -173,8 +174,11 @@ impl<C: Comments> ServerActions<C> {
.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 {
Expand Down Expand Up @@ -224,7 +228,12 @@ impl<C: Comments> ServerActions<C> {
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()
Expand Down Expand Up @@ -302,8 +311,7 @@ impl<C: Comments> ServerActions<C> {
.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 {
Expand Down Expand Up @@ -351,7 +359,12 @@ impl<C: Comments> ServerActions<C> {
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()
Expand Down Expand Up @@ -980,7 +993,8 @@ impl<C: Comments> VisitMut for ServerActions<C> {
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(
Expand Down Expand Up @@ -1031,8 +1045,11 @@ impl<C: Comments> VisitMut for ServerActions<C> {
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,
),
)),
}));
}
Expand Down Expand Up @@ -1106,7 +1123,12 @@ impl<C: Comments> VisitMut for ServerActions<C> {

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::<ActionsMap>();
// Prepend a special comment to the top of the file.
self.comments.add_leading(
Expand Down Expand Up @@ -1280,10 +1302,11 @@ fn attach_name_to_expr(ident: Ident, expr: Expr, extra_items: &mut Vec<ModuleIte
}
}

fn generate_action_id(file_name: &str, export_name: &str) -> 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());
Expand All @@ -1295,12 +1318,10 @@ fn generate_action_id(file_name: &str, export_name: &str) -> String {
fn annotate_ident_as_action(
ident: Ident,
bound: Vec<Option<ExprOrSpread>>,
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,
Expand Down
6 changes: 4 additions & 2 deletions crates/next-custom-transforms/tests/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
Expand Down Expand Up @@ -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(),
)
Expand Down
6 changes: 4 additions & 2 deletions crates/next-custom-transforms/tests/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
Expand All @@ -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(),
)
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/build/swc/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ function getBaseSWCOptions({
// TODO: remove this option
enabled: true,
isReactServerLayer,
hashSalt: '',
}
: undefined,
// For app router we prefer to bundle ESM,
Expand Down

0 comments on commit 1face6e

Please sign in to comment.