swc_ecma_compat_es2015/
duplicate_keys.rs
1use rustc_hash::FxHashSet;
2use swc_atoms::Atom;
3use swc_common::Spanned;
4use swc_ecma_ast::*;
5use swc_ecma_transforms_base::perf::Parallel;
6use swc_ecma_utils::quote_str;
7use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
8use swc_trace_macro::swc_trace;
9
10pub fn duplicate_keys() -> impl Pass {
11 visit_mut_pass(DuplicateKeys)
12}
13
14struct DuplicateKeys;
15
16impl Parallel for DuplicateKeys {
17 fn merge(&mut self, _: Self) {}
18
19 fn create(&self) -> Self {
20 Self
21 }
22}
23
24#[swc_trace]
25impl VisitMut for DuplicateKeys {
26 noop_visit_mut_type!(fail);
27
28 fn visit_mut_expr(&mut self, expr: &mut Expr) {
29 expr.visit_mut_children_with(self);
30
31 if let Expr::Object(ObjectLit { props, .. }) = expr {
32 let mut folder = PropFolder::default();
33 props.visit_mut_with(&mut folder);
34 }
35 }
36}
37
38#[derive(Default)]
39struct PropFolder {
40 getter_props: FxHashSet<Atom>,
41 setter_props: FxHashSet<Atom>,
42}
43
44#[swc_trace]
45impl VisitMut for PropFolder {
46 noop_visit_mut_type!(fail);
47
48 fn visit_mut_expr(&mut self, _: &mut Expr) {}
50
51 fn visit_mut_prop(&mut self, prop: &mut Prop) {
52 match prop {
53 Prop::Shorthand(ident) => {
54 if !self.getter_props.insert(ident.sym.clone())
56 || !self.setter_props.insert(ident.sym.clone())
57 {
58 *prop = Prop::KeyValue(KeyValueProp {
59 key: PropName::Computed(ComputedPropName {
60 span: ident.span,
61 expr: quote_str!(ident.sym.clone()).into(),
62 }),
63 value: ident.clone().into(),
64 })
65 }
66 }
67
68 Prop::Assign(..) => {}
69
70 Prop::Getter(..) => prop.visit_mut_children_with(&mut PropNameFolder {
71 props: &mut self.getter_props,
72 }),
73 Prop::Setter(..) => prop.visit_mut_children_with(&mut PropNameFolder {
74 props: &mut self.setter_props,
75 }),
76 _ => {
77 prop.visit_mut_children_with(&mut PropNameFolder {
78 props: &mut self.getter_props,
79 });
80 prop.visit_mut_children_with(&mut PropNameFolder {
81 props: &mut self.setter_props,
82 })
83 }
84 }
85 }
86}
87
88struct PropNameFolder<'a> {
89 props: &'a mut FxHashSet<Atom>,
90}
91
92#[swc_trace]
93impl VisitMut for PropNameFolder<'_> {
94 noop_visit_mut_type!(fail);
95
96 fn visit_mut_expr(&mut self, _: &mut Expr) {}
97
98 fn visit_mut_prop_name(&mut self, name: &mut PropName) {
99 let span = name.span();
100
101 match name {
102 PropName::Ident(ident) => {
103 if !self.props.insert(ident.sym.clone()) {
104 *name = PropName::Computed(ComputedPropName {
105 span,
106 expr: Lit::Str(Str {
107 span,
108 raw: None,
109 value: ident.sym.clone(),
110 })
111 .into(),
112 })
113 }
114 }
115 PropName::Str(s) => {
116 if !self.props.insert(s.value.clone()) {
117 *name = PropName::Computed(ComputedPropName {
118 span: s.span,
119 expr: s.clone().into(),
120 })
121 }
122 }
123 PropName::Computed(ComputedPropName { expr, .. }) => {
124 if let Expr::Lit(Lit::Str(Str { ref value, .. })) = &**expr {
126 self.props.insert(value.clone());
127 }
128 }
129 _ => {}
130 }
131 }
132}