next_custom_transforms/transforms/
middleware_dynamic.rs

1use swc_core::{
2    ecma::{
3        ast::*,
4        visit::{VisitMut, VisitMutWith},
5    },
6    quote,
7};
8
9enum WrappedExpr {
10    Eval,
11    WasmCompile,
12    WasmInstantiate,
13}
14
15/// Replaces call / expr to dynamic evaluation in the give code to
16/// wrapped expression (__next_eval__, __next_webassembly_compile__,..) to raise
17/// corresponding error.
18///
19/// This transform is specific to edge runtime which are not allowed to
20/// call certain dynamic evaluation (eval, webassembly.instantiate, etc)
21///
22/// check middleware-plugin for corresponding webpack side transform.
23pub fn next_middleware_dynamic() -> MiddlewareDynamic {
24    MiddlewareDynamic {}
25}
26
27pub struct MiddlewareDynamic {}
28
29impl VisitMut for MiddlewareDynamic {
30    fn visit_mut_expr(&mut self, expr: &mut Expr) {
31        let mut should_wrap = None;
32
33        expr.visit_mut_children_with(self);
34
35        if let Expr::Call(call_expr) = &expr {
36            let callee = &call_expr.callee;
37            if let Callee::Expr(callee) = callee {
38                // `eval('some')`, or `Function('some')`
39                if let Expr::Ident(ident) = &**callee {
40                    if ident.sym == "eval" || ident.sym == "Function" {
41                        should_wrap = Some(WrappedExpr::Eval);
42                    }
43                }
44
45                if let Expr::Member(MemberExpr {
46                    obj,
47                    prop: MemberProp::Ident(prop_ident),
48                    ..
49                }) = &**callee
50                {
51                    if let Expr::Ident(ident) = &**obj {
52                        // `global.eval('some')`
53                        if ident.sym == "global" && prop_ident.sym == "eval" {
54                            should_wrap = Some(WrappedExpr::Eval);
55                        }
56
57                        // `WebAssembly.compile('some')` & `WebAssembly.instantiate('some')`
58                        if ident.sym == "WebAssembly" {
59                            if prop_ident.sym == "compile" {
60                                should_wrap = Some(WrappedExpr::WasmCompile);
61                            } else if prop_ident.sym == "instantiate" {
62                                should_wrap = Some(WrappedExpr::WasmInstantiate);
63                            }
64                        }
65                    }
66
67                    if let Expr::Member(MemberExpr {
68                        obj,
69                        prop: MemberProp::Ident(member_prop_ident),
70                        ..
71                    }) = &**obj
72                    {
73                        if let Expr::Ident(ident) = &**obj {
74                            // `global.WebAssembly.compile('some')` &
75                            // `global.WebAssembly.instantiate('some')`
76                            if ident.sym == "global" && member_prop_ident.sym == "WebAssembly" {
77                                if prop_ident.sym == "compile" {
78                                    should_wrap = Some(WrappedExpr::WasmCompile);
79                                } else if prop_ident.sym == "instantiate" {
80                                    should_wrap = Some(WrappedExpr::WasmInstantiate);
81                                }
82                            }
83                        }
84                    }
85                }
86            }
87
88            match should_wrap {
89                Some(WrappedExpr::Eval) => {
90                    *expr = quote!("__next_eval__(function() { return $orig_call });" as Expr, orig_call: Expr = Expr::Call(call_expr.clone()));
91                }
92                Some(WrappedExpr::WasmCompile) => {
93                    *expr = quote!("__next_webassembly_compile__(function() { return $orig_call });" as Expr, orig_call: Expr = Expr::Call(call_expr.clone()));
94                }
95                Some(WrappedExpr::WasmInstantiate) => {
96                    *expr = quote!("__next_webassembly_instantiate__(function() { return $orig_call });" as Expr, orig_call: Expr = Expr::Call(call_expr.clone()));
97                }
98                None => {}
99            }
100        }
101
102        if let Expr::New(NewExpr { callee, .. }) = &expr {
103            if let Expr::Ident(ident) = &**callee {
104                if ident.sym == "Function" {
105                    *expr = quote!("__next_eval__(function() { return $orig_call });" as Expr, orig_call: Expr = expr.clone());
106                }
107            }
108        }
109    }
110}