mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(transformer): add arrow_functions plugin (#1663)
This commit is contained in:
parent
67b7cc0f51
commit
f58b6279c8
8 changed files with 186 additions and 8 deletions
143
crates/oxc_transformer/src/es2015/arrow_functions.rs
Normal file
143
crates/oxc_transformer/src/es2015/arrow_functions.rs
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use oxc_allocator::Vec;
|
||||||
|
use oxc_ast::{ast::*, AstBuilder, AstKind, VisitMut};
|
||||||
|
use oxc_span::{Atom, SPAN};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::context::TransformerCtx;
|
||||||
|
use crate::options::TransformOptions;
|
||||||
|
use crate::TransformTarget;
|
||||||
|
|
||||||
|
/// ES2015 Arrow Functions
|
||||||
|
///
|
||||||
|
/// References:
|
||||||
|
/// * <https://babeljs.io/docs/babel-plugin-transform-arrow-functions>
|
||||||
|
/// * <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-arrow-functions>
|
||||||
|
pub struct ArrowFunctions<'a> {
|
||||||
|
ast: Rc<AstBuilder<'a>>,
|
||||||
|
nodes: Vec<'a, AstKind<'a>>,
|
||||||
|
uid: usize,
|
||||||
|
has_this: bool,
|
||||||
|
/// Insert a variable declaration at the top of the BlockStatement
|
||||||
|
insert: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Deserialize)]
|
||||||
|
pub struct ArrowFunctionsOptions {
|
||||||
|
/// This option enables the following:
|
||||||
|
/// * Wrap the generated function in .bind(this) and keeps uses of this inside the function as-is, instead of using a renamed this.
|
||||||
|
/// * Add a runtime check to ensure the functions are not instantiated.
|
||||||
|
/// * Add names to arrow functions.
|
||||||
|
pub spec: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> VisitMut<'a> for ArrowFunctions<'a> {
|
||||||
|
fn enter_node(&mut self, kind: AstKind<'a>) {
|
||||||
|
self.nodes.push(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn leave_node(&mut self, _kind: AstKind<'a>) {
|
||||||
|
self.nodes.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_jsx_identifier(&mut self, ident: &mut JSXIdentifier) {
|
||||||
|
let parent_kind = self.nodes.last().unwrap();
|
||||||
|
let parent_parent_kind = self.nodes[self.nodes.len() - 2];
|
||||||
|
if ident.name == "this"
|
||||||
|
&& (matches!(parent_kind, AstKind::JSXElementName(_))
|
||||||
|
|| matches!(parent_parent_kind, AstKind::JSXMemberExpression(_)))
|
||||||
|
{
|
||||||
|
if !self.has_this {
|
||||||
|
self.has_this = true;
|
||||||
|
self.uid += 1;
|
||||||
|
}
|
||||||
|
*ident = self.ast.jsx_identifier(SPAN, self.get_this_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ArrowFunctions<'a> {
|
||||||
|
pub fn new(
|
||||||
|
ast: Rc<AstBuilder<'a>>,
|
||||||
|
_: TransformerCtx<'a>,
|
||||||
|
options: &TransformOptions,
|
||||||
|
) -> Option<Self> {
|
||||||
|
(options.target < TransformTarget::ES2015 || options.arrow_functions.is_some()).then(|| {
|
||||||
|
let nodes = ast.new_vec();
|
||||||
|
Self { ast, uid: 0, nodes, has_this: false, insert: false }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_this_name(&self) -> Atom {
|
||||||
|
let uid = if self.uid == 1 { String::new() } else { self.uid.to_string() };
|
||||||
|
format!("_this{uid}",).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transform_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) {
|
||||||
|
if self.insert {
|
||||||
|
let binding_pattern = self.ast.binding_pattern(
|
||||||
|
self.ast
|
||||||
|
.binding_pattern_identifier(BindingIdentifier::new(SPAN, self.get_this_name())),
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let variable_declarator = self.ast.variable_declarator(
|
||||||
|
SPAN,
|
||||||
|
VariableDeclarationKind::Var,
|
||||||
|
binding_pattern,
|
||||||
|
Some(self.ast.this_expression(SPAN)),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
let stmt = self.ast.variable_declaration(
|
||||||
|
SPAN,
|
||||||
|
VariableDeclarationKind::Var,
|
||||||
|
self.ast.new_vec_single(variable_declarator),
|
||||||
|
Modifiers::empty(),
|
||||||
|
);
|
||||||
|
stmts.insert(0, Statement::Declaration(Declaration::VariableDeclaration(stmt)));
|
||||||
|
self.insert = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert to parent block
|
||||||
|
if self.has_this {
|
||||||
|
self.insert = true;
|
||||||
|
self.has_this = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
|
||||||
|
if let Expression::ArrowExpression(arrow_expr) = expr {
|
||||||
|
let mut body = self.ast.copy(&arrow_expr.body);
|
||||||
|
|
||||||
|
if arrow_expr.expression {
|
||||||
|
let first_stmt = body.statements.remove(0);
|
||||||
|
if let Statement::ExpressionStatement(stmt) = first_stmt {
|
||||||
|
let return_statement =
|
||||||
|
self.ast.return_statement(SPAN, Some(self.ast.copy(&stmt.expression)));
|
||||||
|
body.statements.push(return_statement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.visit_function_body(&mut body);
|
||||||
|
|
||||||
|
let new_function = self.ast.function(
|
||||||
|
FunctionType::FunctionExpression,
|
||||||
|
SPAN,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
arrow_expr.generator,
|
||||||
|
arrow_expr.r#async,
|
||||||
|
self.ast.copy(&arrow_expr.params),
|
||||||
|
Some(body),
|
||||||
|
self.ast.copy(&arrow_expr.type_parameters),
|
||||||
|
self.ast.copy(&arrow_expr.return_type),
|
||||||
|
Modifiers::empty(),
|
||||||
|
);
|
||||||
|
|
||||||
|
*expr = Expression::FunctionExpression(new_function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
mod arrow_functions;
|
||||||
mod duplicate_keys;
|
mod duplicate_keys;
|
||||||
mod function_name;
|
mod function_name;
|
||||||
mod shorthand_properties;
|
mod shorthand_properties;
|
||||||
mod template_literals;
|
mod template_literals;
|
||||||
|
|
||||||
|
pub use arrow_functions::{ArrowFunctions, ArrowFunctionsOptions};
|
||||||
pub use duplicate_keys::DuplicateKeys;
|
pub use duplicate_keys::DuplicateKeys;
|
||||||
pub use function_name::FunctionName;
|
pub use function_name::FunctionName;
|
||||||
pub use shorthand_properties::ShorthandProperties;
|
pub use shorthand_properties::ShorthandProperties;
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
|
es2015::ArrowFunctionsOptions,
|
||||||
es2020::NullishCoalescingOperatorOptions,
|
es2020::NullishCoalescingOperatorOptions,
|
||||||
options::{TransformOptions, TransformTarget},
|
options::{TransformOptions, TransformTarget},
|
||||||
react_jsx::{ReactJsxOptions, ReactJsxRuntime, ReactJsxRuntimeOption},
|
react_jsx::{ReactJsxOptions, ReactJsxRuntime, ReactJsxRuntimeOption},
|
||||||
|
|
@ -63,6 +64,7 @@ pub struct Transformer<'a> {
|
||||||
es2016_exponentiation_operator: Option<ExponentiationOperator<'a>>,
|
es2016_exponentiation_operator: Option<ExponentiationOperator<'a>>,
|
||||||
// es2015
|
// es2015
|
||||||
es2015_function_name: Option<FunctionName<'a>>,
|
es2015_function_name: Option<FunctionName<'a>>,
|
||||||
|
es2015_arrow_functions: Option<ArrowFunctions<'a>>,
|
||||||
es2015_shorthand_properties: Option<ShorthandProperties<'a>>,
|
es2015_shorthand_properties: Option<ShorthandProperties<'a>>,
|
||||||
es2015_template_literals: Option<TemplateLiterals<'a>>,
|
es2015_template_literals: Option<TemplateLiterals<'a>>,
|
||||||
es2015_duplicate_keys: Option<DuplicateKeys<'a>>,
|
es2015_duplicate_keys: Option<DuplicateKeys<'a>>,
|
||||||
|
|
@ -100,6 +102,7 @@ impl<'a> Transformer<'a> {
|
||||||
es2016_exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ast), ctx.clone(), &options),
|
es2016_exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ast), ctx.clone(), &options),
|
||||||
// es2015
|
// es2015
|
||||||
es2015_function_name: FunctionName::new(Rc::clone(&ast), ctx.clone(), &options),
|
es2015_function_name: FunctionName::new(Rc::clone(&ast), ctx.clone(), &options),
|
||||||
|
es2015_arrow_functions: ArrowFunctions::new(Rc::clone(&ast), ctx.clone(), &options),
|
||||||
es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
|
es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
|
||||||
es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options),
|
es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options),
|
||||||
es2015_duplicate_keys: DuplicateKeys::new(Rc::clone(&ast), &options),
|
es2015_duplicate_keys: DuplicateKeys::new(Rc::clone(&ast), &options),
|
||||||
|
|
@ -155,6 +158,7 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
|
||||||
self.es2021_logical_assignment_operators.as_mut().map(|t| t.add_vars_to_statements(stmts));
|
self.es2021_logical_assignment_operators.as_mut().map(|t| t.add_vars_to_statements(stmts));
|
||||||
self.es2020_nullish_coalescing_operators.as_mut().map(|t| t.add_vars_to_statements(stmts));
|
self.es2020_nullish_coalescing_operators.as_mut().map(|t| t.add_vars_to_statements(stmts));
|
||||||
self.es2016_exponentiation_operator.as_mut().map(|t| t.add_vars_to_statements(stmts));
|
self.es2016_exponentiation_operator.as_mut().map(|t| t.add_vars_to_statements(stmts));
|
||||||
|
self.es2015_arrow_functions.as_mut().map(|t| t.transform_statements(stmts));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_statement(&mut self, stmt: &mut Statement<'a>) {
|
fn visit_statement(&mut self, stmt: &mut Statement<'a>) {
|
||||||
|
|
@ -174,6 +178,7 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
|
||||||
|
|
||||||
self.es2021_logical_assignment_operators.as_mut().map(|t| t.transform_expression(expr));
|
self.es2021_logical_assignment_operators.as_mut().map(|t| t.transform_expression(expr));
|
||||||
self.es2020_nullish_coalescing_operators.as_mut().map(|t| t.transform_expression(expr));
|
self.es2020_nullish_coalescing_operators.as_mut().map(|t| t.transform_expression(expr));
|
||||||
|
self.es2015_arrow_functions.as_mut().map(|t| t.transform_expression(expr));
|
||||||
self.es2016_exponentiation_operator.as_mut().map(|t| t.transform_expression(expr));
|
self.es2016_exponentiation_operator.as_mut().map(|t| t.transform_expression(expr));
|
||||||
self.es2015_template_literals.as_mut().map(|t| t.transform_expression(expr));
|
self.es2015_template_literals.as_mut().map(|t| t.transform_expression(expr));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use oxc_syntax::assumptions::CompilerAssumptions;
|
use oxc_syntax::assumptions::CompilerAssumptions;
|
||||||
|
|
||||||
use crate::{es2020::NullishCoalescingOperatorOptions, react_jsx::ReactJsxOptions};
|
use crate::{
|
||||||
|
es2015::ArrowFunctionsOptions, es2020::NullishCoalescingOperatorOptions,
|
||||||
|
react_jsx::ReactJsxOptions,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct TransformOptions {
|
pub struct TransformOptions {
|
||||||
|
|
@ -22,6 +25,7 @@ pub struct TransformOptions {
|
||||||
// es2015
|
// es2015
|
||||||
pub duplicate_keys: bool,
|
pub duplicate_keys: bool,
|
||||||
pub function_name: bool,
|
pub function_name: bool,
|
||||||
|
pub arrow_functions: Option<ArrowFunctionsOptions>,
|
||||||
pub shorthand_properties: bool,
|
pub shorthand_properties: bool,
|
||||||
pub sticky_regex: bool,
|
pub sticky_regex: bool,
|
||||||
pub template_literals: bool,
|
pub template_literals: bool,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Passed: 277/1111
|
Passed: 288/1137
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
* babel-plugin-transform-numeric-separator
|
* babel-plugin-transform-numeric-separator
|
||||||
|
|
@ -746,6 +746,25 @@ Passed: 277/1111
|
||||||
# babel-plugin-transform-exponentiation-operator (3/4)
|
# babel-plugin-transform-exponentiation-operator (3/4)
|
||||||
* regression/4349/input.js
|
* regression/4349/input.js
|
||||||
|
|
||||||
|
# babel-plugin-transform-arrow-functions (9/26)
|
||||||
|
* arrow-functions/arguments/input.js
|
||||||
|
* arrow-functions/arguments-global-undeclared/input.js
|
||||||
|
* arrow-functions/arguments-global-var/input.js
|
||||||
|
* arrow-functions/default-parameters/input.js
|
||||||
|
* arrow-functions/destructuring-parameters/input.js
|
||||||
|
* arrow-functions/implicit-var-arguments/input.js
|
||||||
|
* arrow-functions/nested/input.js
|
||||||
|
* arrow-functions/self-referential/input.js
|
||||||
|
* arrow-functions/spec/input.js
|
||||||
|
* arrow-functions/super-call/input.js
|
||||||
|
* arrow-functions/super-prop/input.js
|
||||||
|
* arrow-functions/this/input.js
|
||||||
|
* assumption-newableArrowFunctions-false/basic/input.js
|
||||||
|
* assumption-newableArrowFunctions-false/naming/input.js
|
||||||
|
* assumption-newableArrowFunctions-false/self-referential/input.js
|
||||||
|
* spec/newableArrowFunction-default/input.js
|
||||||
|
* spec/newableArrowFunction-vs-spec-false/input.js
|
||||||
|
|
||||||
# babel-plugin-transform-unicode-regex (1/4)
|
# babel-plugin-transform-unicode-regex (1/4)
|
||||||
* unicode-regex/basic/input.js
|
* unicode-regex/basic/input.js
|
||||||
* unicode-regex/negated-set/input.js
|
* unicode-regex/negated-set/input.js
|
||||||
|
|
@ -869,12 +888,10 @@ Passed: 277/1111
|
||||||
* regression/15768/input.ts
|
* regression/15768/input.ts
|
||||||
* variable-declaration/non-null-in-optional-chain/input.ts
|
* variable-declaration/non-null-in-optional-chain/input.ts
|
||||||
|
|
||||||
# babel-plugin-transform-react-jsx (144/156)
|
# babel-plugin-transform-react-jsx (146/156)
|
||||||
* autoImport/after-polyfills-compiled-to-cjs/input.mjs
|
* autoImport/after-polyfills-compiled-to-cjs/input.mjs
|
||||||
* autoImport/complicated-scope-module/input.js
|
* autoImport/complicated-scope-module/input.js
|
||||||
* react/arrow-functions/input.js
|
|
||||||
* react/optimisation.react.constant-elements/input.js
|
* react/optimisation.react.constant-elements/input.js
|
||||||
* react-automatic/arrow-functions/input.js
|
|
||||||
* react-automatic/optimisation.react.constant-elements/input.js
|
* react-automatic/optimisation.react.constant-elements/input.js
|
||||||
* react-automatic/should-handle-attributed-elements/input.js
|
* react-automatic/should-handle-attributed-elements/input.js
|
||||||
* react-automatic/should-throw-when-filter-is-specified/input.js
|
* react-automatic/should-throw-when-filter-is-specified/input.js
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Passed: 375/441
|
Passed: 377/444
|
||||||
|
|
||||||
# All Passed:
|
# All Passed:
|
||||||
* babel-plugin-transform-class-static-block
|
* babel-plugin-transform-class-static-block
|
||||||
|
|
@ -94,3 +94,6 @@ Passed: 375/441
|
||||||
* object-spread-loose-builtins/no-object-assign-exec/exec.js
|
* object-spread-loose-builtins/no-object-assign-exec/exec.js
|
||||||
* object-spread-loose-builtins/side-effect/exec.js
|
* object-spread-loose-builtins/side-effect/exec.js
|
||||||
|
|
||||||
|
# babel-plugin-transform-arrow-functions (2/3)
|
||||||
|
* arrow-functions/implicit-var-arguments/exec.js
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ const CASES: &[&str] = &[
|
||||||
// ES2016
|
// ES2016
|
||||||
"babel-plugin-transform-exponentiation-operator",
|
"babel-plugin-transform-exponentiation-operator",
|
||||||
// ES2015
|
// ES2015
|
||||||
|
"babel-plugin-transform-arrow-functions",
|
||||||
"babel-plugin-transform-shorthand-properties",
|
"babel-plugin-transform-shorthand-properties",
|
||||||
"babel-plugin-transform-sticky-regex",
|
"babel-plugin-transform-sticky-regex",
|
||||||
"babel-plugin-transform-unicode-regex",
|
"babel-plugin-transform-unicode-regex",
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ use oxc_semantic::SemanticBuilder;
|
||||||
use oxc_span::{SourceType, VALID_EXTENSIONS};
|
use oxc_span::{SourceType, VALID_EXTENSIONS};
|
||||||
use oxc_tasks_common::{normalize_path, print_diff_in_terminal, BabelOptions};
|
use oxc_tasks_common::{normalize_path, print_diff_in_terminal, BabelOptions};
|
||||||
use oxc_transformer::{
|
use oxc_transformer::{
|
||||||
NullishCoalescingOperatorOptions, ReactJsxOptions, TransformOptions, TransformTarget,
|
ArrowFunctionsOptions, NullishCoalescingOperatorOptions, ReactJsxOptions, TransformOptions,
|
||||||
Transformer,
|
TransformTarget, Transformer,
|
||||||
};
|
};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
@ -95,6 +95,9 @@ pub trait TestCase {
|
||||||
assumptions: options.assumptions,
|
assumptions: options.assumptions,
|
||||||
class_static_block: options.get_plugin("transform-class-static-block").is_some(),
|
class_static_block: options.get_plugin("transform-class-static-block").is_some(),
|
||||||
function_name: options.get_plugin("transform-function-name").is_some(),
|
function_name: options.get_plugin("transform-function-name").is_some(),
|
||||||
|
arrow_functions: options
|
||||||
|
.get_plugin("transform-arrow-functions")
|
||||||
|
.map(get_options::<ArrowFunctionsOptions>),
|
||||||
logical_assignment_operators: options
|
logical_assignment_operators: options
|
||||||
.get_plugin("transform-logical-assignment-operators")
|
.get_plugin("transform-logical-assignment-operators")
|
||||||
.is_some(),
|
.is_some(),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue