swc_ecma_utils/
factory.rs

1use std::iter;
2
3use swc_atoms::atom;
4use swc_common::{util::take::Take, Span, Spanned, DUMMY_SP};
5use swc_ecma_ast::*;
6
7/// Extension methods for [Expr].
8///
9/// Note that many types implements `Into<Expr>` and you can do like
10///
11/// ```rust
12/// use swc_ecma_utils::ExprFactory;
13///
14/// let _args = vec![0f64.as_arg()];
15/// ```
16///
17/// to create literals. Almost all rust core types implements `Into<Expr>`.
18#[allow(clippy::wrong_self_convention)]
19pub trait ExprFactory: Into<Box<Expr>> {
20    /// Creates an [ExprOrSpread] using the given [Expr].
21    ///
22    /// This is recommended way to create [ExprOrSpread].
23    ///
24    /// # Example
25    ///
26    /// ```rust
27    /// use swc_common::DUMMY_SP;
28    /// use swc_ecma_ast::*;
29    /// use swc_ecma_utils::ExprFactory;
30    ///
31    /// let first = Lit::Num(Number {
32    ///     span: DUMMY_SP,
33    ///     value: 0.0,
34    ///     raw: None,
35    /// });
36    /// let _args = vec![first.as_arg()];
37    /// ```
38    #[cfg_attr(not(debug_assertions), inline(always))]
39    fn as_arg(self) -> ExprOrSpread {
40        ExprOrSpread {
41            expr: self.into(),
42            spread: None,
43        }
44    }
45
46    /// Creates an expression statement with `self`.
47    #[cfg_attr(not(debug_assertions), inline(always))]
48    fn into_stmt(self) -> Stmt {
49        let expr = self.into();
50        ExprStmt {
51            span: expr.span(),
52            expr,
53        }
54        .into()
55    }
56
57    /// Creates a statement whcih return `self`.
58    #[cfg_attr(not(debug_assertions), inline(always))]
59    fn into_return_stmt(self) -> ReturnStmt {
60        ReturnStmt {
61            span: DUMMY_SP,
62            arg: Some(self.into()),
63        }
64    }
65
66    #[cfg_attr(not(debug_assertions), inline(always))]
67    fn as_callee(self) -> Callee {
68        Callee::Expr(self.into())
69    }
70
71    #[cfg_attr(not(debug_assertions), inline(always))]
72    fn as_iife(self) -> CallExpr {
73        CallExpr {
74            span: DUMMY_SP,
75            callee: self.as_callee(),
76            ..Default::default()
77        }
78    }
79
80    /// create a ArrowExpr which return self
81    /// - `(params) => $self`
82    #[cfg_attr(not(debug_assertions), inline(always))]
83    fn into_lazy_arrow(self, params: Vec<Pat>) -> ArrowExpr {
84        ArrowExpr {
85            params,
86            body: Box::new(BlockStmtOrExpr::Expr(self.into())),
87            ..Default::default()
88        }
89    }
90
91    /// create a Function which return self
92    /// - `function(params) { return $self; }`
93    #[cfg_attr(not(debug_assertions), inline(always))]
94    fn into_lazy_fn(self, params: Vec<Param>) -> Function {
95        Function {
96            params,
97            decorators: Default::default(),
98            span: DUMMY_SP,
99            body: Some(BlockStmt {
100                span: DUMMY_SP,
101                stmts: vec![self.into_return_stmt().into()],
102                ..Default::default()
103            }),
104            ..Default::default()
105        }
106    }
107
108    #[cfg_attr(not(debug_assertions), inline(always))]
109    fn into_lazy_auto(self, params: Vec<Pat>, support_arrow: bool) -> Expr {
110        if support_arrow {
111            self.into_lazy_arrow(params).into()
112        } else {
113            self.into_lazy_fn(params.into_iter().map(From::from).collect())
114                .into_fn_expr(None)
115                .into()
116        }
117    }
118
119    /// create a var declartor using self as init
120    /// - `var name = expr`
121    #[cfg_attr(not(debug_assertions), inline(always))]
122    fn into_var_decl(self, kind: VarDeclKind, name: Pat) -> VarDecl {
123        let var_declarator = VarDeclarator {
124            span: DUMMY_SP,
125            name,
126            init: Some(self.into()),
127            definite: false,
128        };
129
130        VarDecl {
131            kind,
132            decls: vec![var_declarator],
133            ..Default::default()
134        }
135    }
136
137    #[cfg_attr(not(debug_assertions), inline(always))]
138    fn into_new_expr(self, span: Span, args: Option<Vec<ExprOrSpread>>) -> NewExpr {
139        NewExpr {
140            span,
141            callee: self.into(),
142            args,
143            ..Default::default()
144        }
145    }
146
147    #[cfg_attr(not(debug_assertions), inline(always))]
148    fn apply(self, span: Span, this: Box<Expr>, args: Vec<ExprOrSpread>) -> Expr {
149        let apply = self.make_member(IdentName::new(atom!("apply"), span));
150
151        CallExpr {
152            span,
153            callee: apply.as_callee(),
154            args: iter::once(this.as_arg()).chain(args).collect(),
155
156            ..Default::default()
157        }
158        .into()
159    }
160
161    #[cfg_attr(not(debug_assertions), inline(always))]
162    fn call_fn(self, span: Span, args: Vec<ExprOrSpread>) -> Expr {
163        CallExpr {
164            span,
165            args,
166            callee: self
167                .make_member(IdentName::new(atom!("call"), span))
168                .as_callee(),
169            ..Default::default()
170        }
171        .into()
172    }
173
174    #[cfg_attr(not(debug_assertions), inline(always))]
175    fn as_call(self, span: Span, args: Vec<ExprOrSpread>) -> Expr {
176        CallExpr {
177            span,
178            args,
179            callee: self.as_callee(),
180            ..Default::default()
181        }
182        .into()
183    }
184
185    #[cfg_attr(not(debug_assertions), inline(always))]
186    fn as_fn_decl(self) -> Option<FnDecl> {
187        match *self.into() {
188            Expr::Fn(FnExpr {
189                ident: Some(ident),
190                function,
191            }) => Some(FnDecl {
192                ident,
193                declare: false,
194                function,
195            }),
196            _ => None,
197        }
198    }
199
200    #[cfg_attr(not(debug_assertions), inline(always))]
201    fn as_class_decl(self) -> Option<ClassDecl> {
202        match *self.into() {
203            Expr::Class(ClassExpr {
204                ident: Some(ident),
205                class,
206            }) => Some(ClassDecl {
207                ident,
208                declare: false,
209                class,
210            }),
211            _ => None,
212        }
213    }
214
215    #[cfg_attr(not(debug_assertions), inline(always))]
216    fn wrap_with_paren(self) -> Expr {
217        let expr = self.into();
218        let span = expr.span();
219        ParenExpr { expr, span }.into()
220    }
221
222    /// Creates a binary expr `$self === `
223    #[cfg_attr(not(debug_assertions), inline(always))]
224    fn make_eq<T>(self, right: T) -> Expr
225    where
226        T: Into<Expr>,
227    {
228        self.make_bin(op!("==="), right)
229    }
230
231    /// Creates a binary expr `$self $op $rhs`
232    #[cfg_attr(not(debug_assertions), inline(always))]
233    fn make_bin<T>(self, op: BinaryOp, right: T) -> Expr
234    where
235        T: Into<Expr>,
236    {
237        let right = Box::new(right.into());
238
239        BinExpr {
240            span: DUMMY_SP,
241            left: self.into(),
242            op,
243            right,
244        }
245        .into()
246    }
247
248    /// Creates a assign expr `$lhs $op $self`
249    #[cfg_attr(not(debug_assertions), inline(always))]
250    fn make_assign_to(self, op: AssignOp, left: AssignTarget) -> Expr {
251        let right = self.into();
252
253        AssignExpr {
254            span: DUMMY_SP,
255            left,
256            op,
257            right,
258        }
259        .into()
260    }
261
262    #[cfg_attr(not(debug_assertions), inline(always))]
263    fn make_member(self, prop: IdentName) -> MemberExpr {
264        MemberExpr {
265            obj: self.into(),
266            span: DUMMY_SP,
267            prop: MemberProp::Ident(prop),
268        }
269    }
270
271    #[cfg_attr(not(debug_assertions), inline(always))]
272    fn computed_member<T>(self, prop: T) -> MemberExpr
273    where
274        T: Into<Box<Expr>>,
275    {
276        MemberExpr {
277            obj: self.into(),
278            span: DUMMY_SP,
279            prop: MemberProp::Computed(ComputedPropName {
280                span: DUMMY_SP,
281                expr: prop.into(),
282            }),
283        }
284    }
285}
286
287impl<T: Into<Box<Expr>>> ExprFactory for T {}
288
289pub trait IntoIndirectCall
290where
291    Self: std::marker::Sized,
292{
293    type Item;
294    fn into_indirect(self) -> Self::Item;
295}
296
297impl IntoIndirectCall for CallExpr {
298    type Item = CallExpr;
299
300    #[cfg_attr(not(debug_assertions), inline(always))]
301    fn into_indirect(self) -> CallExpr {
302        let callee = self.callee.into_indirect();
303
304        CallExpr { callee, ..self }
305    }
306}
307
308impl IntoIndirectCall for Callee {
309    type Item = Callee;
310
311    #[cfg_attr(not(debug_assertions), inline(always))]
312    fn into_indirect(self) -> Callee {
313        SeqExpr {
314            span: DUMMY_SP,
315            exprs: vec![0f64.into(), self.expect_expr()],
316        }
317        .as_callee()
318    }
319}
320
321impl IntoIndirectCall for TaggedTpl {
322    type Item = TaggedTpl;
323
324    #[cfg_attr(not(debug_assertions), inline(always))]
325    fn into_indirect(mut self) -> Self {
326        Self {
327            tag: Box::new(
328                SeqExpr {
329                    span: DUMMY_SP,
330                    exprs: vec![0f64.into(), self.tag.take()],
331                }
332                .into(),
333            ),
334            ..self
335        }
336    }
337}
338
339pub trait FunctionFactory: Into<Box<Function>> {
340    #[cfg_attr(not(debug_assertions), inline(always))]
341    fn into_fn_expr(self, ident: Option<Ident>) -> FnExpr {
342        FnExpr {
343            ident,
344            function: self.into(),
345        }
346    }
347
348    #[cfg_attr(not(debug_assertions), inline(always))]
349    fn into_fn_decl(self, ident: Ident) -> FnDecl {
350        FnDecl {
351            ident,
352            declare: false,
353            function: self.into(),
354        }
355    }
356
357    #[cfg_attr(not(debug_assertions), inline(always))]
358    fn into_method_prop(self, key: PropName) -> MethodProp {
359        MethodProp {
360            key,
361            function: self.into(),
362        }
363    }
364}
365
366impl<T: Into<Box<Function>>> FunctionFactory for T {}