mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(transformer/decorators): support static member (#2235)
This commit is contained in:
parent
3b85e1813b
commit
a79988d5e2
4 changed files with 133 additions and 100 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1801,6 +1801,7 @@ dependencies = [
|
||||||
name = "oxc_transformer"
|
name = "oxc_transformer"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bitflags 2.4.2",
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
"oxc_ast",
|
"oxc_ast",
|
||||||
"oxc_codegen",
|
"oxc_codegen",
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ workspace = true
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bitflags = { workspace = true }
|
||||||
oxc_ast = { workspace = true }
|
oxc_ast = { workspace = true }
|
||||||
oxc_span = { workspace = true }
|
oxc_span = { workspace = true }
|
||||||
oxc_allocator = { workspace = true }
|
oxc_allocator = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{collections::HashMap, rc::Rc};
|
use std::{collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
|
use bitflags::bitflags;
|
||||||
use oxc_allocator::{Box, Vec};
|
use oxc_allocator::{Box, Vec};
|
||||||
use oxc_ast::{ast::*, AstBuilder};
|
use oxc_ast::{ast::*, AstBuilder};
|
||||||
use oxc_span::{Atom, SPAN};
|
use oxc_span::{Atom, SPAN};
|
||||||
|
|
@ -25,6 +26,30 @@ pub struct Decorators<'a> {
|
||||||
uid_map: HashMap<Atom, u32>,
|
uid_map: HashMap<Atom, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct DecoratorFlags: u8 {
|
||||||
|
const Field = 0;
|
||||||
|
const Accessor = 1;
|
||||||
|
const Method = 2;
|
||||||
|
const Getter = 3;
|
||||||
|
const Setter = 4;
|
||||||
|
const Static = 8;
|
||||||
|
const DecoratorsHaveThis = 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DecoratorFlags {
|
||||||
|
pub fn get_flag_by_kind(kind: MethodDefinitionKind) -> Self {
|
||||||
|
match kind {
|
||||||
|
MethodDefinitionKind::Method => Self::Method,
|
||||||
|
MethodDefinitionKind::Get => Self::Getter,
|
||||||
|
MethodDefinitionKind::Set => Self::Setter,
|
||||||
|
MethodDefinitionKind::Constructor => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, Deserialize)]
|
#[derive(Debug, Clone, Copy, Default, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct DecoratorsOptions {
|
pub struct DecoratorsOptions {
|
||||||
|
|
@ -251,6 +276,8 @@ impl<'a> Decorators<'a> {
|
||||||
let mut c_elements = self.ast.new_vec();
|
let mut c_elements = self.ast.new_vec();
|
||||||
let mut e_elements = self.ast.new_vec();
|
let mut e_elements = self.ast.new_vec();
|
||||||
|
|
||||||
|
let mut init_static_name = None;
|
||||||
|
|
||||||
// insert member decorators
|
// insert member decorators
|
||||||
let mut member_decorators_vec = self.ast.new_vec();
|
let mut member_decorators_vec = self.ast.new_vec();
|
||||||
let mut class_decorators_argument =
|
let mut class_decorators_argument =
|
||||||
|
|
@ -330,115 +357,108 @@ impl<'a> Decorators<'a> {
|
||||||
class.body.body.insert(0, static_block);
|
class.body.body.insert(0, static_block);
|
||||||
}
|
}
|
||||||
} else if has_member_decorator {
|
} else if has_member_decorator {
|
||||||
let elements: std::vec::Vec<_> = {
|
let get_decorator_info =
|
||||||
class
|
|name: Option<Atom>, flag: u8, decorator: &Decorator<'a>, ast: &AstBuilder<'a>| {
|
||||||
.body
|
let mut decorator_elements = ast.new_vec_with_capacity(2);
|
||||||
.body
|
decorator_elements
|
||||||
.iter_mut()
|
.push(ArrayExpressionElement::Expression(ast.copy(&decorator.expression)));
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(index, element)| match element {
|
|
||||||
ClassElement::MethodDefinition(def) => {
|
|
||||||
if def.decorators.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((
|
|
||||||
index,
|
|
||||||
def.key.name(),
|
|
||||||
None,
|
|
||||||
def.r#static,
|
|
||||||
def.computed,
|
|
||||||
&def.decorators,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClassElement::PropertyDefinition(def) => {
|
|
||||||
if def.decorators.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((
|
|
||||||
index,
|
|
||||||
def.key.name(),
|
|
||||||
self.ast.copy(&def.value),
|
|
||||||
def.r#static,
|
|
||||||
def.computed,
|
|
||||||
&def.decorators,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut replace_elements = HashMap::new();
|
|
||||||
|
|
||||||
for (index, name, value, r#static, computed, decorators) in elements {
|
|
||||||
let init_name = self.get_unique_name(&if computed {
|
|
||||||
"init_computedKey".into()
|
|
||||||
} else {
|
|
||||||
format!("init_{}", name.clone().unwrap()).into()
|
|
||||||
});
|
|
||||||
decorators.iter().for_each(|decorator| {
|
|
||||||
e_elements.push(self.get_assignment_target_maybe_default(init_name.clone()));
|
|
||||||
let mut decorator_elements = self.ast.new_vec_with_capacity(2);
|
|
||||||
decorator_elements.push(ArrayExpressionElement::Expression(
|
decorator_elements.push(ArrayExpressionElement::Expression(
|
||||||
self.ast.copy(&decorator.expression),
|
ast.literal_number_expression(NumberLiteral::new(
|
||||||
));
|
|
||||||
decorator_elements.push(ArrayExpressionElement::Expression(
|
|
||||||
self.ast.literal_number_expression(NumberLiteral::new(
|
|
||||||
SPAN,
|
SPAN,
|
||||||
0f64,
|
0f64,
|
||||||
if r#static { "8" } else { "0" },
|
ast.new_str(flag.to_string().as_str()),
|
||||||
oxc_syntax::NumberBase::Decimal,
|
oxc_syntax::NumberBase::Decimal,
|
||||||
)),
|
)),
|
||||||
));
|
));
|
||||||
if let Some(name) = name.clone() {
|
if let Some(name) = name {
|
||||||
decorator_elements.push(ArrayExpressionElement::Expression(
|
decorator_elements.push(ArrayExpressionElement::Expression(
|
||||||
self.ast.literal_string_expression(StringLiteral::new(SPAN, name)),
|
ast.literal_string_expression(StringLiteral::new(SPAN, name)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
member_decorators_vec.push(ArrayExpressionElement::Expression(
|
ast.array_expression(SPAN, decorator_elements, None)
|
||||||
self.ast.array_expression(SPAN, decorator_elements, None),
|
};
|
||||||
));
|
|
||||||
});
|
|
||||||
|
|
||||||
declarations.push(self.get_variable_declarator(init_name.clone()));
|
let mut is_static = false;
|
||||||
|
class.body.body.iter_mut().for_each(|element| {
|
||||||
let mut arguments =
|
if !element.has_decorator() {
|
||||||
self.ast.new_vec_single(Argument::Expression(self.ast.this_expression(SPAN)));
|
return;
|
||||||
|
|
||||||
if let Some(default_value) = value {
|
|
||||||
arguments.push(Argument::Expression(default_value));
|
|
||||||
}
|
}
|
||||||
|
match element {
|
||||||
replace_elements.insert(
|
ClassElement::MethodDefinition(def) => {
|
||||||
index,
|
if def.r#static {
|
||||||
self.ast.call_expression(
|
is_static = def.r#static;
|
||||||
SPAN,
|
|
||||||
self.ast.identifier_reference_expression(IdentifierReference::new(
|
|
||||||
SPAN, init_name,
|
|
||||||
)),
|
|
||||||
arguments,
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace the element with `name = init_name(this)`
|
|
||||||
for (index, element) in class.body.body.iter_mut().enumerate() {
|
|
||||||
if let Some(new_element) = replace_elements.remove(&index) {
|
|
||||||
match element {
|
|
||||||
ClassElement::PropertyDefinition(definition) => {
|
|
||||||
definition.decorators.clear();
|
|
||||||
definition.value = Some(new_element);
|
|
||||||
}
|
}
|
||||||
ClassElement::MethodDefinition(definition) => {
|
let name = def.key.name();
|
||||||
definition.decorators.clear();
|
let mut flag = DecoratorFlags::get_flag_by_kind(def.kind).bits();
|
||||||
|
if def.r#static {
|
||||||
|
flag += DecoratorFlags::Static.bits();
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
|
def.decorators.drain(..).for_each(|decorator| {
|
||||||
|
member_decorators_vec.push(ArrayExpressionElement::Expression(
|
||||||
|
get_decorator_info(name.clone(), flag, &decorator, &self.ast),
|
||||||
|
));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
ClassElement::PropertyDefinition(def) => {
|
||||||
|
let flag = if def.r#static {
|
||||||
|
DecoratorFlags::Static
|
||||||
|
} else {
|
||||||
|
DecoratorFlags::Field
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = def.key.name();
|
||||||
|
|
||||||
|
let init_name = self.get_unique_name(&if def.computed {
|
||||||
|
"init_computedKey".into()
|
||||||
|
} else {
|
||||||
|
format!("init_{}", name.clone().unwrap()).into()
|
||||||
|
});
|
||||||
|
|
||||||
|
e_elements
|
||||||
|
.push(self.get_assignment_target_maybe_default(init_name.clone()));
|
||||||
|
|
||||||
|
def.decorators.drain(..).for_each(|decorator| {
|
||||||
|
member_decorators_vec.push(ArrayExpressionElement::Expression(
|
||||||
|
get_decorator_info(
|
||||||
|
name.clone(),
|
||||||
|
flag.bits(),
|
||||||
|
&decorator,
|
||||||
|
&self.ast,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut arguments = self
|
||||||
|
.ast
|
||||||
|
.new_vec_single(Argument::Expression(self.ast.this_expression(SPAN)));
|
||||||
|
|
||||||
|
if let Some(value) = &mut def.value {
|
||||||
|
arguments.push(Argument::Expression(self.ast.move_expression(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
def.value = Some(self.ast.call_expression(
|
||||||
|
SPAN,
|
||||||
|
self.ast.identifier_reference_expression(IdentifierReference::new(
|
||||||
|
SPAN,
|
||||||
|
init_name.clone(),
|
||||||
|
)),
|
||||||
|
arguments,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
|
||||||
|
declarations.push(self.get_variable_declarator(init_name));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if is_static {
|
||||||
|
let name = self.get_unique_name(&"initStatic".into());
|
||||||
|
e_elements.push(self.get_assignment_target_maybe_default(name.clone()));
|
||||||
|
declarations.push(self.get_variable_declarator(name.clone()));
|
||||||
|
init_static_name = Some(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -500,6 +520,21 @@ impl<'a> Decorators<'a> {
|
||||||
|
|
||||||
let mut statements = self.ast.new_vec();
|
let mut statements = self.ast.new_vec();
|
||||||
statements.push(self.ast.expression_statement(SPAN, new_expr));
|
statements.push(self.ast.expression_statement(SPAN, new_expr));
|
||||||
|
|
||||||
|
if let Some(init_static_name) = init_static_name {
|
||||||
|
// call initStatic
|
||||||
|
let callee = self.ast.identifier_reference_expression(IdentifierReference::new(
|
||||||
|
SPAN,
|
||||||
|
init_static_name,
|
||||||
|
));
|
||||||
|
let arguments =
|
||||||
|
self.ast.new_vec_single(Argument::Expression(self.ast.this_expression(SPAN)));
|
||||||
|
statements.push(self.ast.expression_statement(
|
||||||
|
SPAN,
|
||||||
|
self.ast.call_expression(SPAN, callee, arguments, false, None),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let static_block = self.ast.static_block(SPAN, statements);
|
let static_block = self.ast.static_block(SPAN, statements);
|
||||||
class.body.body.insert(0, static_block);
|
class.body.body.insert(0, static_block);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Passed: 331/1369
|
Passed: 335/1369
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
* babel-plugin-transform-numeric-separator
|
* babel-plugin-transform-numeric-separator
|
||||||
|
|
@ -911,7 +911,7 @@ Passed: 331/1369
|
||||||
* spread-transform/transform-to-babel-extend/input.js
|
* spread-transform/transform-to-babel-extend/input.js
|
||||||
* spread-transform/transform-to-object-assign/input.js
|
* spread-transform/transform-to-object-assign/input.js
|
||||||
|
|
||||||
# babel-plugin-proposal-decorators (7/190)
|
# babel-plugin-proposal-decorators (11/190)
|
||||||
* 2018-09-transformation/async-generator-method/input.js
|
* 2018-09-transformation/async-generator-method/input.js
|
||||||
* 2018-09-transformation/class-decorators-yield-await/input.js
|
* 2018-09-transformation/class-decorators-yield-await/input.js
|
||||||
* 2021-12-accessors/context-name/input.js
|
* 2021-12-accessors/context-name/input.js
|
||||||
|
|
@ -1032,7 +1032,6 @@ Passed: 331/1369
|
||||||
* 2023-05-getters/private/input.js
|
* 2023-05-getters/private/input.js
|
||||||
* 2023-05-getters/public/input.js
|
* 2023-05-getters/public/input.js
|
||||||
* 2023-05-getters/static-private/input.js
|
* 2023-05-getters/static-private/input.js
|
||||||
* 2023-05-getters/static-public/input.js
|
|
||||||
* 2023-05-getters--to-es2015/context-name/input.js
|
* 2023-05-getters--to-es2015/context-name/input.js
|
||||||
* 2023-05-getters--to-es2015/private/input.js
|
* 2023-05-getters--to-es2015/private/input.js
|
||||||
* 2023-05-getters--to-es2015/public/input.js
|
* 2023-05-getters--to-es2015/public/input.js
|
||||||
|
|
@ -1041,7 +1040,6 @@ Passed: 331/1369
|
||||||
* 2023-05-getters-and-setters/private/input.js
|
* 2023-05-getters-and-setters/private/input.js
|
||||||
* 2023-05-getters-and-setters/public/input.js
|
* 2023-05-getters-and-setters/public/input.js
|
||||||
* 2023-05-getters-and-setters/static-private/input.js
|
* 2023-05-getters-and-setters/static-private/input.js
|
||||||
* 2023-05-getters-and-setters/static-public/input.js
|
|
||||||
* 2023-05-getters-and-setters--to-es2015/private/input.js
|
* 2023-05-getters-and-setters--to-es2015/private/input.js
|
||||||
* 2023-05-getters-and-setters--to-es2015/public/input.js
|
* 2023-05-getters-and-setters--to-es2015/public/input.js
|
||||||
* 2023-05-getters-and-setters--to-es2015/static-private/input.js
|
* 2023-05-getters-and-setters--to-es2015/static-private/input.js
|
||||||
|
|
@ -1050,7 +1048,6 @@ Passed: 331/1369
|
||||||
* 2023-05-methods/private/input.js
|
* 2023-05-methods/private/input.js
|
||||||
* 2023-05-methods/public/input.js
|
* 2023-05-methods/public/input.js
|
||||||
* 2023-05-methods/static-private/input.js
|
* 2023-05-methods/static-private/input.js
|
||||||
* 2023-05-methods/static-public/input.js
|
|
||||||
* 2023-05-methods--to-es2015/context-name/input.js
|
* 2023-05-methods--to-es2015/context-name/input.js
|
||||||
* 2023-05-methods--to-es2015/private/input.js
|
* 2023-05-methods--to-es2015/private/input.js
|
||||||
* 2023-05-methods--to-es2015/public/input.js
|
* 2023-05-methods--to-es2015/public/input.js
|
||||||
|
|
@ -1084,7 +1081,6 @@ Passed: 331/1369
|
||||||
* 2023-05-setters/private/input.js
|
* 2023-05-setters/private/input.js
|
||||||
* 2023-05-setters/public/input.js
|
* 2023-05-setters/public/input.js
|
||||||
* 2023-05-setters/static-private/input.js
|
* 2023-05-setters/static-private/input.js
|
||||||
* 2023-05-setters/static-public/input.js
|
|
||||||
* 2023-05-setters--to-es2015/context-name/input.js
|
* 2023-05-setters--to-es2015/context-name/input.js
|
||||||
* 2023-05-setters--to-es2015/private/input.js
|
* 2023-05-setters--to-es2015/private/input.js
|
||||||
* 2023-05-setters--to-es2015/public/input.js
|
* 2023-05-setters--to-es2015/public/input.js
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue