1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use swc_atoms::JsWord;
use swc_common::collections::AHashMap;
use swc_ecma_ast::*;
use swc_ecma_transforms_base::enable_helper;
use swc_ecma_visit::{
    as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitWith,
};

use crate::{module_decl_strip::LinkFlag, util::ImportInterop};

pub fn import_analyzer(
    import_interop: ImportInterop,
    ignore_dynamic: bool,
) -> impl Fold + VisitMut {
    as_folder(ImportAnalyzer {
        import_interop,
        ignore_dynamic,
        flag_record: Default::default(),
        dynamic_import_found: false,
    })
}

pub struct ImportAnalyzer {
    import_interop: ImportInterop,
    ignore_dynamic: bool,

    flag_record: AHashMap<JsWord, LinkFlag>,
    dynamic_import_found: bool,
}

/// Inject required helpers methods **for** module transform passes.
impl VisitMut for ImportAnalyzer {
    noop_visit_mut_type!();

    fn visit_mut_module(&mut self, module: &mut Module) {
        self.visit_module(&*module);
    }
}

impl Visit for ImportAnalyzer {
    noop_visit_type!();

    fn visit_module_items(&mut self, n: &[ModuleItem]) {
        for item in n.iter() {
            if item.is_module_decl() {
                item.visit_with(self);
            }
        }

        let flag_record = &self.flag_record;

        if flag_record.values().any(|flag| flag.export_star()) {
            enable_helper!(export_star);
        }

        if self.import_interop.is_none() {
            return;
        }

        if self.import_interop.is_swc()
            && flag_record
                .values()
                .any(|flag| flag.interop() && !flag.has_named())
        {
            enable_helper!(interop_require_default);
        }

        if flag_record.values().any(|flag| flag.namespace()) {
            enable_helper!(interop_require_wildcard);
        } else if !self.ignore_dynamic {
            // `import/export * as foo from "foo"` not found
            // but it may be used with dynamic import
            for item in n.iter() {
                if item.is_stmt() {
                    item.visit_with(self);
                }
                if self.dynamic_import_found {
                    enable_helper!(interop_require_wildcard);
                    break;
                }
            }
        }
    }

    fn visit_import_decl(&mut self, n: &ImportDecl) {
        let flag = self.flag_record.entry(n.src.value.clone()).or_default();
        for s in &n.specifiers {
            *flag |= s.into();
        }
    }

    fn visit_named_export(&mut self, n: &NamedExport) {
        if let Some(src) = n.src.clone() {
            let flag = self.flag_record.entry(src.value).or_default();
            for s in &n.specifiers {
                *flag |= s.into();
            }
        }
    }

    fn visit_export_all(&mut self, n: &ExportAll) {
        *self.flag_record.entry(n.src.value.clone()).or_default() |= LinkFlag::EXPORT_STAR;
    }

    fn visit_import(&mut self, _: &Import) {
        self.dynamic_import_found = true;
    }
}