next_custom_transforms/transforms/
import_analyzer.rs

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