swc_ecma_transforms_module/
import_analysis.rs

1use rustc_hash::FxHashMap;
2use swc_atoms::Atom;
3use swc_ecma_ast::*;
4use swc_ecma_transforms_base::enable_helper;
5use swc_ecma_visit::{
6    noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitWith,
7};
8
9use crate::{module_decl_strip::LinkFlag, util::ImportInterop};
10
11pub fn import_analyzer(import_interop: ImportInterop, ignore_dynamic: bool) -> impl Pass {
12    visit_mut_pass(ImportAnalyzer {
13        import_interop,
14        ignore_dynamic,
15        flag_record: Default::default(),
16        dynamic_import_found: false,
17    })
18}
19
20pub struct ImportAnalyzer {
21    import_interop: ImportInterop,
22    ignore_dynamic: bool,
23
24    flag_record: FxHashMap<Atom, LinkFlag>,
25    dynamic_import_found: bool,
26}
27
28/// Inject required helpers methods **for** module transform passes.
29impl VisitMut for ImportAnalyzer {
30    noop_visit_mut_type!(fail);
31
32    fn visit_mut_module(&mut self, module: &mut Module) {
33        self.visit_module(&*module);
34    }
35}
36
37impl Visit for ImportAnalyzer {
38    noop_visit_type!(fail);
39
40    fn visit_module_items(&mut self, n: &[ModuleItem]) {
41        for item in n.iter() {
42            if item.is_module_decl() {
43                item.visit_with(self);
44            }
45        }
46
47        let flag_record = &self.flag_record;
48
49        if flag_record.values().any(|flag| flag.export_star()) {
50            enable_helper!(export_star);
51        }
52
53        if self.import_interop.is_none() {
54            return;
55        }
56
57        if self.import_interop.is_swc()
58            && flag_record
59                .values()
60                .any(|flag| flag.interop() && !flag.has_named())
61        {
62            enable_helper!(interop_require_default);
63        }
64
65        if flag_record.values().any(|flag| flag.namespace()) {
66            enable_helper!(interop_require_wildcard);
67        } else if !self.ignore_dynamic {
68            // `import/export * as foo from "foo"` not found
69            // but it may be used with dynamic import
70            for item in n.iter() {
71                if item.is_stmt() {
72                    item.visit_with(self);
73                }
74                if self.dynamic_import_found {
75                    enable_helper!(interop_require_wildcard);
76                    break;
77                }
78            }
79        }
80    }
81
82    fn visit_import_decl(&mut self, n: &ImportDecl) {
83        let flag = self.flag_record.entry(n.src.value.clone()).or_default();
84        for s in &n.specifiers {
85            *flag |= s.into();
86        }
87    }
88
89    fn visit_named_export(&mut self, n: &NamedExport) {
90        if let Some(src) = n.src.clone() {
91            let flag = self.flag_record.entry(src.value).or_default();
92            for s in &n.specifiers {
93                *flag |= s.into();
94            }
95        }
96    }
97
98    fn visit_export_all(&mut self, n: &ExportAll) {
99        *self.flag_record.entry(n.src.value.clone()).or_default() |= LinkFlag::EXPORT_STAR;
100    }
101
102    fn visit_import(&mut self, _: &Import) {
103        self.dynamic_import_found = true;
104    }
105}