Skip to main content

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::{Config as DCEConfig, dce},
8        visit::{Fold, FoldWith, VisitMutWith, fold_pass},
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) if !self.ignore.contains(&fn_decl.ident.sym) => {
52                self.remove_export = true;
53            }
54            Decl::Class(class_decl) if !self.ignore.contains(&class_decl.ident.sym) => {
55                self.remove_export = true;
56            }
57            Decl::Var(var_decl) => {
58                var_decl.decls = var_decl
59                    .decls
60                    .iter()
61                    .filter_map(|var_decl| {
62                        if let Pat::Ident(BindingIdent { id, .. }) = &var_decl.name
63                            && self.ignore.contains(&id.sym)
64                        {
65                            return Some(var_decl.to_owned());
66                        }
67                        None
68                    })
69                    .collect();
70                if var_decl.decls.is_empty() {
71                    self.remove_export = true;
72                }
73            }
74            _ => {}
75        }
76        decl
77    }
78
79    fn fold_named_export(&mut self, mut export: NamedExport) -> NamedExport {
80        export.specifiers = export
81            .specifiers
82            .into_iter()
83            .filter_map(|spec| {
84                if let ExportSpecifier::Named(named_spec) = spec {
85                    if let Some(ident) = &named_spec.exported {
86                        if let ModuleExportName::Ident(ident) = ident
87                            && self.ignore.contains(&ident.sym)
88                        {
89                            return Some(ExportSpecifier::Named(named_spec));
90                        }
91                    } else if let ModuleExportName::Ident(ident) = &named_spec.orig
92                        && self.ignore.contains(&ident.sym)
93                    {
94                        return Some(ExportSpecifier::Named(named_spec));
95                    }
96                }
97                None
98            })
99            .collect();
100        if export.specifiers.is_empty() {
101            self.remove_export = true
102        }
103        export
104    }
105
106    fn fold_export_default_decl(&mut self, decl: ExportDefaultDecl) -> ExportDefaultDecl {
107        if !self.ignore.contains(&atom!("default")) {
108            self.remove_export = true
109        }
110        decl
111    }
112
113    fn fold_export_default_expr(&mut self, expr: ExportDefaultExpr) -> ExportDefaultExpr {
114        if !self.ignore.contains(&atom!("default")) {
115            self.remove_export = true
116        }
117        expr
118    }
119}