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    /// Noop
49    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                //
55                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                // Computed property might collide
125                if let Expr::Lit(Lit::Str(Str { ref value, .. })) = &**expr {
126                    self.props.insert(value.clone());
127                }
128            }
129            _ => {}
130        }
131    }
132}