mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(transformer/typescript): support for transform enum (#2997)
The current implementation is copied from the previous implementation
This commit is contained in:
parent
e43c245388
commit
6732e8b9af
5 changed files with 1109 additions and 0 deletions
|
|
@ -14,6 +14,7 @@ mod options;
|
||||||
// Presets: <https://babel.dev/docs/presets>
|
// Presets: <https://babel.dev/docs/presets>
|
||||||
mod react;
|
mod react;
|
||||||
mod typescript;
|
mod typescript;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
mod helpers {
|
mod helpers {
|
||||||
pub mod module_imports;
|
pub mod module_imports;
|
||||||
|
|
@ -210,4 +211,9 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
|
||||||
self.x0_typescript.transform_statement(stmt);
|
self.x0_typescript.transform_statement(stmt);
|
||||||
walk_mut::walk_statement_mut(self, stmt);
|
walk_mut::walk_statement_mut(self, stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_declaration(&mut self, decl: &mut Declaration<'a>) {
|
||||||
|
self.x0_typescript.transform_declaration(decl);
|
||||||
|
walk_mut::walk_declaration_mut(self, decl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
230
crates/oxc_transformer/src/typescript/enum.rs
Normal file
230
crates/oxc_transformer/src/typescript/enum.rs
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use oxc_allocator::{Box, Vec};
|
||||||
|
use oxc_ast::ast::*;
|
||||||
|
use oxc_span::{Atom, SPAN};
|
||||||
|
use oxc_syntax::{
|
||||||
|
operator::{AssignmentOperator, BinaryOperator, LogicalOperator},
|
||||||
|
NumberBase,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::utils::is_valid_identifier;
|
||||||
|
|
||||||
|
use super::TypeScript;
|
||||||
|
|
||||||
|
impl<'a> TypeScript<'a> {
|
||||||
|
/// ```TypeScript
|
||||||
|
/// enum Foo {
|
||||||
|
/// X
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// ```JavaScript
|
||||||
|
/// var Foo = ((Foo) => {
|
||||||
|
/// const X = 0; Foo[Foo["X"] = X] = "X";
|
||||||
|
/// return Foo;
|
||||||
|
/// })(Foo || {});
|
||||||
|
/// ```
|
||||||
|
pub fn transform_ts_enum(
|
||||||
|
&self,
|
||||||
|
decl: &mut Box<'a, TSEnumDeclaration<'a>>,
|
||||||
|
) -> Option<Declaration<'a>> {
|
||||||
|
if decl.modifiers.contains(ModifierKind::Declare) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let span = decl.span;
|
||||||
|
let ident = decl.id.clone();
|
||||||
|
let kind = self.ctx.ast.binding_pattern_identifier(ident);
|
||||||
|
let id = self.ctx.ast.binding_pattern(kind, None, false);
|
||||||
|
|
||||||
|
let mut params = self.ctx.ast.new_vec();
|
||||||
|
|
||||||
|
// ((Foo) => {
|
||||||
|
params.push(self.ctx.ast.formal_parameter(
|
||||||
|
SPAN,
|
||||||
|
id,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
self.ctx.ast.new_vec(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let params = self.ctx.ast.formal_parameters(
|
||||||
|
SPAN,
|
||||||
|
FormalParameterKind::ArrowFormalParameters,
|
||||||
|
params,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Foo[Foo["X"] = 0] = "X";
|
||||||
|
let enum_name = decl.id.name.clone();
|
||||||
|
let statements = self.transform_ts_enum_members(&mut decl.members, &enum_name);
|
||||||
|
let body = self.ctx.ast.function_body(decl.span, self.ctx.ast.new_vec(), statements);
|
||||||
|
|
||||||
|
let callee =
|
||||||
|
self.ctx.ast.arrow_function_expression(SPAN, false, false, params, body, None, None);
|
||||||
|
|
||||||
|
// })(Foo || {});
|
||||||
|
let mut arguments = self.ctx.ast.new_vec();
|
||||||
|
let op = LogicalOperator::Or;
|
||||||
|
let left = self
|
||||||
|
.ctx
|
||||||
|
.ast
|
||||||
|
.identifier_reference_expression(IdentifierReference::new(SPAN, enum_name.clone()));
|
||||||
|
let right = self.ctx.ast.object_expression(SPAN, self.ctx.ast.new_vec(), None);
|
||||||
|
let expression = self.ctx.ast.logical_expression(SPAN, left, op, right);
|
||||||
|
arguments.push(Argument::Expression(expression));
|
||||||
|
|
||||||
|
let call_expression = self.ctx.ast.call_expression(SPAN, callee, arguments, false, None);
|
||||||
|
|
||||||
|
let kind = VariableDeclarationKind::Var;
|
||||||
|
let decls = {
|
||||||
|
let mut decls = self.ctx.ast.new_vec();
|
||||||
|
|
||||||
|
let binding_identifier = BindingIdentifier::new(SPAN, enum_name.clone());
|
||||||
|
let binding_pattern_kind = self.ctx.ast.binding_pattern_identifier(binding_identifier);
|
||||||
|
let binding = self.ctx.ast.binding_pattern(binding_pattern_kind, None, false);
|
||||||
|
let decl =
|
||||||
|
self.ctx.ast.variable_declarator(SPAN, kind, binding, Some(call_expression), false);
|
||||||
|
|
||||||
|
decls.push(decl);
|
||||||
|
decls
|
||||||
|
};
|
||||||
|
let variable_declaration =
|
||||||
|
self.ctx.ast.variable_declaration(span, kind, decls, Modifiers::empty());
|
||||||
|
|
||||||
|
Some(Declaration::VariableDeclaration(variable_declaration))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transform_ts_enum_members(
|
||||||
|
&self,
|
||||||
|
members: &mut Vec<'a, TSEnumMember<'a>>,
|
||||||
|
enum_name: &Atom<'a>,
|
||||||
|
) -> Vec<'a, Statement<'a>> {
|
||||||
|
let mut default_init = self.ctx.ast.literal_number_expression(NumericLiteral {
|
||||||
|
span: SPAN,
|
||||||
|
value: 0.0,
|
||||||
|
raw: "0",
|
||||||
|
base: NumberBase::Decimal,
|
||||||
|
});
|
||||||
|
let mut statements = self.ctx.ast.new_vec();
|
||||||
|
|
||||||
|
for member in members.iter_mut() {
|
||||||
|
let (member_name, member_span) = match &member.id {
|
||||||
|
TSEnumMemberName::Identifier(id) => (&id.name, id.span),
|
||||||
|
TSEnumMemberName::StringLiteral(str) => (&str.value, str.span),
|
||||||
|
TSEnumMemberName::ComputedPropertyName(..)
|
||||||
|
| TSEnumMemberName::NumericLiteral(..) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut init = self
|
||||||
|
.ctx
|
||||||
|
.ast
|
||||||
|
.move_expression(member.initializer.as_mut().unwrap_or(&mut default_init));
|
||||||
|
|
||||||
|
let is_str = init.is_string_literal();
|
||||||
|
|
||||||
|
let mut self_ref = {
|
||||||
|
let obj = self.ctx.ast.identifier_reference_expression(IdentifierReference::new(
|
||||||
|
SPAN,
|
||||||
|
enum_name.clone(),
|
||||||
|
));
|
||||||
|
let expr = self
|
||||||
|
.ctx
|
||||||
|
.ast
|
||||||
|
.literal_string_expression(StringLiteral::new(SPAN, member_name.clone()));
|
||||||
|
self.ctx.ast.computed_member_expression(SPAN, obj, expr, false)
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_valid_identifier(member_name, true) {
|
||||||
|
let ident = IdentifierReference::new(member_span, member_name.clone());
|
||||||
|
|
||||||
|
self_ref = self.ctx.ast.identifier_reference_expression(ident.clone());
|
||||||
|
let init =
|
||||||
|
mem::replace(&mut init, self.ctx.ast.identifier_reference_expression(ident));
|
||||||
|
|
||||||
|
let kind = VariableDeclarationKind::Const;
|
||||||
|
let decls = {
|
||||||
|
let mut decls = self.ctx.ast.new_vec();
|
||||||
|
|
||||||
|
let binding_identifier = BindingIdentifier::new(SPAN, member_name.clone());
|
||||||
|
let binding_pattern_kind =
|
||||||
|
self.ctx.ast.binding_pattern_identifier(binding_identifier);
|
||||||
|
let binding = self.ctx.ast.binding_pattern(binding_pattern_kind, None, false);
|
||||||
|
let decl =
|
||||||
|
self.ctx.ast.variable_declarator(SPAN, kind, binding, Some(init), false);
|
||||||
|
|
||||||
|
decls.push(decl);
|
||||||
|
decls
|
||||||
|
};
|
||||||
|
let decl = self.ctx.ast.variable_declaration(SPAN, kind, decls, Modifiers::empty());
|
||||||
|
let stmt: Statement<'_> =
|
||||||
|
Statement::Declaration(Declaration::VariableDeclaration(decl));
|
||||||
|
|
||||||
|
statements.push(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foo["x"] = init
|
||||||
|
let member_expr = {
|
||||||
|
let obj = self.ctx.ast.identifier_reference_expression(IdentifierReference::new(
|
||||||
|
SPAN,
|
||||||
|
enum_name.clone(),
|
||||||
|
));
|
||||||
|
let expr = self
|
||||||
|
.ctx
|
||||||
|
.ast
|
||||||
|
.literal_string_expression(StringLiteral::new(SPAN, member_name.clone()));
|
||||||
|
|
||||||
|
self.ctx.ast.computed_member(SPAN, obj, expr, false)
|
||||||
|
};
|
||||||
|
let left = self.ctx.ast.simple_assignment_target_member_expression(member_expr);
|
||||||
|
let mut expr =
|
||||||
|
self.ctx.ast.assignment_expression(SPAN, AssignmentOperator::Assign, left, init);
|
||||||
|
|
||||||
|
// Foo[Foo["x"] = init] = "x"
|
||||||
|
if !is_str {
|
||||||
|
let member_expr = {
|
||||||
|
let obj = self.ctx.ast.identifier_reference_expression(
|
||||||
|
IdentifierReference::new(SPAN, enum_name.clone()),
|
||||||
|
);
|
||||||
|
self.ctx.ast.computed_member(SPAN, obj, expr, false)
|
||||||
|
};
|
||||||
|
let left = self.ctx.ast.simple_assignment_target_member_expression(member_expr);
|
||||||
|
let right = self
|
||||||
|
.ctx
|
||||||
|
.ast
|
||||||
|
.literal_string_expression(StringLiteral::new(SPAN, member_name.clone()));
|
||||||
|
expr = self.ctx.ast.assignment_expression(
|
||||||
|
SPAN,
|
||||||
|
AssignmentOperator::Assign,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
statements.push(self.ctx.ast.expression_statement(member.span, expr));
|
||||||
|
|
||||||
|
// 1 + Foo["x"]
|
||||||
|
default_init = {
|
||||||
|
let one = self.ctx.ast.literal_number_expression(NumericLiteral {
|
||||||
|
span: SPAN,
|
||||||
|
value: 1.0,
|
||||||
|
raw: "1",
|
||||||
|
base: NumberBase::Decimal,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.ctx.ast.binary_expression(SPAN, one, BinaryOperator::Addition, self_ref)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let enum_ref = self
|
||||||
|
.ctx
|
||||||
|
.ast
|
||||||
|
.identifier_reference_expression(IdentifierReference::new(SPAN, enum_name.clone()));
|
||||||
|
// return Foo;
|
||||||
|
let return_stmt = self.ctx.ast.return_statement(SPAN, Some(enum_ref));
|
||||||
|
statements.push(return_stmt);
|
||||||
|
|
||||||
|
statements
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
mod annotations;
|
mod annotations;
|
||||||
mod collector;
|
mod collector;
|
||||||
|
mod r#enum;
|
||||||
mod namespace;
|
mod namespace;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
@ -148,4 +149,21 @@ impl<'a> TypeScript<'a> {
|
||||||
pub fn transform_statement(&mut self, stmt: &mut Statement<'a>) {
|
pub fn transform_statement(&mut self, stmt: &mut Statement<'a>) {
|
||||||
self.annotations.transform_statement(stmt);
|
self.annotations.transform_statement(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn transform_declaration(&mut self, decl: &mut Declaration<'a>) {
|
||||||
|
match decl {
|
||||||
|
Declaration::TSImportEqualsDeclaration(ts_import_equals)
|
||||||
|
if ts_import_equals.import_kind.is_value() =>
|
||||||
|
{
|
||||||
|
// TODO: support for transform_ts_import_equals function
|
||||||
|
// *decl = self.transform_ts_import_equals(ts_import_equals);
|
||||||
|
}
|
||||||
|
Declaration::TSEnumDeclaration(ts_enum_declaration) => {
|
||||||
|
if let Some(expr) = self.transform_ts_enum(ts_enum_declaration) {
|
||||||
|
*decl = expr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
crates/oxc_transformer/src/utils.rs
Normal file
12
crates/oxc_transformer/src/utils.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
use oxc_syntax::{identifier::is_identifier_name, keyword::is_keyword};
|
||||||
|
|
||||||
|
pub fn is_valid_identifier(name: &str, reserved: bool) -> bool {
|
||||||
|
if reserved && (is_keyword(name) || is_reserved_word(name, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
is_identifier_name(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_reserved_word(name: &str, in_module: bool) -> bool {
|
||||||
|
(in_module && name == "await") || name == "enum"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue