next_custom_transforms/transforms/
import_analyzer.rs

1use rustc_hash::{FxHashMap, FxHashSet};
2use swc_core::{
3    atoms::Atom,
4    ecma::{
5        ast::{
6            Expr, Id, ImportDecl, ImportNamedSpecifier, ImportSpecifier, MemberExpr, MemberProp,
7            Module, ModuleExportName,
8        },
9        visit::{noop_visit_type, Visit, VisitWith},
10    },
11};
12
13#[derive(Debug, Default)]
14pub(crate) struct ImportMap {
15    /// Map from module name to (module path, exported symbol)
16    imports: FxHashMap<Id, (Atom, Atom)>,
17
18    namespace_imports: FxHashMap<Id, Atom>,
19
20    imported_modules: FxHashSet<Atom>,
21}
22
23#[allow(unused)]
24impl ImportMap {
25    pub fn is_module_imported(&mut self, module: &Atom) -> bool {
26        self.imported_modules.contains(module)
27    }
28
29    /// Returns true if `e` is an import of `orig_name` from `module`.
30    pub fn is_import(&self, e: &Expr, module: &str, orig_name: &str) -> bool {
31        match e {
32            Expr::Ident(i) => {
33                if let Some((i_src, i_sym)) = self.imports.get(&i.to_id()) {
34                    i_src == module && i_sym == orig_name
35                } else {
36                    false
37                }
38            }
39
40            Expr::Member(MemberExpr {
41                obj: box Expr::Ident(obj),
42                prop: MemberProp::Ident(prop),
43                ..
44            }) => {
45                if let Some(obj_src) = self.namespace_imports.get(&obj.to_id()) {
46                    obj_src == module && prop.sym == *orig_name
47                } else {
48                    false
49                }
50            }
51
52            _ => false,
53        }
54    }
55
56    pub fn analyze(m: &Module) -> Self {
57        let mut data = ImportMap::default();
58
59        m.visit_with(&mut Analyzer { data: &mut data });
60
61        data
62    }
63}
64
65struct Analyzer<'a> {
66    data: &'a mut ImportMap,
67}
68
69impl Visit for Analyzer<'_> {
70    noop_visit_type!();
71
72    fn visit_import_decl(&mut self, import: &ImportDecl) {
73        self.data.imported_modules.insert(import.src.value.clone());
74
75        for s in &import.specifiers {
76            let (local, orig_sym) = match s {
77                ImportSpecifier::Named(ImportNamedSpecifier {
78                    local, imported, ..
79                }) => match imported {
80                    Some(imported) => (local.to_id(), orig_name(imported)),
81                    _ => (local.to_id(), local.sym.clone()),
82                },
83                ImportSpecifier::Default(s) => (s.local.to_id(), "default".into()),
84                ImportSpecifier::Namespace(s) => {
85                    self.data
86                        .namespace_imports
87                        .insert(s.local.to_id(), import.src.value.clone());
88                    continue;
89                }
90            };
91
92            self.data
93                .imports
94                .insert(local, (import.src.value.clone(), orig_sym));
95        }
96    }
97}
98
99fn orig_name(n: &ModuleExportName) -> Atom {
100    match n {
101        ModuleExportName::Ident(v) => v.sym.clone(),
102        ModuleExportName::Str(v) => v.value.clone(),
103    }
104}