diff --git a/crates/oxc/src/compiler.rs b/crates/oxc/src/compiler.rs index fe9efb716..39332e654 100644 --- a/crates/oxc/src/compiler.rs +++ b/crates/oxc/src/compiler.rs @@ -65,8 +65,8 @@ pub trait CompilerInterface { None } - fn transform_options(&self) -> Option { - Some(TransformOptions::default()) + fn transform_options(&self) -> Option<&TransformOptions> { + None } fn define_options(&self) -> Option { @@ -267,7 +267,7 @@ pub trait CompilerInterface { #[allow(clippy::too_many_arguments)] fn transform<'a>( &self, - options: TransformOptions, + options: &TransformOptions, allocator: &'a Allocator, program: &mut Program<'a>, source_path: &Path, diff --git a/crates/oxc_transformer/examples/transformer.rs b/crates/oxc_transformer/examples/transformer.rs index 27c9880b1..d6153ac8b 100644 --- a/crates/oxc_transformer/examples/transformer.rs +++ b/crates/oxc_transformer/examples/transformer.rs @@ -66,7 +66,7 @@ fn main() { TransformOptions::enable_all() }; - let ret = Transformer::new(&allocator, path, transform_options).build_with_symbols_and_scopes( + let ret = Transformer::new(&allocator, path, &transform_options).build_with_symbols_and_scopes( symbols, scopes, &mut program, diff --git a/crates/oxc_transformer/src/common/arrow_function_converter.rs b/crates/oxc_transformer/src/common/arrow_function_converter.rs index 77f5d2f0b..ac3ffe31f 100644 --- a/crates/oxc_transformer/src/common/arrow_function_converter.rs +++ b/crates/oxc_transformer/src/common/arrow_function_converter.rs @@ -98,7 +98,7 @@ use oxc_syntax::{ use oxc_traverse::{Ancestor, BoundIdentifier, Traverse, TraverseCtx}; use rustc_hash::FxHashMap; -use crate::TransformOptions; +use crate::EnvOptions; /// Mode for arrow function conversion #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -129,12 +129,10 @@ pub struct ArrowFunctionConverter<'a> { } impl<'a> ArrowFunctionConverter<'a> { - pub fn new(options: &TransformOptions) -> Self { - let mode = if options.env.es2015.arrow_function.is_some() { + pub fn new(env: &EnvOptions) -> Self { + let mode = if env.es2015.arrow_function.is_some() { ArrowFunctionConverterMode::Enabled - } else if options.env.es2017.async_to_generator - || options.env.es2018.async_generator_functions - { + } else if env.es2017.async_to_generator || env.es2018.async_generator_functions { ArrowFunctionConverterMode::AsyncOnly } else { ArrowFunctionConverterMode::Disabled diff --git a/crates/oxc_transformer/src/common/mod.rs b/crates/oxc_transformer/src/common/mod.rs index 8c6ba957e..8fc341179 100644 --- a/crates/oxc_transformer/src/common/mod.rs +++ b/crates/oxc_transformer/src/common/mod.rs @@ -5,7 +5,7 @@ use oxc_allocator::Vec as ArenaVec; use oxc_ast::ast::*; use oxc_traverse::{Traverse, TraverseCtx}; -use crate::{TransformCtx, TransformOptions}; +use crate::{EnvOptions, TransformCtx}; pub mod arrow_function_converter; pub mod helper_loader; @@ -28,7 +28,7 @@ pub struct Common<'a, 'ctx> { } impl<'a, 'ctx> Common<'a, 'ctx> { - pub fn new(options: &TransformOptions, ctx: &'ctx TransformCtx<'a>) -> Self { + pub fn new(options: &EnvOptions, ctx: &'ctx TransformCtx<'a>) -> Self { Self { module_imports: ModuleImports::new(ctx), var_declarations: VarDeclarations::new(ctx), diff --git a/crates/oxc_transformer/src/es2015/options.rs b/crates/oxc_transformer/src/es2015/options.rs index c97238688..9201e92d9 100644 --- a/crates/oxc_transformer/src/es2015/options.rs +++ b/crates/oxc_transformer/src/es2015/options.rs @@ -2,7 +2,7 @@ use serde::Deserialize; use super::ArrowFunctionsOptions; -#[derive(Debug, Default, Clone, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2015Options { #[serde(skip)] diff --git a/crates/oxc_transformer/src/es2016/options.rs b/crates/oxc_transformer/src/es2016/options.rs index f50af0d88..6d330043c 100644 --- a/crates/oxc_transformer/src/es2016/options.rs +++ b/crates/oxc_transformer/src/es2016/options.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -#[derive(Debug, Default, Clone, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2016Options { #[serde(skip)] diff --git a/crates/oxc_transformer/src/es2017/options.rs b/crates/oxc_transformer/src/es2017/options.rs index 9be2f2a7e..d90763386 100644 --- a/crates/oxc_transformer/src/es2017/options.rs +++ b/crates/oxc_transformer/src/es2017/options.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -#[derive(Debug, Default, Clone, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2017Options { #[serde(skip)] diff --git a/crates/oxc_transformer/src/es2018/options.rs b/crates/oxc_transformer/src/es2018/options.rs index bcb9535c7..e391d8eeb 100644 --- a/crates/oxc_transformer/src/es2018/options.rs +++ b/crates/oxc_transformer/src/es2018/options.rs @@ -2,7 +2,7 @@ use serde::Deserialize; use super::ObjectRestSpreadOptions; -#[derive(Debug, Default, Clone, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2018Options { #[serde(skip)] diff --git a/crates/oxc_transformer/src/es2019/options.rs b/crates/oxc_transformer/src/es2019/options.rs index 0f3d4e549..fc6ece2cc 100644 --- a/crates/oxc_transformer/src/es2019/options.rs +++ b/crates/oxc_transformer/src/es2019/options.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -#[derive(Debug, Default, Clone, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2019Options { #[serde(skip)] diff --git a/crates/oxc_transformer/src/es2020/options.rs b/crates/oxc_transformer/src/es2020/options.rs index 78b94930e..cd76e6acd 100644 --- a/crates/oxc_transformer/src/es2020/options.rs +++ b/crates/oxc_transformer/src/es2020/options.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -#[derive(Debug, Default, Clone, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2020Options { #[serde(skip)] diff --git a/crates/oxc_transformer/src/es2021/options.rs b/crates/oxc_transformer/src/es2021/options.rs index 5eea0fc56..6d1583dcc 100644 --- a/crates/oxc_transformer/src/es2021/options.rs +++ b/crates/oxc_transformer/src/es2021/options.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -#[derive(Debug, Default, Clone, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2021Options { #[serde(skip)] diff --git a/crates/oxc_transformer/src/jsx/comments.rs b/crates/oxc_transformer/src/jsx/comments.rs index 41cafc967..db488a637 100644 --- a/crates/oxc_transformer/src/jsx/comments.rs +++ b/crates/oxc_transformer/src/jsx/comments.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use oxc_ast::{Comment, CommentKind}; use oxc_syntax::identifier::is_irregular_whitespace; -use crate::{JsxRuntime, TransformCtx, TransformOptions}; +use crate::{JsxOptions, JsxRuntime, TransformCtx, TypeScriptOptions}; /// Scan through all comments and find the following pragmas: /// @@ -18,16 +18,18 @@ use crate::{JsxRuntime, TransformCtx, TransformOptions}; /// This behavior is aligned with Babel. pub(crate) fn update_options_with_comments( comments: &[Comment], - options: &mut TransformOptions, + typescript: &mut TypeScriptOptions, + jsx: &mut JsxOptions, ctx: &TransformCtx, ) { for comment in comments { - update_options_with_comment(options, comment, ctx.source_text); + update_options_with_comment(typescript, jsx, comment, ctx.source_text); } } fn update_options_with_comment( - options: &mut TransformOptions, + typescript: &mut TypeScriptOptions, + jsx: &mut JsxOptions, comment: &Comment, source_text: &str, ) { @@ -38,14 +40,14 @@ fn update_options_with_comment( "" => { // Don't set React option unless React transform is enabled // otherwise can cause error in `ReactJsx::new` - if options.jsx.jsx_plugin || options.jsx.development { - options.jsx.pragma = Some(remainder.to_string()); + if jsx.jsx_plugin || jsx.development { + jsx.pragma = Some(remainder.to_string()); } - options.typescript.jsx_pragma = Cow::from(remainder.to_string()); + typescript.jsx_pragma = Cow::from(remainder.to_string()); } // @jsxRuntime "Runtime" => { - options.jsx.runtime = match remainder { + jsx.runtime = match remainder { "classic" => JsxRuntime::Classic, "automatic" => JsxRuntime::Automatic, _ => return, @@ -53,16 +55,16 @@ fn update_options_with_comment( } // @jsxImportSource "ImportSource" => { - options.jsx.import_source = Some(remainder.to_string()); + jsx.import_source = Some(remainder.to_string()); } // @jsxFrag "Frag" => { // Don't set React option unless React transform is enabled // otherwise can cause error in `ReactJsx::new` - if options.jsx.jsx_plugin || options.jsx.development { - options.jsx.pragma_frag = Some(remainder.to_string()); + if jsx.jsx_plugin || jsx.development { + jsx.pragma_frag = Some(remainder.to_string()); } - options.typescript.jsx_pragma_frag = Cow::from(remainder.to_string()); + typescript.jsx_pragma_frag = Cow::from(remainder.to_string()); } _ => {} } diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 9f2f7b1ba..b80458ab4 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -70,14 +70,24 @@ pub struct TransformerReturn { pub struct Transformer<'a> { ctx: TransformCtx<'a>, - options: TransformOptions, + // options: TransformOptions, allocator: &'a Allocator, + + typescript: TypeScriptOptions, + jsx: JsxOptions, + env: EnvOptions, } impl<'a> Transformer<'a> { - pub fn new(allocator: &'a Allocator, source_path: &Path, options: TransformOptions) -> Self { - let ctx = TransformCtx::new(source_path, &options); - Self { ctx, options, allocator } + pub fn new(allocator: &'a Allocator, source_path: &Path, options: &TransformOptions) -> Self { + let ctx = TransformCtx::new(source_path, options); + Self { + ctx, + allocator, + typescript: options.typescript.clone(), + jsx: options.jsx.clone(), + env: options.env, + } } pub fn build_with_symbols_and_scopes( @@ -91,24 +101,29 @@ impl<'a> Transformer<'a> { self.ctx.source_type = program.source_type; self.ctx.source_text = program.source_text; - jsx::update_options_with_comments(&program.comments, &mut self.options, &self.ctx); + jsx::update_options_with_comments( + &program.comments, + &mut self.typescript, + &mut self.jsx, + &self.ctx, + ); let mut transformer = TransformerImpl { - common: Common::new(&self.options, &self.ctx), + common: Common::new(&self.env, &self.ctx), x0_typescript: program .source_type .is_typescript() - .then(|| TypeScript::new(&self.options.typescript, &self.ctx)), - x1_jsx: Jsx::new(self.options.jsx, ast_builder, &self.ctx), - x2_es2022: ES2022::new(self.options.env.es2022, &self.ctx), - x2_es2021: ES2021::new(self.options.env.es2021, &self.ctx), - x2_es2020: ES2020::new(self.options.env.es2020, &self.ctx), - x2_es2019: ES2019::new(self.options.env.es2019), - x2_es2018: ES2018::new(self.options.env.es2018, &self.ctx), - x2_es2016: ES2016::new(self.options.env.es2016, &self.ctx), - x2_es2017: ES2017::new(self.options.env.es2017, &self.ctx), - x3_es2015: ES2015::new(self.options.env.es2015, &self.ctx), - x4_regexp: RegExp::new(self.options.env.regexp, &self.ctx), + .then(|| TypeScript::new(&self.typescript, &self.ctx)), + x1_jsx: Jsx::new(self.jsx, ast_builder, &self.ctx), + x2_es2022: ES2022::new(self.env.es2022, &self.ctx), + x2_es2021: ES2021::new(self.env.es2021, &self.ctx), + x2_es2020: ES2020::new(self.env.es2020, &self.ctx), + x2_es2019: ES2019::new(self.env.es2019), + x2_es2018: ES2018::new(self.env.es2018, &self.ctx), + x2_es2016: ES2016::new(self.env.es2016, &self.ctx), + x2_es2017: ES2017::new(self.env.es2017, &self.ctx), + x3_es2015: ES2015::new(self.env.es2015, &self.ctx), + x4_regexp: RegExp::new(self.env.regexp, &self.ctx), }; let (symbols, scopes) = traverse_mut(&mut transformer, allocator, program, symbols, scopes); diff --git a/crates/oxc_transformer/src/options/env.rs b/crates/oxc_transformer/src/options/env.rs index 814038c5f..0377782e7 100644 --- a/crates/oxc_transformer/src/options/env.rs +++ b/crates/oxc_transformer/src/options/env.rs @@ -18,7 +18,7 @@ use crate::{ use super::{babel::BabelEnvOptions, ESFeature, ESTarget, Engine}; -#[derive(Debug, Default, Clone, Deserialize)] +#[derive(Debug, Default, Clone, Copy, Deserialize)] #[serde(try_from = "BabelEnvOptions")] pub struct EnvOptions { pub regexp: RegExpOptions, diff --git a/crates/oxc_transformer/src/options/mod.rs b/crates/oxc_transformer/src/options/mod.rs index cdbfc3832..f85ce5944 100644 --- a/crates/oxc_transformer/src/options/mod.rs +++ b/crates/oxc_transformer/src/options/mod.rs @@ -161,7 +161,7 @@ impl TryFrom<&BabelOptions> for TransformOptions { jsx_options }; - let env = options.presets.env.clone().unwrap_or_default(); + let env = options.presets.env.unwrap_or_default(); let regexp = RegExpOptions { sticky_flag: env.regexp.sticky_flag || options.plugins.sticky_flag, diff --git a/crates/oxc_transformer/tests/integrations/es_target.rs b/crates/oxc_transformer/tests/integrations/es_target.rs index ad61efef6..afc1d2fcc 100644 --- a/crates/oxc_transformer/tests/integrations/es_target.rs +++ b/crates/oxc_transformer/tests/integrations/es_target.rs @@ -21,15 +21,15 @@ fn es_target() { ]; // Test no transformation for esnext. + let options = TransformOptions::from(ESTarget::from_str("esnext").unwrap()); for (_, case) in cases { - let options = TransformOptions::from(ESTarget::from_str("esnext").unwrap()); - assert_eq!(test(case, options), Ok(codegen(case, SourceType::mjs()))); + assert_eq!(test(case, &options), Ok(codegen(case, SourceType::mjs()))); } let snapshot = cases.into_iter().enumerate().fold(String::new(), |mut w, (i, (target, case))| { let options = TransformOptions::from_target(target).unwrap(); - let result = match test(case, options) { + let result = match test(case, &options) { Ok(code) => code, Err(errors) => errors .into_iter() diff --git a/crates/oxc_transformer/tests/integrations/main.rs b/crates/oxc_transformer/tests/integrations/main.rs index bd71302e8..c7726c8e8 100644 --- a/crates/oxc_transformer/tests/integrations/main.rs +++ b/crates/oxc_transformer/tests/integrations/main.rs @@ -23,7 +23,7 @@ pub fn codegen(source_text: &str, source_type: SourceType) -> String { pub(crate) fn test( source_text: &str, - options: TransformOptions, + options: &TransformOptions, ) -> Result> { let source_type = SourceType::default(); let allocator = Allocator::default(); diff --git a/crates/oxc_transformer/tests/integrations/targets.rs b/crates/oxc_transformer/tests/integrations/targets.rs index 160a4b209..7ea88e0bf 100644 --- a/crates/oxc_transformer/tests/integrations/targets.rs +++ b/crates/oxc_transformer/tests/integrations/targets.rs @@ -16,21 +16,21 @@ fn targets() { ]; // Test no transformation for default targets. + let options = TransformOptions { + env: EnvOptions::from_browserslist_query("defaults").unwrap(), + ..TransformOptions::default() + }; for case in cases { - let options = TransformOptions { - env: EnvOptions::from_browserslist_query("defaults").unwrap(), - ..TransformOptions::default() - }; - assert_eq!(Ok(codegen(case, SourceType::mjs())), test(case, options)); + assert_eq!(Ok(codegen(case, SourceType::mjs())), test(case, &options)); } // Test transformation for very low targets. + let options = TransformOptions::from(ESTarget::ES5); + let options_node = TransformOptions { + env: EnvOptions::from_browserslist_query("node 0.10").unwrap(), + ..TransformOptions::default() + }; for case in cases { - let options = TransformOptions::from(ESTarget::ES5); - let options_node = TransformOptions { - env: EnvOptions::from_browserslist_query("node 0.10").unwrap(), - ..TransformOptions::default() - }; - assert_eq!(test(case, options), test(case, options_node)); + assert_eq!(test(case, &options), test(case, &options_node)); } } diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index 6840cc6c0..871d9b9e6 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -243,7 +243,7 @@ impl Oxc { if run_options.transform.unwrap_or_default() { let options = TransformOptions::enable_all(); - let result = Transformer::new(&allocator, &path, options) + let result = Transformer::new(&allocator, &path, &options) .build_with_symbols_and_scopes(symbols, scopes, &mut program); if !result.errors.is_empty() { self.save_diagnostics( diff --git a/napi/transform/src/transformer.rs b/napi/transform/src/transformer.rs index be8f239b0..0e5dca677 100644 --- a/napi/transform/src/transformer.rs +++ b/napi/transform/src/transformer.rs @@ -111,8 +111,8 @@ impl CompilerInterface for Compiler { self.sourcemap } - fn transform_options(&self) -> Option { - Some(self.transform_options.clone()) + fn transform_options(&self) -> Option<&oxc::transformer::TransformOptions> { + Some(&self.transform_options) } fn isolated_declaration_options(&self) -> Option { diff --git a/tasks/benchmark/benches/transformer.rs b/tasks/benchmark/benches/transformer.rs index d0a99f102..f12d4e872 100644 --- a/tasks/benchmark/benches/transformer.rs +++ b/tasks/benchmark/benches/transformer.rs @@ -20,6 +20,11 @@ fn bench_transformer(criterion: &mut Criterion) { // both the warmup and measurement phases let mut allocator = Allocator::default(); + let mut transform_options = TransformOptions::enable_all(); + // Even the plugins are unfinished, we still want to enable all of them + // to track the performance changes during the development. + transform_options.env = EnvOptions::enable_all(/* include_unfinished_plugins */ true); + group.bench_function(id, |b| { b.iter_with_setup_wrapper(|runner| { // Reset allocator at start of each iteration @@ -35,14 +40,17 @@ fn bench_transformer(criterion: &mut Criterion) { .semantic .into_symbol_table_and_scope_tree(); - let mut options = TransformOptions::enable_all(); - // Even the plugins are unfinished, we still want to enable all of them - // to track the performance changes during the development. - options.env = EnvOptions::enable_all(/* include_unfinished_plugins */ true); - runner.run(|| { - let ret = Transformer::new(&allocator, Path::new(&file.file_name), options) - .build_with_symbols_and_scopes(symbols, scopes, &mut program); + let ret = Transformer::new( + &allocator, + Path::new(&file.file_name), + &transform_options, + ) + .build_with_symbols_and_scopes( + symbols, + scopes, + &mut program, + ); // Return the `TransformerReturn`, so it's dropped outside of the measured section. // `TransformerReturn` contains `ScopeTree` and `SymbolTable` which are costly to drop. diff --git a/tasks/coverage/src/driver.rs b/tasks/coverage/src/driver.rs index 474f0e703..bec0b0bbf 100644 --- a/tasks/coverage/src/driver.rs +++ b/tasks/coverage/src/driver.rs @@ -49,8 +49,8 @@ impl CompilerInterface for Driver { true } - fn transform_options(&self) -> Option { - self.transform.clone() + fn transform_options(&self) -> Option<&TransformOptions> { + self.transform.as_ref() } fn compress_options(&self) -> Option { diff --git a/tasks/coverage/src/runtime/mod.rs b/tasks/coverage/src/runtime/mod.rs index 1656307fe..f4e5daafd 100644 --- a/tasks/coverage/src/runtime/mod.rs +++ b/tasks/coverage/src/runtime/mod.rs @@ -173,7 +173,7 @@ impl Test262RuntimeCase { options.jsx.refresh = None; options.helper_loader.mode = HelperLoaderMode::External; options.typescript.only_remove_type_imports = true; - Transformer::new(&allocator, self.path(), options).build_with_symbols_and_scopes( + Transformer::new(&allocator, self.path(), &options).build_with_symbols_and_scopes( symbols, scopes, &mut program, diff --git a/tasks/transform_conformance/src/driver.rs b/tasks/transform_conformance/src/driver.rs index 2a595d8d7..668bc4852 100644 --- a/tasks/transform_conformance/src/driver.rs +++ b/tasks/transform_conformance/src/driver.rs @@ -18,8 +18,8 @@ pub struct Driver { } impl CompilerInterface for Driver { - fn transform_options(&self) -> Option { - Some(self.options.clone()) + fn transform_options(&self) -> Option<&TransformOptions> { + Some(&self.options) } fn codegen_options(&self) -> Option {