swc_ecma_compiler/es2020/
export_namespace_from.rs

1use std::borrow::Cow;
2
3use swc_atoms::Atom;
4use swc_ecma_ast::*;
5use swc_ecma_utils::private_ident;
6
7use crate::CompilerImpl;
8
9impl<'a> CompilerImpl<'a> {
10    pub(crate) fn transform_export_namespace_from(&mut self, items: &mut Vec<ModuleItem>) {
11        let count = items
12            .iter()
13            .filter(|m| {
14                matches!(m, ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
15                        specifiers,
16                        src: Some(..),
17                        type_only: false,
18                        ..
19                    })) if specifiers.iter().any(|s| s.is_namespace()))
20            })
21            .count();
22
23        if count == 0 {
24            return;
25        }
26
27        let mut stmts = Vec::<ModuleItem>::with_capacity(items.len() + count);
28
29        for item in items.drain(..) {
30            match item {
31                ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
32                    span,
33                    specifiers,
34                    src: Some(src),
35                    type_only: false,
36                    with,
37                })) if specifiers.iter().any(|s| s.is_namespace()) => {
38                    let mut origin_specifiers = Vec::new();
39
40                    let mut import_specifiers = Vec::new();
41                    let mut export_specifiers = Vec::new();
42
43                    for s in specifiers.into_iter() {
44                        match s {
45                            ExportSpecifier::Namespace(ExportNamespaceSpecifier { span, name }) => {
46                                let local_bridge =
47                                    private_ident!(format!("_{}", normalize_name(&name)));
48
49                                import_specifiers.push(ImportSpecifier::Namespace(
50                                    ImportStarAsSpecifier {
51                                        span,
52                                        local: local_bridge.clone(),
53                                    },
54                                ));
55                                export_specifiers.push(ExportSpecifier::Named(
56                                    ExportNamedSpecifier {
57                                        span,
58                                        orig: local_bridge.into(),
59                                        exported: Some(name),
60                                        is_type_only: false,
61                                    },
62                                ))
63                            }
64                            ExportSpecifier::Default(..) | ExportSpecifier::Named(..) => {
65                                origin_specifiers.push(s);
66                            }
67                            #[cfg(swc_ast_unknown)]
68                            _ => panic!("unable to access unknown nodes"),
69                        }
70                    }
71
72                    stmts.push(
73                        ImportDecl {
74                            span,
75                            specifiers: import_specifiers,
76                            src: src.clone(),
77                            type_only: false,
78                            with: with.clone(),
79                            phase: Default::default(),
80                        }
81                        .into(),
82                    );
83
84                    stmts.push(
85                        NamedExport {
86                            span,
87                            specifiers: export_specifiers,
88                            src: None,
89                            type_only: false,
90                            with: None,
91                        }
92                        .into(),
93                    );
94
95                    if !origin_specifiers.is_empty() {
96                        stmts.push(
97                            NamedExport {
98                                span,
99                                specifiers: origin_specifiers,
100                                src: Some(src),
101                                type_only: false,
102                                with,
103                            }
104                            .into(),
105                        );
106                    }
107                }
108                _ => {
109                    stmts.push(item);
110                }
111            }
112        }
113
114        *items = stmts;
115    }
116}
117
118fn normalize_name(module_export_name: &ModuleExportName) -> Cow<Atom> {
119    match module_export_name {
120        ModuleExportName::Ident(Ident { sym: name, .. }) => Cow::Borrowed(name),
121        ModuleExportName::Str(Str { value: name, .. }) => {
122            // Normally, the export name should be valid UTF-8. But it might also contain
123            // unpaired surrogates.   Node would give an error in this case:
124            // `SyntaxError: Invalid module export name: contains unpaired
125            // surrogate`.  Here, we temporarily replace the unpaired surrogates
126            // with U+FFFD REPLACEMENT CHARACTER by using Wtf8::to_string_lossy.
127            Cow::Owned(Atom::from(name.to_string_lossy()))
128        }
129        #[cfg(swc_ast_unknown)]
130        _ => panic!("unable to access unknown nodes"),
131    }
132}