swc_ecma_transforms_react/display_name/
mod.rs

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