swc_ecma_compat_es2015/
duplicate_keys.rs1use rustc_hash::FxHashSet;
2use swc_atoms::{Atom, Wtf8Atom};
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#[inline]
45fn atom_from_wtf8(value: &Wtf8Atom) -> Atom {
46 value
47 .as_str()
48 .map(Atom::from)
49 .unwrap_or_else(|| Atom::from(value.to_string_lossy()))
50}
51
52#[swc_trace]
53impl VisitMut for PropFolder {
54 noop_visit_mut_type!(fail);
55
56 fn visit_mut_expr(&mut self, _: &mut Expr) {}
58
59 fn visit_mut_prop(&mut self, prop: &mut Prop) {
60 match prop {
61 Prop::Shorthand(ident) => {
62 if !self.getter_props.insert(ident.sym.clone())
64 || !self.setter_props.insert(ident.sym.clone())
65 {
66 *prop = Prop::KeyValue(KeyValueProp {
67 key: PropName::Computed(ComputedPropName {
68 span: ident.span,
69 expr: quote_str!(ident.sym.clone()).into(),
70 }),
71 value: ident.clone().into(),
72 })
73 }
74 }
75
76 Prop::Assign(..) => {}
77
78 Prop::Getter(..) => prop.visit_mut_children_with(&mut PropNameFolder {
79 props: &mut self.getter_props,
80 }),
81 Prop::Setter(..) => prop.visit_mut_children_with(&mut PropNameFolder {
82 props: &mut self.setter_props,
83 }),
84 _ => {
85 prop.visit_mut_children_with(&mut PropNameFolder {
86 props: &mut self.getter_props,
87 });
88 prop.visit_mut_children_with(&mut PropNameFolder {
89 props: &mut self.setter_props,
90 })
91 }
92 }
93 }
94}
95
96struct PropNameFolder<'a> {
97 props: &'a mut FxHashSet<Atom>,
98}
99
100#[swc_trace]
101impl VisitMut for PropNameFolder<'_> {
102 noop_visit_mut_type!(fail);
103
104 fn visit_mut_expr(&mut self, _: &mut Expr) {}
105
106 fn visit_mut_prop_name(&mut self, name: &mut PropName) {
107 let span = name.span();
108
109 match name {
110 PropName::Ident(ident) => {
111 if !self.props.insert(ident.sym.clone()) {
112 *name = PropName::Computed(ComputedPropName {
113 span,
114 expr: Lit::Str(Str {
115 span,
116 raw: None,
117 value: ident.sym.clone().into(),
118 })
119 .into(),
120 })
121 }
122 }
123 PropName::Str(s) => {
124 if !self.props.insert(atom_from_wtf8(&s.value)) {
125 *name = PropName::Computed(ComputedPropName {
126 span: s.span,
127 expr: s.clone().into(),
128 })
129 }
130 }
131 PropName::Computed(ComputedPropName { expr, .. }) => {
132 if let Expr::Lit(Lit::Str(Str { ref value, .. })) = &**expr {
134 self.props.insert(atom_from_wtf8(value));
135 }
136 }
137 _ => {}
138 }
139 }
140}