Skip to content

Commit c6964e7

Browse files
authored
Merge 99eaf19 into 440b391
2 parents 440b391 + 99eaf19 commit c6964e7

File tree

10 files changed

+180
-20
lines changed

10 files changed

+180
-20
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/swc_ecma_compat_es2020/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ swc_common = { version = "17.0.1", path = "../swc_common" }
2323
swc_ecma_ast = { version = "18.0.0", path = "../swc_ecma_ast" }
2424
swc_ecma_compat_es2022 = { version = "31.0.0", path = "../swc_ecma_compat_es2022" }
2525
swc_ecma_compiler = { version = "8.0.0", path = "../swc_ecma_compiler" }
26+
swc_ecma_transformer = { version = "0.1.0", path = "../swc_ecma_transformer" }
2627
swc_ecma_transforms_base = { version = "30.0.0", path = "../swc_ecma_transforms_base" }
2728
swc_ecma_utils = { version = "24.0.0", path = "../swc_ecma_utils" }
2829
swc_ecma_visit = { version = "18.0.1", path = "../swc_ecma_visit" }
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use swc_ecma_ast::Pass;
2-
use swc_ecma_compiler::{Compiler, Features};
32

43
pub fn export_namespace_from() -> impl Pass {
5-
Compiler::new(swc_ecma_compiler::Config {
6-
includes: Features::EXPORT_NAMESPACE_FROM,
7-
..Default::default()
8-
})
4+
let mut options = swc_ecma_transformer::Options::default();
5+
options.env.es2020.export_namespace_from = true;
6+
options.into_pass()
97
}

crates/swc_ecma_preset_env/src/lib.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,10 @@ where
156156
pass,
157157
/* ES2022 */ | PrivatePropertyInObject
158158
/* ES2021 */ | LogicalAssignmentOperators
159-
/* ES2020 */ | ExportNamespaceFrom
160159
);
160+
if !caniuse(Feature::ExportNamespaceFrom) {
161+
options.env.es2020.export_namespace_from = true;
162+
}
161163

162164
// ES2020
163165
let pass = add!(
@@ -209,6 +211,9 @@ where
209211
// ES2016
210212
let pass = add!(pass, ExponentiationOperator, es2016::exponentiation());
211213

214+
// Single-pass compiler
215+
let pass = (pass, options.into_pass());
216+
212217
// ES2015
213218
let pass = add!(pass, BlockScopedFunctions, es2015::block_scoped_functions());
214219
let pass = add!(
@@ -325,13 +330,11 @@ where
325330
bugfixes::template_literal_caching()
326331
);
327332

328-
let pass = add!(
333+
add!(
329334
pass,
330335
BugfixSafariIdDestructuringCollisionInFunctionExpression,
331336
bugfixes::safari_id_destructuring_collision_in_function_expression()
332-
);
333-
334-
(pass, options.into_pass())
337+
)
335338
}
336339

337340
pub fn transform_from_env<C>(

crates/swc_ecma_preset_env/tests/fixtures/corejs3/usage-regexp/output.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import "core-js/modules/es.regexp.exec.js";
33
import "core-js/modules/es.regexp.to-string.js";
44
var a = RegExp("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})", "u");
55
var b = RegExp(".", "s");
6-
var c = new RegExp(".", "imsuy");
6+
var c = RegExp(".", "imsuy");
77
console.log(a.unicode);
88
console.log(b.dotAll);
99
console.log(c.sticky);

crates/swc_ecma_transformer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ version = "0.1.0"
1313
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(swc_ast_unknown)'] }
1414

1515
[dependencies]
16+
swc_atoms = { version = "9.0.0", path = "../swc_atoms" }
1617
swc_common = { version = "17.0.1", path = "../swc_common" }
1718
swc_ecma_ast = { version = "18.0.0", path = "../swc_ecma_ast" }
1819
swc_ecma_hooks = { version = "0.2.0", path = "../swc_ecma_hooks" }
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use swc_ecma_ast::*;
2+
use swc_ecma_hooks::VisitMutHook;
3+
use swc_ecma_utils::private_ident;
4+
5+
use crate::{utils::normalize_module_export_name, TraverseCtx};
6+
7+
pub fn hook() -> impl VisitMutHook<TraverseCtx> {
8+
ExportNamespaceFromPass
9+
}
10+
11+
struct ExportNamespaceFromPass;
12+
13+
impl ExportNamespaceFromPass {
14+
fn transform_export_namespace_from(&mut self, items: &mut Vec<ModuleItem>) {
15+
let count = items
16+
.iter()
17+
.filter(|m| {
18+
matches!(m, ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
19+
specifiers,
20+
src: Some(..),
21+
type_only: false,
22+
..
23+
})) if specifiers.iter().any(|s| s.is_namespace()))
24+
})
25+
.count();
26+
27+
if count == 0 {
28+
return;
29+
}
30+
31+
let mut stmts = Vec::<ModuleItem>::with_capacity(items.len() + count);
32+
33+
for item in items.drain(..) {
34+
match item {
35+
ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
36+
span,
37+
specifiers,
38+
src: Some(src),
39+
type_only: false,
40+
with,
41+
})) if specifiers.iter().any(|s| s.is_namespace()) => {
42+
let mut origin_specifiers = Vec::new();
43+
44+
let mut import_specifiers = Vec::new();
45+
let mut export_specifiers = Vec::new();
46+
47+
for s in specifiers.into_iter() {
48+
match s {
49+
ExportSpecifier::Namespace(ExportNamespaceSpecifier { span, name }) => {
50+
let local_bridge = private_ident!(format!(
51+
"_{}",
52+
normalize_module_export_name(&name)
53+
));
54+
55+
import_specifiers.push(ImportSpecifier::Namespace(
56+
ImportStarAsSpecifier {
57+
span,
58+
local: local_bridge.clone(),
59+
},
60+
));
61+
export_specifiers.push(ExportSpecifier::Named(
62+
ExportNamedSpecifier {
63+
span,
64+
orig: local_bridge.into(),
65+
exported: Some(name),
66+
is_type_only: false,
67+
},
68+
))
69+
}
70+
ExportSpecifier::Default(..) | ExportSpecifier::Named(..) => {
71+
origin_specifiers.push(s);
72+
}
73+
#[cfg(swc_ast_unknown)]
74+
_ => panic!("unable to access unknown nodes"),
75+
}
76+
}
77+
78+
stmts.push(
79+
ImportDecl {
80+
span,
81+
specifiers: import_specifiers,
82+
src: src.clone(),
83+
type_only: false,
84+
with: with.clone(),
85+
phase: Default::default(),
86+
}
87+
.into(),
88+
);
89+
90+
stmts.push(
91+
NamedExport {
92+
span,
93+
specifiers: export_specifiers,
94+
src: None,
95+
type_only: false,
96+
with: None,
97+
}
98+
.into(),
99+
);
100+
101+
if !origin_specifiers.is_empty() {
102+
stmts.push(
103+
NamedExport {
104+
span,
105+
specifiers: origin_specifiers,
106+
src: Some(src),
107+
type_only: false,
108+
with,
109+
}
110+
.into(),
111+
);
112+
}
113+
}
114+
_ => {
115+
stmts.push(item);
116+
}
117+
}
118+
}
119+
120+
*items = stmts;
121+
}
122+
}
123+
124+
impl VisitMutHook<TraverseCtx> for ExportNamespaceFromPass {
125+
fn exit_program(&mut self, node: &mut Program, _: &mut TraverseCtx) {
126+
let Program::Module(module) = node else {
127+
return;
128+
};
129+
130+
self.transform_export_namespace_from(&mut module.body);
131+
}
132+
}

crates/swc_ecma_transformer/src/es2020/mod.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
use swc_ecma_hooks::VisitMutHook;
22

3-
use crate::TraverseCtx;
3+
use crate::{hook_utils::OptionalHook, TraverseCtx};
4+
5+
mod export_namespace_from;
46

57
#[derive(Debug, Default)]
68
#[non_exhaustive]
7-
pub struct Es2020Options {}
8-
9-
pub fn hook(options: Es2020Options) -> impl VisitMutHook<TraverseCtx> {
10-
Es2020Pass { options }
9+
pub struct Es2020Options {
10+
pub export_namespace_from: bool,
1111
}
1212

13-
struct Es2020Pass {
14-
options: Es2020Options,
13+
pub fn hook(options: Es2020Options) -> impl VisitMutHook<TraverseCtx> {
14+
OptionalHook(if options.export_namespace_from {
15+
Some(self::export_namespace_from::hook())
16+
} else {
17+
None
18+
})
1519
}
16-
17-
impl VisitMutHook<TraverseCtx> for Es2020Pass {}

crates/swc_ecma_transformer/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod jsx;
2323
mod options;
2424
mod regexp;
2525
mod typescript;
26+
mod utils;
2627

2728
pub struct TraverseCtx {}
2829

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use std::borrow::Cow;
2+
3+
use swc_atoms::Atom;
4+
use swc_ecma_ast::*;
5+
6+
pub(crate) fn normalize_module_export_name(module_export_name: &ModuleExportName) -> Cow<Atom> {
7+
match module_export_name {
8+
ModuleExportName::Ident(Ident { sym: name, .. }) => Cow::Borrowed(name),
9+
ModuleExportName::Str(Str { value: name, .. }) => {
10+
// Normally, the export name should be valid UTF-8. But it might also contain
11+
// unpaired surrogates. Node would give an error in this case:
12+
// `SyntaxError: Invalid module export name: contains unpaired
13+
// surrogate`. Here, we temporarily replace the unpaired surrogates
14+
// with U+FFFD REPLACEMENT CHARACTER by using Wtf8::to_string_lossy.
15+
Cow::Owned(Atom::from(name.to_string_lossy()))
16+
}
17+
#[cfg(swc_ast_unknown)]
18+
_ => panic!("unable to access unknown nodes"),
19+
}
20+
}

0 commit comments

Comments
 (0)