diff --git a/Cargo.lock b/Cargo.lock index 6fe03135b1821..333fed4776edb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1752,8 +1752,6 @@ dependencies = [ "oxc_tasks_common", "oxc_transformer", "pico-args", - "serde", - "serde_json", "walkdir", ] @@ -1761,7 +1759,6 @@ dependencies = [ name = "oxc_transformer" version = "0.11.0" dependencies = [ - "bitflags 2.5.0", "oxc_allocator", "oxc_ast", "oxc_codegen", @@ -1769,10 +1766,6 @@ dependencies = [ "oxc_parser", "oxc_semantic", "oxc_span", - "oxc_syntax", - "phf", - "rustc-hash", - "serde", ] [[package]] diff --git a/crates/oxc_transformer/Cargo.toml b/crates/oxc_transformer/Cargo.toml index 9efc675efffce..4034b7fdcabfe 100644 --- a/crates/oxc_transformer/Cargo.toml +++ b/crates/oxc_transformer/Cargo.toml @@ -19,17 +19,11 @@ workspace = true doctest = false [dependencies] -bitflags = { workspace = true } oxc_ast = { workspace = true } oxc_span = { workspace = true } oxc_allocator = { workspace = true } -oxc_syntax = { workspace = true } oxc_semantic = { workspace = true } oxc_diagnostics = { workspace = true } -rustc-hash = { workspace = true } - -serde = { workspace = true, features = ["derive"] } -phf = { workspace = true, features = ["macros"] } [dev-dependencies] oxc_parser = { workspace = true } diff --git a/crates/oxc_transformer/examples/transformer.rs b/crates/oxc_transformer/examples/transformer.rs index d3531898e646d..5906ca7986754 100644 --- a/crates/oxc_transformer/examples/transformer.rs +++ b/crates/oxc_transformer/examples/transformer.rs @@ -8,10 +8,7 @@ use oxc_codegen::{Codegen, CodegenOptions}; use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; -use oxc_transformer::{ - ReactJsxOptions, ReactJsxRuntime, ReactJsxRuntimeOption, TransformOptions, TransformTarget, - Transformer, -}; +use oxc_transformer::{TransformOptions, Transformer}; // Instruction: // create a `test.js`, @@ -45,14 +42,7 @@ fn main() { .semantic; let program = allocator.alloc(ret.program); - let transform_options = TransformOptions { - target: TransformTarget::ES5, - react_jsx: Some(ReactJsxOptions { - runtime: Some(ReactJsxRuntimeOption::Valid(ReactJsxRuntime::Classic)), - ..ReactJsxOptions::default() - }), - ..TransformOptions::default() - }; + let transform_options = TransformOptions {}; Transformer::new(&allocator, source_type, semantic, transform_options).build(program).unwrap(); let printed = Codegen::::new("", &source_text, CodegenOptions::default()) diff --git a/crates/oxc_transformer/src/context.rs b/crates/oxc_transformer/src/context.rs deleted file mode 100644 index 074d0d2c23bd1..0000000000000 --- a/crates/oxc_transformer/src/context.rs +++ /dev/null @@ -1,66 +0,0 @@ -use std::{ - borrow::Cow, - cell::{Ref, RefCell, RefMut}, - mem, - rc::Rc, -}; - -use oxc_ast::AstBuilder; -use oxc_diagnostics::Error; -use oxc_semantic::{ScopeId, ScopeTree, Semantic, SymbolId, SymbolTable}; -use oxc_span::{CompactStr, SourceType}; - -use crate::TransformOptions; - -#[derive(Clone)] -pub struct TransformerCtx<'a> { - pub ast: Rc>, - pub options: Cow<'a, TransformOptions>, - semantic: Rc>>, - errors: Rc>>, -} - -impl<'a> TransformerCtx<'a> { - pub fn new(ast: Rc>, semantic: Semantic<'a>, options: TransformOptions) -> Self { - Self { - ast, - semantic: Rc::new(RefCell::new(semantic)), - options: Cow::Owned(options), - errors: Rc::new(RefCell::new(vec![])), - } - } - - pub fn semantic(&self) -> Ref<'_, Semantic<'a>> { - self.semantic.borrow() - } - - pub fn symbols(&self) -> Ref<'_, SymbolTable> { - Ref::map(self.semantic.borrow(), |semantic| semantic.symbols()) - } - - pub fn scopes(&self) -> Ref<'_, ScopeTree> { - Ref::map(self.semantic.borrow(), |semantic| semantic.scopes()) - } - - pub fn scopes_mut(&self) -> RefMut<'_, ScopeTree> { - RefMut::map(self.semantic.borrow_mut(), |semantic| semantic.scopes_mut()) - } - - pub fn add_binding(&self, name: CompactStr) { - // TODO: use the correct scope and symbol id - self.scopes_mut().add_binding(ScopeId::new(0), name, SymbolId::new(0)); - } - - pub fn source_type(&self) -> Ref<'_, SourceType> { - Ref::map(self.semantic.borrow(), |semantic| semantic.source_type()) - } - - pub fn errors(&self) -> Vec { - mem::take(&mut self.errors.borrow_mut()) - } - - /// Push a Transform Error - pub fn error>(&self, error: T) { - self.errors.borrow_mut().push(error.into()); - } -} diff --git a/crates/oxc_transformer/src/es2015/arrow_functions.rs b/crates/oxc_transformer/src/es2015/arrow_functions.rs deleted file mode 100644 index 173e1c43ee0df..0000000000000 --- a/crates/oxc_transformer/src/es2015/arrow_functions.rs +++ /dev/null @@ -1,142 +0,0 @@ -use std::rc::Rc; - -use oxc_allocator::Vec; -use oxc_ast::visit::walk_mut::walk_jsx_identifier_mut; -use oxc_ast::{ast::*, AstBuilder, AstType, VisitMut}; -use oxc_span::{Atom, SPAN}; -use serde::Deserialize; - -use crate::context::TransformerCtx; -use crate::TransformTarget; - -/// ES2015 Arrow Functions -/// -/// References: -/// * -/// * -pub struct ArrowFunctions<'a> { - ast: Rc>, - nodes: Vec<'a, AstType>, - 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: AstType) { - self.nodes.push(kind); - } - - fn leave_node(&mut self, _kind: AstType) { - self.nodes.pop(); - } - - fn visit_jsx_identifier(&mut self, ident: &mut JSXIdentifier<'a>) { - let parent_kind = self.nodes.last().unwrap(); - let parent_parent_kind = self.nodes[self.nodes.len() - 2]; - if ident.name == "this" - && (matches!(parent_kind, AstType::JSXElementName) - || matches!(parent_parent_kind, AstType::JSXMemberExpression)) - { - if !self.has_this { - self.has_this = true; - self.uid += 1; - } - *ident = self.ast.jsx_identifier(SPAN, self.get_this_name()); - } - - walk_jsx_identifier_mut(self, ident); - } -} - -impl<'a> ArrowFunctions<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2015 || ctx.options.arrow_functions.is_some()) - .then(|| { - let nodes = ctx.ast.new_vec(); - Self { ast: ctx.ast, uid: 0, nodes, has_this: false, insert: false } - }) - } - - fn get_this_name(&self) -> Atom<'a> { - let uid = if self.uid == 1 { String::new() } else { self.uid.to_string() }; - self.ast.new_atom(&format!("_this{uid}")) - } - - 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::ArrowFunctionExpression(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.r#async, - None, - 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); - } - } -} diff --git a/crates/oxc_transformer/src/es2015/duplicate_keys.rs b/crates/oxc_transformer/src/es2015/duplicate_keys.rs deleted file mode 100644 index 9a34f76baa121..0000000000000 --- a/crates/oxc_transformer/src/es2015/duplicate_keys.rs +++ /dev/null @@ -1,75 +0,0 @@ -#![allow(clippy::similar_names)] - -use std::{collections::HashSet, rc::Rc}; - -use oxc_ast::{ast::*, AstBuilder}; -use oxc_span::{CompactStr, SPAN}; - -use crate::{context::TransformerCtx, options::TransformTarget}; - -/// ES2015: Duplicate Keys -/// -/// References: -/// * -/// * -pub struct DuplicateKeys<'a> { - ast: Rc>, -} - -impl<'a> DuplicateKeys<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2015 || ctx.options.duplicate_keys) - .then_some(Self { ast: ctx.ast }) - } - - pub fn transform_object_expression<'b>(&mut self, obj_expr: &'b mut ObjectExpression<'a>) { - let mut visited_data: HashSet = HashSet::new(); - let mut visited_getters: HashSet = HashSet::new(); - let mut visited_setters: HashSet = HashSet::new(); - - for property in obj_expr.properties.iter_mut() { - let ObjectPropertyKind::ObjectProperty(obj_property) = property else { - continue; - }; - - if obj_property.computed { - continue; - } - - if let Some(name) = &obj_property.key.static_name() { - let mut is_duplicate = false; - - match obj_property.kind { - PropertyKind::Get => { - if visited_data.contains(name) || visited_getters.contains(name) { - is_duplicate = true; - } - visited_getters.insert(name.clone()); - } - PropertyKind::Set => { - if visited_data.contains(name) || visited_setters.contains(name) { - is_duplicate = true; - } - visited_setters.insert(name.clone()); - } - PropertyKind::Init => { - if visited_data.contains(name) - || visited_setters.contains(name) - || visited_getters.contains(name) - { - is_duplicate = true; - } - visited_data.insert(name.clone()); - } - } - - if is_duplicate { - obj_property.computed = true; - let string_literal = StringLiteral::new(SPAN, self.ast.new_atom(name)); - let expr = self.ast.literal_string_expression(string_literal); - obj_property.key = PropertyKey::Expression(expr); - } - } - } - } -} diff --git a/crates/oxc_transformer/src/es2015/function_name.rs b/crates/oxc_transformer/src/es2015/function_name.rs deleted file mode 100644 index 3288b84d4602b..0000000000000 --- a/crates/oxc_transformer/src/es2015/function_name.rs +++ /dev/null @@ -1,166 +0,0 @@ -// use lazy_static::lazy_static; -use oxc_ast::ast::*; -use oxc_semantic::ScopeId; -use oxc_span::{Atom, Span}; -use oxc_syntax::identifier::is_identifier_part; -use oxc_syntax::operator::AssignmentOperator; -// use regex::Regex; - -use crate::context::TransformerCtx; -use crate::utils::is_valid_identifier; -use crate::TransformTarget; - -/// ES2015: Function Name -/// -/// References: -/// * -/// * -pub struct FunctionName<'a> { - ctx: TransformerCtx<'a>, - unicode_escapes: bool, -} - -impl<'a> FunctionName<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2015 || ctx.options.function_name).then_some( - Self { - ctx, - // TODO hook up the plugin - unicode_escapes: true, - }, - ) - } - - pub fn transform_assignment_expression(&mut self, expr: &mut AssignmentExpression<'a>) { - if expr.right.is_function() && matches!(expr.operator, AssignmentOperator::Assign) { - if let AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(target), - ) = &expr.left - { - if let Some(id) = - self.create_valid_identifier(target.span, &target.name, self.unicode_escapes) - { - let scope_id = self.ctx.symbols().get_scope_id_from_span(&target.span); - - self.transform_expression(&mut expr.right, id, scope_id); - } - } - } - } - - pub fn transform_object_expression(&mut self, expr: &mut ObjectExpression<'a>) { - for property_kind in expr.properties.iter_mut() { - if let ObjectPropertyKind::ObjectProperty(property) = property_kind { - if property.computed || !property.value.is_function() { - continue; - } - - let (id, scope_id) = match &property.key { - PropertyKey::Identifier(ident) => ( - self.create_valid_identifier(ident.span, &ident.name, self.unicode_escapes), - self.ctx.symbols().get_scope_id_from_span(&ident.span), - ), - PropertyKey::PrivateIdentifier(ident) => ( - self.create_valid_identifier(ident.span, &ident.name, self.unicode_escapes), - self.ctx.symbols().get_scope_id_from_span(&ident.span), - ), - PropertyKey::Expression(_) => continue, - }; - - if let Some(id) = id { - self.transform_expression(&mut property.value, id, scope_id); - } - } - } - } - - pub fn transform_variable_declarator(&mut self, decl: &mut VariableDeclarator<'a>) { - let Some(init) = &mut decl.init else { return }; - - if let BindingPatternKind::BindingIdentifier(ident) = &decl.id.kind { - // Create a new ID instead of cloning to avoid local binding/refs - if let Some(id) = - self.create_valid_identifier(ident.span, &ident.name, self.unicode_escapes) - { - let scope_id = match ident.symbol_id.get() { - Some(symbol_id) => Some(self.ctx.symbols().get_scope_id(symbol_id)), - None => self.ctx.symbols().get_scope_id_from_span(&ident.span), - }; - - self.transform_expression(init, id, scope_id); - } - }; - } - - // Internal only - fn transform_expression( - &mut self, - expr: &mut Expression<'a>, - mut id: BindingIdentifier<'a>, - scope_id: Option, - ) { - // function () {} -> function name() {} - if let Expression::FunctionExpression(func) = expr { - let mut count = 0; - - // Check for nested params/vars of the same name - if let Some(scope_id) = scope_id { - let scopes = self.ctx.scopes(); - - for scope in scopes.descendants(scope_id) { - for binding in scopes.get_bindings(scope) { - if binding.0 == &id.name { - count += 1; - } - } - } - } - - // If we're shadowing, change the name - if count > 0 { - id.name = self.ctx.ast.new_atom(&format!("{}{}", id.name, count)); - } - - if func.id.is_none() { - func.id = Some(id); - } - } - } - - // https://github.com/babel/babel/blob/main/packages/babel-helper-function-name/src/index.ts - // https://github.com/babel/babel/blob/main/packages/babel-types/src/converters/toBindingIdentifierName.ts#L3 - // https://github.com/babel/babel/blob/main/packages/babel-types/src/converters/toIdentifier.ts#L4 - #[allow(clippy::unnecessary_wraps)] - fn create_valid_identifier( - &self, - span: Span, - atom: &Atom<'a>, - _unicode_escapes: bool, - ) -> Option> { - // NOTE: this regex fails to compile on Rust - // lazy_static! { - // static ref UNICODE_NAME: Regex = Regex::new(r"(?u)[\u{D800}-\u{DFFF}]").unwrap(); - // } - - // if !unicode_escapes && UNICODE_NAME.is_match(atom.as_str()) { - // return None; - // } - - let id = - atom.chars().map(|c| if is_identifier_part(c) { c } else { '_' }).collect::(); - - let id = if id.is_empty() { - self.ctx.ast.new_atom("_") - } else if id == "eval" - || id == "arguments" - || id == "null" - || !is_valid_identifier(&id, true) - { - self.ctx.ast.new_atom(&format!("_{id}")) - } else { - atom.clone() - }; - - Some(BindingIdentifier::new(span, id)) - } -} diff --git a/crates/oxc_transformer/src/es2015/instanceof.rs b/crates/oxc_transformer/src/es2015/instanceof.rs deleted file mode 100644 index 64cc08985d284..0000000000000 --- a/crates/oxc_transformer/src/es2015/instanceof.rs +++ /dev/null @@ -1,70 +0,0 @@ -use oxc_ast::{ast::*, AstKind}; -use oxc_semantic::AstNodeId; -use oxc_span::SPAN; - -use oxc_syntax::operator::BinaryOperator; - -use crate::context::TransformerCtx; -use crate::TransformTarget; - -/// ES2015: instanceof -/// -/// References: -/// * -/// * -pub struct Instanceof<'a> { - ctx: TransformerCtx<'a>, -} - -impl<'a> Instanceof<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2015 || ctx.options.instanceof) - .then_some(Self { ctx }) - } - - pub fn transform_expression(&mut self, expr: &mut Expression<'a>) { - // if instanceof syntax is under a helper, it should not be transformed - if let Expression::BinaryExpression(be) = expr { - if let BinaryExpression { operator: BinaryOperator::Instanceof, left, right, .. } = - &**be - { - let is_under_helper = self - .ctx - .semantic() - .nodes() - // FIXME: how to get the AstNodeId of the expression? - .iter_parents(AstNodeId::new(1)) // expr.get_id or sth - .any(|parent| { - if let AstKind::CallExpression(CallExpression { callee, .. }) = - parent.kind() - { - return callee.is_specific_member_access("babelHelpers", "instanceof"); - }; - false - }); - - if is_under_helper { - return; - } - - let object = self.ctx.ast.identifier_reference_expression( - IdentifierReference::new(SPAN, "babelHelpers".into()), - ); - - let property = IdentifierName::new(SPAN, "instanceof".into()); - let helper = - self.ctx.ast.member_expression(MemberExpression::StaticMemberExpression( - StaticMemberExpression { span: SPAN, object, property, optional: false }, - )); - - let left = self.ctx.ast.copy(left); - let right = self.ctx.ast.copy(right); - let mut args = self.ctx.ast.new_vec_with_capacity(2); - args.push(Argument::Expression(left)); - args.push(Argument::Expression(right)); - - *expr = self.ctx.ast.call_expression(SPAN, helper, args, false, None); - } - } - } -} diff --git a/crates/oxc_transformer/src/es2015/literals.rs b/crates/oxc_transformer/src/es2015/literals.rs deleted file mode 100644 index 06bc9f768183a..0000000000000 --- a/crates/oxc_transformer/src/es2015/literals.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::rc::Rc; - -use oxc_ast::{ast::*, AstBuilder}; - -use crate::{context::TransformerCtx, options::TransformTarget}; - -/// ES2015: Literals -/// -/// References: -/// * -/// * -pub struct Literals<'a> { - _ast: Rc>, -} - -impl<'a> Literals<'a> { - #![allow(clippy::unused_self)] - - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2015 || ctx.options.literals) - .then_some(Self { _ast: ctx.ast }) - } - - pub fn transform_number_literal(&mut self, lit: &mut NumericLiteral<'a>) { - // early return if number's raw value is empty or shorter than 2 characters, - // both `0bxxx` and `0oxxx` need at least 3 characters be defined. - if lit.raw.len() <= 2 { - return; - } - - if let [b'0', b'b' | b'B' | b'o' | b'O'] = lit.raw[0..2].as_bytes() { - // Set binary and octal raw values to empty, It would force the codegen, - // to generate them from their value. - lit.raw = ""; - } - } - - pub fn transform_string_literal(&mut self, _: &mut StringLiteral<'a>) { - // TODO: As of today oxc_lexer takes care of this, We have to rework it - // so it can be controlled via the transformer. - } -} diff --git a/crates/oxc_transformer/src/es2015/mod.rs b/crates/oxc_transformer/src/es2015/mod.rs deleted file mode 100644 index 94618af441cb5..0000000000000 --- a/crates/oxc_transformer/src/es2015/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -mod arrow_functions; -mod duplicate_keys; -mod function_name; -mod instanceof; -mod literals; -mod new_target; -mod shorthand_properties; -mod template_literals; - -pub use arrow_functions::{ArrowFunctions, ArrowFunctionsOptions}; -pub use duplicate_keys::DuplicateKeys; -pub use function_name::FunctionName; -pub use instanceof::Instanceof; -pub use literals::Literals; -pub use new_target::NewTarget; -pub use shorthand_properties::ShorthandProperties; -pub use template_literals::TemplateLiterals; diff --git a/crates/oxc_transformer/src/es2015/new_target.rs b/crates/oxc_transformer/src/es2015/new_target.rs deleted file mode 100644 index 4d8d21125304e..0000000000000 --- a/crates/oxc_transformer/src/es2015/new_target.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::{context::TransformerCtx, TransformTarget}; -use oxc_allocator::Vec; -use oxc_ast::ast::*; -use oxc_diagnostics::miette; -use oxc_span::{Atom, Span, SPAN}; -use oxc_syntax::operator::BinaryOperator; - -/// ES2015: New Target -/// -/// References: -/// * -/// * -pub struct NewTarget<'a> { - ctx: TransformerCtx<'a>, - kinds: Vec<'a, NewTargetKind<'a>>, -} - -#[derive(Debug)] -enum NewTargetKind<'a> { - Method, - Constructor, - Function(Option>), -} - -impl<'a> NewTarget<'a> { - pub(crate) fn enter_method_definition(&mut self, def: &MethodDefinition<'a>) { - let kind = match def.kind { - MethodDefinitionKind::Get - | MethodDefinitionKind::Set - | MethodDefinitionKind::Method => NewTargetKind::Method, - MethodDefinitionKind::Constructor => NewTargetKind::Constructor, - }; - self.push(kind); - } - - pub(crate) fn leave_method_definition(&mut self, _: &MethodDefinition) { - self.pop(); - } - - pub(crate) fn enter_object_property(&mut self, prop: &ObjectProperty<'a>) { - if prop.method { - self.push(NewTargetKind::Method); - } - } - - pub(crate) fn leave_object_property(&mut self, prop: &ObjectProperty<'a>) { - if prop.method { - self.pop(); - } - } - - pub(crate) fn enter_function(&mut self, func: &Function<'a>) { - if let Some(kind) = self.function_new_target_kind(func) { - self.push(kind); - } - } - - pub(crate) fn leave_function(&mut self, func: &Function<'a>) { - if self.function_new_target_kind(func).is_some() { - self.pop(); - } - } - - fn function_new_target_kind(&self, func: &Function<'a>) -> Option> { - // oxc visitor `MethodDefinitionKind` will enter `Function` node, here need to exclude it - if let Some(kind) = self.kinds.last() { - if !matches!(kind, NewTargetKind::Function(_)) { - return None; - } - } - func.id.as_ref().map(|id| NewTargetKind::Function(Some(id.name.clone()))) - } -} - -impl<'a> NewTarget<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - let kinds = ctx.ast.new_vec(); - (ctx.options.target < TransformTarget::ES2015 || ctx.options.new_target) - .then_some(Self { ctx, kinds }) - } - - fn push(&mut self, kind: NewTargetKind<'a>) { - self.kinds.push(kind); - } - - fn pop(&mut self) { - self.kinds.pop(); - } - - fn create_constructor_expr(&self, span: Span) -> Expression<'a> { - self.ctx.ast.static_member_expression( - span, - self.ctx.ast.this_expression(span), - IdentifierName { span, name: "constructor".into() }, - false, - ) - } - - pub fn transform_expression<'b>(&mut self, expr: &'b mut Expression<'a>) { - if let Expression::MetaProperty(meta) = expr { - if meta.meta.name == "new" && meta.property.name == "target" { - if let Some(kind) = self.kinds.last() { - match kind { - NewTargetKind::Constructor => { - *expr = self.create_constructor_expr(meta.span); - } - NewTargetKind::Method => { - *expr = self.ctx.ast.void_0(); - } - NewTargetKind::Function(name) => { - // TODO packages/babel-helper-create-class-features-plugin/src/fields.ts#L192 unshadow - // It will mutate previous ast node, it is difficult at now. - let id = name.clone().unwrap_or_else(|| { - self.ctx - .ast - .new_atom(self.ctx.scopes().generate_uid("target").as_str()) - }); - let test = self.ctx.ast.binary_expression( - SPAN, - self.ctx.ast.this_expression(SPAN), - BinaryOperator::Instanceof, - self.ctx.ast.identifier_reference_expression( - IdentifierReference::new(SPAN, id), - ), - ); - let consequent = self.ctx.ast.static_member_expression( - SPAN, - self.ctx.ast.this_expression(SPAN), - IdentifierName { span: SPAN, name: "constructor".into() }, - false, - ); - *expr = self.ctx.ast.conditional_expression( - meta.span, - test, - consequent, - self.ctx.ast.void_0(), - ); - } - } - } else { - self.ctx.error(miette::Error::msg( - "new.target must be under a (non-arrow) function or a class.", - )); - } - } - } - } -} diff --git a/crates/oxc_transformer/src/es2015/shorthand_properties.rs b/crates/oxc_transformer/src/es2015/shorthand_properties.rs deleted file mode 100644 index 0151042e0da7d..0000000000000 --- a/crates/oxc_transformer/src/es2015/shorthand_properties.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::rc::Rc; - -use oxc_ast::{ast::*, AstBuilder}; -use oxc_span::GetSpan; - -use crate::{context::TransformerCtx, options::TransformTarget}; - -/// ES2015: Shorthand Properties -/// -/// References: -/// * -/// * -pub struct ShorthandProperties<'a> { - ast: Rc>, -} - -impl<'a> ShorthandProperties<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2015 || ctx.options.shorthand_properties) - .then_some(Self { ast: ctx.ast }) - } - - pub fn transform_object_property<'b>(&mut self, obj_prop: &'b mut ObjectProperty<'a>) { - if !obj_prop.shorthand && !obj_prop.method { - return; - } - - obj_prop.shorthand = false; - obj_prop.method = false; - - if obj_prop.computed { - // all computed key can never be transformed to `__proto__` setter unexpectedly - return; - } - - // We should handle the edge case of `__proto__` property. - // All shorthand properties with key `__proto__` can never be `__proto__` setter. - // But the transformed result can be `__proto__` setter unexpectedly. - // It's easy to fix it by using computed property. - - let is_proto_string = obj_prop.key.is_specific_string_literal("__proto__"); - - if !is_proto_string && !obj_prop.key.is_specific_id("__proto__") { - return; - } - // We reach here, it means that the key is `__proto__` or `"__proto__"`. - - // Transform `__proto__`/`"__proto__"` to computed property. - obj_prop.computed = true; - - if is_proto_string { - // After the transformation, the string literal `"__proto__"` is already expected result. - // - // input: - // "__proto__"() {} - // output: - // ["__proto__"]: function() {} - return; - } - - // Convert `[__proto__]` to `["__proto__"]` - - let proto = StringLiteral { span: obj_prop.key.span(), value: "__proto__".into() }; - let expr = self.ast.literal_string_expression(proto); - obj_prop.key = PropertyKey::Expression(expr); - } -} diff --git a/crates/oxc_transformer/src/es2015/template_literals.rs b/crates/oxc_transformer/src/es2015/template_literals.rs deleted file mode 100644 index 336410d3946bb..0000000000000 --- a/crates/oxc_transformer/src/es2015/template_literals.rs +++ /dev/null @@ -1,118 +0,0 @@ -use oxc_allocator::Vec; -use oxc_ast::{ast::*, AstBuilder}; -use oxc_span::{Atom, Span, SPAN}; -use std::{mem, rc::Rc}; - -use crate::{context::TransformerCtx, TransformTarget}; - -/// ES2015: Template Literals -/// -/// References: -/// * -/// * -pub struct TemplateLiterals<'a> { - ast: Rc>, -} - -impl<'a> TemplateLiterals<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2015 || ctx.options.template_literals) - .then_some(Self { ast: ctx.ast }) - } - - pub fn transform_expression<'b>(&mut self, expr: &'b mut Expression<'a>) { - #[allow(clippy::single_match)] - match expr { - Expression::TemplateLiteral(template_literal) => { - let quasis = mem::replace(&mut template_literal.quasis, self.ast.new_vec()); - let mut nodes = self.ast.new_vec_with_capacity(quasis.len()); - let mut expr_iter = template_literal.expressions.iter_mut(); - for quasi in quasis { - if let Some(cooked) = &quasi.value.cooked { - if cooked.as_str() != "" { - let string_literal = StringLiteral::new(SPAN, cooked.clone()); - let string_literal = self.ast.literal_string_expression(string_literal); - nodes.push(string_literal); - } - } - - if let Some(expr) = expr_iter.next() { - let expr = self.ast.move_expression(expr); - if is_non_empty_string_literal(&expr) { - nodes.push(expr); - } - } - } - - // make sure the first node is a string - if !matches!(nodes.first(), Some(Expression::StringLiteral(_))) { - let literal = StringLiteral::new(SPAN, Atom::from("")); - let string_literal = self.ast.literal_string_expression(literal); - nodes.insert(0, string_literal); - } - - if let Some(call_expr) = build_concat_call_expr(nodes, &Rc::clone(&self.ast)) { - *expr = call_expr; - } - } - // TODO: Expression::TaggedTemplateExpression - _ => {} - } - } -} - -/// This function groups the objects into multiple calls to `.concat()` in -/// order to preserve execution order of the primitive conversion -/// -/// ```javascript -/// "".concat(obj.foo, "foo", obj2.foo, "foo2") -/// ``` -/// The above code would evaluate both member expressions _first_ then, `concat` will -/// convert each one to a primitive, whereas -/// -/// ```javascript -/// "".concat(obj.foo, "foo").concat(obj2.foo, "foo2") -/// ``` -/// would evaluate the member, then convert it to a primitive, then evaluate -/// the second member and convert that one, which reflects the spec behavior -/// of template literals. -fn build_concat_call_expr<'a>( - nodes: Vec>, - ast: &Rc>, -) -> Option> { - // `1${"2"}${"3"}${a}${b}${"4"}${"5"}${c}` -> 1".concat("2", "3", a).concat(b, "4", "5").concat(c) - let mut avail = false; - nodes.into_iter().reduce(|mut left, right| { - let mut can_be_inserted = matches!(right, Expression::StringLiteral(_)); - - // for spec compatibility, we shouldn't keep two or more non-string node in one concat call. - // but we want group multiple node in one concat call as much as possible - // only the first encounter of non-string node can be inserted directly in the previous concat call - // other concat call will contains non-string node already. - if !can_be_inserted && avail { - can_be_inserted = true; - avail = false; - } - if can_be_inserted { - if let Expression::CallExpression(call_expr) = &mut left { - let argument = Argument::Expression(right); - call_expr.arguments.push(argument); - return left; - } - } - - let property = IdentifierName::new(Span::default(), "concat".into()); - let member_expr = ast.static_member_expression(Span::default(), left, property, false); - let arguments = ast.new_vec_single(Argument::Expression(right)); - let call_expr = ast.call_expression(Span::default(), member_expr, arguments, false, None); - call_expr - }) -} - -fn is_non_empty_string_literal(expr: &Expression) -> bool { - if let Expression::StringLiteral(string_literal) = expr { - return string_literal.value.as_str() != ""; - } - - true -} diff --git a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs deleted file mode 100644 index 64f859dbcdaea..0000000000000 --- a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs +++ /dev/null @@ -1,243 +0,0 @@ -use oxc_allocator::Vec; -use oxc_ast::ast::*; -use oxc_span::{Atom, Span}; -use oxc_syntax::operator::{AssignmentOperator, BinaryOperator}; - -use crate::{context::TransformerCtx, options::TransformTarget, utils::CreateVars}; - -/// ES2016: Exponentiation Operator -/// -/// References: -/// * -/// * -/// * -pub struct ExponentiationOperator<'a> { - ctx: TransformerCtx<'a>, - vars: Vec<'a, VariableDeclarator<'a>>, -} - -struct Exploded<'a> { - reference: AssignmentTarget<'a>, - uid: Expression<'a>, -} - -impl<'a> CreateVars<'a> for ExponentiationOperator<'a> { - fn ctx(&self) -> &TransformerCtx<'a> { - &self.ctx - } - - fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> { - &mut self.vars - } -} - -impl<'a> ExponentiationOperator<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2016 || ctx.options.exponentiation_operator).then( - || { - let vars = ctx.ast.new_vec(); - Self { ctx, vars } - }, - ) - } - - pub fn transform_expression(&mut self, expr: &mut Expression<'a>) { - // left ** right - if let Expression::BinaryExpression(binary_expr) = expr { - if binary_expr.operator == BinaryOperator::Exponential { - let left = self.ctx.ast.move_expression(&mut binary_expr.left); - let right = self.ctx.ast.move_expression(&mut binary_expr.right); - *expr = self.math_pow(left, right); - } - } - - // left **= right - if let Expression::AssignmentExpression(assign_expr) = expr { - if assign_expr.operator == AssignmentOperator::Exponential { - let mut nodes = self.ctx.ast.new_vec(); - let left = self.ctx.ast.move_assignment_target(&mut assign_expr.left); - let Some(Exploded { reference, uid }) = self.explode(left, &mut nodes) else { - return; - }; - let right = self.ctx.ast.move_expression(&mut assign_expr.right); - let right = self.math_pow(uid, right); - let assign_expr = self.ctx.ast.assignment_expression( - Span::default(), - AssignmentOperator::Assign, - reference, - right, - ); - nodes.push(assign_expr); - *expr = self.ctx.ast.sequence_expression(Span::default(), nodes); - } - } - } - - /// `left ** right` -> `Math.pow(left, right)` - fn math_pow(&mut self, left: Expression<'a>, right: Expression<'a>) -> Expression<'a> { - let ident_math = IdentifierReference::new(Span::default(), Atom::from("Math")); - let object = self.ctx.ast.identifier_reference_expression(ident_math); - let property = IdentifierName::new(Span::default(), Atom::from("pow")); - let callee = - self.ctx.ast.static_member_expression(Span::default(), object, property, false); - let mut arguments = self.ctx.ast.new_vec_with_capacity(2); - arguments.push(Argument::Expression(left)); - arguments.push(Argument::Expression(right)); - self.ctx.ast.call_expression(Span::default(), callee, arguments, false, None) - } - - /// 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: AssignmentTarget<'a>, - nodes: &mut Vec<'a, Expression<'a>>, - ) -> Option> { - let node = match node { - AssignmentTarget::SimpleAssignmentTarget(target) => target, - AssignmentTarget::AssignmentTargetPattern(_) => { - // Invalid Syntax - return None; - } - }; - let obj = self.get_obj_ref(self.ctx.ast.copy(&node), nodes)?; - let (reference, uid) = match node { - SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => { - let reference = AssignmentTarget::SimpleAssignmentTarget( - SimpleAssignmentTarget::AssignmentTargetIdentifier(ident), - ); - (reference, obj) - } - SimpleAssignmentTarget::MemberAssignmentTarget(member_expr) => { - let computed = member_expr.is_computed(); - let prop = self.get_prop_ref(member_expr.unbox(), nodes)?; - let optional = false; - let obj_clone = self.ctx.ast.copy(&obj); - let span = Span::default(); - let (reference, uid) = match &prop { - Expression::Identifier(ident) if !computed => { - let ident = IdentifierName::new(span, ident.name.clone()); - ( - self.ctx.ast.static_member(span, obj_clone, ident.clone(), optional), - self.ctx.ast.static_member_expression(span, obj, ident, optional), - ) - } - _ => { - let prop_clone = self.ctx.ast.copy(&prop); - ( - self.ctx.ast.computed_member(span, obj_clone, prop_clone, optional), - self.ctx.ast.computed_member_expression(span, obj, prop, optional), - ) - } - }; - (self.ctx.ast.simple_assignment_target_member_expression(reference), 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: SimpleAssignmentTarget<'a>, - nodes: &mut Vec<'a, Expression<'a>>, - ) -> Option> { - let reference = match node { - SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => { - if ident - .reference_id - .get() - .is_some_and(|reference_id| self.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(self.ctx.ast.identifier_reference_expression(ident.unbox())); - } - // could possibly trigger a getter so we need to only evaluate it once - self.ctx.ast.identifier_reference_expression(ident.unbox()) - } - SimpleAssignmentTarget::MemberAssignmentTarget(member_expr) => { - let expr = match member_expr.unbox() { - MemberExpression::ComputedMemberExpression(e) => e.object, - MemberExpression::StaticMemberExpression(e) => e.object, - MemberExpression::PrivateFieldExpression(e) => e.object, - }; - // 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| self.ctx.symbols().has_binding(reference_id))) - { - return Some(expr); - } - expr - } - _ => return None, - }; - Some(self.add_new_reference(reference, nodes)) - } - - /// Make sure side-effects of evaluating `ref` of `obj.ref` and `obj[ref]` only happen once. - fn get_prop_ref( - &mut self, - node: MemberExpression<'a>, - nodes: &mut Vec<'a, Expression<'a>>, - ) -> Option> { - let prop = match node { - MemberExpression::ComputedMemberExpression(expr) => { - let expr = expr.expression; - if expr.is_literal() { - return Some(expr); - } - expr - } - MemberExpression::StaticMemberExpression(expr) => { - let ident = expr.property; - let string_literal = StringLiteral::new(Span::default(), ident.name); - return Some(self.ctx.ast.literal_string_expression(string_literal)); - } - 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(prop, nodes)) - } - - fn add_new_reference( - &mut self, - expr: Expression<'a>, - nodes: &mut Vec<'a, Expression<'a>>, - ) -> Expression<'a> { - let ident = self.create_new_var_with_expression(&expr); - // Add new reference `_name = name` to nodes - let target = self.ctx.ast.simple_assignment_target_identifier(ident.clone()); - let op = AssignmentOperator::Assign; - nodes.push(self.ctx.ast.assignment_expression(Span::default(), op, target, expr)); - self.ctx.ast.identifier_reference_expression(ident) - } -} - -#[test] -fn test() { - use crate::{ - options::{TransformOptions, TransformTarget}, - tester::Tester, - }; - - let options = - TransformOptions { target: TransformTarget::ES2015, ..TransformOptions::default() }; - - let tests = &[( - "let x = {}; let y = 0; let z = 0; x[z++] **= y;", - "var _z; let x = {}; let y = 0; let z = 0; _z = z++,x[_z] = Math.pow(x[_z], y);", - )]; - - Tester::new("test.js", options).test(tests); -} diff --git a/crates/oxc_transformer/src/es2016/mod.rs b/crates/oxc_transformer/src/es2016/mod.rs deleted file mode 100644 index f2ea3e064d0b8..0000000000000 --- a/crates/oxc_transformer/src/es2016/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod exponentiation_operator; - -pub use exponentiation_operator::ExponentiationOperator; diff --git a/crates/oxc_transformer/src/es2019/json_strings.rs b/crates/oxc_transformer/src/es2019/json_strings.rs deleted file mode 100644 index dc9c4c7d4df37..0000000000000 --- a/crates/oxc_transformer/src/es2019/json_strings.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::rc::Rc; - -use oxc_ast::{ast::*, AstBuilder}; -use oxc_span::Atom; -use oxc_syntax::identifier::{LS, PS}; - -use crate::{context::TransformerCtx, options::TransformTarget}; - -/// ES2019: Json Strings -/// -/// References: -/// * -/// * -pub struct JsonStrings<'a> { - ast: Rc>, -} - -impl<'a> JsonStrings<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2019 || ctx.options.json_strings) - .then_some(Self { ast: ctx.ast }) - } - - // Allow `U+2028` and `U+2029` in string literals - // - // - fn normalize_str(&self, str: &str) -> Option> { - if !str.contains(LS) && !str.contains(PS) { - return None; - } - let mut buf = String::new(); - let mut is_escaped = false; - for c in str.chars() { - match (is_escaped, c) { - (false, LS) => buf.push_str("\\u2028"), - (false, PS) => buf.push_str("\\u2029"), - _ => buf.push(c), - } - is_escaped = !is_escaped && matches!(c, '\\'); - } - Some(self.ast.new_atom(&buf)) - } - - #[allow(clippy::unused_self)] - // TODO oxc_codegen currently prints json strings correctly, - // but we need a way to turn off this behaviour from codegen - // and do the transformation here. - pub fn transform_string_literal(&mut self, _literal: &mut StringLiteral) { - // let str = &self.ctx.semantic().source_text()[literal.span.start as usize + 1..literal.span.end as usize - 1]; - // if let Some(value) = Self::normalize_str(str) { - // literal.value = value; - // } - } - - #[allow(clippy::unused_self)] - pub fn transform_directive(&mut self, directive: &mut Directive<'a>) { - if let Some(value) = self.normalize_str(directive.directive.as_str()) { - directive.directive = value; - } - } -} diff --git a/crates/oxc_transformer/src/es2019/mod.rs b/crates/oxc_transformer/src/es2019/mod.rs deleted file mode 100644 index 4330b086186d6..0000000000000 --- a/crates/oxc_transformer/src/es2019/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod json_strings; -mod optional_catch_binding; -pub use json_strings::JsonStrings; -pub use optional_catch_binding::OptionalCatchBinding; diff --git a/crates/oxc_transformer/src/es2019/optional_catch_binding.rs b/crates/oxc_transformer/src/es2019/optional_catch_binding.rs deleted file mode 100644 index bdd67d75a177c..0000000000000 --- a/crates/oxc_transformer/src/es2019/optional_catch_binding.rs +++ /dev/null @@ -1,43 +0,0 @@ -use oxc_allocator::Vec; -use oxc_ast::ast::*; -use oxc_span::SPAN; - -use crate::{context::TransformerCtx, options::TransformTarget, utils::CreateVars}; - -/// ES2019: Optional Catch Binding -/// -/// References: -/// * -/// * -pub struct OptionalCatchBinding<'a> { - ctx: TransformerCtx<'a>, - vars: Vec<'a, VariableDeclarator<'a>>, -} - -impl<'a> CreateVars<'a> for OptionalCatchBinding<'a> { - fn ctx(&self) -> &TransformerCtx<'a> { - &self.ctx - } - - fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> { - &mut self.vars - } -} - -impl<'a> OptionalCatchBinding<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2019 || ctx.options.optional_catch_binding) - .then_some(Self { vars: ctx.ast.new_vec(), ctx }) - } - - pub fn transform_catch_clause<'b>(&mut self, clause: &'b mut CatchClause<'a>) { - if clause.param.is_some() { - return; - } - let unused = self.create_new_named_var("unused"); - let binding_identifier = BindingIdentifier::new(SPAN, unused.name); - let binding_pattern_kind = self.ctx.ast.binding_pattern_identifier(binding_identifier); - let binding_pattern = self.ctx.ast.binding_pattern(binding_pattern_kind, None, false); - clause.param = Some(binding_pattern); - } -} diff --git a/crates/oxc_transformer/src/es2020/mod.rs b/crates/oxc_transformer/src/es2020/mod.rs deleted file mode 100644 index f1442912c631b..0000000000000 --- a/crates/oxc_transformer/src/es2020/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod nullish_coalescing_operator; - -pub use nullish_coalescing_operator::{ - NullishCoalescingOperator, NullishCoalescingOperatorOptions, -}; diff --git a/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs b/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs deleted file mode 100644 index 2d3dd1a2c2a57..0000000000000 --- a/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs +++ /dev/null @@ -1,97 +0,0 @@ -use serde::Deserialize; - -use oxc_allocator::Vec; -use oxc_ast::ast::*; -use oxc_span::SPAN; -use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator}; - -use crate::{context::TransformerCtx, options::TransformTarget, utils::CreateVars}; - -#[derive(Debug, Default, Clone, Copy, Deserialize)] -pub struct NullishCoalescingOperatorOptions { - /// When true, this transform will pretend `document.all` does not exist, - /// and perform loose equality checks with null instead of strict equality checks against both null and undefined. - #[serde(default)] - loose: bool, -} - -/// ES2020: Nullish Coalescing Operator -/// -/// References: -/// * -/// * -pub struct NullishCoalescingOperator<'a> { - no_document_all: bool, - ctx: TransformerCtx<'a>, - vars: Vec<'a, VariableDeclarator<'a>>, -} - -impl<'a> CreateVars<'a> for NullishCoalescingOperator<'a> { - fn ctx(&self) -> &TransformerCtx<'a> { - &self.ctx - } - - fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> { - &mut self.vars - } -} - -impl<'a> NullishCoalescingOperator<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2020 - || ctx.options.nullish_coalescing_operator.is_some()) - .then(|| { - let no_document_all = ctx.options.assumptions.no_document_all - || ctx.options.nullish_coalescing_operator.is_some_and(|o| o.loose); - let vars = ctx.ast.new_vec(); - Self { no_document_all, ctx, 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 reference; - let assignment; - - // skip creating extra reference when `left` is static - if self.ctx.symbols().is_static(&logical_expr.left) { - reference = self.ctx.ast.copy(&logical_expr.left); - assignment = self.ctx.ast.copy(&logical_expr.left); - } else { - let ident = self.create_new_var_with_expression(&logical_expr.left); - reference = self.ctx.ast.identifier_reference_expression(ident.clone()); - let left = self.ctx.ast.simple_assignment_target_identifier(ident); - let right = self.ctx.ast.copy(&logical_expr.left); - assignment = - self.ctx.ast.assignment_expression(SPAN, AssignmentOperator::Assign, left, right); - }; - - let test = if self.no_document_all { - let null = self.ctx.ast.literal_null_expression(NullLiteral::new(SPAN)); - self.ctx.ast.binary_expression(SPAN, assignment, BinaryOperator::Inequality, null) - } else { - let op = BinaryOperator::StrictInequality; - let null = self.ctx.ast.literal_null_expression(NullLiteral::new(SPAN)); - let left = - self.ctx.ast.binary_expression(SPAN, self.ctx.ast.copy(&assignment), op, null); - - let right = self.ctx.ast.binary_expression( - SPAN, - self.ctx.ast.copy(&reference), - op, - self.ctx.ast.void_0(), - ); - - self.ctx.ast.logical_expression(SPAN, left, LogicalOperator::And, right) - }; - - let right = self.ctx.ast.move_expression(&mut logical_expr.right); - - *expr = self.ctx.ast.conditional_expression(SPAN, test, reference, right); - } -} diff --git a/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs b/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs deleted file mode 100644 index a33f311fba1cc..0000000000000 --- a/crates/oxc_transformer/src/es2021/logical_assignment_operators.rs +++ /dev/null @@ -1,195 +0,0 @@ -use oxc_allocator::Vec; -use oxc_ast::ast::*; -use oxc_span::SPAN; -use oxc_syntax::operator::{AssignmentOperator, LogicalOperator}; - -use crate::{context::TransformerCtx, options::TransformTarget, utils::CreateVars}; - -/// ES2021: Logical Assignment Operators -/// -/// References: -/// * -/// * -pub struct LogicalAssignmentOperators<'a> { - ctx: TransformerCtx<'a>, - vars: Vec<'a, VariableDeclarator<'a>>, -} - -impl<'a> CreateVars<'a> for LogicalAssignmentOperators<'a> { - fn ctx(&self) -> &TransformerCtx<'a> { - &self.ctx - } - - fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> { - &mut self.vars - } -} - -impl<'a> LogicalAssignmentOperators<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2021 || ctx.options.logical_assignment_operators) - .then(|| { - let vars = ctx.ast.new_vec(); - Self { ctx, vars } - }) - } - - pub fn transform_expression<'b>(&mut self, expr: &'b mut Expression<'a>) { - let Expression::AssignmentExpression(assignment_expr) = expr else { return }; - - // `&&=` `||=` `??=` - let operator = match assignment_expr.operator { - AssignmentOperator::LogicalAnd => LogicalOperator::And, - AssignmentOperator::LogicalOr => LogicalOperator::Or, - AssignmentOperator::LogicalNullish => LogicalOperator::Coalesce, - _ => return, - }; - - // `a &&= c` -> `a && (a = c);` - // ^ ^ assign_target - // ^ left_expr - - let left_expr: Expression<'a>; - let assign_target: AssignmentTarget<'_>; - - // TODO: refactor this block, add tests, cover private identifier - match &assignment_expr.left { - AssignmentTarget::SimpleAssignmentTarget(target) => match target { - SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => { - left_expr = self.ctx.ast.identifier_reference_expression((*ident).clone()); - assign_target = - self.ctx.ast.simple_assignment_target_identifier((*ident).clone()); - } - SimpleAssignmentTarget::MemberAssignmentTarget(member_expr) => { - let op = AssignmentOperator::Assign; - - // `a.b &&= c` -> `var _a; (_a = a).b && (_a.b = c)` - match &**member_expr { - MemberExpression::StaticMemberExpression(static_expr) => { - if let Some(ident) = self.maybe_generate_memoised(&static_expr.object) { - let right = self.ctx.ast.copy(&static_expr.object); - let mut expr = self.ctx.ast.copy(static_expr); - let target = - self.ctx.ast.simple_assignment_target_identifier(ident.clone()); - expr.object = - self.ctx.ast.assignment_expression(SPAN, op, target, right); - left_expr = self.ctx.ast.member_expression( - MemberExpression::StaticMemberExpression(expr), - ); - - let mut expr = self.ctx.ast.copy(static_expr); - expr.object = self.ctx.ast.identifier_reference_expression(ident); - assign_target = - self.ctx.ast.simple_assignment_target_member_expression( - MemberExpression::StaticMemberExpression(expr), - ); - } else { - left_expr = self.ctx.ast.member_expression( - MemberExpression::StaticMemberExpression( - self.ctx.ast.copy(static_expr), - ), - ); - assign_target = - self.ctx.ast.simple_assignment_target_member_expression( - self.ctx.ast.copy(member_expr), - ); - }; - } - // `a[b.y] &&= c;` -> - // `var _a, _b$y; (_a = a)[_b$y = b.y] && (_a[_b$y] = c);` - MemberExpression::ComputedMemberExpression(computed_expr) => { - if let Some(ident) = self.maybe_generate_memoised(&computed_expr.object) - { - let property_ident = - self.maybe_generate_memoised(&computed_expr.expression); - - let right = self.ctx.ast.copy(&computed_expr.object); - let mut expr = self.ctx.ast.copy(computed_expr); - let target = - self.ctx.ast.simple_assignment_target_identifier(ident.clone()); - expr.object = - self.ctx.ast.assignment_expression(SPAN, op, target, right); - if let Some(property_ident) = &property_ident { - let left = self.ctx.ast.simple_assignment_target_identifier( - property_ident.clone(), - ); - let right = self.ctx.ast.copy(&computed_expr.expression); - expr.expression = - self.ctx.ast.assignment_expression(SPAN, op, left, right); - } - left_expr = self.ctx.ast.member_expression( - MemberExpression::ComputedMemberExpression(expr), - ); - - // `(_a[_b$y] = c)` part - let mut expr = self.ctx.ast.copy(computed_expr); - expr.object = self.ctx.ast.identifier_reference_expression(ident); - if let Some(property_ident) = property_ident { - expr.expression = self - .ctx - .ast - .identifier_reference_expression(property_ident); - } - assign_target = - self.ctx.ast.simple_assignment_target_member_expression( - MemberExpression::ComputedMemberExpression(expr), - ); - } else { - let property_ident = - self.maybe_generate_memoised(&computed_expr.expression); - - // let right = self.ctx.ast.copy(&computed_expr.object); - let mut expr = self.ctx.ast.copy(computed_expr); - // let target = AssignmentTarget::SimpleAssignmentTarget( - // self.ctx.ast.simple_assignment_target_identifier(ident.clone()), - // ); - // expr.object = - // self.ctx.ast.assignment_expression(span, op, target, right); - if let Some(property_ident) = &property_ident { - let left = self.ctx.ast.simple_assignment_target_identifier( - property_ident.clone(), - ); - let right = self.ctx.ast.copy(&computed_expr.expression); - expr.expression = - self.ctx.ast.assignment_expression(SPAN, op, left, right); - } - left_expr = self.ctx.ast.member_expression( - MemberExpression::ComputedMemberExpression(expr), - ); - - let mut expr = self.ctx.ast.copy(computed_expr); - // expr.object = self.ctx.ast.identifier_reference_expression(ident); - if let Some(property_ident) = property_ident { - expr.expression = self - .ctx - .ast - .identifier_reference_expression(property_ident); - } - assign_target = - self.ctx.ast.simple_assignment_target_member_expression( - MemberExpression::ComputedMemberExpression(expr), - ); - }; - } - MemberExpression::PrivateFieldExpression(_) => return, - } - } - // All other are TypeScript syntax. - _ => return, - }, - // It is a Syntax Error if AssignmentTargetType of LeftHandSideExpression is not simple. - // So safe to return here. - AssignmentTarget::AssignmentTargetPattern(_) => return, - }; - - let assign_op = AssignmentOperator::Assign; - let right = self.ctx.ast.move_expression(&mut assignment_expr.right); - let right = self.ctx.ast.assignment_expression(SPAN, assign_op, assign_target, right); - - let logical_expr = self.ctx.ast.logical_expression(SPAN, left_expr, operator, right); - - *expr = logical_expr; - } -} - -// TODO: test all permutations diff --git a/crates/oxc_transformer/src/es2021/mod.rs b/crates/oxc_transformer/src/es2021/mod.rs deleted file mode 100644 index 9601b563fc948..0000000000000 --- a/crates/oxc_transformer/src/es2021/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod logical_assignment_operators; -mod numeric_separator; - -pub use logical_assignment_operators::LogicalAssignmentOperators; -pub use numeric_separator::NumericSeparator; diff --git a/crates/oxc_transformer/src/es2021/numeric_separator.rs b/crates/oxc_transformer/src/es2021/numeric_separator.rs deleted file mode 100644 index a507fb107248e..0000000000000 --- a/crates/oxc_transformer/src/es2021/numeric_separator.rs +++ /dev/null @@ -1,33 +0,0 @@ -use oxc_ast::ast::*; -use oxc_span::Atom; - -use crate::{context::TransformerCtx, options::TransformTarget}; - -/// ES2021: Numeric Separator -/// -/// References: -/// * -pub struct NumericSeparator<'a> { - ctx: TransformerCtx<'a>, -} - -impl<'a> NumericSeparator<'a> { - #![allow(clippy::unused_self)] - - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2021 || ctx.options.numeric_separator) - .then_some(Self { ctx }) - } - - pub fn transform_number_literal(&mut self, lit: &mut NumericLiteral<'a>) { - if !lit.raw.is_empty() { - lit.raw = self.ctx.ast.new_str(lit.raw.replace('_', "").as_str()); - } - } - - pub fn transform_bigint_literal(&mut self, lit: &mut BigIntLiteral<'a>) { - if !lit.raw.is_empty() { - lit.raw = Atom::from(self.ctx.ast.new_str(lit.raw.replace('_', "").as_str())); - } - } -} diff --git a/crates/oxc_transformer/src/es2022/class_static_block.rs b/crates/oxc_transformer/src/es2022/class_static_block.rs deleted file mode 100644 index bea4adba51caa..0000000000000 --- a/crates/oxc_transformer/src/es2022/class_static_block.rs +++ /dev/null @@ -1,123 +0,0 @@ -use std::{borrow::Cow, collections::HashSet, rc::Rc}; - -use oxc_ast::{ast::*, AstBuilder}; -use oxc_span::{Atom, SPAN}; - -use crate::{context::TransformerCtx, options::TransformTarget}; - -/// ES2022: Class Static Block -/// -/// References: -/// * -/// * -pub struct ClassStaticBlock<'a> { - ast: Rc>, -} - -impl<'a> ClassStaticBlock<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target < TransformTarget::ES2022 || ctx.options.class_static_block) - .then_some(Self { ast: ctx.ast }) - } - - pub fn transform_class_body<'b>(&mut self, class_body: &'b mut ClassBody<'a>) { - if !class_body.body.iter().any(|e| matches!(e, ClassElement::StaticBlock(..))) { - return; - } - - let private_names: HashSet> = class_body - .body - .iter() - .filter_map(ClassElement::property_key) - .filter_map(PropertyKey::private_name) - .cloned() - .collect(); - - let mut i = 0; - for element in class_body.body.iter_mut() { - let ClassElement::StaticBlock(block) = element else { - continue; - }; - - let static_block_private_id = self.generate_uid(&private_names, &mut i); - let key = - PropertyKey::PrivateIdentifier(self.ast.alloc(PrivateIdentifier { - span: SPAN, - name: static_block_private_id.clone(), - })); - - let value = match block.body.len() { - 0 => None, - 1 if matches!(block.body[0], Statement::ExpressionStatement(..)) => { - // We special-case the single expression case to avoid the iife, since it's common. - // - // We prefer to emit: - // ```JavaScript - // class Foo { - // static bar = 42; - // static #_ = this.foo = Foo.bar; - // } - // ``` - // instead of: - // ```JavaScript - // class Foo { - // static bar = 42; - // static #_ = (() => this.foo = Foo.bar)(); - // } - // ``` - - let stmt = self.ast.move_statement(&mut (*block.body)[0]); - let Statement::ExpressionStatement(mut expr_stmt) = stmt else { - unreachable!() - }; - let value = self.ast.move_expression(&mut expr_stmt.expression); - Some(value) - } - _ => { - let params = self.ast.formal_parameters( - SPAN, - FormalParameterKind::ArrowFormalParameters, - self.ast.new_vec(), - None, - ); - - let statements = self.ast.move_statement_vec(&mut block.body); - let function_body = - self.ast.function_body(SPAN, self.ast.new_vec(), statements); - - let callee = self.ast.arrow_function_expression( - SPAN, - false, - false, - params, - function_body, - None, - None, - ); - - let callee = self.ast.parenthesized_expression(SPAN, callee); - - let value = - self.ast.call_expression(SPAN, callee, self.ast.new_vec(), false, None); - Some(value) - } - }; - - let r#type = PropertyDefinitionType::PropertyDefinition; - *element = - self.ast.class_property(r#type, SPAN, key, value, false, true, self.ast.new_vec()); - } - } - - fn generate_uid(&self, deny_list: &HashSet>, i: &mut u32) -> Atom<'a> { - *i += 1; - - let mut uid = if *i == 1 { Cow::Borrowed("_") } else { Cow::Owned(format!("_{i}")) }; - while deny_list.iter().any(|id| id.as_str() == uid.as_ref()) { - *i += 1; - uid = format!("_{i}").into(); - } - - self.ast.new_atom(&uid) - } -} diff --git a/crates/oxc_transformer/src/es2022/mod.rs b/crates/oxc_transformer/src/es2022/mod.rs deleted file mode 100644 index ea8bfb6fffd72..0000000000000 --- a/crates/oxc_transformer/src/es2022/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod class_static_block; - -pub use class_static_block::ClassStaticBlock; diff --git a/crates/oxc_transformer/src/es3/mod.rs b/crates/oxc_transformer/src/es3/mod.rs deleted file mode 100644 index 62bd4eccbcd93..0000000000000 --- a/crates/oxc_transformer/src/es3/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod property_literals; -pub use property_literals::PropertyLiteral; diff --git a/crates/oxc_transformer/src/es3/property_literals.rs b/crates/oxc_transformer/src/es3/property_literals.rs deleted file mode 100644 index a55c742c03d79..0000000000000 --- a/crates/oxc_transformer/src/es3/property_literals.rs +++ /dev/null @@ -1,36 +0,0 @@ -use oxc_ast::{ast::*, AstBuilder}; -use oxc_span::SPAN; -use std::rc::Rc; - -use crate::context::TransformerCtx; -use crate::utils::is_valid_es3_identifier; -use crate::TransformTarget; - -/// ES3: PropertyLiteral -/// -/// References: -/// * -pub struct PropertyLiteral<'a> { - ast: Rc>, -} - -impl<'a> PropertyLiteral<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - (ctx.options.target <= TransformTarget::ES3 || ctx.options.property_literals) - .then_some(Self { ast: ctx.ast }) - } - - pub fn transform_object_property<'b>(&mut self, expr: &'b mut ObjectProperty<'a>) { - if expr.computed { - return; - } - if let PropertyKey::Identifier(ident) = &expr.key { - if !is_valid_es3_identifier(&ident.name) { - let string_lit = self - .ast - .literal_string_expression(StringLiteral::new(SPAN, ident.name.clone())); - expr.key = PropertyKey::Expression(string_lit); - } - } - } -} diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 37123a934de02..9be093ecd6f3d 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -1,5 +1,3 @@ -#![allow(clippy::wildcard_imports, clippy::option_map_unit_fn)] - //! Transformer / Transpiler //! //! References: @@ -7,279 +5,38 @@ //! * //! * -mod context; -mod es2015; -mod es2016; -mod es2019; -mod es2020; -mod es2021; -mod es2022; -mod es3; mod options; -mod proposals; -mod react_jsx; -mod regexp; -#[cfg(test)] -mod tester; -mod typescript; -mod utils; - -use std::{rc::Rc, sync::Arc}; -use es2015::TemplateLiterals; use oxc_allocator::Allocator; -use oxc_ast::{ - ast::*, - visit::walk_mut::{ - walk_assignment_expression_mut, walk_catch_clause_mut, walk_class_body_mut, - walk_declaration_mut, walk_expression_mut, walk_function_mut, walk_method_definition_mut, - walk_object_expression_mut, walk_object_property_mut, walk_program_mut, walk_statement_mut, - walk_statements_mut, walk_variable_declarator_mut, - }, - AstBuilder, VisitMut, -}; +use oxc_ast::ast::Program; use oxc_diagnostics::Error; -use oxc_semantic::{ScopeFlags, Semantic}; +use oxc_semantic::Semantic; use oxc_span::SourceType; -use proposals::Decorators; -use crate::{ - context::TransformerCtx, - es2015::*, - es2016::ExponentiationOperator, - es2019::{JsonStrings, OptionalCatchBinding}, - es2020::NullishCoalescingOperator, - es2021::{LogicalAssignmentOperators, NumericSeparator}, - es2022::ClassStaticBlock, - es3::PropertyLiteral, - react_jsx::ReactJsx, - regexp::RegexpFlags, - typescript::TypeScript, - utils::CreateVars, -}; - -pub use crate::{ - es2015::ArrowFunctionsOptions, - es2020::NullishCoalescingOperatorOptions, - options::{TransformOptions, TransformTarget}, - proposals::DecoratorsOptions, - react_jsx::{ReactJsxOptions, ReactJsxRuntime, ReactJsxRuntimeOption}, - typescript::TypescriptOptions, -}; +pub use crate::options::TransformOptions; +#[allow(unused)] pub struct Transformer<'a> { - ctx: TransformerCtx<'a>, - decorators: Option>, - #[allow(unused)] - typescript: Option>, - react_jsx: Option>, - regexp_flags: Option>, - // es2022 - es2022_class_static_block: Option>, - // es2021 - es2021_logical_assignment_operators: Option>, - es2021_numeric_separator: Option>, - // es2020 - es2020_nullish_coalescing_operators: Option>, - // es2019 - es2019_json_strings: Option>, - es2019_optional_catch_binding: Option>, - // es2016 - es2016_exponentiation_operator: Option>, - // es2015 - es2015_function_name: Option>, - es2015_arrow_functions: Option>, - es2015_shorthand_properties: Option>, - es2015_template_literals: Option>, - es2015_duplicate_keys: Option>, - es2015_instanceof: Option>, - es2015_literals: Option>, - es2015_new_target: Option>, - es3_property_literal: Option>, + allocator: &'a Allocator, + source_type: SourceType, + semantic: Semantic<'a>, + options: TransformOptions, } impl<'a> Transformer<'a> { - #[rustfmt::skip] pub fn new( allocator: &'a Allocator, source_type: SourceType, semantic: Semantic<'a>, options: TransformOptions, ) -> Self { - let ast = Rc::new(AstBuilder::new(allocator)); - let ctx = TransformerCtx::new( - ast, - semantic, - options, - ); - - Self { - decorators: Decorators::new(ctx.clone()), - typescript: source_type.is_typescript().then(|| TypeScript::new(ctx.clone())), - regexp_flags: RegexpFlags::new(ctx.clone()), - // es2022 - es2022_class_static_block: es2022::ClassStaticBlock::new(ctx.clone()), - // es2021 - es2021_logical_assignment_operators: LogicalAssignmentOperators::new(ctx.clone()), - es2021_numeric_separator: NumericSeparator::new(ctx.clone()), - // es2020 - es2020_nullish_coalescing_operators: NullishCoalescingOperator::new(ctx.clone()), - // es2019 - es2019_json_strings: JsonStrings::new(ctx.clone()), - es2019_optional_catch_binding: OptionalCatchBinding::new(ctx.clone()), - // es2016 - es2016_exponentiation_operator: ExponentiationOperator::new(ctx.clone()), - // es2015 - es2015_function_name: FunctionName::new(ctx.clone()), - es2015_arrow_functions: ArrowFunctions::new(ctx.clone()), - es2015_shorthand_properties: ShorthandProperties::new(ctx.clone()), - es2015_template_literals: TemplateLiterals::new(ctx.clone()), - es2015_duplicate_keys: DuplicateKeys::new(ctx.clone()), - es2015_instanceof: Instanceof::new(ctx.clone()), - es2015_literals: Literals::new(ctx.clone()), - es2015_new_target: NewTarget::new(ctx.clone()), - // other - es3_property_literal: PropertyLiteral::new(ctx.clone()), - react_jsx: ReactJsx::new(ctx.clone()), - // original context - ctx, - } + Self { allocator, source_type, semantic, options } } /// # Errors + /// /// Returns `Vec` if any errors were collected during the transformation. - pub fn build(mut self, program: &mut Program<'a>) -> Result<(), Vec> { - self.visit_program(program); - let errors: Vec<_> = self - .ctx - .errors() - .into_iter() - .map(|e| e.with_source_code(Arc::new(self.ctx.semantic().source_text().to_string()))) - .collect(); - - if errors.is_empty() { - Ok(()) - } else { - Err(errors) - } - } -} - -impl<'a> VisitMut<'a> for Transformer<'a> { - fn visit_program(&mut self, program: &mut Program<'a>) { - walk_program_mut(self, program); - self.typescript.as_mut().map(|t| t.transform_program(program)); - self.react_jsx.as_mut().map(|t| t.add_react_jsx_runtime_imports(program)); - self.decorators.as_mut().map(|t| t.transform_program(program)); - } - - fn visit_assignment_expression(&mut self, expr: &mut AssignmentExpression<'a>) { - self.es2015_function_name.as_mut().map(|t| t.transform_assignment_expression(expr)); - walk_assignment_expression_mut(self, expr); - } - - fn visit_statements(&mut self, stmts: &mut oxc_allocator::Vec<'a, Statement<'a>>) { - self.typescript.as_mut().map(|t| t.transform_statements(stmts)); - - walk_statements_mut(self, stmts); - - // TODO: we need scope id to insert the vars into the correct statements - 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.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>) { - self.typescript.as_mut().map(|t| t.transform_statement(stmt)); - self.decorators.as_mut().map(|t| t.transform_statement(stmt)); - walk_statement_mut(self, stmt); - } - - fn visit_declaration(&mut self, decl: &mut Declaration<'a>) { - walk_declaration_mut(self, decl); - self.typescript.as_mut().map(|t| t.transform_declaration(decl)); - self.decorators.as_mut().map(|t| t.transform_declaration(decl)); - } - - fn visit_expression(&mut self, expr: &mut Expression<'a>) { - // self.typescript.as_mut().map(|t| t.transform_expression(expr)); - self.react_jsx.as_mut().map(|t| t.transform_expression(expr)); - 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.es2015_arrow_functions.as_mut().map(|t| t.transform_expression(expr)); - self.es2015_instanceof.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_new_target.as_mut().map(|t| t.transform_expression(expr)); - - walk_expression_mut(self, expr); - } - - fn visit_catch_clause(&mut self, clause: &mut CatchClause<'a>) { - self.es2019_optional_catch_binding.as_mut().map(|t| t.transform_catch_clause(clause)); - walk_catch_clause_mut(self, clause); - } - - fn visit_object_expression(&mut self, expr: &mut ObjectExpression<'a>) { - self.es2015_function_name.as_mut().map(|t| t.transform_object_expression(expr)); - self.es2015_duplicate_keys.as_mut().map(|t| t.transform_object_expression(expr)); - walk_object_expression_mut(self, expr); - } - - fn visit_object_property(&mut self, prop: &mut ObjectProperty<'a>) { - self.es2015_new_target.as_mut().map(|t| t.enter_object_property(prop)); - self.es2015_shorthand_properties.as_mut().map(|t| t.transform_object_property(prop)); - self.es3_property_literal.as_mut().map(|t| t.transform_object_property(prop)); - - walk_object_property_mut(self, prop); - - self.es2015_new_target.as_mut().map(|t| t.leave_object_property(prop)); - } - - fn visit_class_body(&mut self, body: &mut ClassBody<'a>) { - self.es2022_class_static_block.as_mut().map(|t| t.transform_class_body(body)); - - walk_class_body_mut(self, body); - } - - fn visit_variable_declarator(&mut self, declarator: &mut VariableDeclarator<'a>) { - self.es2015_function_name.as_mut().map(|t| t.transform_variable_declarator(declarator)); - walk_variable_declarator_mut(self, declarator); - } - - fn visit_directive(&mut self, directive: &mut Directive<'a>) { - self.es2019_json_strings.as_mut().map(|t| t.transform_directive(directive)); - } - - fn visit_number_literal(&mut self, lit: &mut NumericLiteral<'a>) { - self.es2021_numeric_separator.as_mut().map(|t| t.transform_number_literal(lit)); - self.es2015_literals.as_mut().map(|t| t.transform_number_literal(lit)); - } - - fn visit_bigint_literal(&mut self, lit: &mut BigIntLiteral<'a>) { - self.es2021_numeric_separator.as_mut().map(|t| t.transform_bigint_literal(lit)); - } - - fn visit_string_literal(&mut self, lit: &mut StringLiteral<'a>) { - self.es2019_json_strings.as_mut().map(|t| t.transform_string_literal(lit)); - self.es2015_literals.as_mut().map(|t| t.transform_string_literal(lit)); - } - - fn visit_method_definition(&mut self, def: &mut MethodDefinition<'a>) { - self.es2015_new_target.as_mut().map(|t| t.enter_method_definition(def)); - self.typescript.as_mut().map(|t| t.transform_method_definition(def)); - - walk_method_definition_mut(self, def); - - self.es2015_new_target.as_mut().map(|t| t.leave_method_definition(def)); - } - - fn visit_function(&mut self, func: &mut Function<'a>, flags: Option) { - self.es2015_new_target.as_mut().map(|t| t.enter_function(func)); - walk_function_mut(self, func, flags); - self.es2015_new_target.as_mut().map(|t| t.leave_function(func)); + pub fn build(self, _program: &mut Program<'a>) -> Result<(), Vec> { + Ok(()) } } diff --git a/crates/oxc_transformer/src/options.rs b/crates/oxc_transformer/src/options.rs index 1ce80b3625cf1..02eda29a01a4d 100644 --- a/crates/oxc_transformer/src/options.rs +++ b/crates/oxc_transformer/src/options.rs @@ -1,60 +1,3 @@ -use oxc_syntax::assumptions::CompilerAssumptions; - -use crate::{ - es2015::ArrowFunctionsOptions, es2020::NullishCoalescingOperatorOptions, - proposals::DecoratorsOptions, react_jsx::ReactJsxOptions, typescript::TypescriptOptions, -}; - +#[allow(clippy::empty_structs_with_brackets)] #[derive(Debug, Default, Clone)] -pub struct TransformOptions { - pub target: TransformTarget, - pub assumptions: CompilerAssumptions, - - pub react_jsx: Option, - - pub typescript: Option, - - // es2022 - pub class_static_block: bool, - // es2021 - pub logical_assignment_operators: bool, - pub numeric_separator: bool, - // es2020 - pub nullish_coalescing_operator: Option, - // es2019 - pub optional_catch_binding: bool, - pub json_strings: bool, - // es2016 - pub exponentiation_operator: bool, - // es2015 - pub duplicate_keys: bool, - pub function_name: bool, - pub arrow_functions: Option, - pub shorthand_properties: bool, - pub literals: bool, - pub sticky_regex: bool, - pub template_literals: bool, - pub property_literals: bool, - pub babel_8_breaking: Option, - pub instanceof: bool, - pub new_target: bool, - // Proposal - pub decorators: Option, -} - -/// See -#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] -pub enum TransformTarget { - ES3, - ES5, - ES2015, - ES2016, - ES2018, - ES2019, - ES2020, - ES2021, - ES2022, - ES2024, - #[default] - ESNext, -} +pub struct TransformOptions {} diff --git a/crates/oxc_transformer/src/proposals/decorators.rs b/crates/oxc_transformer/src/proposals/decorators.rs deleted file mode 100644 index cad9125964561..0000000000000 --- a/crates/oxc_transformer/src/proposals/decorators.rs +++ /dev/null @@ -1,1084 +0,0 @@ -use std::{borrow::Cow, collections::HashMap}; - -use bitflags::bitflags; -use oxc_allocator::{Box, Vec}; -use oxc_ast::ast::*; -use oxc_span::{Atom, CompactStr, SPAN}; -use oxc_syntax::operator::{AssignmentOperator, LogicalOperator}; -use serde::Deserialize; - -use crate::context::TransformerCtx; - -/// Proposal: Decorators -/// -/// References: -/// * -/// * -/// * -pub struct Decorators<'a> { - ctx: TransformerCtx<'a>, - options: DecoratorsOptions, - // Insert to the top of the program - top_statements: Vec<'a, Statement<'a>>, - // Insert to the bottom of the program - bottom_statements: Vec<'a, Statement<'a>>, - uid_map: HashMap, -} - -bitflags! { - #[derive(Debug, Clone, Copy)] - pub struct DecoratorFlags: u8 { - // flag is 0 - const Field = 1 << 0; - // flag is 1 - const Accessor = 1 << 1; - // flag is 2 - const Method = 1 << 2; - // flag is 3 - const Getter = 1 << 3; - // flag is 4 - const Setter = 1 << 4; - // flag is 8 - const Static = 1 << 5; - // flag is 16 - const DecoratorsHaveThis = 1 << 6; - } -} - -impl DecoratorFlags { - pub fn is_static(self) -> bool { - self.contains(Self::Static) - } - pub fn get_flag_by_kind(kind: MethodDefinitionKind) -> Self { - match kind { - MethodDefinitionKind::Method => Self::Method, - MethodDefinitionKind::Get => Self::Getter, - MethodDefinitionKind::Set => Self::Setter, - MethodDefinitionKind::Constructor => unreachable!(), - } - } - pub fn to_value(self) -> u8 { - if self.contains(DecoratorFlags::DecoratorsHaveThis) { - return 16; - } - let mut value: u8 = 0; - if self.contains(DecoratorFlags::Accessor) { - value += 1; - } - if self.contains(DecoratorFlags::Method) { - value += 2; - } - if self.contains(DecoratorFlags::Getter) { - value += 3; - } - if self.contains(DecoratorFlags::Setter) { - value += 4; - } - if self.contains(DecoratorFlags::Static) { - value += 8; - } - value - } -} - -#[derive(Debug, Clone, Copy, Default, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct DecoratorsOptions { - version: Version, -} - -#[derive(Debug, Default, Clone, Copy, Deserialize)] -#[serde(rename_all = "camelCase")] -enum Version { - Legacy, - #[serde(rename = "2023-05")] - #[default] - Year202305, -} - -impl Version { - fn is_legacy(self) -> bool { - matches!(self, Self::Legacy) - } -} - -impl<'a> Decorators<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - let top_statements = ctx.ast.new_vec(); - let bottom_statements = ctx.ast.new_vec(); - ctx.options.decorators.map(|options| Self { - ctx, - options, - top_statements, - bottom_statements, - uid_map: HashMap::new(), - }) - } - - pub fn get_variable_declarator(&self, name: &str) -> VariableDeclarator<'a> { - let name = self.ctx.ast.new_atom(name); - self.ctx.ast.variable_declarator( - SPAN, - VariableDeclarationKind::Var, - BindingPattern::new_with_kind( - self.ctx.ast.binding_pattern_identifier(BindingIdentifier::new(SPAN, name)), - ), - None, - false, - ) - } - - #[allow(clippy::unnecessary_wraps)] - pub fn get_assignment_target_maybe_default( - &self, - name: &str, - ) -> Option> { - let name = self.ctx.ast.new_atom(name); - Some(AssignmentTargetMaybeDefault::AssignmentTarget( - self.ctx.ast.simple_assignment_target_identifier(IdentifierReference::new(SPAN, name)), - )) - } - - // TODO: use generate_uid of scope to generate unique name - pub fn get_unique_name(&mut self, name: &str) -> CompactStr { - let uid = self.uid_map.entry(CompactStr::new(name)).or_insert(0); - *uid += 1; - CompactStr::from(format!( - "_{name}{}", - if *uid == 1 { String::new() } else { uid.to_string() } - )) - } - - pub fn get_call_with_this(&self, name: &str) -> Expression<'a> { - self.get_call_with_arguments( - name, - self.ctx.ast.new_vec_single(Argument::Expression(self.ctx.ast.this_expression(SPAN))), - ) - } - - pub fn get_call_with_arguments( - &self, - name: &str, - arguments: Vec<'a, Argument<'a>>, - ) -> Expression<'a> { - let name = self.ctx.ast.new_atom(name); - self.ctx.ast.call_expression( - SPAN, - self.ctx.ast.identifier_reference_expression(IdentifierReference::new(SPAN, name)), - arguments, - false, - None, - ) - } - - pub fn transform_program(&mut self, program: &mut Program<'a>) { - program.body.splice(0..0, self.top_statements.drain(..)); - program.body.append(&mut self.bottom_statements); - } - - pub fn transform_statement(&mut self, stmt: &mut Statement<'a>) { - if let Statement::ModuleDeclaration(decl) = stmt { - let new_stmt = match &mut **decl { - ModuleDeclaration::ExportNamedDeclaration(export) => { - // remove export - export.declaration.as_mut().map_or_else( - || None, - |declaration| { - if let Declaration::ClassDeclaration(class) = declaration { - if !Self::can_transform(class) { - return None; - } - let has_decorator = !class.decorators.is_empty(); - let class_name = if has_decorator { - class - .id - .clone() - .map(|id| self.get_unique_name(&id.name)) - .or_else(|| Some(self.get_unique_name("class"))) - } else { - None - }; - - if has_decorator { - self.bottom_statements.push(self.ctx.ast.module_declaration( - ModuleDeclaration::ExportNamedDeclaration( - self.ctx.ast.export_named_declaration( - SPAN, - None, - self.ctx.ast.new_vec_single(ExportSpecifier::new( - SPAN, - ModuleExportName::Identifier( - IdentifierName::new( - SPAN, - self.ctx.ast.new_atom( - class_name.as_ref().unwrap(), - ), - ), - ), - ModuleExportName::Identifier( - IdentifierName::new( - SPAN, - class.id.clone().unwrap().name, - ), - ), - )), - None, - ImportOrExportKind::Value, - None, - ), - ), - )); - } - - let new_declaration = self.transform_class(class, class_name); - if has_decorator { - return Some(Statement::Declaration(new_declaration)); - } - *declaration = new_declaration; - return None; - } - None - }, - ) - } - ModuleDeclaration::ExportDefaultDeclaration(export) => { - if let ExportDefaultDeclarationKind::ClassDeclaration(class) = - &mut export.declaration - { - if !Self::can_transform(class) { - return; - } - let class_has_decorator = !class.decorators.is_empty(); - let class_name = if class_has_decorator { - class - .id - .clone() - .map(|id| self.get_unique_name(&id.name)) - .or_else(|| Some(self.get_unique_name("class"))) - } else { - None - }; - - if class_has_decorator { - self.bottom_statements.push(self.ctx.ast.module_declaration( - ModuleDeclaration::ExportNamedDeclaration( - self.ctx.ast.export_named_declaration( - SPAN, - None, - self.ctx.ast.new_vec_single(ExportSpecifier::new( - SPAN, - ModuleExportName::Identifier(IdentifierName::new( - SPAN, - self.ctx.ast.new_atom(class_name.as_ref().unwrap()), - )), - ModuleExportName::Identifier(IdentifierName::new( - SPAN, - self.ctx.ast.new_atom("default"), - )), - )), - None, - ImportOrExportKind::Value, - None, - ), - ), - )); - } - - Some(Statement::Declaration(self.transform_class(class, class_name))) - } else { - None - } - } - _ => None, - }; - if let Some(new_stmt) = new_stmt { - *stmt = new_stmt; - } - } - } - pub fn transform_declaration(&mut self, decl: &mut Declaration<'a>) { - let new_decl = match decl { - Declaration::ClassDeclaration(class) => { - if Self::can_transform(class) { - Some(self.transform_class(class, None)) - } else { - None - } - } - _ => None, - }; - if let Some(new_decl) = new_decl { - *decl = new_decl; - } - } - - pub fn can_transform(class: &Class<'a>) -> bool { - !class.decorators.is_empty() || class.body.body.iter().any(ClassElement::has_decorator) - } - - /// transform version: 2023-05 - pub fn transform_class( - &mut self, - class: &mut Box<'a, Class<'a>>, - class_name: Option, - ) -> Declaration<'a> { - if self.options.version.is_legacy() { - return self.transform_class_legacy(class, class_name); - } - - let has_decorator = !class.decorators.is_empty(); - let has_member_decorator = class.body.body.iter().any(ClassElement::has_decorator); - - let mut declarations = self.ctx.ast.new_vec(); - - let mut c_elements = self.ctx.ast.new_vec(); - let mut e_elements = self.ctx.ast.new_vec(); - - let mut private_in_expression = None; - - let mut init_static_name = None; - - // insert member decorators - let mut member_decorators_vec = self.ctx.ast.new_vec(); - let mut class_decorators_argument = - Argument::Expression(self.ctx.ast.array_expression(SPAN, self.ctx.ast.new_vec(), None)); - - if has_decorator { - let class_name = class_name.unwrap_or_else(|| { - self.get_unique_name( - class.id.as_ref().map_or_else(|| "class".into(), |id| id.name.clone()).as_ref(), - ) - }); - - let init_class_name = self.get_unique_name("initClass"); - - { - // insert var _initClass, _classDecs; - declarations.push(self.get_variable_declarator(&init_class_name)); - } - - { - let decorators_exists = class.decorators.iter().any(|decorator| { - decorator - .name() - .is_some_and(|name| // TODO: We should use current node's scope id - self.ctx.scopes().has_binding(self.ctx.scopes().root_scope_id(), name)) - }); - - if decorators_exists { - let mut elements = self.ctx.ast.new_vec(); - - elements.extend(class.decorators.drain(..).map(|decorator| { - ArrayExpressionElement::Expression(self.ctx.ast.copy(&decorator.expression)) - })); - class_decorators_argument = - Argument::Expression(self.ctx.ast.array_expression(SPAN, elements, None)); - } else { - let class_decs_name = self.get_unique_name("classDecs"); - - // insert var _classDecs; - declarations.push(self.get_variable_declarator(&class_decs_name)); - - // insert _classDecs = decorators; - let left = self.ctx.ast.simple_assignment_target_identifier( - IdentifierReference::new(SPAN, self.ctx.ast.new_atom(&class_decs_name)), - ); - - let right = self.ctx.ast.array_expression( - SPAN, - { - let mut elements = self.ctx.ast.new_vec(); - elements.extend(class.decorators.drain(..).map(|d| { - ArrayExpressionElement::Expression(self.ctx.ast.copy(&d.expression)) - })); - elements - }, - None, - ); - let assign_class_decs = self.ctx.ast.expression_statement( - SPAN, - self.ctx.ast.assignment_expression( - SPAN, - AssignmentOperator::Assign, - left, - right, - ), - ); - self.top_statements.push(assign_class_decs); - - class_decorators_argument = - Argument::Expression(self.ctx.ast.identifier_reference_expression( - IdentifierReference::new(SPAN, self.ctx.ast.new_atom(&class_decs_name)), - )); - } - }; - - { - // insert let _className - let declarations = - self.ctx.ast.new_vec_single(self.get_variable_declarator(&class_name)); - let variable_declaration = self.ctx.ast.variable_declaration( - SPAN, - VariableDeclarationKind::Let, - declarations, - Modifiers::empty(), - ); - self.top_statements.push(Statement::Declaration(Declaration::VariableDeclaration( - variable_declaration, - ))); - } - - c_elements.push(self.get_assignment_target_maybe_default(&class_name)); - c_elements.push(self.get_assignment_target_maybe_default(&init_class_name)); - - { - // call _initClass - let callee = self.ctx.ast.identifier_reference_expression( - IdentifierReference::new(SPAN, self.ctx.ast.new_atom(&init_class_name)), - ); - let call_expr = - self.ctx.ast.call_expression(SPAN, callee, self.ctx.ast.new_vec(), false, None); - let statements = - self.ctx.ast.new_vec_single(self.ctx.ast.expression_statement(SPAN, call_expr)); - let static_block = self.ctx.ast.static_block(SPAN, statements); - class.body.body.insert(0, static_block); - } - } - - if has_member_decorator { - let mut is_proto = false; - let mut is_static = false; - - let mut name; - for element in class.body.body.iter_mut() { - if !element.has_decorator() { - continue; - } - match element { - ClassElement::MethodDefinition(def) => { - if def.r#static { - is_static = def.r#static; - } else { - is_proto = true; - } - let mut flag = DecoratorFlags::get_flag_by_kind(def.kind); - if def.r#static { - flag |= DecoratorFlags::Static; - } - - for decorator in &def.decorators { - member_decorators_vec.push(ArrayExpressionElement::Expression( - self.get_decorator_info( - &def.key, - Some(self.ctx.ast.copy(&def.value)), - flag, - decorator, - ), - )); - } - - def.decorators.clear(); - - if def.key.is_private_identifier() { - { - if !flag.is_static() { - // _ => #a in _; - private_in_expression = - Some(self.get_is_private_function( - def.key.private_name().unwrap(), - )); - } - } - - name = self.get_unique_name(&if def.computed { - Cow::Borrowed("init_computedKey") - } else { - Cow::Owned(format!("call_{}", def.key.name().unwrap())) - }); - - let mut arguments = self.ctx.ast.new_vec_with_capacity(2); - arguments - .push(Argument::Expression(self.ctx.ast.this_expression(SPAN))); - arguments.push(Argument::Expression( - self.ctx.ast.identifier_reference_expression( - IdentifierReference::new(SPAN, self.ctx.ast.new_atom("v")), - ), - )); - - let is_setter = def.kind == MethodDefinitionKind::Set; - - def.value = self.ctx.ast.function( - def.value.r#type, - def.value.span, - self.ctx.ast.copy(&def.value.id), - def.value.generator, - def.value.r#async, - self.ctx.ast.copy(&def.value.this_param), - self.ctx.ast.formal_parameters( - SPAN, - FormalParameterKind::FormalParameter, - if is_setter { - self.ctx.ast.new_vec_single(self.ctx.ast.formal_parameter( - SPAN, - self.ctx.ast.binding_pattern( - self.ctx.ast.binding_pattern_identifier( - BindingIdentifier::new( - SPAN, - self.ctx.ast.new_atom("v"), - ), - ), - None, - false, - ), - None, - false, - false, - self.ctx.ast.new_vec(), - )) - } else { - self.ctx.ast.new_vec() - }, - None, - ), - Some(self.ctx.ast.function_body( - SPAN, - self.ctx.ast.new_vec(), - self.ctx.ast.new_vec_single(if is_setter { - self.ctx.ast.expression_statement( - SPAN, - self.get_call_with_arguments(&name, arguments), - ) - } else { - self.ctx.ast.return_statement( - SPAN, - Some(self.get_call_with_this(&name)), - ) - }), - )), - self.ctx.ast.copy(&def.value.type_parameters), - self.ctx.ast.copy(&def.value.return_type), - self.ctx.ast.copy(&def.value.modifiers), - ); - } else { - continue; - } - } - ClassElement::PropertyDefinition(def) => { - let flag = if def.r#static { - DecoratorFlags::Static - } else { - DecoratorFlags::Field - }; - - if def.key.is_private_identifier() && !flag.is_static() { - private_in_expression = - Some(self.get_is_private_function(def.key.private_name().unwrap())); - } - - name = self.get_unique_name(&if def.computed { - Cow::Borrowed("init_computedKey") - } else { - Cow::Owned(format!("init_{}", def.key.name().unwrap())) - }); - - for decorator in &def.decorators { - member_decorators_vec.push(ArrayExpressionElement::Expression( - self.get_decorator_info(&def.key, None, flag, decorator), - )); - } - def.decorators.clear(); - - let mut arguments = self.ctx.ast.new_vec_single(Argument::Expression( - self.ctx.ast.this_expression(SPAN), - )); - - if let Some(value) = &mut def.value { - arguments - .push(Argument::Expression(self.ctx.ast.move_expression(value))); - } - - def.value = Some(self.ctx.ast.call_expression( - SPAN, - self.ctx.ast.identifier_reference_expression(IdentifierReference::new( - SPAN, - self.ctx.ast.new_atom(&name), - )), - arguments, - false, - None, - )); - } - _ => continue, - } - - e_elements.push(self.get_assignment_target_maybe_default(&name)); - declarations.push(self.get_variable_declarator(&name)); - } - - if is_proto { - // The class has method decorator and is not static - let name = self.get_unique_name("initProto"); - e_elements.push(self.get_assignment_target_maybe_default(&name)); - declarations.push(self.get_variable_declarator(&name)); - - // constructor() { _initProto(this) } - if let Some(constructor_element) = class.body.body.iter_mut().find( - |element| matches!(element, ClassElement::MethodDefinition(def) if def.kind.is_constructor()), - ) { - if let ClassElement::MethodDefinition(def) = constructor_element { - if let Some(body) = &mut def.value.body { - body.statements.insert(0, self.ctx.ast.expression_statement(SPAN, self.get_call_with_this(&name))); - } - } else { - unreachable!(); - }; - } else { - // if the class has no constructor, insert a empty constructor and call initProto - class.body.body.insert( - 0, - self.ctx.ast.class_constructor( - SPAN, - self.ctx.ast.function( - FunctionType::FunctionExpression, - SPAN, - None, - false, - false, - None, - self.ctx.ast.formal_parameters( - SPAN, - FormalParameterKind::FormalParameter, - self.ctx.ast.new_vec(), - None, - ), - Some(self.ctx.ast.function_body( - SPAN, - self.ctx.ast.new_vec(), - self.ctx.ast.new_vec_single( - self.ctx.ast.expression_statement(SPAN, self.get_call_with_this(&name)) - ), - )), - None, - None, - Modifiers::empty(), - ), - ), - ); - } - } - - if is_static { - let name = self.get_unique_name("initStatic"); - e_elements.push(self.get_assignment_target_maybe_default(&name)); - declarations.push(self.get_variable_declarator(&name)); - init_static_name = Some(name); - } - } - - { - // insert all variable_declarator in same variable_declaration - let variable_declaration = self.ctx.ast.variable_declaration( - SPAN, - VariableDeclarationKind::Var, - declarations, - Modifiers::empty(), - ); - - self.top_statements.insert( - 0, - Statement::Declaration(Declaration::VariableDeclaration(variable_declaration)), - ); - } - - { - // applyDecs2305( - // targetClass: any, - // memberDecs: DecoratorInfo[], - // classDecs: Function[], - // classDecsHaveThis: number, - // instanceBrand: Function, - // parentClass: any, - // ) {} - // call babelHelpers.applyDecs2305 - let callee = self.ctx.ast.static_member_expression( - SPAN, - self.ctx.ast.identifier_reference_expression(IdentifierReference::new( - SPAN, - self.ctx.ast.new_atom("babelHelpers"), - )), - IdentifierName::new(SPAN, self.ctx.ast.new_atom("applyDecs2305")), - false, - ); - - let mut arguments = self - .ctx - .ast - .new_vec_single(Argument::Expression(self.ctx.ast.this_expression(SPAN))); - arguments.push(Argument::Expression(self.ctx.ast.array_expression( - SPAN, - member_decorators_vec, - None, - ))); - arguments.push(class_decorators_argument); - if let Some(private_in_expression) = private_in_expression { - // classDecsHaveThis - arguments.push(Argument::Expression(self.ctx.ast.literal_number_expression( - // TODO: use correct number instead of `0` - self.ctx.ast.number_literal(SPAN, 0f64, "0", oxc_syntax::NumberBase::Decimal), - ))); - // instanceBrand - arguments.push(Argument::Expression(private_in_expression)); - } - - let mut call_expr = self.ctx.ast.call_expression(SPAN, callee, arguments, false, None); - - if has_decorator && has_decorator == has_member_decorator { - let mut properties = self.ctx.ast.new_vec_with_capacity(2); - properties.push(self.ctx.ast.assignment_target_property_property( - SPAN, - self.ctx.ast.property_key_identifier(IdentifierName::new(SPAN, "e".into())), - self.ctx.ast.array_assignment_target_maybe_default( - ArrayAssignmentTarget::new_with_elements(SPAN, e_elements), - ), - )); - properties.push(self.ctx.ast.assignment_target_property_property( - SPAN, - self.ctx.ast.property_key_identifier(IdentifierName::new(SPAN, "c".into())), - self.ctx.ast.array_assignment_target_maybe_default( - ArrayAssignmentTarget::new_with_elements(SPAN, c_elements), - ), - )); - call_expr = self.ctx.ast.assignment_expression( - SPAN, - AssignmentOperator::Assign, - self.ctx.ast.object_assignment_target( - ObjectAssignmentTarget::new_with_properties(SPAN, properties), - ), - call_expr, - ); - } else if has_decorator || has_member_decorator { - call_expr = self.ctx.ast.static_member_expression( - SPAN, - call_expr, - IdentifierName::new( - SPAN, - self.ctx.ast.new_atom(if has_decorator { "c" } else { "e" }), - ), - false, - ); - - let left = - self.ctx.ast.array_assignment_target(ArrayAssignmentTarget::new_with_elements( - SPAN, - if has_decorator { c_elements } else { e_elements }, - )); - - call_expr = self.ctx.ast.assignment_expression( - SPAN, - AssignmentOperator::Assign, - left, - call_expr, - ); - } - - let mut statements = self.ctx.ast.new_vec(); - statements.push(self.ctx.ast.expression_statement(SPAN, call_expr)); - - if let Some(init_static_name) = init_static_name { - // call initStatic - let callee = self.ctx.ast.identifier_reference_expression( - IdentifierReference::new(SPAN, self.ctx.ast.new_atom(&init_static_name)), - ); - let arguments = self - .ctx - .ast - .new_vec_single(Argument::Expression(self.ctx.ast.this_expression(SPAN))); - statements.push(self.ctx.ast.expression_statement( - SPAN, - self.ctx.ast.call_expression(SPAN, callee, arguments, false, None), - )); - } - - let static_block = self.ctx.ast.static_block(SPAN, statements); - class.body.body.insert(0, static_block); - } - - Declaration::ClassDeclaration(self.ctx.ast.copy(class)) - } - - /// transform version: legacy - pub fn transform_class_legacy( - &mut self, - class: &mut Box<'a, Class<'a>>, - class_name: Option, - ) -> Declaration<'a> { - let class_binding_identifier = &class.id.clone().unwrap_or_else(|| { - let class_name = class_name.unwrap_or_else(|| self.get_unique_name("class")); - BindingIdentifier::new(SPAN, self.ctx.ast.new_atom(&class_name)) - }); - let class_name = BindingPattern::new_with_kind( - self.ctx.ast.binding_pattern_identifier(self.ctx.ast.copy(class_binding_identifier)), - ); - - let init = { - let class_identifier_name = self.get_unique_name("class"); - let class_identifier = - IdentifierReference::new(SPAN, self.ctx.ast.new_atom(&class_identifier_name)); - - let decl = self.ctx.ast.variable_declaration( - SPAN, - VariableDeclarationKind::Var, - self.ctx.ast.new_vec_single(self.get_variable_declarator(&class_identifier_name)), - Modifiers::empty(), - ); - self.top_statements - .push(Statement::Declaration(Declaration::VariableDeclaration(decl))); - - let left = self.ctx.ast.simple_assignment_target_identifier(class_identifier.clone()); - let right = self.ctx.ast.class_expression(self.ctx.ast.copy(class)); - let new_expr = - self.ctx.ast.assignment_expression(SPAN, AssignmentOperator::Assign, left, right); - - let new_expr = class.decorators.drain(..).fold(new_expr, |new_expr, decorator| { - match &decorator.expression { - Expression::Identifier(identifier) => self.ctx.ast.call_expression( - SPAN, - self.ctx.ast.identifier_reference_expression(IdentifierReference::new( - SPAN, - identifier.name.clone(), - )), - self.ctx - .ast - .new_vec_single(Argument::Expression(self.ctx.ast.copy(&new_expr))), - false, - None, - ), - _ => new_expr, - } - }); - - self.ctx.ast.logical_expression( - SPAN, - new_expr, - LogicalOperator::Or, - self.ctx.ast.identifier_reference_expression(class_identifier), - ) - }; - - let declarator = self.ctx.ast.variable_declarator( - SPAN, - VariableDeclarationKind::Let, - class_name, - Some(init), - false, - ); - - Declaration::VariableDeclaration(self.ctx.ast.variable_declaration( - SPAN, - VariableDeclarationKind::Let, - self.ctx.ast.new_vec_single(declarator), - Modifiers::empty(), - )) - } - - /// https://github.com/babel/babel/blob/eccbd203383487f6957dcf086aa83d773691560b/packages/babel-helpers/src/helpers/applyDecs2305.ts#L7-L45 - fn get_decorator_info( - &self, - key: &PropertyKey<'a>, - value: Option>>, - flag: DecoratorFlags, - decorator: &Decorator<'a>, - ) -> Expression<'a> { - let name = key.name(); - // [dec, flag, name, defaultValue | (o) => o.#a, (o, v) => o.#a = v] - let mut decorator_elements = self.ctx.ast.new_vec_with_capacity(2); - decorator_elements - .push(ArrayExpressionElement::Expression(self.ctx.ast.copy(&decorator.expression))); - decorator_elements.push(ArrayExpressionElement::Expression( - self.ctx.ast.literal_number_expression(NumericLiteral::new( - SPAN, - 0f64, - self.ctx.ast.new_str(flag.to_value().to_string().as_str()), - oxc_syntax::NumberBase::Decimal, - )), - )); - if let Some(name) = name { - decorator_elements.push(ArrayExpressionElement::Expression( - self.ctx.ast.literal_string_expression(StringLiteral::new( - SPAN, - self.ctx.ast.new_atom(&name), - )), - )); - - if key.is_private_identifier() { - if let Some(value) = value { - decorator_elements.push(ArrayExpressionElement::Expression( - Expression::FunctionExpression(value), - )); - } else { - // o => o.#a - let mut items = self.ctx.ast.new_vec_single(self.ctx.ast.formal_parameter( - SPAN, - self.ctx.ast.binding_pattern( - self.ctx.ast.binding_pattern_identifier(BindingIdentifier::new( - SPAN, - self.ctx.ast.new_atom("o"), - )), - None, - false, - ), - None, - false, - false, - self.ctx.ast.new_vec(), - )); - let private_field = self.ctx.ast.private_field( - SPAN, - self.ctx.ast.identifier_reference_expression(IdentifierReference::new( - SPAN, - self.ctx.ast.new_atom("o"), - )), - PrivateIdentifier::new(SPAN, self.ctx.ast.new_atom(&name)), - false, - ); - let params = self.ctx.ast.formal_parameters( - SPAN, - FormalParameterKind::ArrowFormalParameters, - self.ctx.ast.copy(&items), - None, - ); - decorator_elements.push(ArrayExpressionElement::Expression( - self.ctx.ast.arrow_function_expression( - SPAN, - true, - false, - params, - self.ctx.ast.function_body( - SPAN, - self.ctx.ast.new_vec(), - self.ctx.ast.new_vec_single( - self.ctx.ast.expression_statement( - SPAN, - self.ctx - .ast - .member_expression(self.ctx.ast.copy(&private_field)), - ), - ), - ), - None, - None, - ), - )); - - { - // (o, v) => o.#a = v - items.push(self.ctx.ast.formal_parameter( - SPAN, - self.ctx.ast.binding_pattern( - self.ctx.ast.binding_pattern_identifier(BindingIdentifier::new( - SPAN, - self.ctx.ast.new_atom("v"), - )), - None, - false, - ), - None, - false, - false, - self.ctx.ast.new_vec(), - )); - - let params = self.ctx.ast.formal_parameters( - SPAN, - FormalParameterKind::ArrowFormalParameters, - items, - None, - ); - - decorator_elements.push(ArrayExpressionElement::Expression( - self.ctx.ast.arrow_function_expression( - SPAN, - true, - false, - params, - self.ctx.ast.function_body( - SPAN, - self.ctx.ast.new_vec(), - self.ctx.ast.new_vec_single( - self.ctx.ast.expression_statement( - SPAN, - self.ctx.ast.assignment_expression( - SPAN, - AssignmentOperator::Assign, - self.ctx - .ast - .simple_assignment_target_member_expression( - private_field, - ), - self.ctx.ast.identifier_reference_expression( - IdentifierReference::new( - SPAN, - self.ctx.ast.new_atom("v"), - ), - ), - ), - ), - ), - ), - None, - None, - ), - )); - } - } - } - } - self.ctx.ast.array_expression(SPAN, decorator_elements, None) - } - - // _ => #a in _; - fn get_is_private_function(&self, name: &Atom<'a>) -> Expression<'a> { - self.ctx.ast.arrow_function_expression( - SPAN, - true, - false, - self.ctx.ast.formal_parameters( - SPAN, - FormalParameterKind::ArrowFormalParameters, - self.ctx.ast.new_vec_single(self.ctx.ast.formal_parameter( - SPAN, - self.ctx.ast.binding_pattern( - self.ctx.ast.binding_pattern_identifier(BindingIdentifier::new( - SPAN, - self.ctx.ast.new_atom("_"), - )), - None, - false, - ), - None, - false, - false, - self.ctx.ast.new_vec(), - )), - None, - ), - self.ctx.ast.function_body( - SPAN, - self.ctx.ast.new_vec(), - self.ctx.ast.new_vec_single(self.ctx.ast.expression_statement( - SPAN, - self.ctx.ast.private_in_expression( - SPAN, - PrivateIdentifier::new(SPAN, name.clone()), - self.ctx.ast.identifier_reference_expression(IdentifierReference::new( - SPAN, - self.ctx.ast.new_atom("_"), - )), - ), - )), - ), - None, - None, - ) - } -} diff --git a/crates/oxc_transformer/src/proposals/mod.rs b/crates/oxc_transformer/src/proposals/mod.rs deleted file mode 100644 index 0a69a3d45f1ba..0000000000000 --- a/crates/oxc_transformer/src/proposals/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod decorators; - -pub use decorators::{Decorators, DecoratorsOptions}; diff --git a/crates/oxc_transformer/src/react_jsx/mod.rs b/crates/oxc_transformer/src/react_jsx/mod.rs deleted file mode 100644 index a1bbac5b36900..0000000000000 --- a/crates/oxc_transformer/src/react_jsx/mod.rs +++ /dev/null @@ -1,806 +0,0 @@ -mod options; - -use oxc_allocator::Vec; -use oxc_ast::ast::*; -use oxc_diagnostics::{ - miette::{self, Diagnostic}, - thiserror::Error, -}; -use oxc_span::{CompactStr, GetSpan, Span, SPAN}; -use oxc_syntax::{ - identifier::{is_irregular_whitespace, is_line_terminator}, - xml_entities::XML_ENTITIES, -}; - -pub use self::options::{ReactJsxOptions, ReactJsxRuntime, ReactJsxRuntimeOption}; -use crate::context::TransformerCtx; - -#[derive(Debug, Error, Diagnostic)] -#[error("pragma and pragmaFrag cannot be set when runtime is automatic.")] -#[diagnostic(severity(warning), help("Remove `pragma` and `pragmaFrag` options."))] -struct PragmaAndPragmaFragCannotBeSet; - -#[derive(Debug, Error, Diagnostic)] -#[error("importSource cannot be set when runtime is classic.")] -#[diagnostic(severity(warning), help("Remove `importSource` option."))] -struct ImportSourceCannotBeSet; - -#[derive(Debug, Error, Diagnostic)] -#[error("Namespace tags are not supported by default. React's JSX doesn't support namespace tags. You can set `throwIfNamespace: false` to bypass this warning.")] -#[diagnostic(severity(warning))] -struct NamespaceDoesNotSupport(#[label] Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Please provide an explicit key value. Using \"key\" as a shorthand for \"key={{true}}\" is not allowed.")] -#[diagnostic(severity(warning))] -struct ValuelessKey(#[label] Span); - -#[derive(Debug, Error, Diagnostic)] -#[error("Spread children are not supported in React.")] -#[diagnostic(severity(warning))] -struct SpreadChildrenAreNotSupported(#[label] Span); - -/// Transform React JSX -/// -/// References: -/// * -/// * -pub struct ReactJsx<'a> { - ctx: TransformerCtx<'a>, - options: ReactJsxOptions, - - imports: Vec<'a, Statement<'a>>, - import_jsx: bool, - import_jsxs: bool, - import_fragment: bool, - import_create_element: bool, - require_jsx_runtime: bool, - jsx_runtime_importer: CompactStr, - pub babel_8_breaking: Option, - default_runtime: ReactJsxRuntime, -} - -enum JSXElementOrFragment<'a, 'b> { - Element(&'b JSXElement<'a>), - Fragment(&'b JSXFragment<'a>), -} - -impl<'a, 'b> JSXElementOrFragment<'a, 'b> { - fn attributes(&self) -> Option<&'b Vec<'a, JSXAttributeItem<'a>>> { - match self { - Self::Element(e) if !e.opening_element.attributes.is_empty() => { - Some(&e.opening_element.attributes) - } - _ => None, - } - } - - fn children(&self) -> &'b Vec<'a, JSXChild<'a>> { - match self { - Self::Element(e) => &e.children, - Self::Fragment(e) => &e.children, - } - } - - /// The react jsx/jsxs transform falls back to `createElement` when an explicit `key` argument comes after a spread - /// - fn has_key_after_props_spread(&self) -> bool { - let Self::Element(e) = self else { return false }; - let mut spread = false; - for attr in &e.opening_element.attributes { - if matches!(attr, JSXAttributeItem::SpreadAttribute(_)) { - spread = true; - } else if spread && matches!(attr, JSXAttributeItem::Attribute(a) if a.is_key()) { - return true; - } - } - false - } -} - -impl<'a> ReactJsx<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - let imports = ctx.ast.new_vec(); - let jsx_options = ctx.options.react_jsx.clone()?.with_comments(&ctx.semantic()); - if ctx.options.babel_8_breaking == Some(true) { - if jsx_options.use_built_ins.is_some() { - ctx.error(miette::Error::msg("@babel/plugin-transform-react-jsx: Since \"useBuiltIns\" is removed in Babel 8, you can remove it from the config.")); - return None; - } - - if jsx_options.use_spread.is_some() { - ctx.error(miette::Error::msg("@babel/plugin-transform-react-jsx: Since Babel 8, an inline object with spread elements is always used, and the \"useSpread\" option is no longer available. Please remove it from your config.")); - return None; - } - } - - let default_runtime = Self::normalize_default_runtime( - jsx_options.runtime.as_ref(), - ctx.options.babel_8_breaking, - &ctx, - )?; - - let jsx_runtime_importer = - if jsx_options.import_source == "react" || default_runtime.is_classic() { - CompactStr::from("react/jsx-runtime") - } else { - CompactStr::from(format!("{}/jsx-runtime", jsx_options.import_source)) - }; - Some(Self { - options: jsx_options, - imports, - jsx_runtime_importer, - require_jsx_runtime: false, - import_jsx: false, - import_jsxs: false, - import_fragment: false, - import_create_element: false, - babel_8_breaking: ctx.options.babel_8_breaking, - default_runtime, - ctx, - }) - } - - fn normalize_default_runtime( - runtime: Option<&ReactJsxRuntimeOption>, - babel_8_breaking: Option, - ctx: &TransformerCtx<'a>, - ) -> Option { - match runtime { - Some(ReactJsxRuntimeOption::Valid(runtime)) => Some(*runtime), - None => { - // TODO: development mode https://github.com/babel/babel/blob/ff3481746a830e0e94626de4c4cb075ea5f2f5dc/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts#L77-L81 - if babel_8_breaking == Some(true) { - Some(ReactJsxRuntime::Automatic) - } else { - Some(ReactJsxRuntime::Classic) - } - } - Some(_) => { - ctx.error(miette::Error::msg( - "Runtime must be either \"classic\" or \"automatic\".", - )); - None - } - } - } - - pub fn transform_expression(&mut self, expr: &mut Expression<'a>) { - match expr { - Expression::JSXElement(e) => { - *expr = self.transform_jsx(&JSXElementOrFragment::Element(e)); - } - Expression::JSXFragment(e) => { - *expr = self.transform_jsx(&JSXElementOrFragment::Fragment(e)); - } - _ => {} - } - } - - pub fn add_react_jsx_runtime_imports(&mut self, program: &mut Program<'a>) { - if self.default_runtime.is_classic() { - if self.options.import_source != "react" { - self.ctx.error(ImportSourceCannotBeSet); - } - return; - } - - if self.options.pragma != "React.createElement" - || self.options.pragma_frag != "React.Fragment" - { - self.ctx.error(PragmaAndPragmaFragCannotBeSet); - return; - } - - let imports = self.ctx.ast.move_statement_vec(&mut self.imports); - let index = program - .body - .iter() - .rposition(|stmt| matches!(stmt, Statement::ModuleDeclaration(m) if m.is_import())) - .map_or(0, |i| i + 1); - program.body.splice(index..index, imports); - } - - fn new_string_literal(&self, name: &str) -> StringLiteral<'a> { - StringLiteral::new(SPAN, self.ctx.ast.new_atom(name)) - } - - fn add_import<'b>( - &mut self, - e: &JSXElementOrFragment<'a, 'b>, - has_key_after_props_spread: bool, - need_jsxs: bool, - ) { - if self.default_runtime.is_classic() { - return; - } - match e { - JSXElementOrFragment::Element(_) if has_key_after_props_spread => { - self.add_import_create_element(); - } - JSXElementOrFragment::Element(_) if need_jsxs => self.add_import_jsxs(), - JSXElementOrFragment::Element(_) => self.add_import_jsx(), - JSXElementOrFragment::Fragment(_) => { - self.add_import_fragment(); - if need_jsxs { - self.add_import_jsxs(); - } - } - } - } - - fn add_require_jsx_runtime(&mut self) { - if !self.require_jsx_runtime { - self.require_jsx_runtime = true; - self.add_require_statement( - "_reactJsxRuntime", - self.new_string_literal(self.jsx_runtime_importer.as_str()), - false, - ); - } - } - - fn add_import_jsx(&mut self) { - if self.ctx.source_type().is_script() { - self.add_require_jsx_runtime(); - } else if !self.import_jsx { - self.import_jsx = true; - self.add_import_statement( - "jsx", - "_jsx", - self.new_string_literal(self.jsx_runtime_importer.as_str()), - ); - } - } - - fn add_import_jsxs(&mut self) { - if self.ctx.source_type().is_script() { - self.add_require_jsx_runtime(); - } else if !self.import_jsxs { - self.import_jsxs = true; - let source = self.new_string_literal(self.jsx_runtime_importer.as_str()); - self.add_import_statement("jsxs", "_jsxs", source); - } - } - - fn add_import_fragment(&mut self) { - if self.ctx.source_type().is_script() { - self.add_require_jsx_runtime(); - } else if !self.import_fragment { - self.import_fragment = true; - let source = self.new_string_literal(self.jsx_runtime_importer.as_str()); - self.add_import_statement("Fragment", "_Fragment", source); - self.add_import_jsx(); - } - } - - fn add_import_create_element(&mut self) { - if !self.import_create_element { - self.import_create_element = true; - - if self.ctx.source_type().is_script() { - self.add_require_statement( - "_react", - self.new_string_literal(self.options.import_source.as_ref()), - true, - ); - } else { - let source = self.new_string_literal(self.options.import_source.as_ref()); - self.add_import_statement("createElement", "_createElement", source); - } - } - } - - fn add_import_statement(&mut self, imported: &str, local: &str, source: StringLiteral<'a>) { - let mut specifiers = self.ctx.ast.new_vec_with_capacity(1); - specifiers.push(ImportDeclarationSpecifier::ImportSpecifier(ImportSpecifier { - span: SPAN, - imported: ModuleExportName::Identifier(IdentifierName::new( - SPAN, - self.ctx.ast.new_atom(imported), - )), - local: BindingIdentifier::new(SPAN, self.ctx.ast.new_atom(local)), - import_kind: ImportOrExportKind::Value, - })); - let import_statement = self.ctx.ast.import_declaration( - SPAN, - Some(specifiers), - source, - None, - ImportOrExportKind::Value, - ); - let decl = - self.ctx.ast.module_declaration(ModuleDeclaration::ImportDeclaration(import_statement)); - self.imports.push(decl); - } - - fn add_require_statement( - &mut self, - variable_name: &str, - source: StringLiteral<'a>, - front: bool, - ) { - let callee = self - .ctx - .ast - .identifier_reference_expression(IdentifierReference::new(SPAN, "require".into())); - let arguments = self - .ctx - .ast - .new_vec_single(Argument::Expression(self.ctx.ast.literal_string_expression(source))); - let init = self.ctx.ast.call_expression(SPAN, callee, arguments, false, None); - let id = self.ctx.ast.binding_pattern( - self.ctx.ast.binding_pattern_identifier(BindingIdentifier::new( - SPAN, - self.ctx.ast.new_atom(variable_name), - )), - None, - false, - ); - let decl = self.ctx.ast.new_vec_single(self.ctx.ast.variable_declarator( - SPAN, - VariableDeclarationKind::Var, - id, - Some(init), - false, - )); - - let variable_declaration = self.ctx.ast.variable_declaration( - SPAN, - VariableDeclarationKind::Var, - decl, - Modifiers::empty(), - ); - let stmt = Statement::Declaration(Declaration::VariableDeclaration(variable_declaration)); - - if front { - self.imports.insert(0, stmt); - } else { - self.imports.push(stmt); - } - } - - fn transform_jsx<'b>(&mut self, e: &JSXElementOrFragment<'a, 'b>) -> Expression<'a> { - let is_classic = self.default_runtime.is_classic(); - let is_automatic = self.default_runtime.is_automatic(); - let has_key_after_props_spread = e.has_key_after_props_spread(); - - // TODO: compute the correct capacity for both runtimes - let mut arguments = self.ctx.ast.new_vec_with_capacity(1); - - arguments.push(Argument::Expression(match e { - JSXElementOrFragment::Element(e) => { - self.transform_element_name(&e.opening_element.name) - } - JSXElementOrFragment::Fragment(_) => self.get_fragment(), - })); - - // The key prop in `
` - let mut key_prop = None; - - let attributes = e.attributes(); - let attributes_len = attributes.map_or(0, |attrs| attrs.len()); - - // Add `null` to second argument in classic mode - if is_classic && attributes_len == 0 { - let null_expr = self.ctx.ast.literal_null_expression(NullLiteral::new(SPAN)); - arguments.push(Argument::Expression(null_expr)); - } - - // The object properties for the second argument of `React.createElement` - let mut properties = self.ctx.ast.new_vec(); - - if let Some(attributes) = attributes { - // TODO: compute the correct capacity for both runtimes - - for attribute in attributes { - // optimize `{...prop}` to `prop` in static mode - if is_classic && attributes_len == 1 { - if let JSXAttributeItem::SpreadAttribute(spread) = attribute { - // deopt if spreading an object with `__proto__` key - if !matches!(&spread.argument, Expression::ObjectExpression(o) if o.has_proto()) - { - arguments - .push(Argument::Expression(self.ctx.ast.copy(&spread.argument))); - continue; - } - } - } - if let JSXAttributeItem::Attribute(attr) = attribute { - if attr.is_key() { - if attr.value.is_none() { - self.ctx.error(ValuelessKey(attr.name.span())); - } - // In automatic mode, extract the key before spread prop, - // and add it to the third argument later. - if is_automatic && !has_key_after_props_spread { - key_prop = attr.value.as_ref(); - continue; - } - } - } - - // Add attribute to prop object - self.transform_jsx_attribute_item(&mut properties, attribute); - } - } - - let mut need_jsxs = false; - - let children = e.children(); - - // Append children to object properties in automatic mode - if is_automatic { - let allocator = self.ctx.ast.allocator; - let mut children = Vec::from_iter_in( - children.iter().filter_map(|child| self.transform_jsx_child(child)), - allocator, - ); - let children_len = children.len(); - if children_len != 0 { - let value = if children_len == 1 { - children.pop().unwrap() - } else { - let elements = Vec::from_iter_in( - children.into_iter().map(ArrayExpressionElement::Expression), - allocator, - ); - need_jsxs = true; - self.ctx.ast.array_expression(SPAN, elements, None) - }; - - let kind = PropertyKind::Init; - let ident = IdentifierName::new(SPAN, "children".into()); - let key = self.ctx.ast.property_key_identifier(ident); - let object_property = - self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false); - properties.push(ObjectPropertyKind::ObjectProperty(object_property)); - } - } - - self.add_import(e, has_key_after_props_spread, need_jsxs); - - if !properties.is_empty() || is_automatic { - let object_expression = self.ctx.ast.object_expression(SPAN, properties, None); - arguments.push(Argument::Expression(object_expression)); - } - - if is_automatic && key_prop.is_some() { - arguments.push(Argument::Expression(self.transform_jsx_attribute_value(key_prop))); - } - - if is_classic && !children.is_empty() { - arguments.extend( - children - .iter() - .filter_map(|child| self.transform_jsx_child(child)) - .map(Argument::Expression), - ); - } - - let callee = self.get_create_element(has_key_after_props_spread, need_jsxs); - self.ctx.ast.call_expression(SPAN, callee, arguments, false, None) - } - - fn get_react_references(&mut self) -> Expression<'a> { - let ident = IdentifierReference::new(SPAN, "React".into()); - self.ctx.ast.identifier_reference_expression(ident) - } - - fn get_static_member_expression( - &self, - object_ident_name: &str, - property_name: &str, - ) -> Expression<'a> { - let property = IdentifierName::new(SPAN, self.ctx.ast.new_atom(property_name)); - let ident = IdentifierReference::new(SPAN, self.ctx.ast.new_atom(object_ident_name)); - let object = self.ctx.ast.identifier_reference_expression(ident); - self.ctx.ast.static_member_expression(SPAN, object, property, false) - } - - /// Get the callee from `pragma` and `pragmaFrag` - fn get_call_expression_callee(&self, literal_callee: &str) -> Expression<'a> { - let mut callee = literal_callee.split('.'); - let member = callee.next().unwrap(); - let property = callee.next(); - property.map_or_else( - || { - let ident = IdentifierReference::new(SPAN, self.ctx.ast.new_atom(member)); - self.ctx.ast.identifier_reference_expression(ident) - }, - |property_name| self.get_static_member_expression(member, property_name), - ) - } - - fn get_create_element( - &mut self, - has_key_after_props_spread: bool, - jsxs: bool, - ) -> Expression<'a> { - match self.default_runtime { - ReactJsxRuntime::Classic => { - if self.options.pragma == "React.createElement" { - let object = self.get_react_references(); - let property = IdentifierName::new(SPAN, "createElement".into()); - return self.ctx.ast.static_member_expression(SPAN, object, property, false); - } - - self.get_call_expression_callee(self.options.pragma.as_ref()) - } - ReactJsxRuntime::Automatic => { - let is_script = self.ctx.source_type().is_script(); - let name = if is_script { - if has_key_after_props_spread { - "createElement" - } else if jsxs { - "jsxs" - } else { - "jsx" - } - } else if has_key_after_props_spread { - "_createElement" - } else if jsxs { - "_jsxs" - } else { - "_jsx" - }; - - if is_script { - let object_ident_name = - if has_key_after_props_spread { "_react" } else { "_reactJsxRuntime" }; - self.get_static_member_expression(object_ident_name, name) - } else { - let ident = IdentifierReference::new(SPAN, name.into()); - self.ctx.ast.identifier_reference_expression(ident) - } - } - } - } - - fn get_fragment(&mut self) -> Expression<'a> { - match self.default_runtime { - ReactJsxRuntime::Classic => { - if self.options.pragma_frag == "React.Fragment" { - let object = self.get_react_references(); - let property = IdentifierName::new(SPAN, "Fragment".into()); - return self.ctx.ast.static_member_expression(SPAN, object, property, false); - } - - self.get_call_expression_callee(self.options.pragma_frag.as_ref()) - } - ReactJsxRuntime::Automatic => { - if self.ctx.source_type().is_script() { - self.get_static_member_expression("_reactJsxRuntime", "Fragment") - } else { - let ident = IdentifierReference::new(SPAN, "_Fragment".into()); - self.ctx.ast.identifier_reference_expression(ident) - } - } - } - } - - fn get_attribute_name(&self, name: &JSXAttributeName<'a>) -> PropertyKey<'a> { - match name { - JSXAttributeName::Identifier(ident) => { - let name = ident.name.clone(); - if ident.name.contains('-') { - let expr = - self.ctx.ast.literal_string_expression(StringLiteral::new(SPAN, name)); - self.ctx.ast.property_key_expression(expr) - } else { - self.ctx.ast.property_key_identifier(IdentifierName::new(SPAN, name)) - } - } - JSXAttributeName::NamespacedName(name) => { - let name = self.ctx.ast.new_atom(&name.to_string()); - let expr = self.ctx.ast.literal_string_expression(StringLiteral::new(SPAN, name)); - self.ctx.ast.property_key_expression(expr) - } - } - } - - fn transform_element_name(&mut self, name: &JSXElementName<'a>) -> Expression<'a> { - match name { - JSXElementName::Identifier(ident) => { - let name = ident.name.clone(); - if name == "this" { - self.ctx.ast.this_expression(SPAN) - } else if ident.name.chars().next().is_some_and(|c| c.is_ascii_lowercase()) { - self.ctx.ast.literal_string_expression(StringLiteral::new(SPAN, name)) - } else { - self.ctx - .ast - .identifier_reference_expression(IdentifierReference::new(SPAN, name)) - } - } - JSXElementName::MemberExpression(member_expr) => { - self.transform_jsx_member_expression(member_expr) - } - JSXElementName::NamespacedName(name) => { - if self.options.throw_if_namespace { - self.ctx.error(NamespaceDoesNotSupport(name.span)); - } - - let string_literal = - StringLiteral::new(SPAN, self.ctx.ast.new_atom(&name.to_string())); - self.ctx.ast.literal_string_expression(string_literal) - } - } - } - - fn transform_jsx_attribute_item( - &mut self, - properties: &mut Vec<'a, ObjectPropertyKind<'a>>, - attribute: &JSXAttributeItem<'a>, - ) { - match attribute { - JSXAttributeItem::Attribute(attr) => { - let kind = PropertyKind::Init; - let key = self.get_attribute_name(&attr.name); - let value = self.transform_jsx_attribute_value(attr.value.as_ref()); - let object_property = - self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false); - let object_property = ObjectPropertyKind::ObjectProperty(object_property); - properties.push(object_property); - } - JSXAttributeItem::SpreadAttribute(attr) => match &attr.argument { - Expression::ObjectExpression(expr) if !expr.has_proto() => { - properties.extend(self.ctx.ast.copy(&expr.properties)); - } - expr => { - let argument = self.ctx.ast.copy(expr); - let spread_property = self.ctx.ast.spread_element(SPAN, argument); - let object_property = ObjectPropertyKind::SpreadProperty(spread_property); - properties.push(object_property); - } - }, - } - } - - fn transform_jsx_attribute_value( - &mut self, - value: Option<&JSXAttributeValue<'a>>, - ) -> Expression<'a> { - match value { - Some(JSXAttributeValue::StringLiteral(s)) => { - let jsx_text = Self::decode_entities(s.value.as_str()); - let literal = StringLiteral::new(s.span, self.ctx.ast.new_atom(&jsx_text)); - self.ctx.ast.literal_string_expression(literal) - } - Some(JSXAttributeValue::Element(e)) => { - self.transform_jsx(&JSXElementOrFragment::Element(e)) - } - Some(JSXAttributeValue::Fragment(e)) => { - self.transform_jsx(&JSXElementOrFragment::Fragment(e)) - } - Some(JSXAttributeValue::ExpressionContainer(c)) => match &c.expression { - JSXExpression::Expression(e) => self.ctx.ast.copy(e), - JSXExpression::EmptyExpression(_e) => { - self.ctx.ast.literal_boolean_expression(BooleanLiteral::new(SPAN, true)) - } - }, - None => self.ctx.ast.literal_boolean_expression(BooleanLiteral::new(SPAN, true)), - } - } - - fn transform_jsx_member_expression(&self, expr: &JSXMemberExpression<'a>) -> Expression<'a> { - let object = match &expr.object { - JSXMemberExpressionObject::Identifier(ident) => { - self.ctx.ast.identifier_reference_expression(IdentifierReference::new( - SPAN, - ident.name.clone(), - )) - } - JSXMemberExpressionObject::MemberExpression(expr) => { - self.transform_jsx_member_expression(expr) - } - }; - let property = IdentifierName::new(SPAN, expr.property.name.clone()); - self.ctx.ast.static_member_expression(SPAN, object, property, false) - } - - fn transform_jsx_child(&mut self, child: &JSXChild<'a>) -> Option> { - match child { - JSXChild::Text(text) => self.transform_jsx_text(text.value.as_str()), - JSXChild::ExpressionContainer(e) => match &e.expression { - JSXExpression::Expression(e) => Some(self.ctx.ast.copy(e)), - JSXExpression::EmptyExpression(_) => None, - }, - JSXChild::Element(e) => Some(self.transform_jsx(&JSXElementOrFragment::Element(e))), - JSXChild::Fragment(e) => Some(self.transform_jsx(&JSXElementOrFragment::Fragment(e))), - JSXChild::Spread(e) => { - self.ctx.error(SpreadChildrenAreNotSupported(e.span)); - None - } - } - } - - fn transform_jsx_text(&self, text: &str) -> Option> { - Self::fixup_whitespace_and_decode_entities(text).map(|s| { - let s = StringLiteral::new(SPAN, self.ctx.ast.new_atom(&s)); - self.ctx.ast.literal_string_expression(s) - }) - } - - /// JSX trims whitespace at the end and beginning of lines, except that the - /// start/end of a tag is considered a start/end of a line only if that line is - /// on the same line as the closing tag. See examples in - /// tests/cases/conformance/jsx/tsxReactEmitWhitespace.tsx - /// See also https://www.w3.org/TR/html4/struct/text.html#h-9.1 and https://www.w3.org/TR/CSS2/text.html#white-space-model - /// - /// An equivalent algorithm would be: - /// - If there is only one line, return it. - /// - If there is only whitespace (but multiple lines), return `undefined`. - /// - Split the text into lines. - /// - 'trimRight' the first line, 'trimLeft' the last line, 'trim' middle lines. - /// - Decode entities on each line (individually). - /// - Remove empty lines and join the rest with " ". - /// - /// - fn fixup_whitespace_and_decode_entities(text: &str) -> Option { - let mut acc: Option = None; - let mut first_non_whitespace: Option = Some(0); - let mut last_non_whitespace: Option = None; - let mut i: usize = 0; - for c in text.chars() { - if is_line_terminator(c) { - if let (Some(first), Some(last)) = (first_non_whitespace, last_non_whitespace) { - acc = Some(Self::add_line_of_jsx_text(acc, &text[first..=last])); - } - first_non_whitespace = None; - } else if c != ' ' && !is_irregular_whitespace(c) { - last_non_whitespace = Some(i); - if first_non_whitespace.is_none() { - first_non_whitespace.replace(i); - } - } - i += c.len_utf8(); - } - if let Some(first) = first_non_whitespace { - Some(Self::add_line_of_jsx_text(acc, &text[first..])) - } else { - acc - } - } - - fn add_line_of_jsx_text(acc: Option, trimmed_line: &str) -> String { - let decoded = Self::decode_entities(trimmed_line); - if let Some(acc) = acc { - format!("{acc} {decoded}") - } else { - decoded - } - } - - /// * Replace entities like " ", "{", and "�" with the characters they encode. - /// * See https://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references - /// Code adapted from - fn decode_entities(s: &str) -> String { - let mut buffer = vec![]; - let mut chars = s.bytes().enumerate(); - let mut prev = 0; - while let Some((i, c)) = chars.next() { - if c == b'&' { - let start = i; - let mut end = None; - for (j, c) in chars.by_ref() { - if c == b';' { - end.replace(j); - break; - } - } - if let Some(end) = end { - let word = &s[start + 1..end]; - buffer.extend_from_slice(s[prev..start].as_bytes()); - prev = end + 1; - if let Some(c) = XML_ENTITIES.get(word) { - buffer.extend_from_slice(c.to_string().as_bytes()); - } - } - } - } - buffer.extend_from_slice(s[prev..].as_bytes()); - // Safety: The buffer is constructed from valid utf chars. - unsafe { String::from_utf8_unchecked(buffer) } - } -} diff --git a/crates/oxc_transformer/src/react_jsx/options.rs b/crates/oxc_transformer/src/react_jsx/options.rs deleted file mode 100644 index 9ac934efe8b9c..0000000000000 --- a/crates/oxc_transformer/src/react_jsx/options.rs +++ /dev/null @@ -1,149 +0,0 @@ -use std::borrow::Cow; - -use oxc_semantic::Semantic; -use serde::Deserialize; - -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ReactJsxOptions { - /// Decides which runtime to use. - pub runtime: Option, - /// Toggles whether or not to throw an error if an XML namespaced tag name is used. e.g. `` - /// Though the JSX spec allows this, it is disabled by default since React's JSX does not currently have support for it. - #[serde(default = "default_throw_if_namespace")] - pub throw_if_namespace: bool, - /// Replaces the import source when importing functions. default to `react` - #[serde(default = "default_import_source")] - pub import_source: Cow<'static, str>, - /// Replace the function used when compiling JSX expressions. - /// It should be a qualified name (e.g. React.createElement) or an identifier (e.g. createElement). - /// default to `React.createElement` - /// - /// Note that the @jsx React.DOM pragma has been deprecated as of React v0.12 - #[serde(default = "default_pragma")] - pub pragma: Cow<'static, str>, - /// Replace the component used when compiling JSX fragments. It should be a valid JSX tag name. default to `React.Fragment` - #[serde(default = "default_pragma_frag")] - pub pragma_frag: Cow<'static, str>, - - /// When spreading props, use Object.assign directly instead of Babel's extend helper. - /// Use `Some` instead of `bool` because we want to know if user set this field explicitly, - /// which used for creating warning, - pub use_built_ins: Option, - /// When spreading props, use inline object with spread elements directly instead of Babel's extend helper or Object.assign. - /// Use `Some` instead of `bool` because we want to know if user set this field explicitly, - /// which used for creating warning, - pub use_spread: Option, -} - -fn default_throw_if_namespace() -> bool { - true -} - -fn default_import_source() -> Cow<'static, str> { - Cow::Borrowed("react") -} - -fn default_pragma() -> Cow<'static, str> { - Cow::Borrowed("React.createElement") -} - -fn default_pragma_frag() -> Cow<'static, str> { - Cow::Borrowed("React.Fragment") -} - -impl Default for ReactJsxOptions { - fn default() -> Self { - Self { - runtime: None, - throw_if_namespace: default_throw_if_namespace(), - import_source: default_import_source(), - pragma: default_pragma(), - pragma_frag: default_pragma_frag(), - use_built_ins: None, - use_spread: None, - } - } -} - -#[derive(Deserialize, Debug, Clone)] -#[serde(untagged)] -pub enum ReactJsxRuntimeOption { - Valid(ReactJsxRuntime), - // The order matters. The most permissive variant (i.e. the catch-all) - // should be tried last during deserialization. - Unknown(String), -} - -#[derive(Debug, Clone, Copy, Deserialize)] -#[serde(rename_all = "lowercase")] -pub enum ReactJsxRuntime { - /// Does not automatically import anything. - Classic, - /// Auto imports the functions that JSX transpiles to (default). - Automatic, -} - -impl ReactJsxRuntime { - pub fn is_classic(&self) -> bool { - matches!(self, Self::Classic) - } - - pub fn is_automatic(&self) -> bool { - matches!(self, Self::Automatic) - } -} - -impl ReactJsxOptions { - /// Scan through all comments and find the following pragmas - /// - /// * @jsxRuntime classic / automatic - /// - /// The comment does not need to be a jsdoc, - /// otherwise `JSDoc` could be used instead. - /// - /// This behavior is aligned with babel. - pub(crate) fn with_comments(mut self, semantic: &Semantic) -> Self { - for (_, span) in semantic.trivias().comments() { - let mut comment = span.source_text(semantic.source_text()).trim_start(); - // strip leading jsdoc comment `*` and then whitespaces - while let Some(cur_comment) = comment.strip_prefix('*') { - comment = cur_comment.trim_start(); - } - // strip leading `@` - let Some(comment) = comment.strip_prefix('@') else { continue }; - - // read jsxRuntime - match comment.strip_prefix("jsxRuntime").map(str::trim) { - Some("classic") => { - self.runtime = Some(ReactJsxRuntimeOption::Valid(ReactJsxRuntime::Classic)); - continue; - } - Some("automatic") => { - self.runtime = Some(ReactJsxRuntimeOption::Valid(ReactJsxRuntime::Automatic)); - continue; - } - _ => {} - } - - // read jsxImportSource - if let Some(import_source) = comment.strip_prefix("jsxImportSource").map(str::trim) { - self.import_source = Cow::from(import_source.to_string()); - continue; - } - - // read jsxFrag - if let Some(pragma_frag) = comment.strip_prefix("jsxFrag").map(str::trim) { - self.pragma_frag = Cow::from(pragma_frag.to_string()); - continue; - } - - // Put this condition at the end to avoid breaking @jsxXX - // read jsx - if let Some(pragma) = comment.strip_prefix("jsx").map(str::trim) { - self.pragma = Cow::from(pragma.to_string()); - } - } - self - } -} diff --git a/crates/oxc_transformer/src/regexp/mod.rs b/crates/oxc_transformer/src/regexp/mod.rs deleted file mode 100644 index 8d924a96549b0..0000000000000 --- a/crates/oxc_transformer/src/regexp/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod regexp_flags; - -pub use regexp_flags::RegexpFlags; diff --git a/crates/oxc_transformer/src/regexp/regexp_flags.rs b/crates/oxc_transformer/src/regexp/regexp_flags.rs deleted file mode 100644 index 225118a1be4a3..0000000000000 --- a/crates/oxc_transformer/src/regexp/regexp_flags.rs +++ /dev/null @@ -1,71 +0,0 @@ -use oxc_ast::{ast::*, AstBuilder}; -use oxc_span::{Atom, SPAN}; - -use std::rc::Rc; - -use crate::{context::TransformerCtx, TransformOptions, TransformTarget}; - -/// Transforms unsupported regex flags into Regex constructors. -/// -/// i.e. `/regex/flags` -> `new RegExp('regex', 'flags')` -/// -/// * ES2024 [Unicode Sets v](https://babel.dev/docs/babel-plugin-transform-unicode-sets-regex) -/// * ES2022 [Match Indices d](https://github.com/tc39/proposal-regexp-match-indices) -/// * ES2018 [Dotall s](https://babel.dev/docs/babel-plugin-transform-dotall-regex) -/// * ES2015 [Unicode u](https://babel.dev/docs/babel-plugin-transform-unicode-regex) -/// * ES2015 [Sticky y](https://babel.dev/docs/babel-plugin-transform-sticky-regex) -pub struct RegexpFlags<'a> { - ast: Rc>, - transform_flags: RegExpFlags, -} - -impl<'a> RegexpFlags<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Option { - let transform_flags = Self::from_transform_target(&ctx.options); - (!transform_flags.is_empty()).then_some(Self { ast: ctx.ast, transform_flags }) - } - - fn from_transform_target(options: &TransformOptions) -> RegExpFlags { - let target = options.target; - let mut flag = RegExpFlags::empty(); - if target < TransformTarget::ES2015 || options.sticky_regex { - flag |= RegExpFlags::Y; - } - if target < TransformTarget::ES2015 { - flag |= RegExpFlags::U; - } - if target < TransformTarget::ES2018 { - flag |= RegExpFlags::S; - } - if target < TransformTarget::ES2022 { - flag |= RegExpFlags::D; - } - if target < TransformTarget::ES2024 { - flag |= RegExpFlags::V; - } - if target < TransformTarget::ESNext { - flag |= RegExpFlags::I; - flag |= RegExpFlags::M; - } - flag - } - - // `/regex/flags` -> `new RegExp('regex', 'flags')` - pub fn transform_expression(&self, expr: &mut Expression<'a>) { - let Expression::RegExpLiteral(literal) = expr else { return }; - let regex = literal.regex.clone(); - if regex.flags.intersection(self.transform_flags).is_empty() { - return; - } - let ident = IdentifierReference::new(SPAN, Atom::from("RegExp")); - let callee = self.ast.identifier_reference_expression(ident); - let pattern = StringLiteral::new(SPAN, regex.pattern.clone()); - let flags = StringLiteral::new(SPAN, self.ast.new_atom(®ex.flags.to_string())); - let pattern_literal = self.ast.literal_string_expression(pattern); - let flags_literal = self.ast.literal_string_expression(flags); - let mut arguments = self.ast.new_vec_with_capacity(2); - arguments.push(Argument::Expression(pattern_literal)); - arguments.push(Argument::Expression(flags_literal)); - *expr = self.ast.new_expression(SPAN, callee, arguments, None); - } -} diff --git a/crates/oxc_transformer/src/tester.rs b/crates/oxc_transformer/src/tester.rs deleted file mode 100644 index 7abfb12ca88e4..0000000000000 --- a/crates/oxc_transformer/src/tester.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::path::PathBuf; - -use oxc_allocator::Allocator; -use oxc_codegen::{Codegen, CodegenOptions}; -use oxc_diagnostics::Error; -use oxc_parser::Parser; -use oxc_semantic::SemanticBuilder; -use oxc_span::SourceType; - -use crate::{TransformOptions, Transformer}; - -pub struct Tester { - source_type: SourceType, - - options: TransformOptions, - - allocator: Allocator, -} - -impl Tester { - pub fn new(filename: &str, options: TransformOptions) -> Self { - let source_type = SourceType::from_path(filename).unwrap(); - Self { source_type, options, allocator: Allocator::default() } - } - - pub fn test(&self, tests: &[(&str, &str)]) { - for (source_text, expected) in tests { - let transformed = self.transform(source_text).unwrap(); - let expected = self.codegen(expected); - assert_eq!(transformed, expected, "{source_text}"); - } - } - - fn transform(&self, source_text: &str) -> Result> { - let program = Parser::new(&self.allocator, source_text, self.source_type).parse().program; - let semantic = SemanticBuilder::new(source_text, self.source_type) - .build_module_record(PathBuf::new(), &program) - .build(&program) - .semantic; - - let program = self.allocator.alloc(program); - - Transformer::new(&self.allocator, self.source_type, semantic, self.options.clone()) - .build(program)?; - - Ok(Codegen::::new("", source_text, CodegenOptions::default()) - .build(program) - .source_text) - } - - fn codegen(&self, source_text: &str) -> String { - let program = Parser::new(&self.allocator, source_text, self.source_type).parse().program; - Codegen::::new("", source_text, CodegenOptions::default()) - .build(&program) - .source_text - } -} diff --git a/crates/oxc_transformer/src/typescript/mod.rs b/crates/oxc_transformer/src/typescript/mod.rs deleted file mode 100644 index 3e28326155228..0000000000000 --- a/crates/oxc_transformer/src/typescript/mod.rs +++ /dev/null @@ -1,811 +0,0 @@ -use oxc_allocator::{Box, Vec}; -use oxc_ast::ast::*; -use oxc_semantic::SymbolFlags; -use oxc_span::{Atom, SPAN}; -use oxc_syntax::{ - operator::{AssignmentOperator, BinaryOperator, LogicalOperator}, - NumberBase, -}; -use rustc_hash::{FxHashMap, FxHashSet}; -use std::mem; - -mod options; - -pub use self::options::TypescriptOptions; -use crate::{context::TransformerCtx, utils::is_valid_identifier}; - -/// Transform TypeScript -/// -/// References: -/// * -/// * -/// * -pub struct TypeScript<'a> { - ctx: TransformerCtx<'a>, - export_name_set: FxHashSet>, - options: TypescriptOptions, - namespace_arg_names: FxHashMap, usize>, -} - -impl<'a> TypeScript<'a> { - pub fn new(ctx: TransformerCtx<'a>) -> Self { - Self { - export_name_set: FxHashSet::default(), - options: ctx.options.typescript.clone().unwrap_or_default(), - namespace_arg_names: FxHashMap::default(), - ctx, - } - } - - pub fn transform_declaration(&mut self, decl: &mut Declaration<'a>) { - match decl { - Declaration::TSImportEqualsDeclaration(ts_import_equals) - if ts_import_equals.import_kind.is_value() => - { - *decl = self.transform_ts_import_equals(ts_import_equals); - } - Declaration::TSEnumDeclaration(ts_enum_declaration) => { - if let Some(expr) = self.transform_ts_enum(ts_enum_declaration) { - *decl = expr; - } - } - _ => {} - } - } - - pub fn transform_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) { - self.insert_let_decl_for_ts_module_block(stmts); - } - - pub fn transform_statement(&mut self, stmt: &mut Statement<'a>) { - let new_stmt = match stmt { - Statement::ModuleDeclaration(module_decl) => { - if let ModuleDeclaration::ExportNamedDeclaration(export_decl) = &mut **module_decl { - self.transform_export_named_declaration(export_decl).or_else(|| { - export_decl.declaration.as_mut().and_then(|decl| { - if decl.modifiers().is_some_and(Modifiers::is_contains_declare) { - None - } else { - match decl { - Declaration::TSModuleDeclaration(ts_module_decl) => { - Some(self.transform_ts_module_block(ts_module_decl)) - } - _ => None, - } - } - }) - }) - } else { - None - } - } - Statement::Declaration(Declaration::TSModuleDeclaration(ts_module_decl)) => { - if ts_module_decl.modifiers.is_contains_declare() { - None - } else { - Some(self.transform_ts_module_block(ts_module_decl)) - } - } - _ => None, - }; - - if let Some(new_stmt) = new_stmt { - *stmt = new_stmt; - } - } - - /// * Remove the top level import / export statements that are types - /// * Adds `export {}` if all import / export statements are removed, this is used to tell - /// downstream tools that this file is in ESM. - pub fn transform_program(&mut self, program: &mut Program<'a>) { - let mut export_type_names = FxHashSet::default(); - - // // Collect export names - program.body.iter().for_each(|stmt| { - if let Statement::ModuleDeclaration(module_decl) = stmt { - if let ModuleDeclaration::ExportNamedDeclaration(decl) = &**module_decl { - decl.specifiers.iter().for_each(|specifier| { - let name = specifier.exported.name(); - if self.is_import_binding_only(name) - && (decl.export_kind.is_type() || specifier.export_kind.is_type()) - { - export_type_names.insert(name.clone()); - } - }); - } - } - }); - - let mut import_type_names = FxHashSet::default(); - let mut delete_indexes = vec![]; - let mut module_declaration_len = 0; - - for (index, stmt) in program.body.iter_mut().enumerate() { - if let Statement::ModuleDeclaration(module_decl) = stmt { - module_declaration_len += 1; - match &mut **module_decl { - ModuleDeclaration::ExportNamedDeclaration(decl) => { - decl.specifiers.retain(|specifier| { - !(specifier.export_kind.is_type() - || import_type_names.contains(specifier.exported.name())) - }); - - if decl.export_kind.is_type() - || ((decl.declaration.is_none() - || decl.declaration.as_ref().is_some_and(|d| { - d.modifiers().is_some_and(|modifiers| { - modifiers.contains(ModifierKind::Declare) - }) || matches!( - d, - Declaration::TSInterfaceDeclaration(_) - | Declaration::TSTypeAliasDeclaration(_) - ) - })) - && decl.specifiers.is_empty()) - { - delete_indexes.push(index); - } - } - ModuleDeclaration::ImportDeclaration(decl) => { - let is_type = decl.import_kind.is_type(); - - let is_specifiers_empty = - decl.specifiers.as_ref().is_some_and(|s| s.is_empty()); - - if let Some(specifiers) = &mut decl.specifiers { - specifiers.retain(|specifier| match specifier { - ImportDeclarationSpecifier::ImportSpecifier(s) => { - if is_type || s.import_kind.is_type() { - import_type_names.insert(s.local.name.clone()); - return false; - } - - if self.options.only_remove_type_imports { - return true; - } - - if export_type_names.contains(&s.local.name) { - return false; - } - - self.has_value_references(&s.local.name) - } - ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => { - if is_type { - import_type_names.insert(s.local.name.clone()); - return false; - } - - if self.options.only_remove_type_imports { - return true; - } - self.has_value_references(&s.local.name) - } - ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => { - if is_type { - import_type_names.insert(s.local.name.clone()); - } - - if self.options.only_remove_type_imports { - return true; - } - - self.has_value_references(&s.local.name) - } - }); - } - - if decl.import_kind.is_type() - || (!self.options.only_remove_type_imports - && !is_specifiers_empty - && decl - .specifiers - .as_ref() - .is_some_and(|specifiers| specifiers.is_empty())) - { - delete_indexes.push(index); - } - } - _ => {} - } - } - } - - let delete_indexes_len = delete_indexes.len(); - - // remove empty imports/exports - for index in delete_indexes.into_iter().rev() { - program.body.remove(index); - } - - // explicit esm - if module_declaration_len > 0 && module_declaration_len == delete_indexes_len { - let empty_export = self.ctx.ast.export_named_declaration( - SPAN, - None, - self.ctx.ast.new_vec(), - None, - ImportOrExportKind::Value, - None, - ); - let export_decl = ModuleDeclaration::ExportNamedDeclaration(empty_export); - program.body.push(self.ctx.ast.module_declaration(export_decl)); - } - } - - /// ```ts - /// import foo from "foo"; // is import binding only - /// import bar from "bar"; // SymbolFlags::ImportBinding | SymbolFlags::BlockScopedVariable - /// let bar = "xx"; - /// ``` - fn is_import_binding_only(&self, name: &Atom) -> bool { - let root_scope_id = self.ctx.scopes().root_scope_id(); - - self.ctx.scopes().get_binding(root_scope_id, name).is_some_and(|symbol_id| { - let flag = self.ctx.symbols().get_flag(symbol_id); - flag.is_import_binding() - && !flag.intersects( - SymbolFlags::FunctionScopedVariable | SymbolFlags::BlockScopedVariable, - ) - }) - } - - fn has_value_references(&self, name: &Atom) -> bool { - let root_scope_id = self.ctx.scopes().root_scope_id(); - - self.ctx.scopes().get_binding(root_scope_id, name).is_some_and(|symbol_id| { - self.ctx.symbols().get_flag(symbol_id).is_export() - || self.ctx.symbols().get_resolved_references(symbol_id).any(|x| !x.is_type()) - }) - } -} - -impl<'a> TypeScript<'a> { - fn transform_ts_enum_members( - &self, - members: &mut Vec<'a, TSEnumMember<'a>>, - enum_name: &Atom<'a>, - ) -> Vec<'a, Statement<'a>> { - let mut default_init = self.ctx.ast.literal_number_expression(NumericLiteral { - span: SPAN, - value: 0.0, - raw: "0", - base: NumberBase::Decimal, - }); - let mut statements = self.ctx.ast.new_vec(); - - for member in members.iter_mut() { - let (member_name, member_span) = match &member.id { - TSEnumMemberName::Identifier(id) => (&id.name, id.span), - TSEnumMemberName::StringLiteral(str) => (&str.value, str.span), - TSEnumMemberName::ComputedPropertyName(..) - | TSEnumMemberName::NumericLiteral(..) => unreachable!(), - }; - - let mut init = self - .ctx - .ast - .move_expression(member.initializer.as_mut().unwrap_or(&mut default_init)); - - let is_str = init.is_string_literal(); - - let mut self_ref = { - let obj = self.ctx.ast.identifier_reference_expression(IdentifierReference::new( - SPAN, - enum_name.clone(), - )); - let expr = self - .ctx - .ast - .literal_string_expression(StringLiteral::new(SPAN, member_name.clone())); - self.ctx.ast.computed_member_expression(SPAN, obj, expr, false) - }; - - if is_valid_identifier(member_name, true) { - let ident = IdentifierReference::new(member_span, member_name.clone()); - - self_ref = self.ctx.ast.identifier_reference_expression(ident.clone()); - let init = - mem::replace(&mut init, self.ctx.ast.identifier_reference_expression(ident)); - - let kind = VariableDeclarationKind::Const; - let decls = { - let mut decls = self.ctx.ast.new_vec(); - - let binding_identifier = BindingIdentifier::new(SPAN, member_name.clone()); - let binding_pattern_kind = - self.ctx.ast.binding_pattern_identifier(binding_identifier); - let binding = self.ctx.ast.binding_pattern(binding_pattern_kind, None, false); - let decl = - self.ctx.ast.variable_declarator(SPAN, kind, binding, Some(init), false); - - decls.push(decl); - decls - }; - let decl = self.ctx.ast.variable_declaration(SPAN, kind, decls, Modifiers::empty()); - let stmt: Statement<'_> = - Statement::Declaration(Declaration::VariableDeclaration(decl)); - - statements.push(stmt); - } - - // Foo["x"] = init - let member_expr = { - let obj = self.ctx.ast.identifier_reference_expression(IdentifierReference::new( - SPAN, - enum_name.clone(), - )); - let expr = self - .ctx - .ast - .literal_string_expression(StringLiteral::new(SPAN, member_name.clone())); - - self.ctx.ast.computed_member(SPAN, obj, expr, false) - }; - let left = self.ctx.ast.simple_assignment_target_member_expression(member_expr); - let mut expr = - self.ctx.ast.assignment_expression(SPAN, AssignmentOperator::Assign, left, init); - - // Foo[Foo["x"] = init] = "x" - if !is_str { - let member_expr = { - let obj = self.ctx.ast.identifier_reference_expression( - IdentifierReference::new(SPAN, enum_name.clone()), - ); - self.ctx.ast.computed_member(SPAN, obj, expr, false) - }; - let left = self.ctx.ast.simple_assignment_target_member_expression(member_expr); - let right = self - .ctx - .ast - .literal_string_expression(StringLiteral::new(SPAN, member_name.clone())); - expr = self.ctx.ast.assignment_expression( - SPAN, - AssignmentOperator::Assign, - left, - right, - ); - } - - statements.push(self.ctx.ast.expression_statement(member.span, expr)); - - // 1 + Foo["x"] - default_init = { - let one = self.ctx.ast.literal_number_expression(NumericLiteral { - span: SPAN, - value: 1.0, - raw: "1", - base: NumberBase::Decimal, - }); - - self.ctx.ast.binary_expression(SPAN, one, BinaryOperator::Addition, self_ref) - }; - } - - let enum_ref = self - .ctx - .ast - .identifier_reference_expression(IdentifierReference::new(SPAN, enum_name.clone())); - // return Foo; - let return_stmt = self.ctx.ast.return_statement(SPAN, Some(enum_ref)); - statements.push(return_stmt); - - statements - } - - fn transform_ts_type_name(&self, type_name: &mut TSTypeName<'a>) -> Expression<'a> { - match type_name { - TSTypeName::IdentifierReference(reference) => { - self.ctx.ast.identifier_reference_expression(IdentifierReference::new( - SPAN, - reference.name.clone(), - )) - } - TSTypeName::QualifiedName(qualified_name) => self.ctx.ast.static_member_expression( - SPAN, - self.transform_ts_type_name(&mut qualified_name.left), - qualified_name.right.clone(), - false, - ), - } - } - - /// ```TypeScript - /// import b = babel; - /// import AliasModule = LongNameModule; - /// - /// ```JavaScript - /// var b = babel; - /// var AliasModule = LongNameModule; - /// ``` - fn transform_ts_import_equals( - &self, - decl: &mut Box<'a, TSImportEqualsDeclaration<'a>>, - ) -> Declaration<'a> { - let kind = VariableDeclarationKind::Var; - let decls = { - let binding_identifier = BindingIdentifier::new(SPAN, decl.id.name.clone()); - let binding_pattern_kind = self.ctx.ast.binding_pattern_identifier(binding_identifier); - let binding = self.ctx.ast.binding_pattern(binding_pattern_kind, None, false); - - let init = match &mut decl.module_reference.0 { - TSModuleReference::TypeName(type_name) => self.transform_ts_type_name(type_name), - TSModuleReference::ExternalModuleReference(reference) => { - let callee = self.ctx.ast.identifier_reference_expression( - IdentifierReference::new(SPAN, "require".into()), - ); - let arguments = self.ctx.ast.new_vec_single(Argument::Expression( - self.ctx.ast.literal_string_expression(reference.expression.clone()), - )); - self.ctx.ast.call_expression(SPAN, callee, arguments, false, None) - } - }; - self.ctx.ast.new_vec_single(self.ctx.ast.variable_declarator( - SPAN, - kind, - binding, - Some(init), - false, - )) - }; - let variable_declaration = - self.ctx.ast.variable_declaration(SPAN, kind, decls, Modifiers::empty()); - - Declaration::VariableDeclaration(variable_declaration) - } - - /// ```TypeScript - /// enum Foo { - /// X - /// } - /// ``` - /// ```JavaScript - /// var Foo = ((Foo) => { - /// const X = 0; Foo[Foo["X"] = X] = "X"; - /// return Foo; - /// })(Foo || {}); - /// ``` - fn transform_ts_enum( - &self, - decl: &mut Box<'a, TSEnumDeclaration<'a>>, - ) -> Option> { - if decl.modifiers.contains(ModifierKind::Declare) { - return None; - } - - let span = decl.span; - let ident = decl.id.clone(); - let kind = self.ctx.ast.binding_pattern_identifier(ident); - let id = self.ctx.ast.binding_pattern(kind, None, false); - - let mut params = self.ctx.ast.new_vec(); - - // ((Foo) => { - params.push(self.ctx.ast.formal_parameter( - SPAN, - id, - None, - false, - false, - self.ctx.ast.new_vec(), - )); - - let params = self.ctx.ast.formal_parameters( - SPAN, - FormalParameterKind::ArrowFormalParameters, - params, - None, - ); - - // Foo[Foo["X"] = 0] = "X"; - let enum_name = decl.id.name.clone(); - let statements = self.transform_ts_enum_members(&mut decl.members, &enum_name); - let body = self.ctx.ast.function_body(decl.span, self.ctx.ast.new_vec(), statements); - - let callee = - self.ctx.ast.arrow_function_expression(SPAN, false, false, params, body, None, None); - - // })(Foo || {}); - let mut arguments = self.ctx.ast.new_vec(); - let op = LogicalOperator::Or; - let left = self - .ctx - .ast - .identifier_reference_expression(IdentifierReference::new(SPAN, enum_name.clone())); - let right = self.ctx.ast.object_expression(SPAN, self.ctx.ast.new_vec(), None); - let expression = self.ctx.ast.logical_expression(SPAN, left, op, right); - arguments.push(Argument::Expression(expression)); - - let call_expression = self.ctx.ast.call_expression(SPAN, callee, arguments, false, None); - - let kind = VariableDeclarationKind::Var; - let decls = { - let mut decls = self.ctx.ast.new_vec(); - - let binding_identifier = BindingIdentifier::new(SPAN, enum_name.clone()); - let binding_pattern_kind = self.ctx.ast.binding_pattern_identifier(binding_identifier); - let binding = self.ctx.ast.binding_pattern(binding_pattern_kind, None, false); - let decl = - self.ctx.ast.variable_declarator(SPAN, kind, binding, Some(call_expression), false); - - decls.push(decl); - decls - }; - let variable_declaration = - self.ctx.ast.variable_declaration(span, kind, decls, Modifiers::empty()); - - Some(Declaration::VariableDeclaration(variable_declaration)) - } - - /// Remove `export` from merged declaration. - /// We only preserve the first one. - /// for example: - /// ```TypeScript - /// export enum Foo {} - /// export enum Foo {} - /// ``` - /// ```JavaScript - /// export enum Foo {} - /// enum Foo {} - /// ``` - fn transform_export_named_declaration( - &mut self, - decl: &mut Box<'_, ExportNamedDeclaration<'a>>, - ) -> Option> { - let ExportNamedDeclaration { - declaration: Some(declaration), - source: None, - export_kind: ImportOrExportKind::Value, - .. - } = &mut **decl - else { - return None; - }; - - let id = match &declaration { - Declaration::TSEnumDeclaration(decl) => decl.id.name.clone(), - Declaration::TSModuleDeclaration(decl) => { - let TSModuleDeclarationName::Identifier(id) = &decl.id else { - return None; - }; - - id.name.clone() - } - _ => return None, - }; - - if self.export_name_set.insert(id) { - return None; - } - - Some(Statement::Declaration(self.ctx.ast.move_declaration(declaration))) - } - - /// Insert let declaration for ts module block - fn insert_let_decl_for_ts_module_block(&mut self, stmts: &mut Vec<'a, Statement<'a>>) { - let mut insert_var_decl = vec![]; - - for (index, stmt) in stmts.iter().enumerate() { - match stmt { - Statement::Declaration(Declaration::TSModuleDeclaration(decl)) => { - if !decl.modifiers.is_contains_declare() { - insert_var_decl.push((index, decl.id.name().clone(), false)); - } - } - Statement::ModuleDeclaration(module_decl) => { - if let ModuleDeclaration::ExportNamedDeclaration(decl) = &**module_decl { - if let Some(Declaration::TSModuleDeclaration(decl)) = &decl.declaration { - if !decl.modifiers.is_contains_declare() { - insert_var_decl.push((index, decl.id.name().clone(), true)); - } - } - } - } - _ => {} - } - } - - for (index, name, is_export) in insert_var_decl.into_iter().rev() { - let kind = VariableDeclarationKind::Let; - let decls = { - let binding_identifier = BindingIdentifier::new(SPAN, name.clone()); - let binding_pattern_kind = - self.ctx.ast.binding_pattern_identifier(binding_identifier); - let binding = self.ctx.ast.binding_pattern(binding_pattern_kind, None, false); - let decl = self.ctx.ast.variable_declarator(SPAN, kind, binding, None, false); - self.ctx.ast.new_vec_single(decl) - }; - let variable_declaration = - self.ctx.ast.variable_declaration(SPAN, kind, decls, Modifiers::empty()); - - let decl = Declaration::VariableDeclaration(variable_declaration); - - let stmt = if is_export { - self.ctx.ast.module_declaration(ModuleDeclaration::ExportNamedDeclaration( - self.ctx.ast.export_named_declaration( - SPAN, - Some(decl), - self.ctx.ast.new_vec(), - None, - ImportOrExportKind::Value, - None, - ), - )) - } else { - Statement::Declaration(decl) - }; - stmts.insert(index, stmt); - } - } - - fn get_namespace_arg_name(&mut self, name: &Atom<'a>) -> Atom<'a> { - let count = self.namespace_arg_names.entry(name.clone()).or_insert(0); - *count += 1; - self.ctx.ast.new_atom(&format!( - "_{name}{}", - if *count > 1 { count.to_string() } else { String::new() } - )) - } - - /// ```TypeScript - /// // transform ts module block - /// namespace Foo { - /// } - /// // to - /// let Foo; // this line added in `insert_let_decl_for_ts_module_block` - /// (function (_Foo) { - /// })(Foo || (Foo = {})); - /// ``` - fn transform_ts_module_block( - &mut self, - block: &mut Box<'a, TSModuleDeclaration<'a>>, - ) -> Statement<'a> { - let body_statements = match &mut block.body { - Some(TSModuleDeclarationBody::TSModuleDeclaration(decl)) => { - let transformed_module_block = self.transform_ts_module_block(decl); - self.ctx.ast.new_vec_single(transformed_module_block) - } - Some(TSModuleDeclarationBody::TSModuleBlock(ts_module_block)) => { - self.ctx.ast.move_statement_vec(&mut ts_module_block.body) - } - None => self.ctx.ast.new_vec(), - }; - - let name = block.id.name(); - - let callee = { - let body = self.ctx.ast.function_body(SPAN, self.ctx.ast.new_vec(), body_statements); - let arg_name = self.get_namespace_arg_name(name); - let params = self.ctx.ast.formal_parameters( - SPAN, - FormalParameterKind::FormalParameter, - self.ctx.ast.new_vec_single( - self.ctx.ast.formal_parameter( - SPAN, - self.ctx.ast.binding_pattern( - self.ctx - .ast - .binding_pattern_identifier(BindingIdentifier::new(SPAN, arg_name)), - None, - false, - ), - None, - false, - false, - self.ctx.ast.new_vec(), - ), - ), - None, - ); - let function = self.ctx.ast.function( - FunctionType::FunctionExpression, - SPAN, - None, - false, - false, - None, - params, - Some(body), - None, - None, - Modifiers::empty(), - ); - let function_expr = self.ctx.ast.function_expression(function); - self.ctx.ast.parenthesized_expression(SPAN, function_expr) - }; - - let arguments = - { - let right = { - let left = self.ctx.ast.simple_assignment_target_identifier( - IdentifierReference::new(SPAN, name.clone()), - ); - let right = self.ctx.ast.object_expression(SPAN, self.ctx.ast.new_vec(), None); - self.ctx.ast.parenthesized_expression( - SPAN, - self.ctx.ast.assignment_expression( - SPAN, - AssignmentOperator::Assign, - left, - right, - ), - ) - }; - self.ctx.ast.new_vec_single(Argument::Expression(self.ctx.ast.logical_expression( - SPAN, - self.ctx.ast.identifier_reference_expression(IdentifierReference::new( - SPAN, - name.clone(), - )), - LogicalOperator::Or, - right, - ))) - }; - let expr = self.ctx.ast.call_expression(SPAN, callee, arguments, false, None); - self.ctx.ast.expression_statement(SPAN, expr) - } - - /// Transform constructor method - /// ```typescript - /// - /// constructor(public x) { - /// super(); - /// } - /// // to - /// constructor(x) { - /// super(); - /// this.x = x; - /// } - /// ``` - pub fn transform_method_definition(&mut self, def: &mut MethodDefinition<'a>) { - if !def.kind.is_constructor() { - return; - } - - let mut params_name = vec![]; - def.value.params.items.iter().for_each(|param| { - if !param.accessibility.is_some_and(|a| matches!(a, TSAccessibility::Public)) { - return; - } - match ¶m.pattern.kind { - BindingPatternKind::BindingIdentifier(ident) => { - params_name.push(ident.name.clone()); - } - BindingPatternKind::AssignmentPattern(pattern) => { - if let BindingPatternKind::BindingIdentifier(ident) = &pattern.left.kind { - params_name.push(ident.name.clone()); - } - } - _ => {} - } - }); - - let Some(body) = &mut def.value.body else { - return; - }; - - for name in params_name { - // TODO: We should push it before the super call - body.statements.push( - self.ctx.ast.expression_statement( - SPAN, - self.ctx.ast.assignment_expression( - SPAN, - AssignmentOperator::Assign, - self.ctx.ast.simple_assignment_target_member_expression( - self.ctx.ast.static_member( - SPAN, - self.ctx.ast.this_expression(SPAN), - IdentifierName::new(SPAN, name.clone()), - false, - ), - ), - self.ctx - .ast - .identifier_reference_expression(IdentifierReference::new(SPAN, name)), - ), - ), - ); - } - } -} diff --git a/crates/oxc_transformer/src/typescript/options.rs b/crates/oxc_transformer/src/typescript/options.rs deleted file mode 100644 index 4d57ac0ee5f79..0000000000000 --- a/crates/oxc_transformer/src/typescript/options.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::Deserialize; - -#[derive(Debug, Clone, Deserialize, Default)] -#[serde(rename_all = "camelCase")] -pub struct TypescriptOptions { - /// When set to true, the transform will only remove [type-only](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-exports) imports (introduced in TypeScript 3.8). This should only be used if you are using TypeScript >= 3.8. - /// defaults to false - pub only_remove_type_imports: bool, -} diff --git a/crates/oxc_transformer/src/utils.rs b/crates/oxc_transformer/src/utils.rs deleted file mode 100644 index 902c3b8509d6d..0000000000000 --- a/crates/oxc_transformer/src/utils.rs +++ /dev/null @@ -1,166 +0,0 @@ -use std::mem; - -use oxc_allocator::Vec; -use oxc_ast::ast::*; -use oxc_span::{CompactStr, Span}; -use oxc_syntax::identifier::is_identifier_name; - -use crate::context::TransformerCtx; - -pub trait CreateVars<'a> { - fn ctx(&self) -> &TransformerCtx<'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.ctx().ast.new_vec(); - let decls = mem::replace(self.vars_mut(), new_vec); - let kind = VariableDeclarationKind::Var; - let decl = - self.ctx().ast.variable_declaration(Span::default(), kind, decls, Modifiers::empty()); - let stmt = Statement::Declaration(Declaration::VariableDeclaration(decl)); - stmts.insert(0, stmt); - } - - fn create_new_var_with_expression(&mut self, expr: &Expression<'a>) -> IdentifierReference<'a> { - let name = self.ctx().scopes().generate_uid_based_on_node(expr); - create_new_var(self, name) - } - - fn create_new_named_var(&mut self, name: &'a str) -> IdentifierReference<'a> { - let name = self.ctx().scopes().generate_uid(name); - create_new_var(self, name) - } - - /// Possibly generate a memoised identifier if it is not static and has consequences. - /// - fn maybe_generate_memoised( - &mut self, - expr: &Expression<'a>, - ) -> Option> { - if self.ctx().symbols().is_static(expr) { - None - } else { - Some(self.create_new_var_with_expression(expr)) - } - } -} - -pub const RESERVED_WORDS_ES3_ONLY: phf::Set<&str> = phf::phf_set![ - "abstract", - "boolean", - "byte", - "char", - "double", - "enum", - "final", - "float", - "goto", - "implements", - "int", - "interface", - "long", - "native", - "package", - "private", - "protected", - "public", - "short", - "static", - "synchronized", - "throws", - "transient", - "volatile", -]; - -const RESERVED_WORD_STRICT: phf::Set<&str> = phf::phf_set![ - "implements", - "interface", - "let", - "package", - "private", - "protected", - "public", - "static", - "yield", -]; - -pub const KEYWORDS: phf::Set<&str> = phf::phf_set![ - "break", - "case", - "catch", - "continue", - "debugger", - "default", - "do", - "else", - "finally", - "for", - "function", - "if", - "return", - "switch", - "throw", - "try", - "var", - "const", - "while", - "with", - "new", - "this", - "super", - "class", - "extends", - "export", - "import", - "null", - "true", - "false", - "in", - "instanceof", - "typeof", - "void", - "delete", -]; - -pub fn is_valid_identifier(name: &str, reserved: bool) -> bool { - if reserved && (KEYWORDS.contains(name) || is_strict_reserved_word(name, true)) { - return false; - } - is_identifier_name(name) -} - -pub fn is_strict_reserved_word(name: &str, in_module: bool) -> bool { - is_reserved_word(name, in_module) || RESERVED_WORD_STRICT.contains(name) -} - -pub fn is_reserved_word(name: &str, in_module: bool) -> bool { - (in_module && name == "await") || name == "enum" -} - -/// https://github.com/babel/babel/blob/main/packages/babel-types/src/validators/isValidES3Identifier.ts#L35 -pub fn is_valid_es3_identifier(name: &str) -> bool { - is_valid_identifier(name, true) && !RESERVED_WORDS_ES3_ONLY.contains(name) -} - -fn create_new_var<'a, V: CreateVars<'a> + ?Sized>( - create_vars: &mut V, - name: CompactStr, -) -> IdentifierReference<'a> { - // Add `var name` to scope - // TODO: hookup symbol id - let atom = create_vars.ctx().ast.new_atom(name.as_str()); - let binding_identifier = BindingIdentifier::new(Span::default(), atom.clone()); - let binding_pattern_kind = create_vars.ctx().ast.binding_pattern_identifier(binding_identifier); - let binding = create_vars.ctx().ast.binding_pattern(binding_pattern_kind, None, false); - let kind = VariableDeclarationKind::Var; - let decl = - create_vars.ctx().ast.variable_declarator(Span::default(), kind, binding, None, false); - create_vars.ctx().add_binding(name); - create_vars.vars_mut().push(decl); - // TODO: add reference id and flag - IdentifierReference::new(Span::default(), atom) -} diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index 445612f97c04a..7b81770636aa0 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -14,7 +14,7 @@ use oxc::{ parser::Parser, semantic::{ScopeId, Semantic, SemanticBuilder}, span::SourceType, - transformer::{TransformOptions, TransformTarget, Transformer}, + transformer::{TransformOptions, Transformer}, }; use oxc_linter::{LintContext, Linter}; use oxc_prettier::{Prettier, PrettierOptions}; @@ -232,8 +232,7 @@ impl Oxc { .build_module_record(PathBuf::new(), program) .build(program) .semantic; - let options = - TransformOptions { target: TransformTarget::ES2015, ..TransformOptions::default() }; + let options = TransformOptions {}; let result = Transformer::new(&allocator, source_type, semantic, options).build(program); if let Err(errs) = result { diff --git a/tasks/transform_conformance/Cargo.toml b/tasks/transform_conformance/Cargo.toml index 67c6ca10a032e..b797c63c40b2d 100644 --- a/tasks/transform_conformance/Cargo.toml +++ b/tasks/transform_conformance/Cargo.toml @@ -29,9 +29,6 @@ oxc_codegen = { workspace = true } oxc_transformer = { workspace = true } oxc_tasks_common = { workspace = true } oxc_diagnostics = { workspace = true } - -serde_json = { workspace = true } -serde = { workspace = true } walkdir = { workspace = true } pico-args = { workspace = true } indexmap = { workspace = true } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index afb79a2fd20b4..1b0b67847b2cf 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,12 +1,6 @@ -Passed: 320/1415 +Passed: 72/1415 # All Passed: -* babel-plugin-transform-numeric-separator -* babel-plugin-transform-optional-catch-binding -* babel-plugin-transform-json-strings -* babel-plugin-transform-shorthand-properties -* babel-plugin-transform-sticky-regex -* babel-plugin-transform-instanceof * babel-plugin-transform-property-literals @@ -289,7 +283,13 @@ Passed: 320/1415 * regression/T7364/input.mjs * regression/multiple-super-in-termary/input.js -# babel-plugin-transform-class-static-block (6/21) +# babel-plugin-transform-class-static-block (0/21) +* class-static-block/class-binding/input.js +* class-static-block/class-declaration/input.js +* class-static-block/in-class-heritage/input.js +* class-static-block/multiple-static-initializers/input.js +* class-static-block/name-conflict/input.js +* class-static-block/new-target/input.js * class-static-block/preserve-comments/input.js * integration/class-binding/input.js * integration/class-declaration/input.js @@ -517,8 +517,17 @@ Passed: 320/1415 * to-native-fields/static-shadow/input.js * to-native-fields/static-shadowed-binding/input.js -# babel-plugin-transform-logical-assignment-operators (5/6) +# babel-plugin-transform-logical-assignment-operators (0/6) +* logical-assignment/anonymous-functions-transform/input.js +* logical-assignment/arrow-functions-transform/input.js +* logical-assignment/general-semantics/input.js +* logical-assignment/named-functions-transform/input.js * logical-assignment/null-coalescing/input.js +* logical-assignment/null-coalescing-without-other/input.js + +# babel-plugin-transform-numeric-separator (0/2) +* numeric-separator/removal/bigint/input.js +* numeric-separator/used-with-transform-literals/input.js # babel-plugin-transform-export-namespace-from (0/4) * export-namespace/namespace-default/input.mjs @@ -563,9 +572,19 @@ Passed: 320/1415 * systemjs-createImportExpression-false/script/input.js * systemjs-createImportExpression-false/to-string/input.js -# babel-plugin-transform-nullish-coalescing-operator (10/12) +# babel-plugin-transform-nullish-coalescing-operator (0/12) +* assumption-noDocumentAll/transform/input.js +* assumption-noDocumentAll/transform-in-default-destructuring/input.js * assumption-noDocumentAll/transform-in-default-param/input.js +* assumption-noDocumentAll/transform-in-function/input.js +* assumption-noDocumentAll/transform-static-refs-in-default/input.js +* assumption-noDocumentAll/transform-static-refs-in-function/input.js +* nullish-coalescing/transform-in-default-destructuring/input.js * nullish-coalescing/transform-in-default-param/input.js +* nullish-coalescing/transform-in-function/input.js +* nullish-coalescing/transform-loose/input.js +* nullish-coalescing/transform-static-refs-in-default/input.js +* nullish-coalescing/transform-static-refs-in-function/input.js # babel-plugin-transform-optional-chaining (1/45) * assumption-noDocumentAll/assignment/input.js @@ -613,6 +632,14 @@ Passed: 320/1415 * transparent-expr-wrappers/ts-as-member-expression/input.ts * transparent-expr-wrappers/ts-parenthesized-expression-member-call/input.ts +# babel-plugin-transform-optional-catch-binding (2/4) +* optional-catch-bindings/try-catch-block-no-binding/input.js +* optional-catch-bindings/try-catch-finally-no-binding/input.js + +# babel-plugin-transform-json-strings (2/4) +* json-strings/directive-line-separator/input.js +* json-strings/directive-paragraph-separator/input.js + # babel-plugin-transform-async-generator-functions (0/22) * async-generators/class-method/input.js * async-generators/class-private-method/input.js @@ -749,19 +776,30 @@ Passed: 320/1415 * regression/in-uncompiled-class-fields/input.js * regression/regression-2765/input.js -# babel-plugin-transform-exponentiation-operator (3/4) +# babel-plugin-transform-exponentiation-operator (0/4) +* exponentiation-operator/assignment/input.js +* exponentiation-operator/binary/input.js * regression/4349/input.js +* regression/4403/input.js -# babel-plugin-transform-arrow-functions (9/26) +# babel-plugin-transform-arrow-functions (0/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/empty-arguments/input.js +* arrow-functions/empty-block/input.js +* arrow-functions/expression/input.js * arrow-functions/implicit-var-arguments/input.js +* arrow-functions/inside-call/input.js +* arrow-functions/multiple-arguments/input.js * arrow-functions/nested/input.js +* arrow-functions/paran-insertion/input.js * arrow-functions/self-referential/input.js +* arrow-functions/single-argument/input.js * arrow-functions/spec/input.js +* arrow-functions/statement/input.js * arrow-functions/super-call/input.js * arrow-functions/super-prop/input.js * arrow-functions/this/input.js @@ -770,11 +808,15 @@ Passed: 320/1415 * assumption-newableArrowFunctions-false/self-referential/input.js * spec/newableArrowFunction-default/input.js * spec/newableArrowFunction-vs-spec-false/input.js +* spec/newableArrowFunction-vs-spec-true/input.js -# babel-plugin-transform-function-name (6/33) +# babel-plugin-transform-function-name (1/33) * function-name/assignment/input.js +* function-name/await/input.mjs +* function-name/basic/input.js * function-name/class-method/input.js * function-name/collisions/input.js +* function-name/eval/input.js * function-name/export/input.mjs * function-name/export-default-arrow-renaming/input.mjs * function-name/export-default-arrow-renaming-2/input.mjs @@ -795,73 +837,145 @@ Passed: 320/1415 * function-name/own-bindings/input.js * function-name/self-reference/input.js * function-name/shorthand-property/input.js +* function-name/unicode-id-not-supported/input.js * function-name/unicode-id-supported/input.js * function-name/with-arrow-functions-transform/input.js * function-name/with-arrow-functions-transform-spec/input.js +* issues/5004/input.mjs * issues/7199/input.js +# babel-plugin-transform-shorthand-properties (1/7) +* shorthand-properties/method-plain/input.js +* shorthand-properties/proto/input.js +* shorthand-properties/shorthand-comments/input.js +* shorthand-properties/shorthand-mixed/input.js +* shorthand-properties/shorthand-multiple/input.js +* shorthand-properties/shorthand-single/input.js + +# babel-plugin-transform-sticky-regex (1/2) +* sticky-regex/basic/input.js + # babel-plugin-transform-unicode-regex (1/4) * unicode-regex/basic/input.js * unicode-regex/negated-set/input.js * unicode-regex/slash/input.js -# babel-plugin-transform-template-literals (13/32) +# babel-plugin-transform-template-literals (1/32) * assumption-ignoreToPrimitiveHint/escape-quotes/input.js * assumption-ignoreToPrimitiveHint/expression-first/input.js * assumption-ignoreToPrimitiveHint/functions/input.js * assumption-ignoreToPrimitiveHint/literals/input.js +* assumption-ignoreToPrimitiveHint/multiline/input.js * assumption-ignoreToPrimitiveHint/multiple/input.js +* assumption-ignoreToPrimitiveHint/none/input.js * assumption-ignoreToPrimitiveHint/only/input.js * assumption-ignoreToPrimitiveHint/single/input.js * assumption-ignoreToPrimitiveHint/statement/input.js * assumption-ignoreToPrimitiveHint/tag/input.js +* assumption-mutableTemplateObject/no-tag/input.js * assumption-mutableTemplateObject/tag/input.js * assumption-mutableTemplateObject/template-revision/input.js * default/cache-revision/input.js +* default/escape-quotes/input.js +* default/expression-first/input.js +* default/functions/input.js * default/literals/input.js +* default/multiline/input.js +* default/multiple/input.js +* default/none/input.js +* default/only/input.js * default/simple-tag/input.js +* default/single/input.js +* default/statement/input.js * default/tag/input.js * default/tag-with-unicode-escapes/input.js * default/template-revision/input.js * loose/ignoreToPrimitiveHint/input.js * loose/mutableTemplateObject/input.js -# babel-plugin-transform-duplicate-keys (7/8) +# babel-plugin-transform-duplicate-keys (2/8) * combination/dupes/input.js +* duplicate-keys/both-quoted/input.js +* duplicate-keys/dupes/input.js +* duplicate-keys/getter/input.js +* duplicate-keys/getters-and-setters/input.js +* duplicate-keys/one-quoted/input.js + +# babel-plugin-transform-instanceof (0/1) +* instanceof/instanceof/input.js -# babel-plugin-transform-new-target (2/8) +# babel-plugin-transform-new-target (0/8) * general/arrow/input.js +* general/class/input.js * general/class-properties/input.js * general/class-properties-loose/input.js +* general/extended-class/input.js * general/function/input.js * general/function-duplicate-name/input.js * general/object/input.js -# babel-plugin-transform-typescript (97/158) +# babel-plugin-transform-typescript (55/158) +* class/abstract-class-decorated/input.ts * class/abstract-class-decorated-method/input.ts * class/abstract-class-decorated-parameter/input.ts * class/accessor-allowDeclareFields-false/input.ts * class/accessor-allowDeclareFields-true/input.ts * class/declare/input.ts * class/decorated-declare-properties/input.ts +* class/parameter-properties/input.ts * class/parameter-properties-late-super/input.ts * class/parameter-properties-with-class/input.ts * class/parameter-properties-with-class-and-super/input.ts * class/parameter-properties-with-parameters/input.ts +* class/parameter-properties-with-super/input.ts * class/private-method-override-transform-private/input.ts * class/transform-properties-declare-wrong-order/input.ts +* declarations/erased/input.ts +* declarations/export-declare-enum/input.ts +* declarations/nested-namespace/input.mjs * exports/declared-types/input.ts * exports/export-const-enums/input.ts +* exports/export-type/input.ts +* exports/export-type-from/input.ts * exports/export-type-star-from/input.ts * exports/export=/input.ts * exports/export=-to-cjs/input.ts +* exports/imported-types/input.ts +* exports/imported-types-only-remove-type-imports/input.ts +* exports/issue-9916-3/input.ts +* exports/type-only-export-specifier-1/input.ts +* exports/type-only-export-specifier-2/input.ts +* exports/type-only-export-specifier-3/input.ts +* function/overloads-exports/input.mjs * imports/elide-injected/input.ts +* imports/elide-preact/input.ts +* imports/elide-react/input.ts +* imports/elide-type-referenced-in-imports-equal-no/input.ts +* imports/elide-typeof/input.ts +* imports/elision/input.ts +* imports/elision-export-type/input.ts +* imports/elision-locations/input.ts +* imports/elision-qualifiedname/input.ts +* imports/elision-rename/input.ts * imports/enum-id/input.ts * imports/enum-value/input.ts +* imports/import-removed-exceptions/input.ts +* imports/import-type/input.ts +* imports/import-type-func-with-duplicate-name/input.ts +* imports/import-type-not-removed/input.ts +* imports/import=-declaration/input.ts * imports/import=-module/input.ts * imports/import=-module-to-cjs/input.ts +* imports/only-remove-type-imports/input.ts * imports/parameter-decorators/input.ts +* imports/property-signature/input.ts +* imports/type-only-export-specifier-1/input.ts * imports/type-only-export-specifier-2/input.ts +* imports/type-only-import-specifier-1/input.ts +* imports/type-only-import-specifier-2/input.ts +* imports/type-only-import-specifier-3/input.ts +* imports/type-only-import-specifier-4/input.ts +* namespace/alias/input.ts * namespace/ambient-module-nested/input.ts * namespace/ambient-module-nested-exported/input.ts * namespace/canonical/input.ts @@ -869,7 +983,11 @@ Passed: 320/1415 * namespace/clobber-enum/input.ts * namespace/clobber-export/input.ts * namespace/clobber-import/input.ts +* namespace/contentious-names/input.ts +* namespace/declare/input.ts +* namespace/declare-global-nested-namespace/input.ts * namespace/empty-removed/input.ts +* namespace/export/input.ts * namespace/export-type-only/input.ts * namespace/module-nested/input.ts * namespace/module-nested-export/input.ts @@ -883,6 +1001,7 @@ Passed: 320/1415 * namespace/nested-shorthand/input.ts * namespace/nested-shorthand-export/input.ts * namespace/same-name/input.ts +* namespace/undeclared/input.ts * optimize-const-enums/custom-values/input.ts * optimize-const-enums/custom-values-exported/input.ts * optimize-const-enums/declare/input.ts @@ -900,30 +1019,164 @@ Passed: 320/1415 * regression/15768/input.ts * variable-declaration/non-null-in-optional-chain/input.ts -# babel-plugin-transform-react-jsx (135/156) +# babel-plugin-transform-react-jsx (1/156) +* autoImport/after-polyfills/input.mjs +* autoImport/after-polyfills-2/input.mjs * autoImport/after-polyfills-compiled-to-cjs/input.mjs +* autoImport/after-polyfills-script-not-supported/input.js * autoImport/auto-import-react-source-type-module/input.js +* autoImport/auto-import-react-source-type-script/input.js * autoImport/complicated-scope-module/input.js +* autoImport/complicated-scope-script/input.js +* autoImport/import-source/input.js +* autoImport/import-source-pragma/input.js * autoImport/react-defined/input.js +* pure/false-default-pragma-automatic-runtime/input.js +* pure/false-default-pragma-classic-runtime/input.js +* pure/false-pragma-comment-automatic-runtime/input.js +* pure/false-pragma-comment-classic-runtime/input.js +* pure/false-pragma-option-automatic-runtime/input.js +* pure/false-pragma-option-classic-runtime/input.js +* pure/true-default-pragma-automatic-runtime/input.js +* pure/true-default-pragma-classic-runtime/input.js +* pure/true-pragma-comment-automatic-runtime/input.js +* pure/true-pragma-comment-classic-runtime/input.js +* pure/true-pragma-option-automatic-runtime/input.js +* pure/true-pragma-option-classic-runtime/input.js +* pure/unset-default-pragma-automatic-runtime/input.js +* pure/unset-default-pragma-classic-runtime/input.js +* pure/unset-pragma-comment-automatic-runtime/input.js +* pure/unset-pragma-comment-classic-runtime/input.js +* pure/unset-pragma-option-automatic-runtime/input.js +* pure/unset-pragma-option-classic-runtime/input.js +* react/adds-appropriate-newlines-when-using-spread-attribute/input.js +* react/arrow-functions/input.js +* react/assignment/input.js +* react/concatenates-adjacent-string-literals/input.js +* react/does-not-add-source-self/input.mjs +* react/dont-coerce-expression-containers/input.js +* react/duplicate-props/input.js +* react/flattens-spread/input.js +* react/handle-spread-with-proto/input.js +* react/honor-custom-jsx-comment/input.js +* react/honor-custom-jsx-comment-if-jsx-pragma-option-set/input.js +* react/honor-custom-jsx-pragma-option/input.js +* react/jsx-with-retainlines-option/input.js +* react/jsx-without-retainlines-option/input.js * react/optimisation.react.constant-elements/input.js +* react/pragma-works-with-no-space-at-the-end/input.js +* react/proto-in-jsx-attribute/input.js +* react/should-add-quotes-es3/input.js +* react/should-allow-constructor-as-prop/input.js +* react/should-allow-deeper-js-namespacing/input.js +* react/should-allow-elements-as-attributes/input.js +* react/should-allow-js-namespacing/input.js +* react/should-allow-jsx-docs-comment-with-pragma/input.js +* react/should-allow-nested-fragments/input.js +* react/should-allow-no-pragmafrag-if-frag-unused/input.js +* react/should-allow-pragmafrag-and-frag/input.js +* react/should-avoid-wrapping-in-extra-parens-if-not-needed/input.js +* react/should-convert-simple-tags/input.js +* react/should-convert-simple-text/input.js +* react/should-disallow-spread-children/input.js +* react/should-disallow-valueless-key/input.js +* react/should-disallow-xml-namespacing/input.js +* react/should-escape-xhtml-jsxattribute/input.js +* react/should-escape-xhtml-jsxtext/input.js +* react/should-handle-attributed-elements/input.js +* react/should-handle-has-own-property-correctly/input.js +* react/should-have-correct-comma-in-nested-children/input.js +* react/should-insert-commas-after-expressions-before-whitespace/input.js +* react/should-not-add-quotes-to-identifier-names/input.js +* react/should-not-allow-jsx-pragma-to-be-anywhere-in-comment/input.js +* react/should-not-mangle-expressioncontainer-attribute-values/input.js +* react/should-not-strip-nbsp-even-coupled-with-other-whitespace/input.js +* react/should-not-strip-tags-with-a-single-child-of-nbsp/input.js +* react/should-properly-handle-comments-between-props/input.js +* react/should-quote-jsx-attributes/input.js +* react/should-support-xml-namespaces-if-flag/input.js +* react/should-throw-error-namespaces-if-not-flag/input.js +* react/should-transform-known-hyphenated-tags/input.js +* react/should-warn-when-importSource-is-set/input.js +* react/should-warn-when-importSource-pragma-is-set/input.js +* react/this-tag-name/input.js +* react/weird-symbols/input.js +* react/wraps-props-in-react-spread-for-first-spread-attributes/input.js +* react/wraps-props-in-react-spread-for-last-spread-attributes/input.js +* react/wraps-props-in-react-spread-for-middle-spread-attributes/input.js +* react-automatic/adds-appropriate-newlines-when-using-spread-attribute/input.js +* react-automatic/arrow-functions/input.js +* react-automatic/assignment/input.js * react-automatic/concatenates-adjacent-string-literals/input.js * react-automatic/does-not-add-source-self-automatic/input.mjs +* react-automatic/dont-coerce-expression-containers/input.js +* react-automatic/duplicate-props/input.js +* react-automatic/flattens-spread/input.js * react-automatic/handle-fragments/input.js +* react-automatic/handle-fragments-with-key/input.js * react-automatic/handle-fragments-with-no-children/input.js +* react-automatic/handle-nonstatic-children/input.js +* react-automatic/handle-spread-with-proto/input.js * react-automatic/handle-static-children/input.js +* react-automatic/jsx-with-retainlines-option/input.js +* react-automatic/jsx-without-retainlines-option/input.js +* react-automatic/key-undefined-works/input.js * react-automatic/optimisation.react.constant-elements/input.js +* react-automatic/pragma-works-with-no-space-at-the-end/input.js +* react-automatic/should-add-quotes-es3/input.js +* react-automatic/should-allow-constructor-as-prop/input.js +* react-automatic/should-allow-deeper-js-namespacing/input.js +* react-automatic/should-allow-elements-as-attributes/input.js +* react-automatic/should-allow-js-namespacing/input.js * react-automatic/should-allow-nested-fragments/input.js +* react-automatic/should-avoid-wrapping-in-extra-parens-if-not-needed/input.js +* react-automatic/should-convert-simple-tags/input.js +* react-automatic/should-convert-simple-text/input.js +* react-automatic/should-disallow-spread-children/input.js +* react-automatic/should-disallow-valueless-key/input.js +* react-automatic/should-disallow-xml-namespacing/input.js +* react-automatic/should-escape-xhtml-jsxattribute/input.js * react-automatic/should-escape-xhtml-jsxtext/input.js * react-automatic/should-handle-attributed-elements/input.js +* react-automatic/should-handle-has-own-property-correctly/input.js * react-automatic/should-have-correct-comma-in-nested-children/input.js +* react-automatic/should-insert-commas-after-expressions-before-whitespace/input.js +* react-automatic/should-not-add-quotes-to-identifier-names/input.js +* react-automatic/should-not-mangle-expressioncontainer-attribute-values/input.js +* react-automatic/should-not-strip-nbsp-even-coupled-with-other-whitespace/input.js +* react-automatic/should-not-strip-tags-with-a-single-child-of-nbsp/input.js +* react-automatic/should-properly-handle-comments-between-props/input.js * react-automatic/should-properly-handle-keys/input.js +* react-automatic/should-properly-handle-null-prop-spread/input.js +* react-automatic/should-quote-jsx-attributes/input.js +* react-automatic/should-support-xml-namespaces-if-flag/input.js +* react-automatic/should-throw-error-namespaces-if-not-flag/input.js * react-automatic/should-throw-when-filter-is-specified/input.js +* react-automatic/should-transform-known-hyphenated-tags/input.js +* react-automatic/should-use-createElement-when-key-comes-after-spread/input.js +* react-automatic/should-use-jsx-when-key-comes-before-spread/input.js +* react-automatic/should-warn-when-pragma-or-pragmaFrag-is-set/input.js +* react-automatic/this-tag-name/input.js +* react-automatic/weird-symbols/input.js +* react-automatic/wraps-props-in-react-spread-for-last-spread-attributes/input.js +* react-automatic/wraps-props-in-react-spread-for-middle-spread-attributes/input.js * regression/issue-12478-automatic/input.js * regression/issue-12478-classic/input.js +* regression/pragma-frag-set-default-classic-runtime/input.js +* removed-options/invalid-use-builtins-false/input.js +* removed-options/invalid-use-builtins-true/input.js +* removed-options/invalid-use-spread-false/input.js +* removed-options/invalid-use-spread-true/input.js +* runtime/classic/input.js +* runtime/defaults-to-automatic/input.js +* runtime/invalid-runtime/input.js +* runtime/pragma-runtime-classsic/input.js +* runtime/runtime-automatic/input.js +* sourcemaps/JSXText/input.js * spread-transform/transform-to-babel-extend/input.js * spread-transform/transform-to-object-assign/input.js -# babel-plugin-proposal-decorators (3/231) +# babel-plugin-proposal-decorators (2/231) * 2018-09-transformation/async-generator-method/input.js * 2018-09-transformation/class-decorators-yield-await/input.js * 2021-12-accessors/context-name/input.js @@ -1149,6 +1402,7 @@ Passed: 320/1415 * 2023-11-typescript/computed-key-ts-as-expression/input.ts * legacy-decl-to-expression/class-decorators/input.mjs * legacy-decl-to-expression/method-decorators/input.mjs +* legacy-regression/10264/input.mjs * legacy-regression/7030/input.js * legacy-regression/8041/input.mjs * legacy-regression/8559/input.mjs diff --git a/tasks/transform_conformance/babel_exec.snap.md b/tasks/transform_conformance/babel_exec.snap.md index ca3bc47c2ab73..55b8ca8eb4e74 100644 --- a/tasks/transform_conformance/babel_exec.snap.md +++ b/tasks/transform_conformance/babel_exec.snap.md @@ -1,20 +1,22 @@ -Passed: 452/716 +Passed: 459/716 # All Passed: * babel-plugin-transform-class-static-block +* babel-plugin-transform-logical-assignment-operators * babel-plugin-transform-numeric-separator +* babel-plugin-transform-nullish-coalescing-operator * babel-plugin-transform-optional-catch-binding * babel-plugin-transform-json-strings * babel-plugin-transform-async-to-generator * babel-plugin-transform-exponentiation-operator * babel-plugin-transform-template-literals +* babel-plugin-transform-instanceof -# babel-plugin-transform-class-properties (139/149) +# babel-plugin-transform-class-properties (140/149) * nested-class/super-call-in-decorator/exec.js * nested-class/super-property-in-accessor-key/exec.js * nested-class/super-property-in-decorator/exec.js -* private/instance/exec.js * private/parenthesized-optional-member-call/exec.js * private/parenthesized-optional-member-call-with-transform/exec.js * private-loose/access-before-declaration/exec.js @@ -38,12 +40,6 @@ Passed: 452/716 * private-loose/rhs-not-object/exec.js * to-native-fields/rhs-not-object/exec.js -# babel-plugin-transform-logical-assignment-operators (0/4) -* logical-assignment/anonymous-functions-exec/exec.js -* logical-assignment/arrow-functions-exec/exec.js -* logical-assignment/named-functions-exec/exec.js -* logical-assignment/null-coalescing/exec.js - # babel-plugin-transform-dynamic-import (4/18) * commonjs/exec-interop/exec.js * commonjs/exec-interop-null/exec.js @@ -60,10 +56,6 @@ Passed: 452/716 * commonjs-createImportExpression-false/exec-to-string-order/exec.js * commonjs-createImportExpression-false/exec-transpiled-dep/exec.js -# babel-plugin-transform-nullish-coalescing-operator (0/2) -* assumption-noDocumentAll/runtime-semantics/exec.js -* nullish-coalescing/runtime-semantics/exec.js - # babel-plugin-transform-optional-chaining (11/16) * assumption-noDocumentAll/parenthesized-expression-member-call/exec.js * general/parenthesized-expression-member-call/exec.js @@ -94,14 +86,10 @@ Passed: 452/716 # babel-plugin-transform-arrow-functions (2/3) * arrow-functions/implicit-var-arguments/exec.js -# babel-plugin-transform-instanceof (0/1) -* instanceof/instanceof/exec.js - -# babel-plugin-transform-new-target (7/9) -* general/class-properties/exec.js -* general/class-properties-loose/exec.js +# babel-plugin-transform-new-target (8/9) +* exec/reflect-function/exec.js -# babel-plugin-proposal-decorators (61/258) +# babel-plugin-proposal-decorators (59/258) * 2021-12-fields/context-name/exec.js * 2021-12-fields--to-es2015/context-name/exec.js * 2021-12-getters/context-name/exec.js @@ -273,6 +261,8 @@ Passed: 452/716 * 2023-11-setters--to-es2015/static-private/exec.js * 2023-11-setters--to-es2015/static-public/exec.js * 2023-11-typescript/computed-key-ts-as-expression/exec.ts +* legacy-class-constructors/mutate-existing-constructor/exec.js +* legacy-class-constructors/return-new-constructor/exec.js * legacy-class-export-default/exec.mjs * legacy-class-ordering/order/exec.js * legacy-class-ordering/reverse-order/exec.js diff --git a/tasks/transform_conformance/oxc.snap.md b/tasks/transform_conformance/oxc.snap.md index eec67514942f1..3e42de1a55c9f 100644 --- a/tasks/transform_conformance/oxc.snap.md +++ b/tasks/transform_conformance/oxc.snap.md @@ -1,6 +1,9 @@ -Passed: 1/1 +Passed: 0/1 # All Passed: -* babel-plugin-transform-optional-catch-binding + +# babel-plugin-transform-optional-catch-binding (0/1) +* try-catch-shadow/input.js + diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index 23772a5446466..be234c67dbaff 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -10,12 +10,9 @@ use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; use oxc_span::{SourceType, VALID_EXTENSIONS}; use oxc_tasks_common::{normalize_path, print_diff_in_terminal, BabelOptions}; -use oxc_transformer::{ - ArrowFunctionsOptions, DecoratorsOptions, NullishCoalescingOperatorOptions, ReactJsxOptions, - TransformOptions, TransformTarget, Transformer, TypescriptOptions, -}; -use serde::de::DeserializeOwned; -use serde_json::Value; +use oxc_transformer::{TransformOptions, Transformer}; +// use serde::de::DeserializeOwned; +// use serde_json::Value; use crate::{fixture_root, root, TestRunnerEnv}; @@ -81,52 +78,12 @@ pub trait TestCase { fn path(&self) -> &Path; fn transform_options(&self) -> TransformOptions { - fn get_options(value: Option) -> T { - value.and_then(|v| serde_json::from_value::(v).ok()).unwrap_or_default() - } + // fn get_options(value: Option) -> T { + // value.and_then(|v| serde_json::from_value::(v).ok()).unwrap_or_default() + // } - let options = self.options(); - TransformOptions { - target: TransformTarget::ESNext, - babel_8_breaking: options.babel_8_breaking, - decorators: options - .get_plugin("proposal-decorators") - .map(get_options::), - react_jsx: options - .get_plugin("transform-react-jsx") - .map(get_options::), - typescript: options - .get_plugin("transform-typescript") - .map(get_options::), - assumptions: options.assumptions, - class_static_block: options.get_plugin("transform-class-static-block").is_some(), - instanceof: options.get_plugin("transform-instanceof").is_some(), - literals: options.get_plugin("transform-literals").is_some(), - function_name: options.get_plugin("transform-function-name").is_some(), - arrow_functions: options - .get_plugin("transform-arrow-functions") - .map(get_options::), - logical_assignment_operators: options - .get_plugin("transform-logical-assignment-operators") - .is_some(), - numeric_separator: options.get_plugin("transform-numeric-separator").is_some(), - nullish_coalescing_operator: options - .get_plugin("transform-nullish-coalescing-operator") - .map(get_options::), - json_strings: options.get_plugin("transform-json-strings").is_some(), - optional_catch_binding: options - .get_plugin("transform-optional-catch-binding") - .is_some(), - exponentiation_operator: options - .get_plugin("transform-exponentiation-operator") - .is_some(), - shorthand_properties: options.get_plugin("transform-shorthand-properties").is_some(), - sticky_regex: options.get_plugin("transform-sticky-regex").is_some(), - template_literals: options.get_plugin("transform-template-literals").is_some(), - property_literals: options.get_plugin("transform-property-literals").is_some(), - duplicate_keys: options.get_plugin("transform-duplicate-keys").is_some(), - new_target: options.get_plugin("transform-new-target").is_some(), - } + // let options = self.options(); + TransformOptions {} } fn skip_test_case(&self) -> bool { @@ -163,9 +120,7 @@ pub trait TestCase { let allocator = Allocator::default(); let source_text = fs::read_to_string(path).unwrap(); - let source_type = SourceType::from_path(path) - .unwrap() - .with_typescript(self.transform_options().typescript.is_some()); + let source_type = SourceType::from_path(path).unwrap().with_typescript(false); let ret = Parser::new(&allocator, &source_text, source_type).parse(); @@ -232,7 +187,7 @@ impl TestCase for ConformanceTestCase { } else { input_is_js && output_is_js }) - .with_typescript(transform_options.typescript.is_some()); + .with_typescript(false); if filtered { println!("input_path: {:?}", &self.path); diff --git a/tasks/transform_conformance/typescript.snap.md b/tasks/transform_conformance/typescript.snap.md index 3a8136f70f773..5374955d0aa11 100644 --- a/tasks/transform_conformance/typescript.snap.md +++ b/tasks/transform_conformance/typescript.snap.md @@ -14,83 +14,12 @@ # typescript/tests/cases/conformance/enums/enumBasics.ts ```typescript -var E1 = (E1 => { - const A = 0; - E1[E1['A'] = A] = 'A'; - const B = 1 + A; - E1[E1['B'] = B] = 'B'; - const C = 1 + B; - E1[E1['C'] = C] = 'C'; - return E1; -})(E1 || {}); var x = E1.A; var e = E1; var e; var e; var s = E1[e.A]; var s; -var E2 = (E2 => { - const A = 1; - E2[E2['A'] = A] = 'A'; - const B = 2; - E2[E2['B'] = B] = 'B'; - const C = 3; - E2[E2['C'] = C] = 'C'; - return E2; -})(E2 || {}); -var E3 = (E3 => { - const X = 'foo'.length; - E3[E3['X'] = X] = 'X'; - const Y = 4 + 3; - E3[E3['Y'] = Y] = 'Y'; - const Z = +'foo'; - E3[E3['Z'] = Z] = 'Z'; - return E3; -})(E3 || {}); -var E4 = (E4 => { - const X = 0; - E4[E4['X'] = X] = 'X'; - const Y = 1 + X; - E4[E4['Y'] = Y] = 'Y'; - const Z = 'foo'.length; - E4[E4['Z'] = Z] = 'Z'; - return E4; -})(E4 || {}); -var E5 = (E5 => { - const A = 0; - E5[E5['A'] = A] = 'A'; - const B = 3; - E5[E5['B'] = B] = 'B'; - const C = 1 + B; - E5[E5['C'] = C] = 'C'; - return E5; -})(E5 || {}); -var E6 = (E6 => { - const A = 0; - E6[E6['A'] = A] = 'A'; - const B = 0; - E6[E6['B'] = B] = 'B'; - const C = 1 + B; - E6[E6['C'] = C] = 'C'; - return E6; -})(E6 || {}); -var E7 = (E7 => { - const A = 'foo'['foo']; - E7[E7['A'] = A] = 'A'; - return E7; -})(E7 || {}); -var E8 = (E8 => { - const B = 'foo'['foo']; - E8[E8['B'] = B] = 'B'; - return E8; -})(E8 || {}); -var E9 = (E9 => { - const A = 0; - E9[E9['A'] = A] = 'A'; - const B = A; - E9[E9['B'] = B] = 'B'; - return E9; -})(E9 || {}); var doNotPropagate = [E8.B, E7.A, E4.Z, E3.X, E3.Y, E3.Z]; var doPropagate = [E9.A, E9.B, E6.B, E6.C, E6.A, E5.A, E5.B, E5.C]; @@ -98,381 +27,37 @@ var doPropagate = [E9.A, E9.B, E6.B, E6.C, E6.A, E5.A, E5.B, E5.C]; # typescript/tests/cases/conformance/enums/enumClassification.ts ```typescript -var E01 = (E01 => { - const A = 0; - E01[E01['A'] = A] = 'A'; - return E01; -})(E01 || {}); -var E02 = (E02 => { - const A = 123; - E02[E02['A'] = A] = 'A'; - return E02; -})(E02 || {}); -var E03 = (E03 => { - const A = 'hello'; - E03['A'] = A; - return E03; -})(E03 || {}); -var E04 = (E04 => { - const A = 0; - E04[E04['A'] = A] = 'A'; - const B = 1 + A; - E04[E04['B'] = B] = 'B'; - const C = 1 + B; - E04[E04['C'] = C] = 'C'; - return E04; -})(E04 || {}); -var E05 = (E05 => { - const A = 0; - E05[E05['A'] = A] = 'A'; - const B = 10; - E05[E05['B'] = B] = 'B'; - const C = 1 + B; - E05[E05['C'] = C] = 'C'; - return E05; -})(E05 || {}); -var E06 = (E06 => { - const A = 'one'; - E06['A'] = A; - const B = 'two'; - E06['B'] = B; - const C = 'three'; - E06['C'] = C; - return E06; -})(E06 || {}); -var E07 = (E07 => { - const A = 0; - E07[E07['A'] = A] = 'A'; - const B = 1 + A; - E07[E07['B'] = B] = 'B'; - const C = 'hi'; - E07['C'] = C; - const D = 10; - E07[E07['D'] = D] = 'D'; - const E = 1 + D; - E07[E07['E'] = E] = 'E'; - const F = 'bye'; - E07['F'] = F; - return E07; -})(E07 || {}); -var E08 = (E08 => { - const A = 10; - E08[E08['A'] = A] = 'A'; - const B = 'hello'; - E08['B'] = B; - const C = A; - E08[E08['C'] = C] = 'C'; - const D = B; - E08[E08['D'] = D] = 'D'; - const E = C; - E08[E08['E'] = E] = 'E'; - return E08; -})(E08 || {}); -var E10 = (E10 => { - return E10; -})(E10 || {}); -var E11 = (E11 => { - const A = +0; - E11[E11['A'] = A] = 'A'; - const B = 1 + A; - E11[E11['B'] = B] = 'B'; - const C = 1 + B; - E11[E11['C'] = C] = 'C'; - return E11; -})(E11 || {}); -var E12 = (E12 => { - const A = 1 << 0; - E12[E12['A'] = A] = 'A'; - const B = 1 << 1; - E12[E12['B'] = B] = 'B'; - const C = 1 << 2; - E12[E12['C'] = C] = 'C'; - return E12; -})(E12 || {}); -var E20 = (E20 => { - const A = 'foo'.length; - E20[E20['A'] = A] = 'A'; - const B = A + 1; - E20[E20['B'] = B] = 'B'; - const C = +'123'; - E20[E20['C'] = C] = 'C'; - const D = Math.sin(1); - E20[E20['D'] = D] = 'D'; - return E20; -})(E20 || {}); ``` # typescript/tests/cases/conformance/enums/enumConstantMemberWithString.ts ```typescript -var T1 = (T1 => { - const a = '1'; - T1['a'] = a; - const b = '1' + '2'; - T1[T1['b'] = b] = 'b'; - const c = '1' + '2' + '3'; - T1[T1['c'] = c] = 'c'; - const d = 'a' - 'a'; - T1[T1['d'] = d] = 'd'; - const e = 'a' + 1; - T1[T1['e'] = e] = 'e'; - return T1; -})(T1 || {}); -var T2 = (T2 => { - const a = '1'; - T2['a'] = a; - const b = '1' + '2'; - T2[T2['b'] = b] = 'b'; - return T2; -})(T2 || {}); -var T3 = (T3 => { - const a = '1'; - T3['a'] = a; - const b = '1' + '2'; - T3[T3['b'] = b] = 'b'; - const c = 1; - T3[T3['c'] = c] = 'c'; - const d = 1 + 2; - T3[T3['d'] = d] = 'd'; - return T3; -})(T3 || {}); -var T4 = (T4 => { - const a = '1'; - T4['a'] = a; - return T4; -})(T4 || {}); -var T5 = (T5 => { - const a = '1' + '2'; - T5[T5['a'] = a] = 'a'; - return T5; -})(T5 || {}); ``` # typescript/tests/cases/conformance/enums/enumConstantMemberWithStringEmitDeclaration.ts ```typescript -var T1 = (T1 => { - const a = '1'; - T1['a'] = a; - const b = '1' + '2'; - T1[T1['b'] = b] = 'b'; - const c = '1' + '2' + '3'; - T1[T1['c'] = c] = 'c'; - return T1; -})(T1 || {}); -var T2 = (T2 => { - const a = '1'; - T2['a'] = a; - const b = '1' + '2'; - T2[T2['b'] = b] = 'b'; - return T2; -})(T2 || {}); -var T3 = (T3 => { - const a = '1'; - T3['a'] = a; - const b = '1' + '2'; - T3[T3['b'] = b] = 'b'; - return T3; -})(T3 || {}); -var T4 = (T4 => { - const a = '1'; - T4['a'] = a; - return T4; -})(T4 || {}); -var T5 = (T5 => { - const a = '1' + '2'; - T5[T5['a'] = a] = 'a'; - return T5; -})(T5 || {}); ``` # typescript/tests/cases/conformance/enums/enumConstantMemberWithTemplateLiterals.ts ```typescript -var T1 = (T1 => { - const a = `1`; - T1['a'] = a; - return T1; -})(T1 || {}); -var T2 = (T2 => { - const a = `1`; - T2['a'] = a; - const b = '2'; - T2['b'] = b; - const c = 3; - T2[T2['c'] = c] = 'c'; - return T2; -})(T2 || {}); -var T3 = (T3 => { - const a = `1` + `1`; - T3[T3['a'] = a] = 'a'; - return T3; -})(T3 || {}); -var T4 = (T4 => { - const a = `1`; - T4['a'] = a; - const b = `1` + `1`; - T4[T4['b'] = b] = 'b'; - const c = `1` + '2'; - T4[T4['c'] = c] = 'c'; - const d = '2' + `1`; - T4[T4['d'] = d] = 'd'; - const e = '2' + `1` + `1`; - T4[T4['e'] = e] = 'e'; - return T4; -})(T4 || {}); -var T5 = (T5 => { - const a = `1`; - T5['a'] = a; - const b = `1` + `2`; - T5[T5['b'] = b] = 'b'; - const c = `1` + `2` + `3`; - T5[T5['c'] = c] = 'c'; - const d = 1; - T5[T5['d'] = d] = 'd'; - const e = `1` - `1`; - T5[T5['e'] = e] = 'e'; - const f = `1` + 1; - T5[T5['f'] = f] = 'f'; - const g = `1${'2'}3`; - T5['g'] = g; - const h = `1`.length; - T5[T5['h'] = h] = 'h'; - return T5; -})(T5 || {}); -var T6 = (T6 => { - const a = 1; - T6[T6['a'] = a] = 'a'; - const b = `12`.length; - T6[T6['b'] = b] = 'b'; - return T6; -})(T6 || {}); ``` # typescript/tests/cases/conformance/enums/enumConstantMemberWithTemplateLiteralsEmitDeclaration.ts ```typescript -var T1 = (T1 => { - const a = `1`; - T1['a'] = a; - return T1; -})(T1 || {}); -var T2 = (T2 => { - const a = `1`; - T2['a'] = a; - const b = '2'; - T2['b'] = b; - const c = 3; - T2[T2['c'] = c] = 'c'; - return T2; -})(T2 || {}); -var T3 = (T3 => { - const a = `1` + `1`; - T3[T3['a'] = a] = 'a'; - return T3; -})(T3 || {}); -var T4 = (T4 => { - const a = `1`; - T4['a'] = a; - const b = `1` + `1`; - T4[T4['b'] = b] = 'b'; - const c = `1` + '2'; - T4[T4['c'] = c] = 'c'; - const d = '2' + `1`; - T4[T4['d'] = d] = 'd'; - const e = '2' + `1` + `1`; - T4[T4['e'] = e] = 'e'; - return T4; -})(T4 || {}); -var T5 = (T5 => { - const a = `1`; - T5['a'] = a; - const b = `1` + `2`; - T5[T5['b'] = b] = 'b'; - const c = `1` + `2` + `3`; - T5[T5['c'] = c] = 'c'; - const d = 1; - T5[T5['d'] = d] = 'd'; - return T5; -})(T5 || {}); -var T6 = (T6 => { - const a = 1; - T6[T6['a'] = a] = 'a'; - const b = `12`.length; - T6[T6['b'] = b] = 'b'; - return T6; -})(T6 || {}); ``` # typescript/tests/cases/conformance/enums/enumConstantMembers.ts ```typescript -var E1 = (E1 => { - const a = 1; - E1[E1['a'] = a] = 'a'; - const b = 1 + a; - E1[E1['b'] = b] = 'b'; - return E1; -})(E1 || {}); -var E2 = (E2 => { - const a = -1; - E2[E2['a'] = a] = 'a'; - const b = 1 + a; - E2[E2['b'] = b] = 'b'; - return E2; -})(E2 || {}); -var E3 = (E3 => { - const a = 0.1; - E3[E3['a'] = a] = 'a'; - const b = 1 + a; - E3[E3['b'] = b] = 'b'; - return E3; -})(E3 || {}); -var E5 = (E5 => { - const a = 1 / 0; - E5[E5['a'] = a] = 'a'; - const b = 2 / 0.0; - E5[E5['b'] = b] = 'b'; - const c = 1.0 / 0.0; - E5[E5['c'] = c] = 'c'; - const d = 0.0 / 0.0; - E5[E5['d'] = d] = 'd'; - const e = NaN; - E5[E5['e'] = e] = 'e'; - const f = Infinity; - E5[E5['f'] = f] = 'f'; - const g = -Infinity; - E5[E5['g'] = g] = 'g'; - return E5; -})(E5 || {}); -var E6 = (E6 => { - const a = 1 / 0; - E6[E6['a'] = a] = 'a'; - const b = 2 / 0.0; - E6[E6['b'] = b] = 'b'; - const c = 1.0 / 0.0; - E6[E6['c'] = c] = 'c'; - const d = 0.0 / 0.0; - E6[E6['d'] = d] = 'd'; - const e = NaN; - E6[E6['e'] = e] = 'e'; - const f = Infinity; - E6[E6['f'] = f] = 'f'; - const g = -Infinity; - E6[E6['g'] = g] = 'g'; - return E6; -})(E6 || {}); ``` # typescript/tests/cases/conformance/enums/enumErrorOnConstantBindingWithInitializer.ts ```typescript const {value='123'} = thing; -var E = (E => { - const test = value; - E[E['test'] = test] = 'test'; - return E; -})(E || {}); ``` @@ -493,265 +78,16 @@ var E = (E => { # typescript/tests/cases/conformance/enums/enumExportMergingES6.ts ```typescript -export var Animals = (Animals => { - const Cat = 1; - Animals[Animals['Cat'] = Cat] = 'Cat'; - return Animals; -})(Animals || {}); -var Animals = (Animals => { - const Dog = 2; - Animals[Animals['Dog'] = Dog] = 'Dog'; - return Animals; -})(Animals || {}); -var Animals = (Animals => { - const CatDog = Cat | Dog; - Animals[Animals['CatDog'] = CatDog] = 'CatDog'; - return Animals; -})(Animals || {}); ``` # typescript/tests/cases/conformance/enums/enumMerging.ts ```typescript -let M1; -(function(_M1) { - var EImpl1 = (EImpl1 => { - const A = 0; - EImpl1[EImpl1['A'] = A] = 'A'; - const B = 1 + A; - EImpl1[EImpl1['B'] = B] = 'B'; - const C = 1 + B; - EImpl1[EImpl1['C'] = C] = 'C'; - return EImpl1; - })(EImpl1 || {}); - var EImpl1 = (EImpl1 => { - const D = 1; - EImpl1[EImpl1['D'] = D] = 'D'; - const E = 1 + D; - EImpl1[EImpl1['E'] = E] = 'E'; - const F = 1 + E; - EImpl1[EImpl1['F'] = F] = 'F'; - return EImpl1; - })(EImpl1 || {}); -export var EConst1 = (EConst1 => { - const A = 3; - EConst1[EConst1['A'] = A] = 'A'; - const B = 2; - EConst1[EConst1['B'] = B] = 'B'; - const C = 1; - EConst1[EConst1['C'] = C] = 'C'; - return EConst1; - })(EConst1 || {}); - var EConst1 = (EConst1 => { - const D = 7; - EConst1[EConst1['D'] = D] = 'D'; - const E = 9; - EConst1[EConst1['E'] = E] = 'E'; - const F = 8; - EConst1[EConst1['F'] = F] = 'F'; - return EConst1; - })(EConst1 || {}); - var x = [EConst1.A, EConst1.B, EConst1.C, EConst1.D, EConst1.E, EConst1.F]; -})(M1 || (M1 = {})); -let M2; -(function(_M2) { -export var EComp2 = (EComp2 => { - const A = 'foo'.length; - EComp2[EComp2['A'] = A] = 'A'; - const B = 'foo'.length; - EComp2[EComp2['B'] = B] = 'B'; - const C = 'foo'.length; - EComp2[EComp2['C'] = C] = 'C'; - return EComp2; - })(EComp2 || {}); - var EComp2 = (EComp2 => { - const D = 'foo'.length; - EComp2[EComp2['D'] = D] = 'D'; - const E = 'foo'.length; - EComp2[EComp2['E'] = E] = 'E'; - const F = 'foo'.length; - EComp2[EComp2['F'] = F] = 'F'; - return EComp2; - })(EComp2 || {}); - var x = [EComp2.A, EComp2.B, EComp2.C, EComp2.D, EComp2.E, EComp2.F]; -})(M2 || (M2 = {})); -let M3; -(function(_M3) { - var EInit = (EInit => { - const A = 0; - EInit[EInit['A'] = A] = 'A'; - const B = 1 + A; - EInit[EInit['B'] = B] = 'B'; - return EInit; - })(EInit || {}); - var EInit = (EInit => { - const C = 1; - EInit[EInit['C'] = C] = 'C'; - const D = 1 + C; - EInit[EInit['D'] = D] = 'D'; - const E = 1 + D; - EInit[EInit['E'] = E] = 'E'; - return EInit; - })(EInit || {}); -})(M3 || (M3 = {})); -let M4; -(function(_M4) { -export var Color = (Color => { - const Red = 0; - Color[Color['Red'] = Red] = 'Red'; - const Green = 1 + Red; - Color[Color['Green'] = Green] = 'Green'; - const Blue = 1 + Green; - Color[Color['Blue'] = Blue] = 'Blue'; - return Color; - })(Color || {}); -})(M4 || (M4 = {})); -let M5; -(function(_M5) { - var Color = (Color => { - const Red = 0; - Color[Color['Red'] = Red] = 'Red'; - const Green = 1 + Red; - Color[Color['Green'] = Green] = 'Green'; - const Blue = 1 + Green; - Color[Color['Blue'] = Blue] = 'Blue'; - return Color; - })(Color || {}); -})(M5 || (M5 = {})); -let M6; -(function(_M6) { - (function(_A) { - var Color = (Color => { - const Red = 0; - Color[Color['Red'] = Red] = 'Red'; - const Green = 1 + Red; - Color[Color['Green'] = Green] = 'Green'; - const Blue = 1 + Green; - Color[Color['Blue'] = Blue] = 'Blue'; - return Color; - })(Color || {}); - })(A || (A = {})); -})(M6 || (M6 = {})); -let M6; -(function(_M62) { -export let A; - (function(_A2) { - var Color = (Color => { - const Yellow = 1; - Color[Color['Yellow'] = Yellow] = 'Yellow'; - return Color; - })(Color || {}); - })(A || (A = {})); - var t = A.Color.Yellow; - t = A.Color.Red; -})(M6 || (M6 = {})); ``` # typescript/tests/cases/conformance/enums/enumMergingErrors.ts ```typescript -let M; -(function(_M) { -export var E1 = (E1 => { - const A = 0; - E1[E1['A'] = A] = 'A'; - return E1; - })(E1 || {}); -export var E2 = (E2 => { - const C = 0; - E2[E2['C'] = C] = 'C'; - return E2; - })(E2 || {}); -export var E3 = (E3 => { - const A = 0; - E3[E3['A'] = A] = 'A'; - return E3; - })(E3 || {}); -})(M || (M = {})); -let M; -(function(_M2) { - var E1 = (E1 => { - const B = 'foo'.length; - E1[E1['B'] = B] = 'B'; - return E1; - })(E1 || {}); - var E2 = (E2 => { - const B = 'foo'.length; - E2[E2['B'] = B] = 'B'; - return E2; - })(E2 || {}); - var E3 = (E3 => { - const C = 0; - E3[E3['C'] = C] = 'C'; - return E3; - })(E3 || {}); -})(M || (M = {})); -let M; -(function(_M3) { - var E1 = (E1 => { - const C = 0; - E1[E1['C'] = C] = 'C'; - return E1; - })(E1 || {}); - var E2 = (E2 => { - const A = 0; - E2[E2['A'] = A] = 'A'; - return E2; - })(E2 || {}); - var E3 = (E3 => { - const B = 'foo'.length; - E3[E3['B'] = B] = 'B'; - return E3; - })(E3 || {}); -})(M || (M = {})); -let M1; -(function(_M1) { - var E1 = (E1 => { - const A = 0; - E1[E1['A'] = A] = 'A'; - return E1; - })(E1 || {}); -})(M1 || (M1 = {})); -let M1; -(function(_M12) { - var E1 = (E1 => { - const B = 0; - E1[E1['B'] = B] = 'B'; - return E1; - })(E1 || {}); -})(M1 || (M1 = {})); -let M1; -(function(_M13) { - var E1 = (E1 => { - const C = 0; - E1[E1['C'] = C] = 'C'; - return E1; - })(E1 || {}); -})(M1 || (M1 = {})); -let M2; -(function(_M2) { - var E1 = (E1 => { - const A = 0; - E1[E1['A'] = A] = 'A'; - return E1; - })(E1 || {}); -})(M2 || (M2 = {})); -let M2; -(function(_M22) { - var E1 = (E1 => { - const B = 0; - E1[E1['B'] = B] = 'B'; - return E1; - })(E1 || {}); -})(M2 || (M2 = {})); -let M2; -(function(_M23) { - var E1 = (E1 => { - const C = 0; - E1[E1['C'] = C] = 'C'; - return E1; - })(E1 || {}); -})(M2 || (M2 = {})); ``` @@ -759,99 +95,30 @@ let M2; ```typescript { let Infinity = {}; - var En = (En => { - const X = Infinity; - En[En['X'] = X] = 'X'; - return En; - })(En || {}); } { let NaN = {}; - var En = (En => { - const X = NaN; - En[En['X'] = X] = 'X'; - return En; - })(En || {}); } ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/boolean-value/input.ts ```typescript -var E = (E => { - const A = true; - E[E['A'] = A] = 'A'; - return E; -})(E || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/const/input.ts ```typescript -var E = (E => { - return E; -})(E || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/constant-folding/input.ts ```typescript -var E = (E => { - const a = 0; - E[E['a'] = a] = 'a'; - const b = 1 | 2; - E[E['b'] = b] = 'b'; - const c = 1 & 3; - E[E['c'] = c] = 'c'; - const d = 4 >> 1; - E[E['d'] = d] = 'd'; - const e = 8 >>> 1; - E[E['e'] = e] = 'e'; - const f = 1 << 3; - E[E['f'] = f] = 'f'; - const g = 2 ^ 7; - E[E['g'] = g] = 'g'; - const h = 2 * 3; - E[E['h'] = h] = 'h'; - const i = 2 / 3; - E[E['i'] = i] = 'i'; - const j = 2 + 5; - E[E['j'] = j] = 'j'; - const k = 2 - 4; - E[E['k'] = k] = 'k'; - const l = 2.5 % 2; - E[E['l'] = l] = 'l'; - const m = 2 ** 33; - E[E['m'] = m] = 'm'; - const n = +9; - E[E['n'] = n] = 'n'; - const o = -1; - E[E['o'] = o] = 'o'; - const p = ~2; - E[E['p'] = p] = 'p'; - const q = 1 + 2 - 3 * 4 / -5; - E[E['q'] = q] = 'q'; - const r = 1 + q; - E[E['r'] = r] = 'r'; - return E; -})(E || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/enum-merging-inner-references/input.ts ```typescript -var Animals = (Animals => { - const Cat = 1; - Animals[Animals['Cat'] = Cat] = 'Cat'; - const Dog = 2; - Animals[Animals['Dog'] = Dog] = 'Dog'; - return Animals; -})(Animals || {}); -var Animals = (Animals => { - const CatDog = Cat - Dog; - Animals[Animals['CatDog'] = CatDog] = 'CatDog'; - return Animals; -})(Animals || {}); ``` @@ -859,211 +126,64 @@ var Animals = (Animals => { ```typescript const Cat = 10; const Dog = 20; -var Animals = (Animals => { - const Cat = 1; - Animals[Animals['Cat'] = Cat] = 'Cat'; - return Animals; -})(Animals || {}); -var Animals = (Animals => { - const Dog = 2; - Animals[Animals['Dog'] = Dog] = 'Dog'; - return Animals; -})(Animals || {}); -var Animals = (Animals => { - const CatDog = Cat | Dog; - Animals[Animals['CatDog'] = CatDog] = 'CatDog'; - return Animals; -})(Animals || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/export/input.ts ```typescript -export var E = (E => { - const A = 1; - E[E['A'] = A] = 'A'; - return E; -})(E || {}); -var E = (E => { - const B = 2; - E[E['B'] = B] = 'B'; - return E; -})(E || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/inferred/input.ts ```typescript -var E = (E => { - const x = 0; - E[E['x'] = x] = 'x'; - const y = 1 + x; - E[E['y'] = y] = 'y'; - return E; -})(E || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/inner-references/input.ts ```typescript -var E = (E => { - const a = 10; - E[E['a'] = a] = 'a'; - const b = a; - E[E['b'] = b] = 'b'; - return E; -})(E || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/mix-references/input.ts ```typescript var x = 10; -var Foo = (Foo => { - const a = 10; - Foo[Foo['a'] = a] = 'a'; - const b = a; - Foo[Foo['b'] = b] = 'b'; - const c = b + x; - Foo[Foo['c'] = c] = 'c'; - return Foo; -})(Foo || {}); -var Bar = (Bar => { - const D = Foo.a; - Bar[Bar['D'] = D] = 'D'; - const E = D; - Bar[Bar['E'] = E] = 'E'; - const F = Math.E; - Bar[Bar['F'] = F] = 'F'; - const G = E + Foo.c; - Bar[Bar['G'] = G] = 'G'; - return Bar; -})(Bar || {}); -var Baz = (Baz => { - const a = 0; - Baz[Baz['a'] = a] = 'a'; - const b = 1; - Baz[Baz['b'] = b] = 'b'; - const x = a.toString(); - Baz[Baz['x'] = x] = 'x'; - return Baz; -})(Baz || {}); -var A = (A => { - const a = 0; - A[A['a'] = a] = 'a'; - const b = (() => { - let a = 1; - return a + 1; - })(); - A[A['b'] = b] = 'b'; - const c = (() => { - return a + 2; - })(); - A[A['c'] = c] = 'c'; - return A; -})(A || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/non-foldable-constant/input.ts ```typescript -var E = (E => { - const a = Math.sin(1); - E[E['a'] = a] = 'a'; - const b = 1 + a; - E[E['b'] = b] = 'b'; - return E; -})(E || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/non-scoped/input.ts ```typescript -var E = (E => { - const x = 1; - E[E['x'] = x] = 'x'; - const y = 2; - E[E['y'] = y] = 'y'; - return E; -})(E || {}); -var E = (E => { - const z = 3; - E[E['z'] = z] = 'z'; - return E; -})(E || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/outer-references/input.ts ```typescript -var socketType = (socketType => { - const SOCKET = 0; - socketType[socketType['SOCKET'] = SOCKET] = 'SOCKET'; - const SERVER = 1 + SOCKET; - socketType[socketType['SERVER'] = SERVER] = 'SERVER'; - const IPC = 1 + SERVER; - socketType[socketType['IPC'] = IPC] = 'IPC'; - return socketType; -})(socketType || {}); -var constants = (constants => { - const SOCKET = socketType.SOCKET; - constants[constants['SOCKET'] = SOCKET] = 'SOCKET'; - const SERVER = socketType.SERVER; - constants[constants['SERVER'] = SERVER] = 'SERVER'; - const IPC = socketType.IPC; - constants[constants['IPC'] = IPC] = 'IPC'; - const UV_READABLE = 1 + IPC; - constants[constants['UV_READABLE'] = UV_READABLE] = 'UV_READABLE'; - const UV_WRITABLE = 1 + UV_READABLE; - constants[constants['UV_WRITABLE'] = UV_WRITABLE] = 'UV_WRITABLE'; - return constants; -})(constants || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/scoped/input.ts ```typescript { - var E = (E => { - return E; - })(E || {}); } ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/string-value/input.ts ```typescript -var E = (E => { - const A = 0; - E[E['A'] = A] = 'A'; - const B = ''; - E['B'] = B; - const A2 = A; - E[E['A2'] = A2] = 'A2'; - const B2 = B; - E[E['B2'] = B2] = 'B2'; - return E; -})(E || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/string-value-template/input.ts ```typescript -var E = (E => { - const A = `Hey`; - E['A'] = A; - return E; -})(E || {}); ``` # babel/packages/babel-plugin-transform-typescript/test/fixtures/enum/string-values-computed/input.ts ```typescript -var E = (E => { - const A = 'HALLO' + 'WERLD'; - E[E['A'] = A] = 'A'; - return E; -})(E || {}); ``` @@ -1071,28 +191,8 @@ var E = (E => { ```typescript const BaseValue = 10; const Prefix = '/data'; -var Values = (Values => { - const First = BaseValue; - Values[Values['First'] = First] = 'First'; - const Second = 1 + First; - Values[Values['Second'] = Second] = 'Second'; - const Third = 1 + Second; - Values[Values['Third'] = Third] = 'Third'; - return Values; -})(Values || {}); const xxx = 100 + Values.First; const yyy = xxx; -var Routes = (Routes => { - const Parts = `${Prefix}/parts`; - Routes['Parts'] = Parts; - const Invoices = `${Prefix}/invoices`; - Routes['Invoices'] = Invoices; - const x = `${Values.First}/x`; - Routes['x'] = x; - const y = `${yyy}/y`; - Routes['y'] = y; - return Routes; -})(Routes || {}); ```