next_custom_transforms/transforms/
shake_exports.rs

1use serde::Deserialize;
2use swc_core::{
3    common::Mark,
4    ecma::{
5        ast::*,
6        atoms::{atom, Atom},
7        transforms::optimization::simplify::dce::{dce, Config as DCEConfig},
8        visit::{fold_pass, Fold, FoldWith, VisitMutWith},
9    },
10};
11
12#[derive(Clone, Debug, Deserialize)]
13pub struct Config {
14    pub ignore: Vec<Atom>,
15}
16
17pub fn shake_exports(config: Config) -> impl Pass {
18    fold_pass(ExportShaker {
19        ignore: config.ignore,
20        ..Default::default()
21    })
22}
23
24#[derive(Debug, Default)]
25struct ExportShaker {
26    ignore: Vec<Atom>,
27    remove_export: bool,
28}
29
30impl Fold for ExportShaker {
31    fn fold_module(&mut self, mut module: Module) -> Module {
32        module = module.fold_children_with(self);
33        module.visit_mut_with(&mut dce(DCEConfig::default(), Mark::new()));
34        module
35    }
36
37    fn fold_module_items(&mut self, items: Vec<ModuleItem>) -> Vec<ModuleItem> {
38        let mut new_items = vec![];
39        for item in items {
40            let item = item.fold_children_with(self);
41            if !self.remove_export {
42                new_items.push(item)
43            }
44            self.remove_export = false;
45        }
46        new_items
47    }
48
49    fn fold_export_decl(&mut self, mut decl: ExportDecl) -> ExportDecl {
50        match &mut decl.decl {
51            Decl::Fn(fn_decl) => {
52                if !self.ignore.contains(&fn_decl.ident.sym) {
53                    self.remove_export = true;
54                }
55            }
56            Decl::Class(class_decl) => {
57                if !self.ignore.contains(&class_decl.ident.sym) {
58                    self.remove_export = true;
59                }
60            }
61            Decl::Var(var_decl) => {
62                var_decl.decls = var_decl
63                    .decls
64                    .iter()
65                    .filter_map(|var_decl| {
66                        if let Pat::Ident(BindingIdent { id, .. }) = &var_decl.name {
67                            if self.ignore.contains(&id.sym) {
68                                return Some(var_decl.to_owned());
69                            }
70                        }
71                        None
72                    })
73                    .collect();
74                if var_decl.decls.is_empty() {
75                    self.remove_export = true;
76                }
77            }
78            _ => {}
79        }
80        decl
81    }
82
83    fn fold_named_export(&mut self, mut export: NamedExport) -> NamedExport {
84        export.specifiers = export
85            .specifiers
86            .into_iter()
87            .filter_map(|spec| {
88                if let ExportSpecifier::Named(named_spec) = spec {
89                    if let Some(ident) = &named_spec.exported {
90                        if let ModuleExportName::Ident(ident) = ident {
91                            if self.ignore.contains(&ident.sym) {
92                                return Some(ExportSpecifier::Named(named_spec));
93                            }
94                        }
95                    } else if let ModuleExportName::Ident(ident) = &named_spec.orig {
96                        if self.ignore.contains(&ident.sym) {
97                            return Some(ExportSpecifier::Named(named_spec));
98                        }
99                    }
100                }
101                None
102            })
103            .collect();
104        if export.specifiers.is_empty() {
105            self.remove_export = true
106        }
107        export
108    }
109
110    fn fold_export_default_decl(&mut self, decl: ExportDefaultDecl) -> ExportDefaultDecl {
111        if !self.ignore.contains(&atom!("default")) {
112            self.remove_export = true
113        }
114        decl
115    }
116
117    fn fold_export_default_expr(&mut self, expr: ExportDefaultExpr) -> ExportDefaultExpr {
118        if !self.ignore.contains(&atom!("default")) {
119            self.remove_export = true
120        }
121        expr
122    }
123}