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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use std::borrow::Cow;
use swc_atoms::Atom;
use swc_ecma_ast::*;
use swc_ecma_utils::private_ident;
use crate::CompilerImpl;
impl<'a> CompilerImpl<'a> {
pub(crate) fn transform_export_namespace_from(&mut self, items: &mut Vec<ModuleItem>) {
let count = items
.iter()
.filter(|m| {
matches!(m, ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
specifiers,
src: Some(..),
type_only: false,
..
})) if specifiers.iter().any(|s| s.is_namespace()))
})
.count();
if count == 0 {
return;
}
let mut stmts = Vec::<ModuleItem>::with_capacity(items.len() + count);
for item in items.drain(..) {
match item {
ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
span,
specifiers,
src: Some(src),
type_only: false,
with,
})) if specifiers.iter().any(|s| s.is_namespace()) => {
let mut origin_specifiers = Vec::new();
let mut import_specifiers = Vec::new();
let mut export_specifiers = Vec::new();
for s in specifiers.into_iter() {
match s {
ExportSpecifier::Namespace(ExportNamespaceSpecifier { span, name }) => {
let local_bridge =
private_ident!(format!("_{}", normalize_name(&name)));
import_specifiers.push(ImportSpecifier::Namespace(
ImportStarAsSpecifier {
span,
local: local_bridge.clone(),
},
));
export_specifiers.push(ExportSpecifier::Named(
ExportNamedSpecifier {
span,
orig: local_bridge.into(),
exported: Some(name),
is_type_only: false,
},
))
}
ExportSpecifier::Default(..) | ExportSpecifier::Named(..) => {
origin_specifiers.push(s);
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}
stmts.push(
ImportDecl {
span,
specifiers: import_specifiers,
src: src.clone(),
type_only: false,
with: with.clone(),
phase: Default::default(),
}
.into(),
);
stmts.push(
NamedExport {
span,
specifiers: export_specifiers,
src: None,
type_only: false,
with: None,
}
.into(),
);
if !origin_specifiers.is_empty() {
stmts.push(
NamedExport {
span,
specifiers: origin_specifiers,
src: Some(src),
type_only: false,
with,
}
.into(),
);
}
}
_ => {
stmts.push(item);
}
}
}
*items = stmts;
}
}
fn normalize_name(module_export_name: &ModuleExportName) -> Cow<Atom> {
match module_export_name {
ModuleExportName::Ident(Ident { sym: name, .. }) => Cow::Borrowed(name),
ModuleExportName::Str(Str { value: name, .. }) => {
// Normally, the export name should be valid UTF-8. But it might also contain
// unpaired surrogates. Node would give an error in this case:
// `SyntaxError: Invalid module export name: contains unpaired
// surrogate`. Here, we temporarily replace the unpaired surrogates
// with U+FFFD REPLACEMENT CHARACTER by using Wtf8::to_string_lossy.
Cow::Owned(Atom::from(name.to_string_lossy()))
}
#[cfg(swc_ast_unknown)]
_ => panic!("unable to access unknown nodes"),
}
}