swc_ecma_compat_es2018/
object_spread.rs

1use swc_common::{util::take::Take, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_transforms_base::{helper, perf::Parallel};
4use swc_ecma_utils::ExprFactory;
5use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
6use swc_trace_macro::swc_trace;
7
8use super::object_rest_spread::Config;
9
10#[derive(Clone, Copy)]
11pub(super) struct ObjectSpread {
12    pub config: Config,
13}
14
15impl Parallel for ObjectSpread {
16    fn create(&self) -> Self {
17        ObjectSpread {
18            config: self.config,
19        }
20    }
21
22    fn merge(&mut self, _: Self) {}
23}
24
25#[swc_trace]
26impl VisitMut for ObjectSpread {
27    noop_visit_mut_type!(fail);
28
29    fn visit_mut_expr(&mut self, expr: &mut Expr) {
30        expr.visit_mut_children_with(self);
31
32        if let Expr::Object(ObjectLit { span, props }) = expr {
33            let has_spread = props.iter().any(|p| matches!(p, PropOrSpread::Spread(..)));
34            if !has_spread {
35                return;
36            }
37
38            let mut callee = if self.config.set_property {
39                helper!(extends)
40            } else {
41                helper!(object_spread)
42            };
43
44            // { foo, ...x } => ({ foo }, x)
45            let args = {
46                let mut buf = Vec::new();
47                let mut obj = ObjectLit {
48                    span: DUMMY_SP,
49                    props: Vec::new(),
50                };
51                let mut first = true;
52                for prop in props.take() {
53                    match prop {
54                        PropOrSpread::Prop(..) => {
55                            // before is spread element
56                            if !first && obj.props.is_empty() && !self.config.pure_getters {
57                                buf = vec![Expr::Call(CallExpr {
58                                    span: DUMMY_SP,
59                                    callee: callee.clone(),
60                                    args: buf.take(),
61                                    ..Default::default()
62                                })
63                                .as_arg()];
64                            }
65                            obj.props.push(prop)
66                        }
67                        PropOrSpread::Spread(SpreadElement { expr, .. }) => {
68                            // Push object if it's not empty
69                            if first || !obj.props.is_empty() {
70                                buf.push(obj.take().as_arg());
71                                if !first && !self.config.pure_getters {
72                                    buf = vec![Expr::Call(CallExpr {
73                                        span: DUMMY_SP,
74                                        callee: helper!(object_spread_props),
75                                        args: buf.take(),
76                                        ..Default::default()
77                                    })
78                                    .as_arg()];
79                                }
80                                first = false;
81                            }
82
83                            buf.push(expr.as_arg());
84                        }
85                        #[cfg(swc_ast_unknown)]
86                        _ => panic!("unable to access unknown nodes"),
87                    }
88                }
89
90                if !obj.props.is_empty() {
91                    if !self.config.pure_getters {
92                        callee = helper!(object_spread_props);
93                    }
94                    buf.push(obj.as_arg());
95                }
96
97                buf
98            };
99
100            *expr = CallExpr {
101                span: *span,
102                callee,
103                args,
104                ..Default::default()
105            }
106            .into();
107        }
108    }
109}