swc_ecma_transforms_compat/bugfixes/
async_arrows_in_class.rsuse swc_common::{util::take::Take, Mark, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::prepend_stmt;
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith, InjectVars};
use swc_trace_macro::swc_trace;
use crate::es2015::arrow;
#[tracing::instrument(level = "info", skip_all)]
pub fn async_arrows_in_class(unresolved_mark: Mark) -> impl Fold {
AsyncArrowsInClass {
unresolved_mark,
..Default::default()
}
}
#[derive(Default, Clone)]
struct AsyncArrowsInClass {
in_class_method: bool,
unresolved_mark: Mark,
vars: Vec<VarDeclarator>,
}
#[swc_trace]
impl Fold for AsyncArrowsInClass {
noop_fold_type!();
fn fold_class_method(&mut self, n: ClassMethod) -> ClassMethod {
self.in_class_method = true;
let res = n.fold_children_with(self);
self.in_class_method = false;
res
}
fn fold_constructor(&mut self, n: Constructor) -> Constructor {
self.in_class_method = true;
let res = n.fold_children_with(self);
self.in_class_method = false;
res
}
fn fold_expr(&mut self, n: Expr) -> Expr {
let n = n.fold_children_with(self);
if !self.in_class_method {
return n;
}
match n {
Expr::Arrow(ref a) => {
if a.is_async {
let mut v = arrow(self.unresolved_mark);
let n = n.fold_with(&mut v);
self.vars.extend(v.take_vars());
n
} else {
n
}
}
_ => n,
}
}
fn fold_module_items(&mut self, stmts: Vec<ModuleItem>) -> Vec<ModuleItem> {
let mut stmts = stmts.fold_children_with(self);
if !self.vars.is_empty() {
prepend_stmt(
&mut stmts,
VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: self.vars.take(),
}
.into(),
);
}
stmts
}
fn fold_stmts(&mut self, stmts: Vec<Stmt>) -> Vec<Stmt> {
let mut stmts = stmts.fold_children_with(self);
if !self.vars.is_empty() {
prepend_stmt(
&mut stmts,
VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Var,
declare: false,
decls: self.vars.take(),
}
.into(),
);
}
stmts
}
}
#[cfg(test)]
mod tests {
use swc_common::chain;
use swc_ecma_transforms_base::resolver;
use swc_ecma_transforms_testing::test;
use super::*;
fn tr() -> impl Fold {
let unresolved = Mark::new();
chain!(
resolver(unresolved, Mark::new(), false),
async_arrows_in_class(unresolved)
)
}
test!(
::swc_ecma_parser::Syntax::default(),
|_| tr(),
async_arrows,
r#"
class Foo {
constructor() {
this.x = async () => await 1;
}
bar() {
(async () => { })();
}
}"#,
r#"
class Foo {
constructor() {
this.x = async function () {
return await 1;
};
}
bar() {
(async function () {})();
}
}"#
);
test!(
::swc_ecma_parser::Syntax::default(),
|_| tr(),
callback,
r#"
class Foo {
foo() {
bar(async () => await 1);
}
}"#,
r#"
class Foo {
foo() {
bar(async function () {
return await 1;
});
}
}"#
);
test!(
::swc_ecma_parser::Syntax::default(),
|_| tr(),
this,
r#"
class Foo {
constructor() {
this.x = () => async () => await this;
}
}"#,
r#"
class Foo {
constructor() {
var _this = this;
this.x = () => async function () {
return await _this;
};
}
}"#
);
test!(
::swc_ecma_parser::Syntax::default(),
|_| tr(),
non_async_arrow,
r#"
class Foo {
constructor() {
this.x = () => {};
}
}"#,
r#"
class Foo {
constructor() {
this.x = () => {};
}
}"#
);
test!(
::swc_ecma_parser::Syntax::default(),
|_| tr(),
non_class_async_arrow,
"let x = async () => await 1;",
"let x = async () => await 1;"
);
}