-
-
Notifications
You must be signed in to change notification settings - Fork 339
/
class_static_block.rs
123 lines (105 loc) · 4.4 KB
/
class_static_block.rs
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
use std::{borrow::Cow, collections::HashSet, rc::Rc};
use oxc_ast::{ast::*, AstBuilder};
use oxc_span::{Atom, SPAN};
use crate::options::{TransformOptions, TransformTarget};
/// ES2022: Class Static Block
///
/// References:
/// * <https://babel.dev/docs/babel-plugin-transform-class-static-block>
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-class-static-block>
pub struct ClassStaticBlock<'a> {
ast: Rc<AstBuilder<'a>>,
}
impl<'a> ClassStaticBlock<'a> {
pub fn new(ast: Rc<AstBuilder<'a>>, options: &TransformOptions) -> Option<Self> {
(options.target < TransformTarget::ES2022 || options.class_static_block)
.then_some(Self { ast })
}
pub fn transform_class_body<'b>(&mut self, class_body: &'b mut ClassBody<'a>) {
if !class_body.body.iter().any(|e| matches!(e, ClassElement::StaticBlock(..))) {
return;
}
let private_names: HashSet<Atom<'a>> = class_body
.body
.iter()
.filter_map(ClassElement::property_key)
.filter_map(PropertyKey::private_name)
.cloned()
.collect();
let mut i = 0;
for element in class_body.body.iter_mut() {
let ClassElement::StaticBlock(block) = element else {
continue;
};
let static_block_private_id = self.generate_uid(&private_names, &mut i);
let key =
PropertyKey::PrivateIdentifier(self.ast.alloc(PrivateIdentifier {
span: SPAN,
name: static_block_private_id.clone(),
}));
let value = match block.body.len() {
0 => None,
1 if matches!(block.body[0], Statement::ExpressionStatement(..)) => {
// We special-case the single expression case to avoid the iife, since it's common.
//
// We prefer to emit:
// ```JavaScript
// class Foo {
// static bar = 42;
// static #_ = this.foo = Foo.bar;
// }
// ```
// instead of:
// ```JavaScript
// class Foo {
// static bar = 42;
// static #_ = (() => this.foo = Foo.bar)();
// }
// ```
let stmt = self.ast.move_statement(&mut (*block.body)[0]);
let Statement::ExpressionStatement(mut expr_stmt) = stmt else {
unreachable!()
};
let value = self.ast.move_expression(&mut expr_stmt.expression);
Some(value)
}
_ => {
let params = self.ast.formal_parameters(
SPAN,
FormalParameterKind::ArrowFormalParameters,
self.ast.new_vec(),
None,
);
let statements = self.ast.move_statement_vec(&mut block.body);
let function_body =
self.ast.function_body(SPAN, self.ast.new_vec(), statements);
let callee = self.ast.arrow_function_expression(
SPAN,
false,
false,
params,
function_body,
None,
None,
);
let callee = self.ast.parenthesized_expression(SPAN, callee);
let value =
self.ast.call_expression(SPAN, callee, self.ast.new_vec(), false, None);
Some(value)
}
};
let r#type = PropertyDefinitionType::PropertyDefinition;
*element =
self.ast.class_property(r#type, SPAN, key, value, false, true, self.ast.new_vec());
}
}
fn generate_uid(&self, deny_list: &HashSet<Atom<'a>>, i: &mut u32) -> Atom<'a> {
*i += 1;
let mut uid = if *i == 1 { Cow::Borrowed("_") } else { Cow::Owned(format!("_{i}")) };
while deny_list.iter().any(|id| id.as_str() == uid.as_ref()) {
*i += 1;
uid = format!("_{i}").into();
}
self.ast.new_atom(&uid)
}
}