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::{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}