mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(transformer): support exponentiation operator plugin (#4876)
The implementation copy from the original implementation which removed in https://github.com/oxc-project/oxc/pull/2865.
This commit is contained in:
parent
b3e189764f
commit
3a66e5843d
8 changed files with 415 additions and 5 deletions
322
crates/oxc_transformer/src/es2016/exponentiation_operator.rs
Normal file
322
crates/oxc_transformer/src/es2016/exponentiation_operator.rs
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
use std::cell::Cell;
|
||||
|
||||
use oxc_allocator::{CloneIn, Vec};
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_semantic::{ReferenceFlag, SymbolFlags};
|
||||
use oxc_span::SPAN;
|
||||
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator};
|
||||
use oxc_traverse::TraverseCtx;
|
||||
|
||||
use crate::context::Ctx;
|
||||
|
||||
/// ES2016: Exponentiation Operator
|
||||
///
|
||||
/// References:
|
||||
/// * <https://babel.dev/docs/babel-plugin-transform-exponentiation-operator>
|
||||
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-exponentiation-operator>
|
||||
/// * <https://github.com/babel/babel/blob/main/packages/babel-helper-builder-binary-assignment-operator-visitor>
|
||||
pub struct ExponentiationOperator<'a> {
|
||||
_ctx: Ctx<'a>,
|
||||
var_declarations: std::vec::Vec<Vec<'a, VariableDeclarator<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Exploded<'a> {
|
||||
reference: AssignmentTarget<'a>,
|
||||
uid: Expression<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ExponentiationOperator<'a> {
|
||||
pub fn new(ctx: Ctx<'a>) -> Self {
|
||||
Self { _ctx: ctx, var_declarations: vec![] }
|
||||
}
|
||||
|
||||
fn clone_identifier_reference(
|
||||
ident: &IdentifierReference<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> IdentifierReference<'a> {
|
||||
let reference = ctx.symbols().get_reference(ident.reference_id.get().unwrap());
|
||||
let symbol_id = reference.symbol_id();
|
||||
let flag = reference.flag();
|
||||
ctx.create_reference_id(ident.span, ident.name.clone(), symbol_id, *flag)
|
||||
}
|
||||
|
||||
fn clone_expression(expr: &Expression<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
|
||||
match expr {
|
||||
Expression::Identifier(ident) => ctx
|
||||
.ast
|
||||
.expression_from_identifier_reference(Self::clone_identifier_reference(ident, ctx)),
|
||||
_ => expr.clone_in(ctx.ast.allocator),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transform_statements(
|
||||
&mut self,
|
||||
_statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
self.var_declarations.push(ctx.ast.vec());
|
||||
}
|
||||
|
||||
pub fn transform_statements_on_exit(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if let Some(declarations) = self.var_declarations.pop() {
|
||||
if declarations.is_empty() {
|
||||
return;
|
||||
}
|
||||
let variable = ctx.ast.alloc_variable_declaration(
|
||||
SPAN,
|
||||
VariableDeclarationKind::Var,
|
||||
declarations,
|
||||
false,
|
||||
);
|
||||
statements.insert(0, Statement::VariableDeclaration(variable));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transform_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
// left ** right
|
||||
if let Expression::BinaryExpression(binary_expr) = expr {
|
||||
if binary_expr.operator == BinaryOperator::Exponential {
|
||||
let left = ctx.ast.move_expression(&mut binary_expr.left);
|
||||
let right = ctx.ast.move_expression(&mut binary_expr.right);
|
||||
*expr = Self::math_pow(left, right, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// left **= right
|
||||
if let Expression::AssignmentExpression(assign_expr) = expr {
|
||||
if assign_expr.operator == AssignmentOperator::Exponential {
|
||||
let mut nodes = ctx.ast.vec();
|
||||
let Some(Exploded { reference, uid }) =
|
||||
self.explode(&mut assign_expr.left, &mut nodes, ctx)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let right = ctx.ast.move_expression(&mut assign_expr.right);
|
||||
let right = Self::math_pow(uid, right, ctx);
|
||||
let assign_expr = ctx.ast.expression_assignment(
|
||||
SPAN,
|
||||
AssignmentOperator::Assign,
|
||||
reference,
|
||||
right,
|
||||
);
|
||||
nodes.push(assign_expr);
|
||||
*expr = ctx.ast.expression_sequence(SPAN, nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// `left ** right` -> `Math.pow(left, right)`
|
||||
fn math_pow(
|
||||
left: Expression<'a>,
|
||||
right: Expression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
let ident_math =
|
||||
ctx.create_reference_id(SPAN, ctx.ast.atom("Math"), None, ReferenceFlag::Read);
|
||||
let object = ctx.ast.expression_from_identifier_reference(ident_math);
|
||||
let property = ctx.ast.identifier_name(SPAN, "pow");
|
||||
let callee =
|
||||
Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false));
|
||||
let mut arguments = ctx.ast.vec_with_capacity(2);
|
||||
arguments.push(Argument::from(left));
|
||||
arguments.push(Argument::from(right));
|
||||
ctx.ast.expression_call(
|
||||
SPAN,
|
||||
arguments,
|
||||
callee,
|
||||
None::<TSTypeParameterInstantiation<'_>>,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Change `lhs **= 2` to `var temp; temp = lhs, lhs = Math.pow(temp, 2);`.
|
||||
/// If the lhs is a member expression `obj.ref` or `obj[ref]`, assign them to a temporary variable so side-effects are not computed twice.
|
||||
/// For `obj.ref`, change it to `var _obj; _obj = obj, _obj["ref"] = Math.pow(_obj["ref"], 2)`.
|
||||
/// For `obj[ref]`, change it to `var _obj, _ref; _obj = obj, _ref = ref, _obj[_ref] = Math.pow(_obj[_ref], 2);`.
|
||||
fn explode(
|
||||
&mut self,
|
||||
node: &mut AssignmentTarget<'a>,
|
||||
nodes: &mut Vec<'a, Expression<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Exploded<'a>> {
|
||||
let node = node.as_simple_assignment_target_mut()?;
|
||||
let obj = self.get_obj_ref(node, nodes, ctx)?;
|
||||
let (reference, uid) = match node {
|
||||
SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => {
|
||||
let reference = AssignmentTarget::AssignmentTargetIdentifier(
|
||||
ctx.ast.alloc(Self::clone_identifier_reference(ident.as_ref(), ctx)),
|
||||
);
|
||||
(reference, obj)
|
||||
}
|
||||
match_member_expression!(SimpleAssignmentTarget) => {
|
||||
let member_expr = node.to_member_expression_mut();
|
||||
let computed = member_expr.is_computed();
|
||||
let prop = self.get_prop_ref(member_expr, nodes, ctx)?;
|
||||
let optional = false;
|
||||
let obj_clone = Self::clone_expression(&obj, ctx);
|
||||
let (reference, uid) = match &prop {
|
||||
Expression::Identifier(ident) if !computed => {
|
||||
let ident = IdentifierName::new(SPAN, ident.name.clone());
|
||||
(
|
||||
// TODO:
|
||||
// Both of these are the same, but it's in order to avoid after cloning without reference_id.
|
||||
// Related: https://github.com/oxc-project/oxc/issues/4804
|
||||
ctx.ast.member_expression_static(
|
||||
SPAN,
|
||||
obj_clone,
|
||||
ident.clone(),
|
||||
optional,
|
||||
),
|
||||
ctx.ast.member_expression_static(SPAN, obj, ident, optional),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let prop_clone = Self::clone_expression(&prop, ctx);
|
||||
(
|
||||
ctx.ast
|
||||
.member_expression_computed(SPAN, obj_clone, prop_clone, optional),
|
||||
ctx.ast.member_expression_computed(SPAN, obj, prop, optional),
|
||||
)
|
||||
}
|
||||
};
|
||||
(
|
||||
AssignmentTarget::from(
|
||||
ctx.ast.simple_assignment_target_member_expression(reference),
|
||||
),
|
||||
Expression::from(uid),
|
||||
)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
Some(Exploded { reference, uid })
|
||||
}
|
||||
|
||||
/// Make sure side-effects of evaluating `obj` of `obj.ref` and `obj[ref]` only happen once.
|
||||
fn get_obj_ref(
|
||||
&mut self,
|
||||
node: &mut SimpleAssignmentTarget<'a>,
|
||||
nodes: &mut Vec<'a, Expression<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let reference = match node {
|
||||
SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => {
|
||||
if ident
|
||||
.reference_id
|
||||
.get()
|
||||
.is_some_and(|reference_id| ctx.symbols().has_binding(reference_id))
|
||||
{
|
||||
// this variable is declared in scope so we can be 100% sure
|
||||
// that evaluating it multiple times won't trigger a getter
|
||||
// or something else
|
||||
return Some(ctx.ast.expression_from_identifier_reference(
|
||||
Self::clone_identifier_reference(ident, ctx),
|
||||
));
|
||||
}
|
||||
// could possibly trigger a getter so we need to only evaluate it once
|
||||
ctx.ast.expression_from_identifier_reference(Self::clone_identifier_reference(
|
||||
ident, ctx,
|
||||
))
|
||||
}
|
||||
match_member_expression!(SimpleAssignmentTarget) => {
|
||||
let expr = match node {
|
||||
SimpleAssignmentTarget::ComputedMemberExpression(e) => &mut e.object,
|
||||
SimpleAssignmentTarget::StaticMemberExpression(e) => &mut e.object,
|
||||
SimpleAssignmentTarget::PrivateFieldExpression(e) => &mut e.object,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let expr = ctx.ast.move_expression(expr);
|
||||
// the object reference that we need to save is locally declared
|
||||
// so as per the previous comment we can be 100% sure evaluating
|
||||
// it multiple times will be safe
|
||||
// Super cannot be directly assigned so lets return it also
|
||||
if matches!(expr, Expression::Super(_))
|
||||
|| matches!(&expr, Expression::Identifier(ident) if ident
|
||||
.reference_id
|
||||
.get()
|
||||
.is_some_and(|reference_id| ctx.symbols().has_binding(reference_id)))
|
||||
{
|
||||
return Some(expr);
|
||||
}
|
||||
|
||||
expr
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
Some(self.add_new_reference(reference, nodes, ctx))
|
||||
}
|
||||
|
||||
/// Make sure side-effects of evaluating `ref` of `obj.ref` and `obj[ref]` only happen once.
|
||||
fn get_prop_ref(
|
||||
&mut self,
|
||||
node: &mut MemberExpression<'a>,
|
||||
nodes: &mut Vec<'a, Expression<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let expr = match node {
|
||||
MemberExpression::ComputedMemberExpression(expr) => {
|
||||
let expr = ctx.ast.move_expression(&mut expr.expression);
|
||||
if expr.is_literal() {
|
||||
return Some(expr);
|
||||
}
|
||||
expr
|
||||
}
|
||||
MemberExpression::StaticMemberExpression(expr) => {
|
||||
return Some(ctx.ast.expression_string_literal(SPAN, expr.property.name.clone()));
|
||||
}
|
||||
MemberExpression::PrivateFieldExpression(_) => {
|
||||
// From babel: "We can't generate property ref for private name, please install `@babel/plugin-transform-class-properties`"
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(self.add_new_reference(expr, nodes, ctx))
|
||||
}
|
||||
|
||||
fn add_new_reference(
|
||||
&mut self,
|
||||
expr: Expression<'a>,
|
||||
nodes: &mut Vec<'a, Expression<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Expression<'a> {
|
||||
let name = match expr {
|
||||
Expression::Identifier(ref ident) => ident.name.clone().as_str(),
|
||||
_ => "ref",
|
||||
};
|
||||
|
||||
let symbol_id =
|
||||
ctx.generate_uid_in_current_scope(name, SymbolFlags::FunctionScopedVariable);
|
||||
let symbol_name = ctx.ast.atom(ctx.symbols().get_name(symbol_id));
|
||||
|
||||
{
|
||||
// var _name;
|
||||
let binding_identifier = BindingIdentifier {
|
||||
span: SPAN,
|
||||
name: symbol_name.clone(),
|
||||
symbol_id: Cell::new(Some(symbol_id)),
|
||||
};
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let id = ctx.ast.binding_pattern_kind_from_binding_identifier(binding_identifier);
|
||||
let id = ctx.ast.binding_pattern(id, None::<TSTypeAnnotation<'_>>, false);
|
||||
self.var_declarations
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.push(ctx.ast.variable_declarator(SPAN, kind, id, None, false));
|
||||
}
|
||||
|
||||
let ident =
|
||||
ctx.create_reference_id(SPAN, symbol_name, Some(symbol_id), ReferenceFlag::Read);
|
||||
|
||||
// let ident = self.create_new_var_with_expression(&expr);
|
||||
// Add new reference `_name = name` to nodes
|
||||
let left = ctx.ast.simple_assignment_target_from_identifier_reference(
|
||||
Self::clone_identifier_reference(&ident, ctx),
|
||||
);
|
||||
let op = AssignmentOperator::Assign;
|
||||
nodes.push(ctx.ast.expression_assignment(SPAN, op, AssignmentTarget::from(left), expr));
|
||||
ctx.ast.expression_from_identifier_reference(ident)
|
||||
}
|
||||
}
|
||||
52
crates/oxc_transformer/src/es2016/mod.rs
Normal file
52
crates/oxc_transformer/src/es2016/mod.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
mod exponentiation_operator;
|
||||
mod options;
|
||||
|
||||
pub use exponentiation_operator::ExponentiationOperator;
|
||||
pub use options::ES2016Options;
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_traverse::TraverseCtx;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::context::Ctx;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct ES2016<'a> {
|
||||
ctx: Ctx<'a>,
|
||||
options: ES2016Options,
|
||||
|
||||
// Plugins
|
||||
exponentiation_operator: ExponentiationOperator<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ES2016<'a> {
|
||||
pub fn new(options: ES2016Options, ctx: Ctx<'a>) -> Self {
|
||||
Self { exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ctx)), ctx, options }
|
||||
}
|
||||
|
||||
pub fn transform_statements(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if self.options.exponentiation_operator {
|
||||
self.exponentiation_operator.transform_statements(statements, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transform_statements_on_exit(
|
||||
&mut self,
|
||||
statements: &mut Vec<'a, Statement<'a>>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) {
|
||||
if self.options.exponentiation_operator {
|
||||
self.exponentiation_operator.transform_statements_on_exit(statements, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transform_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.exponentiation_operator {
|
||||
self.exponentiation_operator.transform_expression(expr, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
crates/oxc_transformer/src/es2016/options.rs
Normal file
16
crates/oxc_transformer/src/es2016/options.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize)]
|
||||
#[serde(default, rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct ES2016Options {
|
||||
#[serde(skip)]
|
||||
pub exponentiation_operator: bool,
|
||||
}
|
||||
|
||||
impl ES2016Options {
|
||||
#[must_use]
|
||||
pub fn with_exponentiation_operator(mut self, enable: bool) -> Self {
|
||||
self.exponentiation_operator = enable;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ mod options;
|
|||
// Presets: <https://babel.dev/docs/presets>
|
||||
mod env;
|
||||
mod es2015;
|
||||
mod es2016;
|
||||
mod react;
|
||||
mod typescript;
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ mod helpers {
|
|||
|
||||
use std::{path::Path, rc::Rc};
|
||||
|
||||
use es2016::ES2016;
|
||||
use oxc_allocator::{Allocator, Vec};
|
||||
use oxc_ast::{ast::*, AstBuilder, Trivias};
|
||||
use oxc_diagnostics::OxcDiagnostic;
|
||||
|
|
@ -58,6 +60,7 @@ pub struct Transformer<'a> {
|
|||
// NOTE: all callbacks must run in order.
|
||||
x0_typescript: TypeScript<'a>,
|
||||
x1_react: React<'a>,
|
||||
x2_es2016: ES2016<'a>,
|
||||
x3_es2015: ES2015<'a>,
|
||||
}
|
||||
|
||||
|
|
@ -82,6 +85,7 @@ impl<'a> Transformer<'a> {
|
|||
ctx: Rc::clone(&ctx),
|
||||
x0_typescript: TypeScript::new(options.typescript, Rc::clone(&ctx)),
|
||||
x1_react: React::new(options.react, Rc::clone(&ctx)),
|
||||
x2_es2016: ES2016::new(options.es2016, Rc::clone(&ctx)),
|
||||
x3_es2015: ES2015::new(options.es2015, ctx),
|
||||
}
|
||||
}
|
||||
|
|
@ -160,6 +164,7 @@ impl<'a> Traverse<'a> for Transformer<'a> {
|
|||
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.x0_typescript.transform_expression(expr);
|
||||
self.x1_react.transform_expression(expr, ctx);
|
||||
self.x2_es2016.transform_expression(expr, ctx);
|
||||
self.x3_es2015.transform_expression(expr);
|
||||
}
|
||||
|
||||
|
|
@ -244,13 +249,15 @@ impl<'a> Traverse<'a> for Transformer<'a> {
|
|||
self.x0_typescript.transform_property_definition(def);
|
||||
}
|
||||
|
||||
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) {
|
||||
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.x0_typescript.transform_statements(stmts);
|
||||
self.x2_es2016.transform_statements(stmts, ctx);
|
||||
self.x3_es2015.enter_statements(stmts);
|
||||
}
|
||||
|
||||
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.x0_typescript.transform_statements_on_exit(stmts, ctx);
|
||||
self.x2_es2016.transform_statements_on_exit(stmts, ctx);
|
||||
self.x3_es2015.exit_statements(stmts);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::{
|
|||
compiler_assumptions::CompilerAssumptions,
|
||||
env::{can_enable_plugin, EnvOptions, Versions},
|
||||
es2015::{ArrowFunctionsOptions, ES2015Options},
|
||||
es2016::ES2016Options,
|
||||
options::babel::BabelOptions,
|
||||
react::ReactOptions,
|
||||
typescript::TypeScriptOptions,
|
||||
|
|
@ -34,6 +35,8 @@ pub struct TransformOptions {
|
|||
pub react: ReactOptions,
|
||||
|
||||
pub es2015: ES2015Options,
|
||||
|
||||
pub es2016: ES2016Options,
|
||||
}
|
||||
|
||||
impl TransformOptions {
|
||||
|
|
@ -104,6 +107,11 @@ impl TransformOptions {
|
|||
})
|
||||
});
|
||||
|
||||
let es2016 = ES2016Options::default().with_exponentiation_operator({
|
||||
let plugin_name = "transform-exponentiation-operator";
|
||||
enable_plugin(plugin_name, options, &env_options, &targets).is_some()
|
||||
});
|
||||
|
||||
let typescript = {
|
||||
let plugin_name = "transform-typescript";
|
||||
from_value::<TypeScriptOptions>(get_plugin_options(plugin_name, options))
|
||||
|
|
@ -135,6 +143,7 @@ impl TransformOptions {
|
|||
typescript,
|
||||
react,
|
||||
es2015,
|
||||
es2016,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
commit: 12619ffe
|
||||
|
||||
Passed: 453/927
|
||||
Passed: 456/931
|
||||
|
||||
# All Passed:
|
||||
* babel-preset-react
|
||||
|
|
@ -433,6 +433,9 @@ Passed: 453/927
|
|||
* shipped-proposals/new-class-features-chrome-94/input.js
|
||||
* shipped-proposals/new-class-features-firefox-70/input.js
|
||||
|
||||
# babel-plugin-transform-exponentiation-operator (3/4)
|
||||
* regression/4349/input.js
|
||||
|
||||
# babel-plugin-transform-arrow-functions (1/6)
|
||||
* assumption-newableArrowFunctions-false/basic/input.js
|
||||
* assumption-newableArrowFunctions-false/naming/input.js
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
commit: 12619ffe
|
||||
|
||||
Passed: 8/14
|
||||
Passed: 10/16
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-exponentiation-operator
|
||||
* babel-plugin-transform-arrow-functions
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ pub(crate) const PLUGINS: &[&str] = &[
|
|||
// // [Regex] "babel-plugin-transform-named-capturing-groups-regex",
|
||||
// // ES2017
|
||||
// "babel-plugin-transform-async-to-generator",
|
||||
// // ES2016
|
||||
// "babel-plugin-transform-exponentiation-operator",
|
||||
// ES2016
|
||||
"babel-plugin-transform-exponentiation-operator",
|
||||
// // ES2015
|
||||
"babel-plugin-transform-arrow-functions",
|
||||
// "babel-plugin-transform-function-name",
|
||||
|
|
|
|||
Loading…
Reference in a new issue