remove_console/
lib.rs

1use serde::Deserialize;
2use swc_atoms::Atom;
3use swc_common::{SyntaxContext, DUMMY_SP};
4use swc_ecma_ast::*;
5use swc_ecma_visit::{fold_pass, noop_fold_type, Fold, FoldWith};
6
7#[derive(Clone, Debug, Deserialize)]
8#[serde(untagged)]
9pub enum Config {
10    All(bool),
11    WithOptions(Options),
12}
13
14impl Config {
15    pub fn truthy(&self) -> bool {
16        match self {
17            Config::All(b) => *b,
18            Config::WithOptions(_) => true,
19        }
20    }
21}
22
23#[derive(Clone, Debug, Deserialize)]
24pub struct Options {
25    #[serde(default)]
26    pub exclude: Vec<Atom>,
27}
28
29struct RemoveConsole {
30    exclude: Vec<Atom>,
31    unresolved_ctxt: SyntaxContext,
32}
33
34impl RemoveConsole {
35    fn is_global_console(&self, ident: &Ident) -> bool {
36        &ident.sym == "console" && ident.ctxt == self.unresolved_ctxt
37    }
38
39    fn should_remove_call(&mut self, n: &CallExpr) -> bool {
40        let callee = &n.callee;
41        let member_expr = match callee {
42            Callee::Expr(e) => match &**e {
43                Expr::Member(m) => m,
44                _ => return false,
45            },
46            _ => return false,
47        };
48
49        // Don't attempt to evaluate computed properties.
50
51        if matches!(&member_expr.prop, MemberProp::Computed(..)) {
52            return false;
53        }
54
55        // Only proceed if the object is the global `console` object.
56        match &*member_expr.obj {
57            Expr::Ident(i) if self.is_global_console(i) => {}
58            _ => return false,
59        }
60
61        // Check if the property is requested to be excluded.
62        // Here we do an O(n) search on the list of excluded properties because the size
63        // should be small.
64        match &member_expr.prop {
65            MemberProp::Ident(i) if !self.exclude.contains(&i.sym) => {}
66            _ => return false,
67        }
68
69        true
70    }
71}
72
73impl Fold for RemoveConsole {
74    noop_fold_type!();
75
76    fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
77        if let Stmt::Expr(e) = &stmt {
78            if let Expr::Call(c) = &*e.expr {
79                if self.should_remove_call(c) {
80                    return Stmt::Empty(EmptyStmt { span: DUMMY_SP });
81                }
82            }
83        }
84        stmt.fold_children_with(self)
85    }
86}
87
88pub fn remove_console(config: Config, unresolved_ctxt: SyntaxContext) -> impl Pass {
89    let exclude = match config {
90        Config::WithOptions(x) => x.exclude,
91        _ => vec![],
92    };
93    fold_pass(RemoveConsole {
94        exclude,
95        unresolved_ctxt,
96    })
97}