mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
refactor(transformer): move implementation of ArrowFunction to common/ArrowFunctionConverter (#7107)
Part of #7074 In order can reuse the ability of the `ArrowFunction` plugin, we moved out the implementation to common, then we can use use in other plugins
This commit is contained in:
parent
e04ee97870
commit
ff8bd50d38
5 changed files with 533 additions and 365 deletions
439
crates/oxc_transformer/src/common/arrow_function_converter.rs
Normal file
439
crates/oxc_transformer/src/common/arrow_function_converter.rs
Normal file
|
|
@ -0,0 +1,439 @@
|
||||||
|
//! Arrow Functions Converter
|
||||||
|
//!
|
||||||
|
//! This converter transforms arrow functions (`() => {}`) to function expressions (`function () {}`).
|
||||||
|
//!
|
||||||
|
//! ## Example
|
||||||
|
//!
|
||||||
|
//! Input:
|
||||||
|
//! ```js
|
||||||
|
//! var a = () => {};
|
||||||
|
//! var a = b => b;
|
||||||
|
//!
|
||||||
|
//! const double = [1, 2, 3].map(num => num * 2);
|
||||||
|
//! console.log(double); // [2,4,6]
|
||||||
|
//!
|
||||||
|
//! var bob = {
|
||||||
|
//! name: "Bob",
|
||||||
|
//! friends: ["Sally", "Tom"],
|
||||||
|
//! printFriends() {
|
||||||
|
//! this.friends.forEach(f => console.log(this.name + " knows " + f));
|
||||||
|
//! },
|
||||||
|
//! };
|
||||||
|
//! console.log(bob.printFriends());
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Output:
|
||||||
|
//! ```js
|
||||||
|
//! var a = function() {};
|
||||||
|
//! var a = function(b) { return b; };
|
||||||
|
//!
|
||||||
|
//! const double = [1, 2, 3].map(function(num) {
|
||||||
|
//! return num * 2;
|
||||||
|
//! });
|
||||||
|
//! console.log(double); // [2,4,6]
|
||||||
|
//!
|
||||||
|
//! var bob = {
|
||||||
|
//! name: "Bob",
|
||||||
|
//! friends: ["Sally", "Tom"],
|
||||||
|
//! printFriends() {
|
||||||
|
//! var _this = this;
|
||||||
|
//! this.friends.forEach(function(f) {
|
||||||
|
//! return console.log(_this.name + " knows " + f);
|
||||||
|
//! });
|
||||||
|
//! },
|
||||||
|
//! };
|
||||||
|
//! console.log(bob.printFriends());
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! #### Example
|
||||||
|
//!
|
||||||
|
//! Using spec mode with the above example produces:
|
||||||
|
//!
|
||||||
|
//! ```js
|
||||||
|
//! var _this = this;
|
||||||
|
//!
|
||||||
|
//! var a = function a() {
|
||||||
|
//! babelHelpers.newArrowCheck(this, _this);
|
||||||
|
//! }.bind(this);
|
||||||
|
//! var a = function a(b) {
|
||||||
|
//! babelHelpers.newArrowCheck(this, _this);
|
||||||
|
//! return b;
|
||||||
|
//! }.bind(this);
|
||||||
|
//!
|
||||||
|
//! const double = [1, 2, 3].map(
|
||||||
|
//! function(num) {
|
||||||
|
//! babelHelpers.newArrowCheck(this, _this);
|
||||||
|
//! return num * 2;
|
||||||
|
//! }.bind(this)
|
||||||
|
//! );
|
||||||
|
//! console.log(double); // [2,4,6]
|
||||||
|
//!
|
||||||
|
//! var bob = {
|
||||||
|
//! name: "Bob",
|
||||||
|
//! friends: ["Sally", "Tom"],
|
||||||
|
//! printFriends() {
|
||||||
|
//! var _this2 = this;
|
||||||
|
//! this.friends.forEach(
|
||||||
|
//! function(f) {
|
||||||
|
//! babelHelpers.newArrowCheck(this, _this2);
|
||||||
|
//! return console.log(this.name + " knows " + f);
|
||||||
|
//! }.bind(this)
|
||||||
|
//! );
|
||||||
|
//! },
|
||||||
|
//! };
|
||||||
|
//! console.log(bob.printFriends());
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! The Implementation based on
|
||||||
|
//! <https://github.com/babel/babel/blob/d20b314c14533ab86351ecf6ca6b7296b66a57b3/packages/babel-traverse/src/path/conversion.ts#L170-L247>
|
||||||
|
|
||||||
|
use oxc_allocator::{Box as ArenaBox, Vec as ArenaVec};
|
||||||
|
use oxc_ast::ast::*;
|
||||||
|
use oxc_data_structures::stack::SparseStack;
|
||||||
|
use oxc_span::SPAN;
|
||||||
|
use oxc_syntax::{
|
||||||
|
scope::{ScopeFlags, ScopeId},
|
||||||
|
symbol::SymbolFlags,
|
||||||
|
};
|
||||||
|
use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx};
|
||||||
|
|
||||||
|
/// Mode for arrow function conversion
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ArrowFunctionConverterMode {
|
||||||
|
/// Disable arrow function conversion
|
||||||
|
Disabled,
|
||||||
|
|
||||||
|
/// Convert all arrow functions to regular functions
|
||||||
|
Enabled,
|
||||||
|
|
||||||
|
/// Only convert async arrow functions
|
||||||
|
#[expect(unused)]
|
||||||
|
AsyncOnly,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ArrowFunctionConverterOptions {
|
||||||
|
pub mode: ArrowFunctionConverterMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ArrowFunctionConverter<'a> {
|
||||||
|
mode: ArrowFunctionConverterMode,
|
||||||
|
this_var_stack: SparseStack<BoundIdentifier<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ArrowFunctionConverter<'a> {
|
||||||
|
pub fn new(options: &ArrowFunctionConverterOptions) -> Self {
|
||||||
|
// `SparseStack` is created with 1 empty entry, for `Program`
|
||||||
|
Self { mode: options.mode, this_var_stack: SparseStack::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Traverse<'a> for ArrowFunctionConverter<'a> {
|
||||||
|
// Note: No visitors for `TSModuleBlock` because `this` is not legal in TS module blocks.
|
||||||
|
// <https://www.typescriptlang.org/play/?#code/HYQwtgpgzgDiDGEAEAxA9mpBvAsAKCSXjWCgBckANJAXiQAoBKWgPiTIAsBLKAbnwC++fGDQATAK4AbZACEQAJ2z5CxUhWp0mrdtz6D8QA>
|
||||||
|
|
||||||
|
/// Insert `var _this = this;` for the global scope.
|
||||||
|
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
if self.is_disabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(this_var) = self.this_var_stack.take_last() {
|
||||||
|
self.insert_this_var_statement_at_the_top_of_statements(
|
||||||
|
&mut program.body,
|
||||||
|
&this_var,
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
debug_assert!(self.this_var_stack.len() == 1);
|
||||||
|
debug_assert!(self.this_var_stack.last().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_function(&mut self, _func: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||||
|
if self.is_disabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.this_var_stack.push(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ```ts
|
||||||
|
/// function a(){
|
||||||
|
/// return () => console.log(this);
|
||||||
|
/// }
|
||||||
|
/// // to
|
||||||
|
/// function a(){
|
||||||
|
/// var _this = this;
|
||||||
|
/// return function() { return console.log(_this); };
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// Insert the var _this = this; statement outside the arrow function
|
||||||
|
fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
if self.is_disabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(this_var) = self.this_var_stack.pop() {
|
||||||
|
let Some(body) = &mut func.body else { unreachable!() };
|
||||||
|
|
||||||
|
self.insert_this_var_statement_at_the_top_of_statements(
|
||||||
|
&mut body.statements,
|
||||||
|
&this_var,
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_static_block(&mut self, _block: &mut StaticBlock<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||||
|
if self.is_disabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.this_var_stack.push(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
if self.is_disabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(this_var) = self.this_var_stack.pop() {
|
||||||
|
self.insert_this_var_statement_at_the_top_of_statements(
|
||||||
|
&mut block.body,
|
||||||
|
&this_var,
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_jsx_element_name(
|
||||||
|
&mut self,
|
||||||
|
element_name: &mut JSXElementName<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
if self.is_disabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let JSXElementName::ThisExpression(this) = element_name {
|
||||||
|
if let Some(ident) = self.get_this_identifier(this.span, ctx) {
|
||||||
|
*element_name = JSXElementName::IdentifierReference(ident);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_jsx_member_expression_object(
|
||||||
|
&mut self,
|
||||||
|
object: &mut JSXMemberExpressionObject<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
if self.is_disabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let JSXMemberExpressionObject::ThisExpression(this) = object {
|
||||||
|
if let Some(ident) = self.get_this_identifier(this.span, ctx) {
|
||||||
|
*object = JSXMemberExpressionObject::IdentifierReference(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
if self.is_disabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Expression::ThisExpression(this) = expr {
|
||||||
|
if let Some(ident) = self.get_this_identifier(this.span, ctx) {
|
||||||
|
*expr = Expression::Identifier(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
if self.is_disabled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Expression::ArrowFunctionExpression(_) = expr {
|
||||||
|
let Expression::ArrowFunctionExpression(arrow_function_expr) =
|
||||||
|
ctx.ast.move_expression(expr)
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
*expr = self.transform_arrow_function_expression(arrow_function_expr.unbox(), ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ArrowFunctionConverter<'a> {
|
||||||
|
/// Check if arrow function conversion is disabled
|
||||||
|
fn is_disabled(&self) -> bool {
|
||||||
|
self.mode == ArrowFunctionConverterMode::Disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_this_identifier(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) -> Option<ArenaBox<'a, IdentifierReference<'a>>> {
|
||||||
|
// Find arrow function we are currently in (if we are)
|
||||||
|
let arrow_scope_id = Self::get_arrow_function_scope(ctx)?;
|
||||||
|
|
||||||
|
// TODO(improve-on-babel): We create a new UID for every scope. This is pointless, as only one
|
||||||
|
// `this` can be in scope at a time. We could create a single `_this` UID and reuse it in each
|
||||||
|
// scope. But this does not match output for some of Babel's test cases.
|
||||||
|
// <https://github.com/oxc-project/oxc/pull/5840>
|
||||||
|
let this_var = self.this_var_stack.last_or_init(|| {
|
||||||
|
let target_scope_id = ctx
|
||||||
|
.scopes()
|
||||||
|
.ancestors(arrow_scope_id)
|
||||||
|
// Skip arrow function scope
|
||||||
|
.skip(1)
|
||||||
|
.find(|&scope_id| {
|
||||||
|
let scope_flags = ctx.scopes().get_flags(scope_id);
|
||||||
|
scope_flags.intersects(
|
||||||
|
ScopeFlags::Function | ScopeFlags::Top | ScopeFlags::ClassStaticBlock,
|
||||||
|
) && !scope_flags.contains(ScopeFlags::Arrow)
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
ctx.generate_uid("this", target_scope_id, SymbolFlags::FunctionScopedVariable)
|
||||||
|
});
|
||||||
|
Some(ctx.ast.alloc(this_var.create_spanned_read_reference(span, ctx)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find arrow function we are currently in, if it's between current node, and where `this` is bound.
|
||||||
|
/// Return its `ScopeId`.
|
||||||
|
fn get_arrow_function_scope(ctx: &mut TraverseCtx<'a>) -> Option<ScopeId> {
|
||||||
|
// `this` inside a class resolves to `this` *outside* the class in:
|
||||||
|
// * `extends` clause
|
||||||
|
// * Computed method key
|
||||||
|
// * Computed property key
|
||||||
|
// * Computed accessor property key (but `this` in this position is not legal TS)
|
||||||
|
//
|
||||||
|
// ```js
|
||||||
|
// // All these `this` refer to global `this`
|
||||||
|
// class C extends this {
|
||||||
|
// [this] = 123;
|
||||||
|
// static [this] = 123;
|
||||||
|
// [this]() {}
|
||||||
|
// static [this]() {}
|
||||||
|
// accessor [this] = 123;
|
||||||
|
// static accessor [this] = 123;
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// `this` resolves to the class / class instance (i.e. `this` defined *within* the class) in:
|
||||||
|
// * Method body
|
||||||
|
// * Method param
|
||||||
|
// * Property value
|
||||||
|
// * Static block
|
||||||
|
//
|
||||||
|
// ```js
|
||||||
|
// // All these `this` refer to `this` defined within the class
|
||||||
|
// class C {
|
||||||
|
// a = this;
|
||||||
|
// static b = this;
|
||||||
|
// #c = this;
|
||||||
|
// d() { this }
|
||||||
|
// static e() { this }
|
||||||
|
// #f() { this }
|
||||||
|
// g(x = this) {}
|
||||||
|
// accessor h = this;
|
||||||
|
// static accessor i = this;
|
||||||
|
// static { this }
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// So in this loop, we only exit when we encounter one of the above.
|
||||||
|
for ancestor in ctx.ancestors() {
|
||||||
|
match ancestor {
|
||||||
|
// Top level
|
||||||
|
Ancestor::ProgramBody(_)
|
||||||
|
// Function (includes class method body)
|
||||||
|
| Ancestor::FunctionParams(_)
|
||||||
|
| Ancestor::FunctionBody(_)
|
||||||
|
// Class property body
|
||||||
|
| Ancestor::PropertyDefinitionValue(_)
|
||||||
|
// Class accessor property body
|
||||||
|
| Ancestor::AccessorPropertyValue(_)
|
||||||
|
// Class static block
|
||||||
|
| Ancestor::StaticBlockBody(_) => return None,
|
||||||
|
// Arrow function
|
||||||
|
Ancestor::ArrowFunctionExpressionParams(func) => {
|
||||||
|
return Some(func.scope_id().get().unwrap())
|
||||||
|
}
|
||||||
|
Ancestor::ArrowFunctionExpressionBody(func) => {
|
||||||
|
return Some(func.scope_id().get().unwrap())
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::unused_self)]
|
||||||
|
fn transform_arrow_function_expression(
|
||||||
|
&mut self,
|
||||||
|
arrow_function_expr: ArrowFunctionExpression<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) -> Expression<'a> {
|
||||||
|
let mut body = arrow_function_expr.body;
|
||||||
|
|
||||||
|
if arrow_function_expr.expression {
|
||||||
|
assert!(body.statements.len() == 1);
|
||||||
|
let stmt = body.statements.pop().unwrap();
|
||||||
|
let Statement::ExpressionStatement(stmt) = stmt else { unreachable!() };
|
||||||
|
let stmt = stmt.unbox();
|
||||||
|
let return_statement = ctx.ast.statement_return(stmt.span, Some(stmt.expression));
|
||||||
|
body.statements.push(return_statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
let scope_id = arrow_function_expr.scope_id.get().unwrap();
|
||||||
|
let flags = ctx.scopes_mut().get_flags_mut(scope_id);
|
||||||
|
*flags &= !ScopeFlags::Arrow;
|
||||||
|
|
||||||
|
Expression::FunctionExpression(ctx.ast.alloc_function_with_scope_id(
|
||||||
|
FunctionType::FunctionExpression,
|
||||||
|
arrow_function_expr.span,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
arrow_function_expr.r#async,
|
||||||
|
false,
|
||||||
|
arrow_function_expr.type_parameters,
|
||||||
|
None::<TSThisParameter<'a>>,
|
||||||
|
arrow_function_expr.params,
|
||||||
|
arrow_function_expr.return_type,
|
||||||
|
Some(body),
|
||||||
|
scope_id,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert `var _this = this;` at the top of the statements.
|
||||||
|
#[expect(clippy::unused_self)]
|
||||||
|
fn insert_this_var_statement_at_the_top_of_statements(
|
||||||
|
&mut self,
|
||||||
|
statements: &mut ArenaVec<'a, Statement<'a>>,
|
||||||
|
this_var: &BoundIdentifier<'a>,
|
||||||
|
ctx: &TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
let variable_declarator = ctx.ast.variable_declarator(
|
||||||
|
SPAN,
|
||||||
|
VariableDeclarationKind::Var,
|
||||||
|
this_var.create_binding_pattern(ctx),
|
||||||
|
Some(ctx.ast.expression_this(SPAN)),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let stmt = ctx.ast.alloc_variable_declaration(
|
||||||
|
SPAN,
|
||||||
|
VariableDeclarationKind::Var,
|
||||||
|
ctx.ast.vec1(variable_declarator),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let stmt = Statement::VariableDeclaration(stmt);
|
||||||
|
|
||||||
|
statements.insert(0, stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
//! Utility transforms which are in common between other transforms.
|
//! Utility transforms which are in common between other transforms.
|
||||||
|
|
||||||
|
use arrow_function_converter::{
|
||||||
|
ArrowFunctionConverter, ArrowFunctionConverterMode, ArrowFunctionConverterOptions,
|
||||||
|
};
|
||||||
use oxc_allocator::Vec as ArenaVec;
|
use oxc_allocator::Vec as ArenaVec;
|
||||||
use oxc_ast::ast::*;
|
use oxc_ast::ast::*;
|
||||||
use oxc_traverse::{Traverse, TraverseCtx};
|
use oxc_traverse::{Traverse, TraverseCtx};
|
||||||
|
|
||||||
use crate::TransformCtx;
|
use crate::{TransformCtx, TransformOptions};
|
||||||
|
|
||||||
|
pub mod arrow_function_converter;
|
||||||
pub mod helper_loader;
|
pub mod helper_loader;
|
||||||
pub mod module_imports;
|
pub mod module_imports;
|
||||||
pub mod statement_injector;
|
pub mod statement_injector;
|
||||||
|
|
@ -22,15 +26,28 @@ pub struct Common<'a, 'ctx> {
|
||||||
var_declarations: VarDeclarations<'a, 'ctx>,
|
var_declarations: VarDeclarations<'a, 'ctx>,
|
||||||
statement_injector: StatementInjector<'a, 'ctx>,
|
statement_injector: StatementInjector<'a, 'ctx>,
|
||||||
top_level_statements: TopLevelStatements<'a, 'ctx>,
|
top_level_statements: TopLevelStatements<'a, 'ctx>,
|
||||||
|
arrow_function_converter: ArrowFunctionConverter<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'ctx> Common<'a, 'ctx> {
|
impl<'a, 'ctx> Common<'a, 'ctx> {
|
||||||
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
|
pub fn new(options: &TransformOptions, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||||
|
let arrow_function_converter_options = {
|
||||||
|
let mode = if options.env.es2015.arrow_function.is_some() {
|
||||||
|
ArrowFunctionConverterMode::Enabled
|
||||||
|
} else {
|
||||||
|
ArrowFunctionConverterMode::Disabled
|
||||||
|
};
|
||||||
|
ArrowFunctionConverterOptions { mode }
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
module_imports: ModuleImports::new(ctx),
|
module_imports: ModuleImports::new(ctx),
|
||||||
var_declarations: VarDeclarations::new(ctx),
|
var_declarations: VarDeclarations::new(ctx),
|
||||||
statement_injector: StatementInjector::new(ctx),
|
statement_injector: StatementInjector::new(ctx),
|
||||||
top_level_statements: TopLevelStatements::new(ctx),
|
top_level_statements: TopLevelStatements::new(ctx),
|
||||||
|
arrow_function_converter: ArrowFunctionConverter::new(
|
||||||
|
&arrow_function_converter_options,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -40,6 +57,7 @@ impl<'a, 'ctx> Traverse<'a> for Common<'a, 'ctx> {
|
||||||
self.module_imports.exit_program(program, ctx);
|
self.module_imports.exit_program(program, ctx);
|
||||||
self.var_declarations.exit_program(program, ctx);
|
self.var_declarations.exit_program(program, ctx);
|
||||||
self.top_level_statements.exit_program(program, ctx);
|
self.top_level_statements.exit_program(program, ctx);
|
||||||
|
self.arrow_function_converter.exit_program(program, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_statements(
|
fn enter_statements(
|
||||||
|
|
@ -58,4 +76,44 @@ impl<'a, 'ctx> Traverse<'a> for Common<'a, 'ctx> {
|
||||||
self.var_declarations.exit_statements(stmts, ctx);
|
self.var_declarations.exit_statements(stmts, ctx);
|
||||||
self.statement_injector.exit_statements(stmts, ctx);
|
self.statement_injector.exit_statements(stmts, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn enter_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
self.arrow_function_converter.enter_function(func, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
self.arrow_function_converter.exit_function(func, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
self.arrow_function_converter.enter_static_block(block, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
self.arrow_function_converter.exit_static_block(block, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_jsx_element_name(
|
||||||
|
&mut self,
|
||||||
|
element_name: &mut JSXElementName<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
self.arrow_function_converter.enter_jsx_element_name(element_name, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_jsx_member_expression_object(
|
||||||
|
&mut self,
|
||||||
|
object: &mut JSXMemberExpressionObject<'a>,
|
||||||
|
ctx: &mut TraverseCtx<'a>,
|
||||||
|
) {
|
||||||
|
self.arrow_function_converter.enter_jsx_member_expression_object(object, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
self.arrow_function_converter.enter_expression(expr, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
self.arrow_function_converter.exit_expression(expr, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@
|
||||||
//!
|
//!
|
||||||
//! ## Implementation
|
//! ## Implementation
|
||||||
//!
|
//!
|
||||||
//! Implementation based on [@babel/plugin-transform-arrow-functions](https://babel.dev/docs/babel-plugin-transform-arrow-functions).
|
//! The implementation is placed in [`crate::common::arrow_function_converter::ArrowFunctionConverter`],
|
||||||
|
//! which can be used in other plugins.
|
||||||
//!
|
//!
|
||||||
//! ## References:
|
//! ## References:
|
||||||
//!
|
//!
|
||||||
|
|
@ -126,15 +127,9 @@
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use oxc_allocator::{Box as ArenaBox, Vec as ArenaVec};
|
use oxc_traverse::Traverse;
|
||||||
use oxc_ast::ast::*;
|
|
||||||
use oxc_data_structures::stack::SparseStack;
|
use crate::context::TransformCtx;
|
||||||
use oxc_span::SPAN;
|
|
||||||
use oxc_syntax::{
|
|
||||||
scope::{ScopeFlags, ScopeId},
|
|
||||||
symbol::SymbolFlags,
|
|
||||||
};
|
|
||||||
use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx};
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
#[derive(Debug, Default, Clone, Copy, Deserialize)]
|
||||||
pub struct ArrowFunctionsOptions {
|
pub struct ArrowFunctionsOptions {
|
||||||
|
|
@ -146,284 +141,15 @@ pub struct ArrowFunctionsOptions {
|
||||||
pub spec: bool,
|
pub spec: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ArrowFunctions<'a> {
|
pub struct ArrowFunctions<'a, 'ctx> {
|
||||||
_options: ArrowFunctionsOptions,
|
_options: ArrowFunctionsOptions,
|
||||||
this_var_stack: SparseStack<BoundIdentifier<'a>>,
|
_ctx: &'ctx TransformCtx<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ArrowFunctions<'a> {
|
impl<'a, 'ctx> ArrowFunctions<'a, 'ctx> {
|
||||||
pub fn new(options: ArrowFunctionsOptions) -> Self {
|
pub fn new(options: ArrowFunctionsOptions, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||||
// `SparseStack` is created with 1 empty entry, for `Program`
|
Self { _options: options, _ctx: ctx }
|
||||||
Self { _options: options, this_var_stack: SparseStack::new() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Traverse<'a> for ArrowFunctions<'a> {
|
impl<'a, 'ctx> Traverse<'a> for ArrowFunctions<'a, 'ctx> {}
|
||||||
// Note: No visitors for `TSModuleBlock` because `this` is not legal in TS module blocks.
|
|
||||||
// <https://www.typescriptlang.org/play/?#code/HYQwtgpgzgDiDGEAEAxA9mpBvAsAKCSXjWCgBckANJAXiQAoBKWgPiTIAsBLKAbnwC++fGDQATAK4AbZACEQAJ2z5CxUhWp0mrdtz6D8QA>
|
|
||||||
|
|
||||||
/// Insert `var _this = this;` for the global scope.
|
|
||||||
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if let Some(this_var) = self.this_var_stack.take_last() {
|
|
||||||
self.insert_this_var_statement_at_the_top_of_statements(
|
|
||||||
&mut program.body,
|
|
||||||
&this_var,
|
|
||||||
ctx,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
debug_assert!(self.this_var_stack.len() == 1);
|
|
||||||
debug_assert!(self.this_var_stack.last().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_function(&mut self, _func: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) {
|
|
||||||
self.this_var_stack.push(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ```ts
|
|
||||||
/// function a(){
|
|
||||||
/// return () => console.log(this);
|
|
||||||
/// }
|
|
||||||
/// // to
|
|
||||||
/// function a(){
|
|
||||||
/// var _this = this;
|
|
||||||
/// return function() { return console.log(_this); };
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
/// Insert the var _this = this; statement outside the arrow function
|
|
||||||
fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if let Some(this_var) = self.this_var_stack.pop() {
|
|
||||||
let Some(body) = &mut func.body else { unreachable!() };
|
|
||||||
|
|
||||||
self.insert_this_var_statement_at_the_top_of_statements(
|
|
||||||
&mut body.statements,
|
|
||||||
&this_var,
|
|
||||||
ctx,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_static_block(&mut self, _block: &mut StaticBlock<'a>, _ctx: &mut TraverseCtx<'a>) {
|
|
||||||
self.this_var_stack.push(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if let Some(this_var) = self.this_var_stack.pop() {
|
|
||||||
self.insert_this_var_statement_at_the_top_of_statements(
|
|
||||||
&mut block.body,
|
|
||||||
&this_var,
|
|
||||||
ctx,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_jsx_element_name(
|
|
||||||
&mut self,
|
|
||||||
element_name: &mut JSXElementName<'a>,
|
|
||||||
ctx: &mut TraverseCtx<'a>,
|
|
||||||
) {
|
|
||||||
if let JSXElementName::ThisExpression(this) = element_name {
|
|
||||||
if let Some(ident) = self.get_this_identifier(this.span, ctx) {
|
|
||||||
*element_name = JSXElementName::IdentifierReference(ident);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_jsx_member_expression_object(
|
|
||||||
&mut self,
|
|
||||||
object: &mut JSXMemberExpressionObject<'a>,
|
|
||||||
ctx: &mut TraverseCtx<'a>,
|
|
||||||
) {
|
|
||||||
if let JSXMemberExpressionObject::ThisExpression(this) = object {
|
|
||||||
if let Some(ident) = self.get_this_identifier(this.span, ctx) {
|
|
||||||
*object = JSXMemberExpressionObject::IdentifierReference(ident);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if let Expression::ThisExpression(this) = expr {
|
|
||||||
if let Some(ident) = self.get_this_identifier(this.span, ctx) {
|
|
||||||
*expr = Expression::Identifier(ident);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if let Expression::ArrowFunctionExpression(_) = expr {
|
|
||||||
let Expression::ArrowFunctionExpression(arrow_function_expr) =
|
|
||||||
ctx.ast.move_expression(expr)
|
|
||||||
else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
*expr = self.transform_arrow_function_expression(arrow_function_expr.unbox(), ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ArrowFunctions<'a> {
|
|
||||||
fn get_this_identifier(
|
|
||||||
&mut self,
|
|
||||||
span: Span,
|
|
||||||
ctx: &mut TraverseCtx<'a>,
|
|
||||||
) -> Option<ArenaBox<'a, IdentifierReference<'a>>> {
|
|
||||||
// Find arrow function we are currently in (if we are)
|
|
||||||
let arrow_scope_id = Self::get_arrow_function_scope(ctx)?;
|
|
||||||
|
|
||||||
// TODO(improve-on-babel): We create a new UID for every scope. This is pointless, as only one
|
|
||||||
// `this` can be in scope at a time. We could create a single `_this` UID and reuse it in each
|
|
||||||
// scope. But this does not match output for some of Babel's test cases.
|
|
||||||
// <https://github.com/oxc-project/oxc/pull/5840>
|
|
||||||
let this_var = self.this_var_stack.last_or_init(|| {
|
|
||||||
let target_scope_id = ctx
|
|
||||||
.scopes()
|
|
||||||
.ancestors(arrow_scope_id)
|
|
||||||
// Skip arrow function scope
|
|
||||||
.skip(1)
|
|
||||||
.find(|&scope_id| {
|
|
||||||
let scope_flags = ctx.scopes().get_flags(scope_id);
|
|
||||||
scope_flags.intersects(
|
|
||||||
ScopeFlags::Function | ScopeFlags::Top | ScopeFlags::ClassStaticBlock,
|
|
||||||
) && !scope_flags.contains(ScopeFlags::Arrow)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
ctx.generate_uid("this", target_scope_id, SymbolFlags::FunctionScopedVariable)
|
|
||||||
});
|
|
||||||
Some(ctx.ast.alloc(this_var.create_spanned_read_reference(span, ctx)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find arrow function we are currently in, if it's between current node, and where `this` is bound.
|
|
||||||
/// Return its `ScopeId`.
|
|
||||||
fn get_arrow_function_scope(ctx: &mut TraverseCtx<'a>) -> Option<ScopeId> {
|
|
||||||
// `this` inside a class resolves to `this` *outside* the class in:
|
|
||||||
// * `extends` clause
|
|
||||||
// * Computed method key
|
|
||||||
// * Computed property key
|
|
||||||
// * Computed accessor property key (but `this` in this position is not legal TS)
|
|
||||||
//
|
|
||||||
// ```js
|
|
||||||
// // All these `this` refer to global `this`
|
|
||||||
// class C extends this {
|
|
||||||
// [this] = 123;
|
|
||||||
// static [this] = 123;
|
|
||||||
// [this]() {}
|
|
||||||
// static [this]() {}
|
|
||||||
// accessor [this] = 123;
|
|
||||||
// static accessor [this] = 123;
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// `this` resolves to the class / class instance (i.e. `this` defined *within* the class) in:
|
|
||||||
// * Method body
|
|
||||||
// * Method param
|
|
||||||
// * Property value
|
|
||||||
// * Static block
|
|
||||||
//
|
|
||||||
// ```js
|
|
||||||
// // All these `this` refer to `this` defined within the class
|
|
||||||
// class C {
|
|
||||||
// a = this;
|
|
||||||
// static b = this;
|
|
||||||
// #c = this;
|
|
||||||
// d() { this }
|
|
||||||
// static e() { this }
|
|
||||||
// #f() { this }
|
|
||||||
// g(x = this) {}
|
|
||||||
// accessor h = this;
|
|
||||||
// static accessor i = this;
|
|
||||||
// static { this }
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// So in this loop, we only exit when we encounter one of the above.
|
|
||||||
for ancestor in ctx.ancestors() {
|
|
||||||
match ancestor {
|
|
||||||
// Top level
|
|
||||||
Ancestor::ProgramBody(_)
|
|
||||||
// Function (includes class method body)
|
|
||||||
| Ancestor::FunctionParams(_)
|
|
||||||
| Ancestor::FunctionBody(_)
|
|
||||||
// Class property body
|
|
||||||
| Ancestor::PropertyDefinitionValue(_)
|
|
||||||
// Class accessor property body
|
|
||||||
| Ancestor::AccessorPropertyValue(_)
|
|
||||||
// Class static block
|
|
||||||
| Ancestor::StaticBlockBody(_) => return None,
|
|
||||||
// Arrow function
|
|
||||||
Ancestor::ArrowFunctionExpressionParams(func) => {
|
|
||||||
return Some(func.scope_id().get().unwrap())
|
|
||||||
}
|
|
||||||
Ancestor::ArrowFunctionExpressionBody(func) => {
|
|
||||||
return Some(func.scope_id().get().unwrap())
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::unused_self)]
|
|
||||||
fn transform_arrow_function_expression(
|
|
||||||
&mut self,
|
|
||||||
arrow_function_expr: ArrowFunctionExpression<'a>,
|
|
||||||
ctx: &mut TraverseCtx<'a>,
|
|
||||||
) -> Expression<'a> {
|
|
||||||
let mut body = arrow_function_expr.body;
|
|
||||||
|
|
||||||
if arrow_function_expr.expression {
|
|
||||||
assert!(body.statements.len() == 1);
|
|
||||||
let stmt = body.statements.pop().unwrap();
|
|
||||||
let Statement::ExpressionStatement(stmt) = stmt else { unreachable!() };
|
|
||||||
let stmt = stmt.unbox();
|
|
||||||
let return_statement = ctx.ast.statement_return(stmt.span, Some(stmt.expression));
|
|
||||||
body.statements.push(return_statement);
|
|
||||||
}
|
|
||||||
|
|
||||||
let scope_id = arrow_function_expr.scope_id.get().unwrap();
|
|
||||||
let flags = ctx.scopes_mut().get_flags_mut(scope_id);
|
|
||||||
*flags &= !ScopeFlags::Arrow;
|
|
||||||
|
|
||||||
Expression::FunctionExpression(ctx.ast.alloc_function_with_scope_id(
|
|
||||||
FunctionType::FunctionExpression,
|
|
||||||
arrow_function_expr.span,
|
|
||||||
None,
|
|
||||||
false,
|
|
||||||
arrow_function_expr.r#async,
|
|
||||||
false,
|
|
||||||
arrow_function_expr.type_parameters,
|
|
||||||
None::<TSThisParameter<'a>>,
|
|
||||||
arrow_function_expr.params,
|
|
||||||
arrow_function_expr.return_type,
|
|
||||||
Some(body),
|
|
||||||
scope_id,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Insert `var _this = this;` at the top of the statements.
|
|
||||||
#[expect(clippy::unused_self)]
|
|
||||||
fn insert_this_var_statement_at_the_top_of_statements(
|
|
||||||
&mut self,
|
|
||||||
statements: &mut ArenaVec<'a, Statement<'a>>,
|
|
||||||
this_var: &BoundIdentifier<'a>,
|
|
||||||
ctx: &TraverseCtx<'a>,
|
|
||||||
) {
|
|
||||||
let variable_declarator = ctx.ast.variable_declarator(
|
|
||||||
SPAN,
|
|
||||||
VariableDeclarationKind::Var,
|
|
||||||
this_var.create_binding_pattern(ctx),
|
|
||||||
Some(ctx.ast.expression_this(SPAN)),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
let stmt = ctx.ast.alloc_variable_declaration(
|
|
||||||
SPAN,
|
|
||||||
VariableDeclarationKind::Var,
|
|
||||||
ctx.ast.vec1(variable_declarator),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
let stmt = Statement::VariableDeclaration(stmt);
|
|
||||||
|
|
||||||
statements.insert(0, stmt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use oxc_ast::ast::*;
|
use oxc_traverse::Traverse;
|
||||||
use oxc_traverse::{Traverse, TraverseCtx};
|
|
||||||
|
|
||||||
mod arrow_functions;
|
mod arrow_functions;
|
||||||
mod options;
|
mod options;
|
||||||
|
|
@ -7,78 +6,24 @@ mod options;
|
||||||
pub use arrow_functions::{ArrowFunctions, ArrowFunctionsOptions};
|
pub use arrow_functions::{ArrowFunctions, ArrowFunctionsOptions};
|
||||||
pub use options::ES2015Options;
|
pub use options::ES2015Options;
|
||||||
|
|
||||||
pub struct ES2015<'a> {
|
use crate::context::TransformCtx;
|
||||||
|
|
||||||
|
pub struct ES2015<'a, 'ctx> {
|
||||||
|
#[expect(unused)]
|
||||||
options: ES2015Options,
|
options: ES2015Options,
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
arrow_functions: ArrowFunctions<'a>,
|
#[expect(unused)]
|
||||||
|
arrow_functions: ArrowFunctions<'a, 'ctx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ES2015<'a> {
|
impl<'a, 'ctx> ES2015<'a, 'ctx> {
|
||||||
pub fn new(options: ES2015Options) -> Self {
|
pub fn new(options: ES2015Options, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
arrow_functions: ArrowFunctions::new(options.arrow_function.unwrap_or_default()),
|
arrow_functions: ArrowFunctions::new(options.arrow_function.unwrap_or_default(), ctx),
|
||||||
options,
|
options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Traverse<'a> for ES2015<'a> {
|
impl<'a, 'ctx> Traverse<'a> for ES2015<'a, 'ctx> {}
|
||||||
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if self.options.arrow_function.is_some() {
|
|
||||||
self.arrow_functions.exit_program(program, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if self.options.arrow_function.is_some() {
|
|
||||||
self.arrow_functions.enter_function(func, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if self.options.arrow_function.is_some() {
|
|
||||||
self.arrow_functions.exit_function(func, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if self.options.arrow_function.is_some() {
|
|
||||||
self.arrow_functions.enter_expression(expr, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if self.options.arrow_function.is_some() {
|
|
||||||
self.arrow_functions.exit_expression(expr, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if self.options.arrow_function.is_some() {
|
|
||||||
self.arrow_functions.enter_static_block(block, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if self.options.arrow_function.is_some() {
|
|
||||||
self.arrow_functions.exit_static_block(block, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_jsx_element_name(&mut self, node: &mut JSXElementName<'a>, ctx: &mut TraverseCtx<'a>) {
|
|
||||||
if self.options.arrow_function.is_some() {
|
|
||||||
self.arrow_functions.enter_jsx_element_name(node, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_jsx_member_expression_object(
|
|
||||||
&mut self,
|
|
||||||
node: &mut JSXMemberExpressionObject<'a>,
|
|
||||||
ctx: &mut TraverseCtx<'a>,
|
|
||||||
) {
|
|
||||||
if self.options.arrow_function.is_some() {
|
|
||||||
self.arrow_functions.enter_jsx_member_expression_object(node, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ impl<'a> Transformer<'a> {
|
||||||
jsx::update_options_with_comments(&program.comments, &mut self.options, &self.ctx);
|
jsx::update_options_with_comments(&program.comments, &mut self.options, &self.ctx);
|
||||||
|
|
||||||
let mut transformer = TransformerImpl {
|
let mut transformer = TransformerImpl {
|
||||||
|
common: Common::new(&self.options, &self.ctx),
|
||||||
x0_typescript: program
|
x0_typescript: program
|
||||||
.source_type
|
.source_type
|
||||||
.is_typescript()
|
.is_typescript()
|
||||||
|
|
@ -106,9 +107,8 @@ impl<'a> Transformer<'a> {
|
||||||
x2_es2018: ES2018::new(self.options.env.es2018, &self.ctx),
|
x2_es2018: ES2018::new(self.options.env.es2018, &self.ctx),
|
||||||
x2_es2016: ES2016::new(self.options.env.es2016, &self.ctx),
|
x2_es2016: ES2016::new(self.options.env.es2016, &self.ctx),
|
||||||
x2_es2017: ES2017::new(self.options.env.es2017, &self.ctx),
|
x2_es2017: ES2017::new(self.options.env.es2017, &self.ctx),
|
||||||
x3_es2015: ES2015::new(self.options.env.es2015),
|
x3_es2015: ES2015::new(self.options.env.es2015, &self.ctx),
|
||||||
x4_regexp: RegExp::new(self.options.env.regexp, &self.ctx),
|
x4_regexp: RegExp::new(self.options.env.regexp, &self.ctx),
|
||||||
common: Common::new(&self.ctx),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let (symbols, scopes) = traverse_mut(&mut transformer, allocator, program, symbols, scopes);
|
let (symbols, scopes) = traverse_mut(&mut transformer, allocator, program, symbols, scopes);
|
||||||
|
|
@ -127,7 +127,8 @@ struct TransformerImpl<'a, 'ctx> {
|
||||||
x2_es2018: ES2018<'a, 'ctx>,
|
x2_es2018: ES2018<'a, 'ctx>,
|
||||||
x2_es2017: ES2017<'a, 'ctx>,
|
x2_es2017: ES2017<'a, 'ctx>,
|
||||||
x2_es2016: ES2016<'a, 'ctx>,
|
x2_es2016: ES2016<'a, 'ctx>,
|
||||||
x3_es2015: ES2015<'a>,
|
#[expect(unused)]
|
||||||
|
x3_es2015: ES2015<'a, 'ctx>,
|
||||||
x4_regexp: RegExp<'a, 'ctx>,
|
x4_regexp: RegExp<'a, 'ctx>,
|
||||||
common: Common<'a, 'ctx>,
|
common: Common<'a, 'ctx>,
|
||||||
}
|
}
|
||||||
|
|
@ -145,7 +146,6 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
||||||
if let Some(typescript) = self.x0_typescript.as_mut() {
|
if let Some(typescript) = self.x0_typescript.as_mut() {
|
||||||
typescript.exit_program(program, ctx);
|
typescript.exit_program(program, ctx);
|
||||||
}
|
}
|
||||||
self.x3_es2015.exit_program(program, ctx);
|
|
||||||
self.common.exit_program(program, ctx);
|
self.common.exit_program(program, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,11 +198,11 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn enter_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
self.x3_es2015.enter_static_block(block, ctx);
|
self.common.enter_static_block(block, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn exit_static_block(&mut self, block: &mut StaticBlock<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
self.x3_es2015.exit_static_block(block, ctx);
|
self.common.exit_static_block(block, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_ts_module_declaration(
|
fn enter_ts_module_declaration(
|
||||||
|
|
@ -224,15 +224,15 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
||||||
self.x2_es2020.enter_expression(expr, ctx);
|
self.x2_es2020.enter_expression(expr, ctx);
|
||||||
self.x2_es2018.enter_expression(expr, ctx);
|
self.x2_es2018.enter_expression(expr, ctx);
|
||||||
self.x2_es2016.enter_expression(expr, ctx);
|
self.x2_es2016.enter_expression(expr, ctx);
|
||||||
self.x3_es2015.enter_expression(expr, ctx);
|
|
||||||
self.x4_regexp.enter_expression(expr, ctx);
|
self.x4_regexp.enter_expression(expr, ctx);
|
||||||
|
self.common.enter_expression(expr, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
self.x1_jsx.exit_expression(expr, ctx);
|
self.x1_jsx.exit_expression(expr, ctx);
|
||||||
self.x2_es2018.exit_expression(expr, ctx);
|
self.x2_es2018.exit_expression(expr, ctx);
|
||||||
self.x2_es2017.exit_expression(expr, ctx);
|
self.x2_es2017.exit_expression(expr, ctx);
|
||||||
self.x3_es2015.exit_expression(expr, ctx);
|
self.common.exit_expression(expr, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_simple_assignment_target(
|
fn enter_simple_assignment_target(
|
||||||
|
|
@ -267,7 +267,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
||||||
|
|
||||||
fn enter_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn enter_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
self.x2_es2018.enter_function(func, ctx);
|
self.x2_es2018.enter_function(func, ctx);
|
||||||
self.x3_es2015.enter_function(func, ctx);
|
self.common.enter_function(func, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
|
@ -277,7 +277,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
||||||
self.x1_jsx.exit_function(func, ctx);
|
self.x1_jsx.exit_function(func, ctx);
|
||||||
self.x2_es2018.exit_function(func, ctx);
|
self.x2_es2018.exit_function(func, ctx);
|
||||||
self.x2_es2017.exit_function(func, ctx);
|
self.x2_es2017.exit_function(func, ctx);
|
||||||
self.x3_es2015.exit_function(func, ctx);
|
self.common.exit_function(func, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_jsx_element(&mut self, node: &mut JSXElement<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn enter_jsx_element(&mut self, node: &mut JSXElement<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
|
@ -287,7 +287,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_jsx_element_name(&mut self, node: &mut JSXElementName<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn enter_jsx_element_name(&mut self, node: &mut JSXElementName<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
self.x3_es2015.enter_jsx_element_name(node, ctx);
|
self.common.enter_jsx_element_name(node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_jsx_member_expression_object(
|
fn enter_jsx_member_expression_object(
|
||||||
|
|
@ -295,7 +295,7 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
|
||||||
node: &mut JSXMemberExpressionObject<'a>,
|
node: &mut JSXMemberExpressionObject<'a>,
|
||||||
ctx: &mut TraverseCtx<'a>,
|
ctx: &mut TraverseCtx<'a>,
|
||||||
) {
|
) {
|
||||||
self.x3_es2015.enter_jsx_member_expression_object(node, ctx);
|
self.common.enter_jsx_member_expression_object(node, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_jsx_fragment(&mut self, node: &mut JSXFragment<'a>, ctx: &mut TraverseCtx<'a>) {
|
fn enter_jsx_fragment(&mut self, node: &mut JSXFragment<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue