swc_plugin_remove_console/
lib.rs

1use serde::Deserialize;
2use swc_core::{
3    common::util::take::Take,
4    ecma::{
5        ast::{CallExpr, MemberExpr, Program, Stmt},
6        visit::{visit_mut_pass, VisitMut, VisitMutWith},
7    },
8    plugin::{plugin_transform, proxies::TransformPluginProgramMetadata},
9};
10
11#[cfg(test)]
12mod tests;
13
14#[derive(Deserialize, Default)]
15#[serde(default)]
16pub struct Options {
17    exclude: Vec<String>,
18}
19
20const SPECIFY_SUBCOMMAND: [&str; 4] = ["log", "warn", "error", "info"];
21
22pub struct RemoveConsole {
23    options: Options,
24}
25
26impl RemoveConsole {
27    fn is_console(&self, expr: &MemberExpr) -> bool {
28        let obj = &expr.obj;
29        obj.as_ident().map_or(false, |ident| ident.sym == "console")
30    }
31
32    fn is_specify_subcommand(&self, expr: &MemberExpr) -> bool {
33        let prop = &expr.prop;
34        return SPECIFY_SUBCOMMAND.iter().any(|command| {
35            if self.options.exclude.contains(&command.to_string()) {
36                return false;
37            }
38            prop.as_ident().map_or(false, |ident| ident.sym == *command)
39        });
40    }
41
42    fn should_remove(&self, e: &CallExpr) -> bool {
43        if e.callee.is_expr() {
44            if let Some(expr) = e.callee.as_expr() {
45                if let Some(expr) = expr.as_member() {
46                    return self.is_console(expr) && self.is_specify_subcommand(expr);
47                }
48            }
49        }
50        false
51    }
52}
53
54impl VisitMut for RemoveConsole {
55    fn visit_mut_stmt(&mut self, stmt: &mut Stmt) {
56        stmt.visit_mut_children_with(self);
57
58        if let Stmt::Expr(expr) = stmt {
59            if let Some(call_expr) = expr.expr.as_call() {
60                if self.should_remove(call_expr) {
61                    stmt.take();
62                }
63            }
64        }
65    }
66}
67
68#[plugin_transform]
69pub fn remove_console(program: Program, metadata: TransformPluginProgramMetadata) -> Program {
70    let options = metadata
71        .get_transform_plugin_config()
72        .map(|json| {
73            serde_json::from_str::<Options>(&json)
74                .expect("failed to deserialize options for remove-console plugin")
75        })
76        .unwrap_or_default();
77    program.apply(&mut visit_mut_pass(RemoveConsole { options }))
78}