next_custom_transforms/transforms/
named_import_transform.rs

1use std::collections::HashSet;
2
3use serde::Deserialize;
4use swc_core::{
5    common::DUMMY_SP,
6    ecma::{
7        ast::*,
8        visit::{visit_mut_pass, VisitMut},
9    },
10};
11
12#[derive(Clone, Debug, Deserialize)]
13pub struct Config {
14    pub packages: Vec<String>,
15}
16
17pub fn named_import_transform(config: Config) -> impl Pass + VisitMut {
18    visit_mut_pass(NamedImportTransform {
19        packages: config.packages,
20    })
21}
22
23#[derive(Debug, Default)]
24struct NamedImportTransform {
25    packages: Vec<String>,
26}
27
28impl VisitMut for NamedImportTransform {
29    fn visit_mut_import_decl(&mut self, decl: &mut ImportDecl) {
30        // Match named imports and check if it's included in the packages
31        let src_value = &decl.src.value;
32
33        if self
34            .packages
35            .iter()
36            .any(|p| src_value.as_str() == Some(&**p))
37        {
38            let mut specifier_names = HashSet::new();
39
40            // Skip the transform if the default or namespace import is present
41            let mut skip_transform = false;
42
43            for specifier in &decl.specifiers {
44                match specifier {
45                    ImportSpecifier::Named(specifier) => {
46                        // Add the import name as string to the set
47                        specifier_names.insert(specifier.imported.as_ref().map_or_else(
48                            || specifier.local.sym.clone(),
49                            |i| i.atom().into_owned(),
50                        ));
51                    }
52                    ImportSpecifier::Default(_) => {
53                        skip_transform = true;
54                        break;
55                    }
56                    ImportSpecifier::Namespace(_) => {
57                        skip_transform = true;
58                        break;
59                    }
60                }
61            }
62
63            if !skip_transform {
64                let mut names = specifier_names
65                    .iter()
66                    .map(|n| n.as_str())
67                    .collect::<Vec<_>>();
68                // Sort the names to make sure the order is consistent
69                names.sort();
70
71                let new_src = format!(
72                    "__barrel_optimize__?names={}!=!{}",
73                    names.join(","),
74                    src_value.to_string_lossy()
75                );
76
77                *decl.src = Str {
78                    span: DUMMY_SP,
79                    value: new_src.into(),
80                    raw: None,
81                };
82            }
83        }
84    }
85}