Skip to main content

react_compiler_swc/
apply_renames.rs

1// Copyright (c) Meta Platforms, Inc. and affiliates.
2//
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6use std::collections::HashMap;
7
8use react_compiler::entrypoint::compile_result::BindingRenameInfo;
9use react_compiler_ast::scope::BindingId;
10use swc_atoms::Atom;
11use swc_ecma_ast::*;
12use swc_ecma_visit::{VisitMut, VisitMutWith};
13
14use crate::convert_scope::build_scope_info;
15
16pub fn apply_renames(module: &mut Module, renames: &[BindingRenameInfo]) {
17    if renames.is_empty() {
18        return;
19    }
20
21    let scope_info = build_scope_info(module);
22    let renames_by_declaration: HashMap<u32, &BindingRenameInfo> = renames
23        .iter()
24        .map(|rename| (rename.declaration_start, rename))
25        .collect();
26    let mut renamed_bindings: HashMap<BindingId, String> = HashMap::new();
27
28    for binding in &scope_info.bindings {
29        let Some(rename) = binding
30            .declaration_start
31            .and_then(|start| renames_by_declaration.get(&start))
32        else {
33            continue;
34        };
35        if binding.name == rename.original {
36            renamed_bindings.insert(binding.id, rename.renamed.clone());
37        }
38    }
39
40    if renamed_bindings.is_empty() {
41        return;
42    }
43
44    let rewrite_plan: HashMap<u32, String> = scope_info
45        .reference_to_binding
46        .iter()
47        .filter_map(|(&position, binding_id)| {
48            renamed_bindings.get(binding_id).map(|renamed| (position, renamed.clone()))
49        })
50        .collect();
51
52    module.visit_mut_with(&mut RenameApplyVisitor { rewrite_plan });
53}
54
55struct RenameApplyVisitor {
56    rewrite_plan: HashMap<u32, String>,
57}
58
59impl RenameApplyVisitor {
60    fn renamed_at(&self, position: u32) -> Option<String> {
61        self.rewrite_plan.get(&position).cloned()
62    }
63}
64
65impl VisitMut for RenameApplyVisitor {
66    fn visit_mut_ident(&mut self, ident: &mut Ident) {
67        if let Some(renamed) = self.renamed_at(ident.span.lo.0) {
68            ident.sym = Atom::from(renamed);
69        }
70    }
71
72    fn visit_mut_member_expr(&mut self, member: &mut MemberExpr) {
73        member.obj.visit_mut_with(self);
74        if let MemberProp::Computed(computed) = &mut member.prop {
75            computed.visit_mut_with(self);
76        }
77    }
78
79    fn visit_mut_prop(&mut self, prop: &mut Prop) {
80        match prop {
81            Prop::Shorthand(ident) => {
82                if let Some(renamed) = self.renamed_at(ident.span.lo.0) {
83                    let mut value = ident.clone();
84                    value.sym = Atom::from(renamed);
85                    // Shorthand `{ref}` must become `{ref: ref_0}` to preserve property semantics.
86                    *prop = Prop::KeyValue(KeyValueProp {
87                        key: PropName::Ident(IdentName {
88                            span: ident.span,
89                            sym: ident.sym.clone(),
90                        }),
91                        value: Box::new(Expr::Ident(value)),
92                    });
93                }
94            }
95            Prop::Assign(assign) => {
96                assign.value.visit_mut_with(self);
97            }
98            _ => prop.visit_mut_children_with(self),
99        }
100    }
101
102    fn visit_mut_object_pat_prop(&mut self, prop: &mut ObjectPatProp) {
103        match prop {
104            ObjectPatProp::Assign(assign) => {
105                if let Some(value) = &mut assign.value {
106                    value.visit_mut_with(self);
107                }
108
109                if let Some(renamed) = self.renamed_at(assign.key.id.span.lo.0) {
110                    let mut binding = assign.key.clone();
111                    binding.id.sym = Atom::from(renamed);
112                    let value = match assign.value.take() {
113                        Some(default_value) => Pat::Assign(AssignPat {
114                            span: assign.span,
115                            left: Box::new(Pat::Ident(binding)),
116                            right: default_value,
117                        }),
118                        None => Pat::Ident(binding),
119                    };
120
121                    *prop = ObjectPatProp::KeyValue(KeyValuePatProp {
122                        key: PropName::Ident(IdentName {
123                            span: assign.key.id.span,
124                            sym: assign.key.id.sym.clone(),
125                        }),
126                        value: Box::new(value),
127                    });
128                }
129            }
130            _ => prop.visit_mut_children_with(self),
131        }
132    }
133}