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            value.visit_mut_with(&mut Folder {
89                name: Some(match key {
90                    PropName::Ident(ref i) => Lit::Str(Str {
91                        span: i.span,
92                        raw: None,
93                        value: i.sym.clone(),
94                    })
95                    .into(),
96                    PropName::Str(ref s) => Lit::Str(s.clone()).into(),
97                    PropName::Num(ref n) => Lit::Num(n.clone()).into(),
98                    PropName::BigInt(ref b) => Lit::BigInt(b.clone()).into(),
99                    PropName::Computed(ref c) => c.expr.clone(),
100                }),
101            });
102        }
103    }
104
105    fn visit_mut_var_declarator(&mut self, decl: &mut VarDeclarator) {
106        if let Pat::Ident(ref ident) = decl.name {
107            decl.init.visit_mut_with(&mut Folder {
108                name: Some(
109                    Lit::Str(Str {
110                        span: ident.span,
111                        value: ident.sym.clone(),
112                        raw: None,
113                    })
114                    .into(),
115                ),
116            });
117        }
118    }
119}
120
121struct Folder {
122    name: Option<Box<Expr>>,
123}
124
125impl VisitMut for Folder {
126    noop_visit_mut_type!();
127
128    /// Don't recurse into array.
129    fn visit_mut_array_lit(&mut self, _: &mut ArrayLit) {}
130
131    fn visit_mut_call_expr(&mut self, expr: &mut CallExpr) {
132        expr.visit_mut_children_with(self);
133
134        if is_create_class_call(expr) {
135            let name = match self.name.take() {
136                Some(name) => name,
137                None => return,
138            };
139            add_display_name(expr, name)
140        }
141    }
142
143    /// Don't recurse into object.
144    fn visit_mut_object_lit(&mut self, _: &mut ObjectLit) {}
145}
146
147fn is_create_class_call(call: &CallExpr) -> bool {
148    let callee = match &call.callee {
149        Callee::Super(_) | Callee::Import(_) => return false,
150        Callee::Expr(callee) => &**callee,
151    };
152
153    match callee {
154        Expr::Member(MemberExpr { obj, prop, .. }) if prop.is_ident_with("createClass") => {
155            if obj.is_ident_ref_to("React") {
156                return true;
157            }
158        }
159
160        Expr::Ident(Ident { sym, .. }) if &**sym == "createReactClass" => return true,
161        _ => {}
162    }
163
164    false
165}
166
167fn add_display_name(call: &mut CallExpr, name: Box<Expr>) {
168    let props = match call.args.first_mut() {
169        Some(&mut ExprOrSpread { ref mut expr, .. }) => match expr.deref_mut() {
170            Expr::Object(ObjectLit { ref mut props, .. }) => props,
171            _ => return,
172        },
173        _ => return,
174    };
175
176    for prop in &*props {
177        if is_key_display_name(prop) {
178            return;
179        }
180    }
181
182    props.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
183        key: PropName::Ident(atom!("displayName").into()),
184        value: name,
185    }))));
186}
187
188fn is_key_display_name(prop: &PropOrSpread) -> bool {
189    match *prop {
190        PropOrSpread::Prop(ref prop) => match **prop {
191            Prop::Shorthand(ref i) => i.sym == "displayName",
192            Prop::Method(MethodProp { ref key, .. })
193            | Prop::Getter(GetterProp { ref key, .. })
194            | Prop::Setter(SetterProp { ref key, .. })
195            | Prop::KeyValue(KeyValueProp { ref key, .. }) => match *key {
196                PropName::Ident(ref i) => i.sym == "displayName",
197                PropName::Str(ref s) => s.value == "displayName",
198                PropName::Num(..) => false,
199                PropName::BigInt(..) => false,
200                PropName::Computed(..) => false,
201            },
202            Prop::Assign(..) => unreachable!("invalid syntax"),
203        },
204        _ => false,
205        // TODO(kdy1): maybe.. handle spread
206    }
207}