swc_ecma_utils/
factory.rs

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