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