feat(transformer/typescript): insert this assignment after the super call (#3018)

This commit is contained in:
Dunqing 2024-04-18 18:53:01 +08:00 committed by GitHub
parent 5d89e75e48
commit e14ac17c72
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 106 additions and 21 deletions

View file

@ -275,6 +275,10 @@ impl<'a> Expression<'a> {
matches!(self, Expression::CallExpression(_))
}
pub fn is_super_call_expression(&self) -> bool {
matches!(self, Expression::CallExpression(expr) if matches!(&expr.callee, Expression::Super(_)))
}
pub fn is_call_like_expression(&self) -> bool {
self.is_call_expression()
&& matches!(self, Expression::NewExpression(_) | Expression::ImportExpression(_))
@ -1910,6 +1914,12 @@ pub struct FormalParameter<'a> {
pub decorators: Vec<'a, Decorator<'a>>,
}
impl<'a> FormalParameter<'a> {
pub fn is_public(&self) -> bool {
matches!(self.accessibility, Some(TSAccessibility::Public))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
pub enum FormalParameterKind {

View file

@ -162,6 +162,8 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
self.x0_typescript.transform_method_definition(def);
walk_mut::walk_method_definition_mut(self, def);
self.x0_typescript.transform_method_definition_on_exit(def);
}
fn visit_new_expression(&mut self, expr: &mut NewExpression<'a>) {
@ -216,4 +218,9 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
self.x0_typescript.transform_declaration(decl);
walk_mut::walk_declaration_mut(self, decl);
}
fn visit_if_statement(&mut self, stmt: &mut IfStatement<'a>) {
self.x0_typescript.transform_if_statement(stmt);
walk_mut::walk_if_statement_mut(self, stmt);
}
}

View file

@ -17,11 +17,19 @@ pub struct TypeScriptAnnotations<'a> {
#[allow(dead_code)]
options: Rc<TypeScriptOptions>,
ctx: Ctx<'a>,
/// Assignments to be added to the constructor body
assignments: Vec<'a, Statement<'a>>,
has_super_call: bool,
}
impl<'a> TypeScriptAnnotations<'a> {
pub fn new(options: &Rc<TypeScriptOptions>, ctx: &Ctx<'a>) -> Self {
Self { options: Rc::clone(options), ctx: Rc::clone(ctx) }
Self {
has_super_call: false,
assignments: ctx.ast.new_vec(),
options: Rc::clone(options),
ctx: Rc::clone(ctx),
}
}
// Convert `export = expr` into `module.exports = expr`
@ -237,35 +245,44 @@ impl<'a> TypeScriptAnnotations<'a> {
}
pub fn transform_method_definition(&mut self, def: &mut MethodDefinition<'a>) {
def.accessibility = None;
def.optional = false;
def.r#override = false;
// Collects parameter properties so that we can add an assignment
// for each of them in the constructor body.
if def.kind == MethodDefinitionKind::Constructor {
let mut assigns = self.ctx.ast.new_vec();
for param in &def.value.params.items {
if param.pattern.type_annotation.is_none() {
if !param.is_public() {
continue;
}
if let Some(id) = param.pattern.get_identifier() {
assigns.push(self.create_this_property_assignment(id));
let assignment = self.create_this_property_assignment(id);
self.assignments.push(assignment);
}
}
}
if !assigns.is_empty() {
def.accessibility = None;
def.optional = false;
def.r#override = false;
}
pub fn transform_method_definition_on_exit(&mut self, def: &mut MethodDefinition<'a>) {
if def.kind == MethodDefinitionKind::Constructor && !self.assignments.is_empty() {
// When the constructor doesn't have a super call,
// we simply add assignments to the bottom of the function body
if self.has_super_call {
self.assignments.clear();
} else {
def.value
.body
.get_or_insert(self.ctx.ast.function_body(
SPAN,
self.ctx.ast.new_vec(),
self.ctx.ast.new_vec(),
))
.get_or_insert_with(|| {
self.ctx.ast.function_body(
SPAN,
self.ctx.ast.new_vec(),
self.ctx.ast.new_vec(),
)
})
.statements
.extend(assigns);
.extend(self.assignments.drain(..));
}
}
}
@ -302,6 +319,52 @@ impl<'a> TypeScriptAnnotations<'a> {
// Ignore ModuleDeclaration as it's handled in the program
_ => true,
});
// Add assignments after super calls
if !self.assignments.is_empty() {
let mut super_indexes = vec![];
for (index, stmt) in stmts.iter().rev().enumerate() {
if matches!(stmt, Statement::ExpressionStatement(stmt) if stmt.expression.is_super_call_expression())
{
super_indexes.push(index);
}
}
if !super_indexes.is_empty() {
self.has_super_call = true;
for index in super_indexes.iter().rev() {
stmts.splice((index + 1)..=*index, self.ctx.ast.copy(&self.assignments));
}
}
}
}
/// Transform if statement's consequent and alternate to block statements if they are super calls
/// ```ts
/// if (true) super() else super();
/// // to
/// if (true) { super() } else { super() }
/// ```
pub fn transform_if_statement(&mut self, stmt: &mut IfStatement<'a>) {
if !self.assignments.is_empty() {
if matches!(&stmt.consequent, Statement::ExpressionStatement(expr) if expr.expression.is_super_call_expression())
{
stmt.consequent =
self.ctx.ast.block_statement(self.ctx.ast.block(
SPAN,
self.ctx.ast.new_vec_single(self.ctx.ast.copy(&stmt.consequent)),
));
}
if let Some(alternate) = &stmt.alternate {
if matches!(alternate, Statement::ExpressionStatement(expr) if expr.expression.is_super_call_expression())
{
stmt.alternate =
Some(self.ctx.ast.block_statement(self.ctx.ast.block(
SPAN,
self.ctx.ast.new_vec_single(self.ctx.ast.copy(alternate)),
)));
}
}
}
}
pub fn transform_tagged_template_expression(

View file

@ -120,6 +120,10 @@ impl<'a> TypeScript<'a> {
self.annotations.transform_method_definition(def);
}
pub fn transform_method_definition_on_exit(&mut self, def: &mut MethodDefinition<'a>) {
self.annotations.transform_method_definition_on_exit(def);
}
pub fn transform_new_expression(&mut self, expr: &mut NewExpression<'a>) {
self.annotations.transform_new_expression(expr);
}
@ -136,6 +140,10 @@ impl<'a> TypeScript<'a> {
self.annotations.transform_statements_on_exit(stmts);
}
pub fn transform_if_statement(&mut self, stmt: &mut IfStatement<'a>) {
self.annotations.transform_if_statement(stmt);
}
pub fn transform_tagged_template_expression(
&mut self,
expr: &mut TaggedTemplateExpression<'a>,

View file

@ -1,4 +1,4 @@
Passed: 148/209
Passed: 151/209
# All Passed:
* babel-plugin-transform-react-jsx-source
@ -17,12 +17,9 @@ Passed: 148/209
* opts/optimizeConstEnums/input.ts
* opts/rewriteImportExtensions/input.ts
# babel-plugin-transform-typescript (92/139)
# babel-plugin-transform-typescript (95/139)
* class/accessor-allowDeclareFields-false/input.ts
* class/accessor-allowDeclareFields-true/input.ts
* class/parameter-properties/input.ts
* class/parameter-properties-late-super/input.ts
* class/parameter-properties-with-super/input.ts
* exports/declared-types/input.ts
* exports/export-const-enums/input.ts
* exports/export-type-star-from/input.ts