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 if matches!(&member_expr.prop, MemberProp::Computed(..)) {
52 return false;
53 }
54
55 match &*member_expr.obj {
57 Expr::Ident(i) if self.is_global_console(i) => {}
58 _ => return false,
59 }
60
61 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}