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 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 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 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 }
215}