swc_ecma_transforms_module/
module_ref_rewriter.rs

1use rustc_hash::{FxHashMap, FxHashSet};
2use swc_atoms::Atom;
3use swc_common::SyntaxContext;
4use swc_ecma_ast::*;
5use swc_ecma_transforms_base::helpers::HELPERS;
6use swc_ecma_utils::{ExprFactory, QueryRef, RefRewriter};
7use swc_ecma_visit::VisitMutWith;
8
9use crate::util::prop_name;
10
11pub type ImportMap = FxHashMap<Id, (Ident, Option<Atom>)>;
12
13pub(crate) struct ImportQuery {
14    /// ```javascript
15    /// import foo, { a as b, c } from "mod";
16    /// import * as x from "x";
17    /// foo, b, c;
18    /// x;
19    /// ```
20    /// ->
21    /// ```javascript
22    /// _mod.default, _mod.a, _mod.c;
23    /// _x;
24    ///
25    /// Map(
26    ///     foo => (_mod, Some("default")),
27    ///     b => (_mod, Some("a")),
28    ///     c => (_mod, Some("c")),
29    ///     x => (_x, None),
30    /// )
31    /// ```
32    import_map: ImportMap,
33    lazy_record: FxHashSet<Id>,
34    helper_ctxt: Option<SyntaxContext>,
35}
36
37impl QueryRef for ImportQuery {
38    fn query_ref(&self, ident: &Ident) -> Option<Box<Expr>> {
39        self.import_map
40            .get(&ident.to_id())
41            .map(|(mod_ident, mod_prop)| -> Box<Expr> {
42                let mut mod_ident = mod_ident.clone();
43                let span = ident.span;
44                mod_ident.span = span;
45
46                let mod_expr = if self.lazy_record.contains(&mod_ident.to_id()) {
47                    mod_ident.as_call(span, Default::default())
48                } else {
49                    mod_ident.into()
50                };
51
52                if let Some(imported_name) = mod_prop {
53                    let prop = prop_name(imported_name, Default::default()).into();
54
55                    MemberExpr {
56                        obj: Box::new(mod_expr),
57                        span,
58                        prop,
59                    }
60                    .into()
61                } else {
62                    mod_expr.into()
63                }
64            })
65    }
66
67    fn query_lhs(&self, _: &Ident) -> Option<Box<Expr>> {
68        // import binding cannot be used as lhs
69        None
70    }
71
72    fn query_jsx(&self, _: &Ident) -> Option<JSXElementName> {
73        // We do not need to handle JSX since there is no jsx preserve option in swc
74        None
75    }
76
77    fn should_fix_this(&self, ident: &Ident) -> bool {
78        if self.helper_ctxt.iter().any(|ctxt| ctxt == &ident.ctxt) {
79            return false;
80        }
81
82        self.import_map
83            .get(&ident.to_id())
84            .map(|(_, prop)| prop.is_some())
85            .unwrap_or_default()
86    }
87}
88
89pub(crate) fn rewrite_import_bindings<V>(
90    node: &mut V,
91    import_map: ImportMap,
92    lazy_record: FxHashSet<Id>,
93) where
94    V: VisitMutWith<RefRewriter<ImportQuery>>,
95{
96    let mut v = RefRewriter {
97        query: ImportQuery {
98            import_map,
99            lazy_record,
100            helper_ctxt: {
101                HELPERS
102                    .is_set()
103                    .then(|| HELPERS.with(|helper| helper.mark()))
104                    .map(|mark| SyntaxContext::empty().apply_mark(mark))
105            },
106        },
107    };
108
109    node.visit_mut_with(&mut v);
110}