Skip to main content

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(ArrowFunctionBody::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(FunctionBody {
100                span: DUMMY_SP,
101                stmts: vec![self.into_return_stmt().into()],
102            }),
103            ..Default::default()
104        }
105    }
106
107    #[cfg_attr(not(debug_assertions), inline(always))]
108    fn into_lazy_auto(self, params: Vec<Pat>, support_arrow: bool) -> Expr {
109        if support_arrow {
110            self.into_lazy_arrow(params).into()
111        } else {
112            self.into_lazy_fn(params.into_iter().map(From::from).collect())
113                .into_fn_expr(None)
114                .into()
115        }
116    }
117
118    /// create a var declartor using self as init
119    /// - `var name = expr`
120    #[cfg_attr(not(debug_assertions), inline(always))]
121    fn into_var_decl(self, kind: VarDeclKind, name: Pat) -> VarDecl {
122        let var_declarator = VarDeclarator {
123            span: DUMMY_SP,
124            name,
125            init: Some(self.into()),
126            definite: false,
127        };
128
129        VarDecl {
130            kind,
131            decls: vec![var_declarator],
132            ..Default::default()
133        }
134    }
135
136    #[cfg_attr(not(debug_assertions), inline(always))]
137    fn into_new_expr(self, span: Span, args: Option<Vec<ExprOrSpread>>) -> NewExpr {
138        NewExpr {
139            span,
140            callee: self.into(),
141            args,
142            ..Default::default()
143        }
144    }
145
146    #[cfg_attr(not(debug_assertions), inline(always))]
147    fn apply(self, span: Span, this: Box<Expr>, args: Vec<ExprOrSpread>) -> Expr {
148        let apply = self.make_member(IdentName::new(atom!("apply"), span));
149
150        CallExpr {
151            span,
152            callee: apply.as_callee(),
153            args: iter::once(this.as_arg()).chain(args).collect(),
154
155            ..Default::default()
156        }
157        .into()
158    }
159
160    #[cfg_attr(not(debug_assertions), inline(always))]
161    fn call_fn(self, span: Span, args: Vec<ExprOrSpread>) -> Expr {
162        CallExpr {
163            span,
164            args,
165            callee: self
166                .make_member(IdentName::new(atom!("call"), span))
167                .as_callee(),
168            ..Default::default()
169        }
170        .into()
171    }
172
173    #[cfg_attr(not(debug_assertions), inline(always))]
174    fn as_call(self, span: Span, args: Vec<ExprOrSpread>) -> Expr {
175        CallExpr {
176            span,
177            args,
178            callee: self.as_callee(),
179            ..Default::default()
180        }
181        .into()
182    }
183
184    #[cfg_attr(not(debug_assertions), inline(always))]
185    fn as_fn_decl(self) -> Option<FnDecl> {
186        match *self.into() {
187            Expr::Fn(FnExpr {
188                ident: Some(ident),
189                function,
190            }) => Some(FnDecl {
191                ident,
192                declare: false,
193                function,
194            }),
195            _ => None,
196        }
197    }
198
199    #[cfg_attr(not(debug_assertions), inline(always))]
200    fn as_class_decl(self) -> Option<ClassDecl> {
201        match *self.into() {
202            Expr::Class(ClassExpr {
203                ident: Some(ident),
204                class,
205            }) => Some(ClassDecl {
206                ident,
207                declare: false,
208                class,
209            }),
210            _ => None,
211        }
212    }
213
214    #[cfg_attr(not(debug_assertions), inline(always))]
215    fn wrap_with_paren(self) -> Expr {
216        let expr = self.into();
217        let span = expr.span();
218        ParenExpr { expr, span }.into()
219    }
220
221    /// Creates a binary expr `$self === `
222    #[cfg_attr(not(debug_assertions), inline(always))]
223    fn make_eq<T>(self, right: T) -> Expr
224    where
225        T: Into<Expr>,
226    {
227        self.make_bin(op!("==="), right)
228    }
229
230    /// Creates a binary expr `$self $op $rhs`
231    #[cfg_attr(not(debug_assertions), inline(always))]
232    fn make_bin<T>(self, op: BinaryOp, right: T) -> Expr
233    where
234        T: Into<Expr>,
235    {
236        let right = Box::new(right.into());
237
238        BinExpr {
239            span: DUMMY_SP,
240            left: self.into(),
241            op,
242            right,
243        }
244        .into()
245    }
246
247    /// Creates a assign expr `$lhs $op $self`
248    #[cfg_attr(not(debug_assertions), inline(always))]
249    fn make_assign_to(self, op: AssignOp, left: AssignTarget) -> Expr {
250        let right = self.into();
251
252        AssignExpr {
253            span: DUMMY_SP,
254            left,
255            op,
256            right,
257        }
258        .into()
259    }
260
261    #[cfg_attr(not(debug_assertions), inline(always))]
262    fn make_member(self, prop: IdentName) -> MemberExpr {
263        MemberExpr {
264            obj: self.into(),
265            span: DUMMY_SP,
266            prop: MemberProp::Ident(prop),
267        }
268    }
269
270    #[cfg_attr(not(debug_assertions), inline(always))]
271    fn computed_member<T>(self, prop: T) -> MemberExpr
272    where
273        T: Into<Box<Expr>>,
274    {
275        MemberExpr {
276            obj: self.into(),
277            span: DUMMY_SP,
278            prop: MemberProp::Computed(ComputedPropName {
279                span: DUMMY_SP,
280                expr: prop.into(),
281            }),
282        }
283    }
284}
285
286impl<T: Into<Box<Expr>>> ExprFactory for T {}
287
288pub trait IntoIndirectCall
289where
290    Self: std::marker::Sized,
291{
292    type Item;
293    fn into_indirect(self) -> Self::Item;
294}
295
296impl IntoIndirectCall for CallExpr {
297    type Item = CallExpr;
298
299    #[cfg_attr(not(debug_assertions), inline(always))]
300    fn into_indirect(self) -> CallExpr {
301        let callee = self.callee.into_indirect();
302
303        CallExpr { callee, ..self }
304    }
305}
306
307impl IntoIndirectCall for Callee {
308    type Item = Callee;
309
310    #[cfg_attr(not(debug_assertions), inline(always))]
311    fn into_indirect(self) -> Callee {
312        SeqExpr {
313            span: DUMMY_SP,
314            exprs: vec![0f64.into(), self.expect_expr()],
315        }
316        .as_callee()
317    }
318}
319
320impl IntoIndirectCall for TaggedTpl {
321    type Item = TaggedTpl;
322
323    #[cfg_attr(not(debug_assertions), inline(always))]
324    fn into_indirect(mut self) -> Self {
325        Self {
326            tag: Box::new(
327                SeqExpr {
328                    span: DUMMY_SP,
329                    exprs: vec![0f64.into(), self.tag.take()],
330                }
331                .into(),
332            ),
333            ..self
334        }
335    }
336}
337
338pub trait FunctionFactory: Into<Box<Function>> {
339    #[cfg_attr(not(debug_assertions), inline(always))]
340    fn into_fn_expr(self, ident: Option<Ident>) -> FnExpr {
341        FnExpr {
342            ident,
343            function: self.into(),
344        }
345    }
346
347    #[cfg_attr(not(debug_assertions), inline(always))]
348    fn into_fn_decl(self, ident: Ident) -> FnDecl {
349        FnDecl {
350            ident,
351            declare: false,
352            function: self.into(),
353        }
354    }
355
356    #[cfg_attr(not(debug_assertions), inline(always))]
357    fn into_method_prop(self, key: PropName) -> MethodProp {
358        MethodProp {
359            key,
360            function: self.into(),
361        }
362    }
363}
364
365impl<T: Into<Box<Function>>> FunctionFactory for T {}