diff --git a/crates/oxc_transformer/examples/transformer.rs b/crates/oxc_transformer/examples/transformer.rs index 9495a428adca6..08186d3ec4698 100644 --- a/crates/oxc_transformer/examples/transformer.rs +++ b/crates/oxc_transformer/examples/transformer.rs @@ -6,7 +6,7 @@ use oxc_codegen::CodeGenerator; use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; -use oxc_transformer::{ESTarget, TransformOptions, Transformer}; +use oxc_transformer::{ESTarget, EnvOptions, TransformOptions, Transformer}; use pico_args::Arguments; // Instruction: @@ -55,14 +55,11 @@ fn main() { let (symbols, scopes) = ret.semantic.into_symbol_table_and_scope_tree(); - let transform_options = if let Some(_targets) = &targets { - // FIXME - TransformOptions::enable_all() - // TransformOptions::try_from(&BabelEnvOptions { - // targets: Targets::try_from_query(targets).unwrap(), - // ..BabelEnvOptions::default() - // }) - // .unwrap() + let transform_options = if let Some(query) = &targets { + TransformOptions { + env: EnvOptions::from_browerslist_query(query).unwrap(), + ..TransformOptions::default() + } } else if let Some(target) = &target { TransformOptions::from(ESTarget::from_str(target).unwrap()) } else { diff --git a/crates/oxc_transformer/src/options/babel/env/targets.rs b/crates/oxc_transformer/src/options/babel/env/targets.rs index c10640235126f..acff86323d8ee 100644 --- a/crates/oxc_transformer/src/options/babel/env/targets.rs +++ b/crates/oxc_transformer/src/options/babel/env/targets.rs @@ -39,7 +39,7 @@ impl Targets { /// # Errors /// /// * Query is invalid. - pub fn try_from_query(query: &str) -> Result { + pub fn try_from_query(query: &str) -> Result { Query::Single(query.to_string()).exec().map(|v| v.0).map(Self) } diff --git a/crates/oxc_transformer/src/options/env.rs b/crates/oxc_transformer/src/options/env.rs index c8d3aa5c905c8..66cc2bfb2fbab 100644 --- a/crates/oxc_transformer/src/options/env.rs +++ b/crates/oxc_transformer/src/options/env.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use cow_utils::CowUtils; +use oxc_diagnostics::Error; use serde::Deserialize; use crate::{ @@ -13,6 +14,7 @@ use crate::{ es2021::ES2021Options, es2022::{ClassPropertiesOptions, ES2022Options}, regexp::RegExpOptions, + Targets, }; use super::babel::BabelEnvOptions; @@ -131,6 +133,20 @@ impl EnvOptions { }, } } + + /// # Errors + /// + /// * When the query failed to parse. + pub fn from_browerslist_query(query: &str) -> Result { + Self::try_from(BabelEnvOptions { + targets: Targets::try_from_query(query)?, + // This option will be enabled by default in Babel 8. + // + bugfixes: true, + ..BabelEnvOptions::default() + }) + .map_err(|err| Error::msg(err)) + } } impl From for EnvOptions { diff --git a/crates/oxc_transformer/tests/es_target/mod.rs b/crates/oxc_transformer/tests/es_target/mod.rs index 87859972b658e..76e68ca0b89ec 100644 --- a/crates/oxc_transformer/tests/es_target/mod.rs +++ b/crates/oxc_transformer/tests/es_target/mod.rs @@ -1,35 +1,11 @@ -use std::{path::Path, str::FromStr}; +use std::str::FromStr; -use oxc_allocator::Allocator; -use oxc_codegen::{CodeGenerator, CodegenOptions}; -use oxc_parser::Parser; -use oxc_semantic::SemanticBuilder; +use crate::{codegen, test}; use oxc_span::SourceType; -use oxc_transformer::{ESTarget, TransformOptions, Transformer}; - -use crate::run; - -pub(crate) fn test(source_text: &str, target: &str) -> String { - let source_type = SourceType::default(); - let allocator = Allocator::default(); - let ret = Parser::new(&allocator, source_text, source_type).parse(); - let mut program = ret.program; - let (symbols, scopes) = - SemanticBuilder::new().build(&program).semantic.into_symbol_table_and_scope_tree(); - let options = TransformOptions::from(ESTarget::from_str(target).unwrap()); - Transformer::new(&allocator, Path::new(""), options).build_with_symbols_and_scopes( - symbols, - scopes, - &mut program, - ); - CodeGenerator::new() - .with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() }) - .build(&program) - .code -} +use oxc_transformer::{ESTarget, TransformOptions}; #[test] -fn es2015() { +fn es_target() { use std::fmt::Write; let cases = [ @@ -45,11 +21,13 @@ fn es2015() { // Test no transformation for esnext. for (_, case) in cases { - assert_eq!(run(case, SourceType::mjs()), test(case, "esnext")); + let options = TransformOptions::from(ESTarget::from_str("esnext").unwrap()); + assert_eq!(codegen(case, SourceType::mjs()), test(case, options)); } let snapshot = cases.iter().enumerate().fold(String::new(), |mut w, (i, (target, case))| { - let result = test(case, target); + let options = TransformOptions::from(ESTarget::from_str(target).unwrap()); + let result = test(case, options); write!(w, "########## {i} {target}\n{case}\n----------\n{result}\n").unwrap(); w }); diff --git a/crates/oxc_transformer/tests/mod.rs b/crates/oxc_transformer/tests/mod.rs index 1c1df7721adfb..da294218e7526 100644 --- a/crates/oxc_transformer/tests/mod.rs +++ b/crates/oxc_transformer/tests/mod.rs @@ -1,12 +1,17 @@ mod es_target; mod plugins; +mod targets; + +use std::path::Path; use oxc_allocator::Allocator; use oxc_codegen::{CodeGenerator, CodegenOptions}; use oxc_parser::Parser; +use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; +use oxc_transformer::{TransformOptions, Transformer}; -pub fn run(source_text: &str, source_type: SourceType) -> String { +pub fn codegen(source_text: &str, source_type: SourceType) -> String { let allocator = Allocator::default(); let ret = Parser::new(&allocator, source_text, source_type).parse(); CodeGenerator::new() @@ -14,3 +19,21 @@ pub fn run(source_text: &str, source_type: SourceType) -> String { .build(&ret.program) .code } + +pub(crate) fn test(source_text: &str, options: TransformOptions) -> String { + let source_type = SourceType::default(); + let allocator = Allocator::default(); + let ret = Parser::new(&allocator, source_text, source_type).parse(); + let mut program = ret.program; + let (symbols, scopes) = + SemanticBuilder::new().build(&program).semantic.into_symbol_table_and_scope_tree(); + Transformer::new(&allocator, Path::new(""), options).build_with_symbols_and_scopes( + symbols, + scopes, + &mut program, + ); + CodeGenerator::new() + .with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() }) + .build(&program) + .code +} diff --git a/crates/oxc_transformer/tests/plugins/inject_global_variables.rs b/crates/oxc_transformer/tests/plugins/inject_global_variables.rs index daa310abf6fcd..26ee7e9b6b0eb 100644 --- a/crates/oxc_transformer/tests/plugins/inject_global_variables.rs +++ b/crates/oxc_transformer/tests/plugins/inject_global_variables.rs @@ -9,7 +9,7 @@ use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; use oxc_transformer::{InjectGlobalVariables, InjectGlobalVariablesConfig, InjectImport}; -use crate::run; +use crate::codegen; pub(crate) fn test(source_text: &str, expected: &str, config: InjectGlobalVariablesConfig) { let source_type = SourceType::default(); @@ -23,7 +23,7 @@ pub(crate) fn test(source_text: &str, expected: &str, config: InjectGlobalVariab .with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() }) .build(&program) .code; - let expected = run(expected, source_type); + let expected = codegen(expected, source_type); assert_eq!(result, expected, "for source {source_text}"); } diff --git a/crates/oxc_transformer/tests/plugins/replace_global_defines.rs b/crates/oxc_transformer/tests/plugins/replace_global_defines.rs index 783a453e8cb1c..87850340e99a3 100644 --- a/crates/oxc_transformer/tests/plugins/replace_global_defines.rs +++ b/crates/oxc_transformer/tests/plugins/replace_global_defines.rs @@ -5,7 +5,7 @@ use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; use oxc_transformer::{ReplaceGlobalDefines, ReplaceGlobalDefinesConfig}; -use crate::run; +use crate::codegen; pub(crate) fn test(source_text: &str, expected: &str, config: ReplaceGlobalDefinesConfig) { let source_type = SourceType::default(); @@ -19,7 +19,7 @@ pub(crate) fn test(source_text: &str, expected: &str, config: ReplaceGlobalDefin .with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() }) .build(&program) .code; - let expected = run(expected, source_type); + let expected = codegen(expected, source_type); assert_eq!(result, expected, "for source {source_text}"); } diff --git a/crates/oxc_transformer/tests/targets/mod.rs b/crates/oxc_transformer/tests/targets/mod.rs new file mode 100644 index 0000000000000..be690011d6887 --- /dev/null +++ b/crates/oxc_transformer/tests/targets/mod.rs @@ -0,0 +1,36 @@ +use crate::{codegen, test}; +use oxc_span::SourceType; +use oxc_transformer::{ESTarget, EnvOptions, TransformOptions}; + +#[test] +fn targets() { + let cases = [ + ("() => {}"), + ("a ** b"), + // ("async function foo() {}"), + ("({ ...x })"), + ("try {} catch {}"), + ("a ?? b"), + ("a ||= b"), + // ("class foo { static {} }"), + ]; + + // Test no transformation for default targets. + for case in cases { + let options = TransformOptions { + env: EnvOptions::from_browerslist_query("defaults").unwrap(), + ..TransformOptions::default() + }; + assert_eq!(codegen(case, SourceType::mjs()), test(case, options)); + } + + // Test transformation for very low targets. + for case in cases { + let options = TransformOptions::from(ESTarget::ES5); + let options_node = TransformOptions { + env: EnvOptions::from_browerslist_query("node 0.10").unwrap(), + ..TransformOptions::default() + }; + assert_eq!(test(case, options), test(case, options_node)); + } +}