next_custom_transforms/transforms/
shake_exports.rs1use 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}