swc_ecma_transforms_react/display_name/
mod.rs

1use std::ops::DerefMut;
2
3use swc_atoms::atom;
4use swc_common::DUMMY_SP;
5use swc_ecma_ast::*;
6use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
7
8#[cfg(test)]
9mod tests;
10
11/// `@babel/plugin-transform-react-display-name`
12///
13/// Add displayName to React.createClass calls
14pub fn display_name() -> impl Pass {
15    visit_mut_pass(DisplayName)
16}
17
18struct DisplayName;
19
20impl VisitMut for DisplayName {
21    noop_visit_mut_type!();
22
23    fn visit_mut_assign_expr(&mut self, expr: &mut AssignExpr) {
24        expr.visit_mut_children_with(self);
25
26        if expr.op != op!("=") {
27            return;
28        }
29
30        if let AssignTarget::Simple(
31            SimpleAssignTarget::Member(MemberExpr {
32                prop: MemberProp::Ident(prop),
33                ..
34            })
35            | SimpleAssignTarget::SuperProp(SuperPropExpr {
36                prop: SuperProp::Ident(prop),
37                ..
38            }),
39        ) = &expr.left
40        {
41            return expr.right.visit_mut_with(&mut Folder {
42                name: Some(
43                    Lit::Str(Str {
44                        span: prop.span,
45                        raw: None,
46                        value: prop.sym.clone(),
47                    })
48                    .into(),
49                ),
50            });
51        };
52
53        if let Some(ident) = expr.left.as_ident() {
54            expr.right.visit_mut_with(&mut Folder {
55                name: Some(
56                    Lit::Str(Str {
57                        span: ident.span,
58                        raw: None,
59                        value: ident.sym.clone(),
60                    })
61                    .into(),
62                ),
63            });
64        }
65    }
66
67    fn visit_mut_module_decl(&mut self, decl: &mut ModuleDecl) {
68        decl.visit_mut_children_with(self);
69
70        if let ModuleDecl::ExportDefaultExpr(e) = decl {
71            e.visit_mut_with(&mut Folder {
72                name: Some(
73                    Lit::Str(Str {
74                        span: DUMMY_SP,
75                        raw: None,
76                        value: atom!("input"),
77                    })
78                    .into(),
79                ),
80            });
81        }
82    }
83
84    fn visit_mut_prop(&mut self, prop: &mut Prop) {
85        prop.visit_mut_children_with(self);
86
87        if let Prop::KeyValue(KeyValueProp { key, value }) = prop {
88            let name = match key {
89                PropName::Ident(ref i) => Lit::Str(Str {
90                    span: i.span,
91                    raw: None,
92                    value: i.sym.clone(),
93                })
94                .into(),
95                PropName::Str(ref s) => Lit::Str(s.clone()).into(),
96                PropName::Num(ref n) => Lit::Num(n.clone()).into(),
97                PropName::BigInt(ref b) => Lit::BigInt(b.clone()).into(),
98                PropName::Computed(ref c) => c.expr.clone(),
99                #[cfg(swc_ast_unknown)]
100                _ => panic!("unable to access unknown nodes"),
101            };
102
103            value.visit_mut_with(&mut Folder { name: Some(name) });
104        }
105    }
106
107    fn visit_mut_var_declarator(&mut self, decl: &mut VarDeclarator) {
108        if let Pat::Ident(ref ident) = decl.name {
109            decl.init.visit_mut_with(&mut Folder {
110                name: Some(
111                    Lit::Str(Str {
112                        span: ident.span,
113                        value: ident.sym.clone(),
114                        raw: None,
115                    })
116                    .into(),
117                ),
118            });
119        }
120    }
121}
122
123struct Folder {
124    name: Option<Box<Expr>>,
125}
126
127impl VisitMut for Folder {
128    noop_visit_mut_type!();
129
130    /// Don't recurse into array.
131    fn visit_mut_array_lit(&mut self, _: &mut ArrayLit) {}
132
133    fn visit_mut_call_expr(&mut self, expr: &mut CallExpr) {
134        expr.visit_mut_children_with(self);
135
136        if is_create_class_call(expr) {
137            let name = match self.name.take() {
138                Some(name) => name,
139                None => return,
140            };
141            add_display_name(expr, name)
142        }
143    }
144
145    /// Don't recurse into object.
146    fn visit_mut_object_lit(&mut self, _: &mut ObjectLit) {}
147}
148
149fn is_create_class_call(call: &CallExpr) -> bool {
150    let callee = match &call.callee {
151        Callee::Super(_) | Callee::Import(_) => return false,
152        Callee::Expr(callee) => &**callee,
153        #[cfg(swc_ast_unknown)]
154        _ => panic!("unable to access unknown nodes"),
155    };
156
157    match callee {
158        Expr::Member(MemberExpr { obj, prop, .. }) if prop.is_ident_with("createClass") => {
159            if obj.is_ident_ref_to("React") {
160                return true;
161            }
162        }
163
164        Expr::Ident(Ident { sym, .. }) if &**sym == "createReactClass" => return true,
165        _ => {}
166    }
167
168    false
169}
170
171fn add_display_name(call: &mut CallExpr, name: Box<Expr>) {
172    let props = match call.args.first_mut() {
173        Some(&mut ExprOrSpread { ref mut expr, .. }) => match expr.deref_mut() {
174            Expr::Object(ObjectLit { ref mut props, .. }) => props,
175            _ => return,
176        },
177        _ => return,
178    };
179
180    for prop in &*props {
181        if is_key_display_name(prop) {
182            return;
183        }
184    }
185
186    props.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
187        key: PropName::Ident(atom!("displayName").into()),
188        value: name,
189    }))));
190}
191
192fn is_key_display_name(prop: &PropOrSpread) -> bool {
193    match *prop {
194        PropOrSpread::Prop(ref prop) => match **prop {
195            Prop::Shorthand(ref i) => i.sym == "displayName",
196            Prop::Method(MethodProp { ref key, .. })
197            | Prop::Getter(GetterProp { ref key, .. })
198            | Prop::Setter(SetterProp { ref key, .. })
199            | Prop::KeyValue(KeyValueProp { ref key, .. }) => match *key {
200                PropName::Ident(ref i) => i.sym == "displayName",
201                PropName::Str(ref s) => s.value == "displayName",
202                PropName::Num(..) => false,
203                PropName::BigInt(..) => false,
204                PropName::Computed(..) => false,
205                #[cfg(swc_ast_unknown)]
206                _ => panic!("unable to access unknown nodes"),
207            },
208            Prop::Assign(..) => unreachable!("invalid syntax"),
209            #[cfg(swc_ast_unknown)]
210            _ => panic!("unable to access unknown nodes"),
211        },
212        _ => false,
213        // TODO(kdy1): maybe.. handle spread
214    }
215}