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