Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(es/compat): Handle export function in reserved_word #7251

Merged
merged 2 commits into from
Apr 12, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
160 changes: 98 additions & 62 deletions crates/swc_ecma_transforms_compat/src/es3/reserved_word.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use swc_common::{util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
use swc_trace_macro::swc_trace;

/// babel: `@babel/plugin-transform-reserved-words`
Expand All @@ -19,84 +20,106 @@ use swc_trace_macro::swc_trace;
/// var _abstract = 1;
/// var x = _abstract + 1;
/// ```
pub fn reserved_words(preserve_import: bool) -> impl Fold {
ReservedWord { preserve_import }
pub fn reserved_words(preserve_import: bool) -> impl VisitMut + Fold {
as_folder(ReservedWord { preserve_import })
}
struct ReservedWord {
pub preserve_import: bool,
}

#[swc_trace]
impl Fold for ReservedWord {
noop_fold_type!();
impl VisitMut for ReservedWord {
noop_visit_mut_type!();

fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
let mut extra_exports = vec![];

n.iter_mut().for_each(|module_item| {
if let Some((ident, decl)) = match module_item {
ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { decl, .. })) => {
let ident = decl
.as_fn_decl()
.filter(|fn_decl| fn_decl.ident.is_reserved_in_es3())
.map(|fn_decl| fn_decl.ident.clone());

ident.map(|ident| (ident, decl.take()))
}
_ => None,
} {
*module_item = ModuleItem::Stmt(decl.into());

let mut orig = ident.clone();
orig.visit_mut_with(self);

extra_exports.push(
ExportNamedSpecifier {
span: DUMMY_SP,
orig: orig.into(),
exported: Some(ident.into()),
is_type_only: false,
}
.into(),
);
}

fn fold_export_named_specifier(&mut self, n: ExportNamedSpecifier) -> ExportNamedSpecifier {
let ident = match n.orig {
ModuleExportName::Ident(ident) if ident.is_reserved_in_es3() => ident,
_ => return n,
};
module_item.visit_mut_with(self);
});

if !extra_exports.is_empty() {
let module_item = ModuleItem::ModuleDecl(
NamedExport {
span: DUMMY_SP,
specifiers: extra_exports,
src: None,
type_only: false,
asserts: None,
}
.into(),
);

ExportNamedSpecifier {
orig: ident.clone().fold_with(self).into(),
exported: n.exported.or_else(|| Some(ident.into())),
..n
n.push(module_item);
}
}

fn fold_named_export(&mut self, n: NamedExport) -> NamedExport {
if n.src.is_none() {
return n.fold_children_with(self);
fn visit_mut_export_named_specifier(&mut self, n: &mut ExportNamedSpecifier) {
if matches!(&n.orig, ModuleExportName::Ident(ident) if ident.is_reserved_in_es3()) {
n.exported.get_or_insert_with(|| n.orig.clone());
n.orig.visit_mut_with(self);
}

n
}

fn fold_ident(&mut self, i: Ident) -> Ident {
fold_ident(self.preserve_import, i)
fn visit_mut_named_export(&mut self, n: &mut NamedExport) {
if n.src.is_none() {
n.visit_mut_children_with(self);
}
}

fn fold_import_named_specifier(&mut self, s: ImportNamedSpecifier) -> ImportNamedSpecifier {
if s.local.is_reserved_in_es3() {
ImportNamedSpecifier {
imported: s.imported.or_else(|| Some(s.local.clone().into())),
local: s.local.fold_with(self),
..s
}
} else {
s
fn visit_mut_ident(&mut self, i: &mut Ident) {
if self.preserve_import && i.sym == *"import" {
return;
}
}

fn fold_member_expr(&mut self, e: MemberExpr) -> MemberExpr {
MemberExpr {
obj: e.obj.fold_with(self),
prop: if let MemberProp::Computed(c) = e.prop {
MemberProp::Computed(c.fold_with(self))
} else {
e.prop
},
..e
if i.is_reserved_in_es3() {
i.sym = format!("_{}", i.sym).into()
}
}

fn fold_prop_name(&mut self, n: PropName) -> PropName {
n
fn visit_mut_import_named_specifier(&mut self, s: &mut ImportNamedSpecifier) {
if s.local.is_reserved_in_es3() {
s.imported.get_or_insert_with(|| s.local.clone().into());
s.local.visit_mut_with(self);
}
}
}

fn fold_ident(preserve_import: bool, i: Ident) -> Ident {
if preserve_import && i.sym == *"import" {
return i;
}
fn visit_mut_member_expr(&mut self, e: &mut MemberExpr) {
e.obj.visit_mut_with(self);

if i.is_reserved_in_es3() {
return Ident {
sym: format!("_{}", i.sym).into(),
..i
};
if let MemberProp::Computed(c) = &mut e.prop {
c.visit_mut_with(self);
}
}

i
fn visit_mut_prop_name(&mut self, _: &mut PropName) {}
}

#[cfg(test)]
Expand All @@ -109,9 +132,7 @@ mod tests {
($name:ident, $src:literal) => {
test!(
::swc_ecma_parser::Syntax::default(),
|_| ReservedWord {
preserve_import: false
},
|_| reserved_words(false),
$name,
$src,
$src
Expand All @@ -121,9 +142,7 @@ mod tests {

test!(
::swc_ecma_parser::Syntax::default(),
|_| ReservedWord {
preserve_import: false
},
|_| reserved_words(false),
babel_issue_6477,
r#"
function utf8CheckByte(byte) {
Expand All @@ -149,9 +168,7 @@ function utf8CheckByte(_byte) {

test!(
::swc_ecma_parser::Syntax::default(),
|_| ReservedWord {
preserve_import: false
},
|_| reserved_words(false),
issue_7164,
r#"
import { int } from './a.js'
Expand All @@ -164,4 +181,23 @@ function utf8CheckByte(_byte) {
export { _int as int };
"#
);

test!(
Default::default(),
|_| reserved_words(false),
issue_7237,
r#"
export function char() {
console.log("char====char");
return "";
}
"#,
r#"
function _char() {
console.log("char====char");
return "";
}
export { _char as char };
"#
);
}