next_custom_transforms/transforms/
cjs_finder.rs

1use swc_core::ecma::{
2    ast::*,
3    visit::{Visit, VisitWith},
4};
5
6pub fn contains_cjs(m: &Module) -> bool {
7    let mut v = CjsFinder::default();
8    m.visit_with(&mut v);
9    v.found && !v.is_esm
10}
11
12#[derive(Copy, Clone, Default)]
13struct CjsFinder {
14    found: bool,
15    is_esm: bool,
16    ignore_module: bool,
17    ignore_exports: bool,
18}
19
20impl CjsFinder {
21    /// If the given pattern contains `module` as a parameter, we don't need to
22    /// recurse into it because `module` is shadowed.
23    fn adjust_state<'a, I>(&mut self, iter: I)
24    where
25        I: Iterator<Item = &'a Pat>,
26    {
27        iter.for_each(|p| {
28            if let Pat::Ident(i) = p {
29                if &*i.id.sym == "module" {
30                    self.ignore_module = true;
31                }
32                if &*i.id.sym == "exports" {
33                    self.ignore_exports = true;
34                }
35            }
36        })
37    }
38}
39
40/// This visitor implementation supports typescript, because the api of `swc`
41/// does not support changing configuration based on content of the file.
42impl Visit for CjsFinder {
43    fn visit_arrow_expr(&mut self, n: &ArrowExpr) {
44        let old_ignore_module = self.ignore_module;
45        let old_ignore_exports = self.ignore_exports;
46
47        self.adjust_state(n.params.iter());
48
49        n.visit_children_with(self);
50
51        self.ignore_module = old_ignore_module;
52        self.ignore_exports = old_ignore_exports;
53    }
54
55    // Detect `Object.defineProperty(exports, "__esModule", ...)`
56    // Note that `Object.defineProperty(module.exports, ...)` will be handled by
57    // `visit_member_expr`.
58    fn visit_call_expr(&mut self, e: &CallExpr) {
59        if !self.ignore_exports {
60            if let Callee::Expr(expr) = &e.callee {
61                if let Expr::Member(member_expr) = &**expr {
62                    if let (Expr::Ident(obj), MemberProp::Ident(prop)) =
63                        (&*member_expr.obj, &member_expr.prop)
64                    {
65                        if &*obj.sym == "Object" && &*prop.sym == "defineProperty" {
66                            if let Some(ExprOrSpread { expr: expr0, .. }) = e.args.first() {
67                                if let Expr::Ident(arg0) = &**expr0 {
68                                    if &*arg0.sym == "exports" {
69                                        if let Some(ExprOrSpread { expr: expr1, .. }) =
70                                            e.args.get(1)
71                                        {
72                                            if let Expr::Lit(Lit::Str(arg1)) = &**expr1 {
73                                                if &*arg1.value == "__esModule" {
74                                                    self.found = true;
75                                                    return;
76                                                }
77                                            }
78                                        }
79                                    }
80                                }
81                            }
82                        }
83                    }
84                }
85            }
86        }
87
88        e.callee.visit_with(self);
89    }
90
91    fn visit_class_method(&mut self, n: &ClassMethod) {
92        let old_ignore_module = self.ignore_module;
93        let old_ignore_exports = self.ignore_exports;
94
95        self.adjust_state(n.function.params.iter().map(|v| &v.pat));
96
97        n.visit_children_with(self);
98
99        self.ignore_module = old_ignore_module;
100        self.ignore_exports = old_ignore_exports;
101    }
102
103    fn visit_function(&mut self, n: &Function) {
104        let old_ignore_module = self.ignore_module;
105        let old_ignore_exports = self.ignore_exports;
106
107        self.adjust_state(n.params.iter().map(|v| &v.pat));
108
109        n.visit_children_with(self);
110
111        self.ignore_module = old_ignore_module;
112        self.ignore_exports = old_ignore_exports;
113    }
114
115    fn visit_member_expr(&mut self, e: &MemberExpr) {
116        if let Expr::Ident(obj) = &*e.obj {
117            if let MemberProp::Ident(prop) = &e.prop {
118                // Detect `module.exports` and `exports.__esModule`
119                if (!self.ignore_module && &*obj.sym == "module" && &*prop.sym == "exports")
120                    || (!self.ignore_exports && &*obj.sym == "exports")
121                {
122                    self.found = true;
123                    return;
124                }
125            }
126        }
127
128        e.obj.visit_with(self);
129        e.prop.visit_with(self);
130    }
131
132    fn visit_method_prop(&mut self, n: &MethodProp) {
133        let old_ignore_module = self.ignore_module;
134        let old_ignore_exports = self.ignore_exports;
135
136        self.adjust_state(n.function.params.iter().map(|v| &v.pat));
137
138        n.visit_children_with(self);
139
140        self.ignore_module = old_ignore_module;
141        self.ignore_exports = old_ignore_exports;
142    }
143
144    fn visit_module_decl(&mut self, n: &ModuleDecl) {
145        match n {
146            ModuleDecl::Import(_) => {}
147            _ => {
148                self.is_esm = true;
149            }
150        }
151    }
152}