feat(es/trasform): Support static blocks (#2474)

swc_ecma_transforms_proposal:
 - Add transform for static blocks, which is stage 3.
This commit is contained in:
Sosuke Suzuki 2021-10-20 14:18:55 +09:00 committed by GitHub
parent cef2c8666e
commit bb1cc974c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 365 additions and 2 deletions

2
Cargo.lock generated
View File

@ -2909,7 +2909,7 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_proposal"
version = "0.51.0"
version = "0.51.1"
dependencies = [
"either",
"serde",

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecma_transforms_proposal"
repository = "https://github.com/swc-project/swc.git"
version = "0.51.0"
version = "0.51.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,9 +1,11 @@
pub use self::{
decorators::decorators, export_default_from::export_default_from,
import_assertions::import_assertions, private_in_object::private_in_object,
static_blocks::static_blocks,
};
pub mod decorators;
mod export_default_from;
mod import_assertions;
pub mod private_in_object;
pub mod static_blocks;

View File

@ -0,0 +1,116 @@
use swc_atoms::JsWord;
use swc_common::{collections::AHashSet, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
struct ClassStaticBlock;
pub fn static_blocks() -> impl Fold {
ClassStaticBlock
}
impl ClassStaticBlock {
fn fold_class_for_static_block(&mut self, class: Class) -> Class {
let mut private_names = AHashSet::default();
for member in &class.body {
if let ClassMember::PrivateProp(private_property) = member {
private_names.insert(private_property.key.id.sym.clone());
}
}
let mut members = vec![];
for member in class.body {
let new_member = match member {
ClassMember::StaticBlock(static_block) => {
let static_block_private_id: JsWord = {
let mut id_value: JsWord = "_".into();
let mut count = 0;
while private_names.contains(&id_value) {
count = count + 1;
id_value = format!("{}{}", &id_value, count).into();
}
private_names.insert(id_value.clone());
id_value
};
ClassMember::PrivateProp(
self.fold_static_block(static_block, static_block_private_id),
)
}
m => m,
};
members.push(new_member);
}
Class {
body: members,
..class
}
}
fn fold_static_block(&mut self, static_block: StaticBlock, private_id: JsWord) -> PrivateProp {
PrivateProp {
span: DUMMY_SP,
is_static: true,
is_abstract: false,
is_optional: false,
is_override: false,
readonly: false,
computed: false,
definite: false,
type_ann: None,
decorators: Vec::new(),
accessibility: None,
key: PrivateName {
span: DUMMY_SP,
id: Ident {
span: DUMMY_SP,
sym: private_id,
optional: false,
},
},
value: Some(Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: ExprOrSuper::Expr(Box::new(Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: Vec::new(),
is_async: false,
is_generator: false,
type_params: None,
return_type: None,
body: BlockStmtOrExpr::BlockStmt(static_block.body),
}))),
args: Vec::new(),
type_args: None,
}))),
}
}
}
impl Fold for ClassStaticBlock {
noop_fold_type!();
fn fold_decl(&mut self, declaration: Decl) -> Decl {
let declaration = declaration.fold_children_with(self);
match declaration {
Decl::Class(class_declaration) => {
let class = self.fold_class_for_static_block(class_declaration.class);
Decl::Class(ClassDecl {
class,
..class_declaration
})
}
_ => declaration,
}
}
fn fold_expr(&mut self, expression: Expr) -> Expr {
let expression = expression.fold_children_with(self);
match expression {
Expr::Class(class_expression) => {
let class = self.fold_class_for_static_block(class_expression.class);
Expr::Class(ClassExpr {
class,
..class_expression
})
}
_ => expression,
}
}
}

View File

@ -0,0 +1,6 @@
class Foo {
static bar = 42;
static {
this.foo = Foo.bar;
}
}

View File

@ -0,0 +1,6 @@
class Foo {
static bar = 42;
static #_ = (() => {
this.foo = Foo.bar;
})();
}

View File

@ -0,0 +1,13 @@
class Foo extends class extends class Base {
static {
this.qux = 21;
}
} {
static {
this.bar = 21;
}
} {
static {
this.foo = this.bar + this.qux;
}
}

View File

@ -0,0 +1,13 @@
class Foo extends class extends class Base {
static #_ = (() => {
this.qux = 21;
})();
} {
static #_ = (() => {
this.bar = 21;
})();
} {
static #_ = (() => {
this.foo = this.bar + this.qux;
})();
}

View File

@ -0,0 +1,11 @@
class Foo {
static #bar = 21;
static {
this.foo = this.#bar;
this.qux1 = this.qux;
}
static qux = 21;
static {
this.qux2 = this.qux;
}
}

View File

@ -0,0 +1,11 @@
class Foo {
static #bar = 21;
static #_ = (() => {
this.foo = this.#bar;
this.qux1 = this.qux;
})();
static qux = 21;
static #_1 = (() => {
this.qux2 = this.qux;
})();
}

View File

@ -0,0 +1,6 @@
class Foo {
static #_ = 42;
static {
this.foo = this.#_;
}
}

View File

@ -0,0 +1,6 @@
class Foo {
static #_ = 42;
static #_1 = (() => {
this.foo = this.#_;
})();
}

View File

@ -0,0 +1,9 @@
class Base {
constructor() {
this.Foo = class {
static {
this.foo = new.target;
}
};
}
}

View File

@ -0,0 +1,9 @@
class Base {
constructor() {
this.Foo = class {
static #_ = (() => {
this.foo = new.target;
})();
};
}
}

View File

@ -0,0 +1,6 @@
class Foo {
static bar = 42;
static {
this.foo = Foo.bar;
}
}

View File

@ -0,0 +1,8 @@
class Foo {}
_defineProperty(Foo, "bar", 42);
var __ = {
writable: true,
value: (() => {
Foo.foo = Foo.bar;
})(),
};

View File

@ -0,0 +1,13 @@
class Foo extends class extends class Base {
static {
this.qux = 21;
}
} {
static {
this.bar = 21;
}
} {
static {
this.foo = this.bar + this.qux;
}
}

View File

@ -0,0 +1,25 @@
class Foo extends (function () {
class _class extends (function () {
class Base {}
var __ = {
writable: true,
value: (() => {
Base.qux = 21;
})(),
};
return Base;
})() {}
var __ = {
writable: true,
value: (() => {
_class.bar = 21;
})(),
};
return _class;
})() {}
var __ = {
writable: true,
value: (() => {
Foo.foo = Foo.bar + Foo.qux;
})(),
};

View File

@ -0,0 +1,11 @@
class Foo {
static #bar = 21;
static {
this.foo = this.#bar;
this.qux1 = this.qux;
}
static qux = 21;
static {
this.qux2 = this.qux;
}
}

View File

@ -0,0 +1,19 @@
class Foo {}
var _bar = {
writable: true,
value: 21,
};
var __ = {
writable: true,
value: (() => {
Foo.foo = Foo.#bar;
Foo.qux1 = Foo.qux;
})(),
};
_defineProperty(Foo, "qux", 21);
var __1 = {
writable: true,
value: (() => {
Foo.qux2 = Foo.qux;
})(),
};

View File

@ -0,0 +1,6 @@
class Foo {
static #_ = 42;
static {
this.foo = this.#_;
}
}

View File

@ -0,0 +1,11 @@
class Foo {}
var __ = {
writable: true,
value: 42,
};
var __1 = {
writable: true,
value: (() => {
Foo.foo = Foo.#_;
})(),
};

View File

@ -0,0 +1,9 @@
class Base {
constructor() {
this.Foo = class {
static {
this.foo = new.target;
}
};
}
}

View File

@ -0,0 +1,14 @@
class Base {
constructor() {
this.Foo = (function () {
class _class {}
var __ = {
writable: true,
value: (() => {
_class.foo = new.target;
})(),
};
return _class;
})();
}
}

View File

@ -0,0 +1,33 @@
use std::path::PathBuf;
use swc_common::chain;
use swc_ecma_parser::{EsConfig, Syntax};
use swc_ecma_transforms_compat::es2020::class_properties;
use swc_ecma_transforms_proposal::static_blocks;
use swc_ecma_transforms_testing::test_fixture;
use swc_ecma_visit::Fold;
#[testing::fixture("tests/static-blocks/**/input.js")]
fn fixture(input: PathBuf) {
let parent = input.parent().unwrap();
let output = parent.join("output.js");
test_fixture(
Syntax::Es(EsConfig {
static_blocks: true,
..Default::default()
}),
&|_t| {
let pass: Box<dyn Fold> = if input.to_string_lossy().contains("class-properties") {
Box::new(chain!(
static_blocks(),
class_properties(class_properties::Config::default())
))
} else {
Box::new(static_blocks())
};
pass
},
&input,
&output,
)
}