swc_ecma_transforms_react/display_name/
mod.rs1use 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
11pub 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 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 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 }
207}