mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(transformer): ES2020 Nullish Coalescing Operator (#1004)
The test runner needs an update for reading options.jon, which I'll work on in the up coming PR.
This commit is contained in:
parent
0e9104477f
commit
678db1d955
15 changed files with 282 additions and 77 deletions
|
|
@ -101,6 +101,15 @@ impl<'a> AstBuilder<'a> {
|
|||
Program { span, source_type, directives, hashbang, body }
|
||||
}
|
||||
|
||||
/* ---------- Constructors ---------- */
|
||||
|
||||
/// `void 0`
|
||||
pub fn void_0(&self) -> Expression<'a> {
|
||||
let left = self.number_literal(Span::default(), 0.0, "0", NumberBase::Decimal);
|
||||
let num = self.literal_number_expression(left);
|
||||
self.unary_expression(Span::default(), UnaryOperator::Void, num)
|
||||
}
|
||||
|
||||
/* ---------- Literals ---------- */
|
||||
|
||||
pub fn number_literal(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use oxc_allocator::{Box, Vec};
|
|||
#[allow(clippy::wildcard_imports)]
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_syntax::{
|
||||
operator::{BinaryOperator, LogicalOperator, UnaryOperator},
|
||||
operator::{BinaryOperator, UnaryOperator},
|
||||
precedence::{GetPrecedence, Precedence},
|
||||
NumberBase,
|
||||
};
|
||||
|
|
@ -1368,6 +1368,7 @@ impl<'a, const MINIFY: bool> GenExpr<MINIFY> for BinaryExpression<'a> {
|
|||
p.print_space_before_identifier();
|
||||
}
|
||||
self.operator.gen(p, ctx);
|
||||
p.print_soft_space();
|
||||
self.right.gen_expr(p, self.precedence(), ctx.union_in_if(wrap));
|
||||
});
|
||||
}
|
||||
|
|
@ -1406,11 +1407,9 @@ impl<'a, const MINIFY: bool> GenExpr<MINIFY> for LogicalExpression<'a> {
|
|||
);
|
||||
p.wrap(mixed || (precedence > self.precedence()), |p| {
|
||||
self.left.gen_expr(p, self.precedence(), ctx);
|
||||
p.print_soft_space();
|
||||
p.print_str(self.operator.as_str().as_bytes());
|
||||
let _precedence = match self.operator {
|
||||
LogicalOperator::And | LogicalOperator::Coalesce => Precedence::BitwiseOr,
|
||||
LogicalOperator::Or => Precedence::LogicalAnd,
|
||||
};
|
||||
p.print_soft_space();
|
||||
self.right.gen_expr(p, self.precedence(), ctx);
|
||||
});
|
||||
}
|
||||
|
|
@ -1421,9 +1420,13 @@ impl<'a, const MINIFY: bool> GenExpr<MINIFY> for ConditionalExpression<'a> {
|
|||
let wrap = precedence > self.precedence();
|
||||
p.wrap(wrap, |p| {
|
||||
self.test.gen_expr(p, self.precedence(), ctx);
|
||||
p.print_soft_space();
|
||||
p.print(b'?');
|
||||
p.print_soft_space();
|
||||
self.consequent.gen_expr(p, Precedence::Assign, ctx.and_in(true));
|
||||
p.print_soft_space();
|
||||
p.print(b':');
|
||||
p.print_soft_space();
|
||||
self.alternate.gen_expr(p, Precedence::Assign, ctx.union_in_if(wrap));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,13 +40,6 @@ impl<'a> Compressor<'a> {
|
|||
|
||||
/* Utilities */
|
||||
|
||||
/// `void 0`
|
||||
fn create_void_0(&mut self) -> Expression<'a> {
|
||||
let left = self.ast.number_literal(SPAN, 0.0, "0", NumberBase::Decimal);
|
||||
let num = self.ast.literal_number_expression(left);
|
||||
self.ast.unary_expression(SPAN, UnaryOperator::Void, num)
|
||||
}
|
||||
|
||||
/// `1/0`
|
||||
#[allow(unused)]
|
||||
fn create_one_div_zero(&mut self) -> Expression<'a> {
|
||||
|
|
@ -88,7 +81,7 @@ impl<'a> Compressor<'a> {
|
|||
|
||||
fn compress_console(&mut self, expr: &mut Expression<'a>) -> bool {
|
||||
if self.options.drop_console && util::is_console(expr) {
|
||||
*expr = self.create_void_0();
|
||||
*expr = self.ast.void_0();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
|
@ -163,12 +156,12 @@ impl<'a> Compressor<'a> {
|
|||
/* Expressions */
|
||||
|
||||
/// Transforms `undefined` => `void 0`
|
||||
fn compress_undefined(&mut self, expr: &mut Expression<'a>) -> bool {
|
||||
fn compress_undefined(&self, expr: &mut Expression<'a>) -> bool {
|
||||
let Expression::Identifier(ident) = expr else { return false };
|
||||
if ident.name == "undefined" {
|
||||
// if let Some(reference_id) = ident.reference_id.get() {
|
||||
// && self.semantic.symbols().is_global_reference(reference_id)
|
||||
*expr = self.create_void_0();
|
||||
*expr = self.ast.void_0();
|
||||
return true;
|
||||
// }
|
||||
}
|
||||
|
|
@ -209,14 +202,14 @@ impl<'a> Compressor<'a> {
|
|||
|
||||
/// Transforms `typeof foo == "undefined"` into `foo === void 0`
|
||||
/// Enabled by `compress.typeofs`
|
||||
fn compress_typeof_undefined(&mut self, expr: &mut BinaryExpression<'a>) {
|
||||
fn compress_typeof_undefined(&self, expr: &mut BinaryExpression<'a>) {
|
||||
if expr.operator.is_equality() && self.options.typeofs {
|
||||
if let Expression::UnaryExpression(unary_expr) = &expr.left {
|
||||
if unary_expr.operator == UnaryOperator::Typeof {
|
||||
if let Expression::Identifier(ident) = &unary_expr.argument {
|
||||
if expr.right.is_specific_string_literal("undefined") {
|
||||
let left = self.ast.identifier_reference_expression((*ident).clone());
|
||||
let right = self.create_void_0();
|
||||
let right = self.ast.void_0();
|
||||
let operator = BinaryOperator::StrictEquality;
|
||||
*expr = BinaryExpression { span: SPAN, left, operator, right };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use oxc_ast::ast::Expression;
|
||||
use oxc_index::IndexVec;
|
||||
use oxc_span::{Atom, Span};
|
||||
pub use oxc_syntax::{
|
||||
|
|
@ -112,4 +113,26 @@ impl SymbolTable {
|
|||
.iter()
|
||||
.map(|reference_id| &self.references[*reference_id])
|
||||
}
|
||||
|
||||
/// Determine whether evaluating the specific input `node` is a consequenceless reference. ie.
|
||||
/// evaluating it won't result in potentially arbitrary code from being ran. The following are
|
||||
/// allowed and determined not to cause side effects:
|
||||
///
|
||||
/// - `this` expressions
|
||||
/// - `super` expressions
|
||||
/// - Bound identifiers
|
||||
///
|
||||
/// Reference:
|
||||
/// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L557>
|
||||
pub fn is_static(&self, expr: &Expression) -> bool {
|
||||
match expr {
|
||||
Expression::ThisExpression(_) | Expression::Super(_) => true,
|
||||
Expression::Identifier(ident)
|
||||
if ident.reference_id.get().is_some_and(|id| self.has_binding(id)) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ use oxc_codegen::{Codegen, CodegenOptions};
|
|||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::SourceType;
|
||||
use oxc_transformer::{TransformOptions, TransformReactOptions, TransformTarget, Transformer};
|
||||
use oxc_transformer::{
|
||||
Assumptions, TransformOptions, TransformReactOptions, TransformTarget, Transformer,
|
||||
};
|
||||
|
||||
// Instruction:
|
||||
// create a `test.js`,
|
||||
|
|
@ -41,6 +43,7 @@ fn main() {
|
|||
let transform_options = TransformOptions {
|
||||
target: TransformTarget::ES2015,
|
||||
react: Some(TransformReactOptions::default()),
|
||||
assumptions: Assumptions::default(),
|
||||
};
|
||||
Transformer::new(&allocator, source_type, &symbols, transform_options).build(program);
|
||||
let printed = Codegen::<false>::new(source_text.len(), codegen_options).build(program);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::{ast::*, AstBuilder};
|
||||
use oxc_semantic::SymbolTable;
|
||||
use oxc_span::{Atom, Span};
|
||||
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator};
|
||||
|
||||
use std::{cell::RefCell, mem, rc::Rc};
|
||||
use crate::utils::CreateVars;
|
||||
|
||||
/// ES2016: Exponentiation Operator
|
||||
///
|
||||
|
|
@ -23,23 +25,22 @@ struct Exploded<'a> {
|
|||
uid: Expression<'a>,
|
||||
}
|
||||
|
||||
impl<'a> CreateVars<'a> for ExponentiationOperator<'a> {
|
||||
fn ast(&self) -> &AstBuilder<'a> {
|
||||
&self.ast
|
||||
}
|
||||
|
||||
fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> {
|
||||
&mut self.vars
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExponentiationOperator<'a> {
|
||||
pub fn new(ast: Rc<AstBuilder<'a>>, symbols: Rc<RefCell<SymbolTable>>) -> Self {
|
||||
let vars = ast.new_vec();
|
||||
Self { ast, symbols, vars }
|
||||
}
|
||||
|
||||
pub fn leave_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) {
|
||||
if self.vars.is_empty() {
|
||||
return;
|
||||
}
|
||||
let decls = mem::replace(&mut self.vars, self.ast.new_vec());
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let decl = self.ast.variable_declaration(Span::default(), kind, decls, Modifiers::empty());
|
||||
let stmt = Statement::Declaration(Declaration::VariableDeclaration(decl));
|
||||
stmts.insert(0, stmt);
|
||||
}
|
||||
|
||||
pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
|
||||
// left ** right
|
||||
if let Expression::BinaryExpression(binary_expr) = expr {
|
||||
|
|
@ -218,17 +219,7 @@ impl<'a> ExponentiationOperator<'a> {
|
|||
expr: Expression<'a>,
|
||||
nodes: &mut Vec<'a, Expression<'a>>,
|
||||
) -> Expression<'a> {
|
||||
let name = generate_uid_identifier_based_on_node(&expr);
|
||||
// TODO: scope.push({ id: temp });
|
||||
|
||||
// Add `var name` to scope
|
||||
let binding_identifier = BindingIdentifier::new(Span::default(), name.clone());
|
||||
let binding_pattern_kind = self.ast.binding_pattern_identifier(binding_identifier);
|
||||
let binding = self.ast.binding_pattern(binding_pattern_kind, None, false);
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let decl = self.ast.variable_declarator(Span::default(), kind, binding, None, false);
|
||||
self.vars.push(decl);
|
||||
|
||||
let name = self.create_new_var(&expr);
|
||||
// Add new reference `_name = name` to nodes
|
||||
let ident = IdentifierReference::new(Span::default(), name);
|
||||
let target = self.ast.simple_assignment_target_identifier(ident.clone());
|
||||
|
|
@ -239,23 +230,6 @@ impl<'a> ExponentiationOperator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L543>
|
||||
fn generate_uid_identifier_based_on_node(expr: &Expression) -> Atom {
|
||||
let mut parts = std::vec::Vec::with_capacity(1);
|
||||
gather_node_parts(expr, &mut parts);
|
||||
let name = parts.join("$");
|
||||
Atom::from(format!("_{name}"))
|
||||
}
|
||||
|
||||
// TODO: use a trait and add this to oxc_ast (syntax directed operations)
|
||||
fn gather_node_parts(expr: &Expression, parts: &mut std::vec::Vec<Atom>) {
|
||||
match expr {
|
||||
Expression::Identifier(ident) => parts.push(ident.name.clone()),
|
||||
_ => parts.push(Atom::from("ref")),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::{
|
||||
|
|
|
|||
3
crates/oxc_transformer/src/es2020/mod.rs
Normal file
3
crates/oxc_transformer/src/es2020/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
mod nullish_coalescing_operator;
|
||||
|
||||
pub use nullish_coalescing_operator::NullishCoalescingOperator;
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::{ast::*, AstBuilder};
|
||||
use oxc_semantic::SymbolTable;
|
||||
use oxc_span::Span;
|
||||
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator};
|
||||
|
||||
use crate::{options::Assumptions, utils::CreateVars};
|
||||
|
||||
/// ES2020: Nullish Coalescing Operator
|
||||
///
|
||||
/// References:
|
||||
/// * <https://babeljs.io/docs/babel-plugin-transform-nullish-coalescing-operator>
|
||||
/// * <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-nullish-coalescing-operator>
|
||||
pub struct NullishCoalescingOperator<'a> {
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
symbols: Rc<RefCell<SymbolTable>>,
|
||||
assumptions: Assumptions,
|
||||
vars: Vec<'a, VariableDeclarator<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> CreateVars<'a> for NullishCoalescingOperator<'a> {
|
||||
fn ast(&self) -> &AstBuilder<'a> {
|
||||
&self.ast
|
||||
}
|
||||
|
||||
fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> {
|
||||
&mut self.vars
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> NullishCoalescingOperator<'a> {
|
||||
pub fn new(
|
||||
ast: Rc<AstBuilder<'a>>,
|
||||
symbols: Rc<RefCell<SymbolTable>>,
|
||||
assumptions: Assumptions,
|
||||
) -> Self {
|
||||
let vars = ast.new_vec();
|
||||
Self { ast, symbols, assumptions, vars }
|
||||
}
|
||||
|
||||
pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
|
||||
// left ?? right
|
||||
let Expression::LogicalExpression(logical_expr) = expr else { return };
|
||||
if logical_expr.operator != LogicalOperator::Coalesce {
|
||||
return;
|
||||
}
|
||||
|
||||
let span = Span::default();
|
||||
let reference;
|
||||
let assignment;
|
||||
|
||||
// skip creating extra reference when `left` is static
|
||||
if self.symbols.borrow().is_static(&logical_expr.left) {
|
||||
reference = self.ast.copy(&logical_expr.left);
|
||||
assignment = self.ast.copy(&logical_expr.left);
|
||||
} else {
|
||||
let name = self.create_new_var(&logical_expr.left);
|
||||
let ident = IdentifierReference::new(span, name);
|
||||
reference = self.ast.identifier_reference_expression(ident.clone());
|
||||
let left = AssignmentTarget::SimpleAssignmentTarget(
|
||||
self.ast.simple_assignment_target_identifier(ident),
|
||||
);
|
||||
let right = self.ast.copy(&logical_expr.left);
|
||||
assignment =
|
||||
self.ast.assignment_expression(span, AssignmentOperator::Assign, left, right);
|
||||
};
|
||||
|
||||
let test = if self.assumptions.no_document_all {
|
||||
let null = self.ast.literal_null_expression(NullLiteral::new(span));
|
||||
self.ast.binary_expression(span, assignment, BinaryOperator::Inequality, null)
|
||||
} else {
|
||||
let op = BinaryOperator::StrictInequality;
|
||||
let null = self.ast.literal_null_expression(NullLiteral::new(span));
|
||||
let left = self.ast.binary_expression(span, self.ast.copy(&assignment), op, null);
|
||||
|
||||
let right =
|
||||
self.ast.binary_expression(span, self.ast.copy(&reference), op, self.ast.void_0());
|
||||
|
||||
self.ast.logical_expression(span, left, LogicalOperator::And, right)
|
||||
};
|
||||
|
||||
let right = self.ast.move_expression(&mut logical_expr.right);
|
||||
|
||||
*expr = self.ast.conditional_expression(span, test, reference, right);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
mod es2015;
|
||||
mod es2016;
|
||||
mod es2019;
|
||||
mod es2020;
|
||||
mod es2021;
|
||||
mod es2022;
|
||||
mod options;
|
||||
|
|
@ -18,6 +19,7 @@ mod regexp;
|
|||
#[cfg(test)]
|
||||
mod tester;
|
||||
mod typescript;
|
||||
mod utils;
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
|
|
@ -28,12 +30,12 @@ use oxc_span::SourceType;
|
|||
|
||||
use crate::{
|
||||
es2015::ShorthandProperties, es2016::ExponentiationOperator, es2019::OptionalCatchBinding,
|
||||
es2021::LogicalAssignmentOperators, react_jsx::ReactJsx, regexp::RegexpFlags,
|
||||
typescript::TypeScript,
|
||||
es2020::NullishCoalescingOperator, es2021::LogicalAssignmentOperators, react_jsx::ReactJsx,
|
||||
regexp::RegexpFlags, typescript::TypeScript, utils::CreateVars,
|
||||
};
|
||||
|
||||
pub use crate::options::{
|
||||
TransformOptions, TransformReactOptions, TransformReactRuntime, TransformTarget,
|
||||
Assumptions, TransformOptions, TransformReactOptions, TransformReactRuntime, TransformTarget,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -47,6 +49,8 @@ pub struct Transformer<'a> {
|
|||
es2022_class_static_block: Option<es2022::ClassStaticBlock<'a>>,
|
||||
// es2021
|
||||
es2021_logical_assignment_operators: Option<LogicalAssignmentOperators<'a>>,
|
||||
// es2020
|
||||
es2020_nullish_coalescing_operators: Option<NullishCoalescingOperator<'a>>,
|
||||
// es2019
|
||||
es2019_optional_catch_binding: Option<OptionalCatchBinding<'a>>,
|
||||
// es2016
|
||||
|
|
@ -70,6 +74,7 @@ impl<'a> Transformer<'a> {
|
|||
regexp_flags: RegexpFlags::new(Rc::clone(&ast), options.target),
|
||||
es2022_class_static_block: (options.target < TransformTarget::ES2022).then(|| es2022::ClassStaticBlock::new(Rc::clone(&ast))),
|
||||
es2021_logical_assignment_operators: (options.target < TransformTarget::ES2021).then(|| LogicalAssignmentOperators::new(Rc::clone(&ast))),
|
||||
es2020_nullish_coalescing_operators: (options.target < TransformTarget::ES2020).then(|| NullishCoalescingOperator::new(Rc::clone(&ast), Rc::clone(symbols), options.assumptions)),
|
||||
es2019_optional_catch_binding: (options.target < TransformTarget::ES2019).then(|| OptionalCatchBinding::new(Rc::clone(&ast))),
|
||||
es2016_exponentiation_operator: (options.target < TransformTarget::ES2016).then(|| ExponentiationOperator::new(Rc::clone(&ast), Rc::clone(symbols))),
|
||||
es2015_shorthand_properties: (options.target < TransformTarget::ES2015).then(|| ShorthandProperties::new(Rc::clone(&ast))),
|
||||
|
|
@ -86,7 +91,8 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
|
|||
for stmt in stmts.iter_mut() {
|
||||
self.visit_statement(stmt);
|
||||
}
|
||||
self.es2016_exponentiation_operator.as_mut().map(|t| t.leave_statements(stmts));
|
||||
self.es2016_exponentiation_operator.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));
|
||||
}
|
||||
|
||||
fn visit_expression(&mut self, expr: &mut Expression<'a>) {
|
||||
|
|
@ -95,6 +101,7 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
|
|||
self.regexp_flags.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.es2016_exponentiation_operator.as_mut().map(|t| t.transform_expression(expr));
|
||||
|
||||
self.visit_expression_match(expr);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct TransformOptions {
|
||||
pub target: TransformTarget,
|
||||
pub react: Option<TransformReactOptions>,
|
||||
pub assumptions: Assumptions,
|
||||
}
|
||||
|
||||
/// See <https://www.typescriptlang.org/tsconfig#target>
|
||||
|
|
@ -12,6 +13,7 @@ pub enum TransformTarget {
|
|||
ES2016,
|
||||
ES2018,
|
||||
ES2019,
|
||||
ES2020,
|
||||
ES2021,
|
||||
ES2022,
|
||||
ES2024,
|
||||
|
|
@ -19,14 +21,24 @@ pub enum TransformTarget {
|
|||
ESNext,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct TransformReactOptions {
|
||||
_runtime: TransformReactRuntime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub enum TransformReactRuntime {
|
||||
#[default]
|
||||
Classic,
|
||||
Automatic,
|
||||
}
|
||||
|
||||
/// Compiler assumptions
|
||||
///
|
||||
/// See <https://babeljs.io/docs/assumptions>
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct Assumptions {
|
||||
/// When using operators that check for null or undefined, assume that they are never used with the special value document.all.
|
||||
/// See <https://babeljs.io/docs/assumptions#nodocumentall>.
|
||||
pub no_document_all: bool,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ impl Tester {
|
|||
let symbols = Rc::new(RefCell::new(symbols));
|
||||
|
||||
let program = self.allocator.alloc(program);
|
||||
Transformer::new(&self.allocator, self.source_type, &symbols, self.options.clone())
|
||||
.build(program);
|
||||
Transformer::new(&self.allocator, self.source_type, &symbols, self.options).build(program);
|
||||
Codegen::<false>::new(source_text.len(), CodegenOptions).build(program)
|
||||
}
|
||||
|
||||
|
|
|
|||
85
crates/oxc_transformer/src/utils.rs
Normal file
85
crates/oxc_transformer/src/utils.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
use std::mem;
|
||||
|
||||
use oxc_allocator::Vec;
|
||||
use oxc_ast::{ast::*, AstBuilder};
|
||||
use oxc_span::{Atom, Span};
|
||||
|
||||
// TODO:
|
||||
// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L543>
|
||||
pub fn generate_uid_based_on_node(expr: &Expression) -> Atom {
|
||||
let mut parts = std::vec::Vec::with_capacity(1);
|
||||
expr.gather(&mut |part| parts.push(part));
|
||||
let name = parts.join("$");
|
||||
Atom::from(format!("_{name}"))
|
||||
}
|
||||
|
||||
// TODO: <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L61>
|
||||
pub trait GatherNodeParts {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F);
|
||||
}
|
||||
|
||||
impl<'a> GatherNodeParts for Expression<'a> {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
match self {
|
||||
Self::Identifier(ident) => f(ident.name.clone()),
|
||||
Self::MemberExpression(expr) => expr.gather(f),
|
||||
_ => f(Atom::from("ref")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GatherNodeParts for MemberExpression<'a> {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
self.object().gather(f);
|
||||
match self {
|
||||
MemberExpression::ComputedMemberExpression(expr) => expr.expression.gather(f),
|
||||
MemberExpression::StaticMemberExpression(expr) => expr.property.gather(f),
|
||||
MemberExpression::PrivateFieldExpression(expr) => expr.field.gather(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GatherNodeParts for IdentifierName {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
f(self.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl GatherNodeParts for PrivateIdentifier {
|
||||
fn gather<F: FnMut(Atom)>(&self, f: &mut F) {
|
||||
f(self.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateVars<'a> {
|
||||
fn ast(&self) -> &AstBuilder<'a>;
|
||||
|
||||
fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>>;
|
||||
|
||||
fn add_vars_to_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) {
|
||||
if self.vars_mut().is_empty() {
|
||||
return;
|
||||
}
|
||||
let new_vec = self.ast().new_vec();
|
||||
let decls = mem::replace(self.vars_mut(), new_vec);
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let decl =
|
||||
self.ast().variable_declaration(Span::default(), kind, decls, Modifiers::empty());
|
||||
let stmt = Statement::Declaration(Declaration::VariableDeclaration(decl));
|
||||
stmts.insert(0, stmt);
|
||||
}
|
||||
|
||||
fn create_new_var(&mut self, expr: &Expression<'a>) -> Atom {
|
||||
let name = generate_uid_based_on_node(expr);
|
||||
// TODO: scope.push({ id: temp });
|
||||
|
||||
// Add `var name` to scope
|
||||
let binding_identifier = BindingIdentifier::new(Span::default(), name.clone());
|
||||
let binding_pattern_kind = self.ast().binding_pattern_identifier(binding_identifier);
|
||||
let binding = self.ast().binding_pattern(binding_pattern_kind, None, false);
|
||||
let kind = VariableDeclarationKind::Var;
|
||||
let decl = self.ast().variable_declarator(Span::default(), kind, binding, None, false);
|
||||
self.vars_mut().push(decl);
|
||||
name
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ use oxc::{
|
|||
parser::{Parser, ParserReturn},
|
||||
semantic::{SemanticBuilder, SemanticBuilderReturn},
|
||||
span::SourceType,
|
||||
transformer::{TransformOptions, TransformTarget, Transformer},
|
||||
transformer::{Assumptions, TransformOptions, TransformTarget, Transformer},
|
||||
};
|
||||
use oxc_linter::{LintContext, Linter};
|
||||
use oxc_query::{schema, Adapter, SCHEMA_TEXT};
|
||||
|
|
@ -215,7 +215,11 @@ impl Oxc {
|
|||
let semantic = SemanticBuilder::new(source_text, source_type).build(program).semantic;
|
||||
let (symbols, _scope_tree) = semantic.into_symbol_table_and_scope_tree();
|
||||
let symbols = Rc::new(RefCell::new(symbols));
|
||||
let options = TransformOptions { target: TransformTarget::ES2015, react: None };
|
||||
let options = TransformOptions {
|
||||
target: TransformTarget::ES2015,
|
||||
react: None,
|
||||
assumptions: Assumptions::default(),
|
||||
};
|
||||
Transformer::new(&allocator, source_type, &symbols, options).build(program);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Passed: 94/1091
|
||||
Passed: 95/1091
|
||||
|
||||
# babel-plugin-transform-unicode-sets-regex (0/4)
|
||||
* Failed: basic/basic/input.js
|
||||
|
|
@ -491,8 +491,11 @@ Passed: 94/1091
|
|||
* Failed: to-native-fields/static-shadow/input.js
|
||||
* Failed: to-native-fields/static-shadowed-binding/input.js
|
||||
|
||||
# babel-plugin-transform-logical-assignment-operators (3/6)
|
||||
# babel-plugin-transform-logical-assignment-operators (0/6)
|
||||
* Failed: logical-assignment/anonymous-functions-transform/input.js
|
||||
* Failed: logical-assignment/arrow-functions-transform/input.js
|
||||
* Failed: logical-assignment/general-semantics/input.js
|
||||
* Failed: logical-assignment/named-functions-transform/input.js
|
||||
* Failed: logical-assignment/null-coalescing/input.js
|
||||
* Failed: logical-assignment/null-coalescing-without-other/input.js
|
||||
|
||||
|
|
@ -532,19 +535,15 @@ Passed: 94/1091
|
|||
* Failed: export-namespace/namespace-string/input.mjs
|
||||
* Failed: export-namespace/namespace-typescript/input.mjs
|
||||
|
||||
# babel-plugin-transform-nullish-coalescing-operator (0/12)
|
||||
# babel-plugin-transform-nullish-coalescing-operator (4/12)
|
||||
* Failed: assumption-noDocumentAll/transform/input.js
|
||||
* Failed: assumption-noDocumentAll/transform-in-default-destructuring/input.js
|
||||
* Failed: assumption-noDocumentAll/transform-in-default-param/input.js
|
||||
* Failed: assumption-noDocumentAll/transform-in-function/input.js
|
||||
* Failed: assumption-noDocumentAll/transform-static-refs-in-default/input.js
|
||||
* Failed: assumption-noDocumentAll/transform-static-refs-in-function/input.js
|
||||
* Failed: nullish-coalescing/transform-in-default-destructuring/input.js
|
||||
* Failed: nullish-coalescing/transform-in-default-param/input.js
|
||||
* Failed: nullish-coalescing/transform-in-function/input.js
|
||||
* Failed: nullish-coalescing/transform-loose/input.js
|
||||
* Failed: nullish-coalescing/transform-static-refs-in-default/input.js
|
||||
* Failed: nullish-coalescing/transform-static-refs-in-function/input.js
|
||||
|
||||
# babel-plugin-transform-optional-chaining (1/46)
|
||||
* Failed: assumption-noDocumentAll/assignment/input.js
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ use oxc_parser::Parser;
|
|||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::{SourceType, VALID_EXTENSIONS};
|
||||
use oxc_tasks_common::{normalize_path, project_root};
|
||||
use oxc_transformer::{TransformOptions, TransformReactOptions, TransformTarget, Transformer};
|
||||
use oxc_transformer::{
|
||||
Assumptions, TransformOptions, TransformReactOptions, TransformTarget, Transformer,
|
||||
};
|
||||
|
||||
#[test]
|
||||
#[cfg(any(coverage, coverage_nightly))]
|
||||
|
|
@ -155,6 +157,7 @@ fn babel_test(input_path: &Path, options: &BabelOptions) -> bool {
|
|||
let transform_options = TransformOptions {
|
||||
target: TransformTarget::ES5,
|
||||
react: Some(TransformReactOptions::default()),
|
||||
assumptions: Assumptions::default(),
|
||||
};
|
||||
|
||||
let semantic =
|
||||
|
|
|
|||
Loading…
Reference in a new issue