swc_ecma_compat_es2018/
object_spread.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use swc_common::{util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::{helper, perf::Parallel};
use swc_ecma_utils::ExprFactory;
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
use swc_trace_macro::swc_trace;

use super::object_rest_spread::Config;

#[derive(Clone, Copy)]
pub(super) struct ObjectSpread {
    pub config: Config,
}

impl Parallel for ObjectSpread {
    fn create(&self) -> Self {
        ObjectSpread {
            config: self.config,
        }
    }

    fn merge(&mut self, _: Self) {}
}

#[swc_trace]
impl VisitMut for ObjectSpread {
    noop_visit_mut_type!(fail);

    fn visit_mut_expr(&mut self, expr: &mut Expr) {
        expr.visit_mut_children_with(self);

        if let Expr::Object(ObjectLit { span, props }) = expr {
            let has_spread = props.iter().any(|p| matches!(p, PropOrSpread::Spread(..)));
            if !has_spread {
                return;
            }

            let mut callee = if self.config.set_property {
                helper!(extends)
            } else {
                helper!(object_spread)
            };

            // { foo, ...x } => ({ foo }, x)
            let args = {
                let mut buf = Vec::new();
                let mut obj = ObjectLit {
                    span: DUMMY_SP,
                    props: Vec::new(),
                };
                let mut first = true;
                for prop in props.take() {
                    match prop {
                        PropOrSpread::Prop(..) => {
                            // before is spread element
                            if !first && obj.props.is_empty() && !self.config.pure_getters {
                                buf = vec![Expr::Call(CallExpr {
                                    span: DUMMY_SP,
                                    callee: callee.clone(),
                                    args: buf.take(),
                                    ..Default::default()
                                })
                                .as_arg()];
                            }
                            obj.props.push(prop)
                        }
                        PropOrSpread::Spread(SpreadElement { expr, .. }) => {
                            // Push object if it's not empty
                            if first || !obj.props.is_empty() {
                                buf.push(obj.take().as_arg());
                                if !first && !self.config.pure_getters {
                                    buf = vec![Expr::Call(CallExpr {
                                        span: DUMMY_SP,
                                        callee: helper!(object_spread_props),
                                        args: buf.take(),
                                        ..Default::default()
                                    })
                                    .as_arg()];
                                }
                                first = false;
                            }

                            buf.push(expr.as_arg());
                        }
                    }
                }

                if !obj.props.is_empty() {
                    if !self.config.pure_getters {
                        callee = helper!(object_spread_props);
                    }
                    buf.push(obj.as_arg());
                }

                buf
            };

            *expr = CallExpr {
                span: *span,
                callee,
                args,
                ..Default::default()
            }
            .into();
        }
    }
}