swc_ecma_minifier/compress/pure/
properties.rs

1use swc_atoms::Atom;
2use swc_ecma_ast::*;
3
4use super::Pure;
5use crate::compress::util::is_valid_identifier;
6
7impl Pure<'_> {
8    pub(super) fn optimize_property_of_member_expr(
9        &mut self,
10        obj: Option<&Expr>,
11        c: &mut ComputedPropName,
12    ) -> Option<IdentName> {
13        if !self.options.props {
14            return None;
15        }
16        if let Some(Expr::Array(..) | Expr::Await(..) | Expr::Yield(..) | Expr::Lit(..)) = obj {
17            return None;
18        }
19
20        match &*c.expr {
21            Expr::Lit(Lit::Str(s)) => {
22                let value = s.value.as_str()?;
23                if value.is_reserved()
24                    || value.is_reserved_in_es3()
25                    || is_valid_identifier(value, true)
26                {
27                    self.changed = true;
28                    report_change!(
29                        "properties: Computed member => member expr with identifier as a prop"
30                    );
31
32                    Some(IdentName {
33                        span: s.span,
34                        // SAFETY: We just checked that s.value is valid UTF-8.
35                        sym: unsafe { Atom::from_wtf8_unchecked(s.value.clone()) },
36                    })
37                } else {
38                    None
39                }
40            }
41
42            _ => None,
43        }
44    }
45
46    /// If a key of is `'str'` (like `{ 'str': 1 }`) change it to [Ident] like
47    /// (`{ str: 1, }`)
48    pub(super) fn optimize_computed_prop_name_as_normal(&mut self, p: &mut PropName) {
49        if !self.options.computed_props {
50            return;
51        }
52
53        if let PropName::Computed(c) = p {
54            match &mut *c.expr {
55                Expr::Lit(Lit::Str(s)) => {
56                    let Some(value) = s.value.as_str() else {
57                        return;
58                    };
59                    if value == "constructor" || value == "__proto__" {
60                        return;
61                    }
62
63                    if value.is_reserved()
64                        || value.is_reserved_in_es3()
65                        || is_valid_identifier(value, false)
66                    {
67                        *p = PropName::Ident(IdentName::new(
68                            // SAFETY: reserved words and valid identifiers are valid UTF-8.
69                            unsafe { Atom::from_wtf8_unchecked(s.value.clone()) },
70                            s.span,
71                        ));
72                    } else {
73                        *p = PropName::Str(s.clone());
74                    }
75                }
76                Expr::Lit(Lit::Num(n)) => {
77                    if n.value.is_sign_positive() {
78                        *p = PropName::Num(n.clone());
79                    }
80                }
81                _ => {}
82            }
83        }
84    }
85
86    pub(super) fn optimize_prop_name(&mut self, name: &mut PropName) {
87        if let PropName::Str(s) = name {
88            let Some(value) = s.value.as_str() else {
89                return;
90            };
91            if value.is_reserved()
92                || value.is_reserved_in_es3()
93                || is_valid_identifier(value, false)
94            {
95                self.changed = true;
96                report_change!("misc: Optimizing string property name");
97                *name = PropName::Ident(IdentName {
98                    span: s.span,
99                    // SAFETY: reserved words and valid identifiers are valid UTF-8.
100                    sym: unsafe { Atom::from_wtf8_unchecked(s.value.clone()) },
101                });
102                return;
103            }
104
105            if (!value.starts_with('0') && !value.starts_with('+')) || value.len() <= 1 {
106                if let Ok(v) = value.parse::<u32>() {
107                    self.changed = true;
108                    report_change!("misc: Optimizing numeric property name");
109                    *name = PropName::Num(Number {
110                        span: s.span,
111                        value: v as _,
112                        raw: None,
113                    });
114                }
115            }
116        }
117    }
118
119    pub(super) fn handle_known_computed_member_expr(
120        &mut self,
121        c: &mut ComputedPropName,
122    ) -> Option<IdentName> {
123        if !self.options.props || !self.options.evaluate {
124            return None;
125        }
126
127        match &*c.expr {
128            Expr::Lit(Lit::Str(s)) => {
129                let value = s.value.as_str()?;
130                if value.is_empty()
131                    || value.starts_with(|c: char| c.is_ascii_digit())
132                    || value
133                        .contains(|c: char| !matches!(c, '0'..='9' | 'a'..='z' | 'A'..='Z' | '$'))
134                {
135                    return None;
136                }
137
138                self.changed = true;
139
140                Some(IdentName::new(
141                    // SAFETY: We just checked that s.value is valid UTF-8.
142                    unsafe { Atom::from_wtf8_unchecked(s.value.clone()) },
143                    s.span,
144                ))
145            }
146            _ => None,
147        }
148    }
149}