From 0eb7602e1840aa588f39a625a36d033d7604dc95 Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:20:50 +0000 Subject: [PATCH] feat(transformer): support `TransformOptions::from_preset_env` API (#5323) --- crates/oxc_transformer/src/env/data/babel.rs | 4 +- crates/oxc_transformer/src/es2015/options.rs | 16 +- crates/oxc_transformer/src/es2016/options.rs | 16 +- crates/oxc_transformer/src/es2018/options.rs | 23 ++- crates/oxc_transformer/src/es2019/options.rs | 16 +- crates/oxc_transformer/src/es2020/options.rs | 16 +- crates/oxc_transformer/src/es2021/options.rs | 16 +- .../src/options/transformer.rs | 151 +++++++++++------- 8 files changed, 186 insertions(+), 72 deletions(-) diff --git a/crates/oxc_transformer/src/env/data/babel.rs b/crates/oxc_transformer/src/env/data/babel.rs index 146d0e7d4086e..fc368ca97bb0c 100644 --- a/crates/oxc_transformer/src/env/data/babel.rs +++ b/crates/oxc_transformer/src/env/data/babel.rs @@ -43,11 +43,11 @@ fn bugfix_features() -> &'static FxHashMap { }) } -pub fn can_enable_plugin(name: &str, targets: &Versions, bugfixes: bool) -> bool { +pub fn can_enable_plugin(name: &str, targets: Option<&Versions>, bugfixes: bool) -> bool { let versions = if bugfixes { bugfix_features().get(name).unwrap_or_else(|| &features()[name]) } else { &features()[name] }; - targets.should_enable(versions) + targets.is_some_and(|v| v.should_enable(versions)) } diff --git a/crates/oxc_transformer/src/es2015/options.rs b/crates/oxc_transformer/src/es2015/options.rs index 260966ea244fb..d248163cc2b37 100644 --- a/crates/oxc_transformer/src/es2015/options.rs +++ b/crates/oxc_transformer/src/es2015/options.rs @@ -1,5 +1,7 @@ use serde::Deserialize; +use crate::env::{can_enable_plugin, Versions}; + use super::ArrowFunctionsOptions; #[derive(Debug, Default, Clone, Deserialize)] @@ -10,9 +12,19 @@ pub struct ES2015Options { } impl ES2015Options { - #[must_use] - pub fn with_arrow_function(mut self, arrow_function: Option) -> Self { + pub fn with_arrow_function( + &mut self, + arrow_function: Option, + ) -> &mut Self { self.arrow_function = arrow_function; self } + + #[must_use] + pub fn from_targets_and_bugfixes(targets: Option<&Versions>, bugfixes: bool) -> Self { + Self { + arrow_function: can_enable_plugin("transform-arrow-functions", targets, bugfixes) + .then(Default::default), + } + } } diff --git a/crates/oxc_transformer/src/es2016/options.rs b/crates/oxc_transformer/src/es2016/options.rs index 802f750e3e135..99f3b8615b141 100644 --- a/crates/oxc_transformer/src/es2016/options.rs +++ b/crates/oxc_transformer/src/es2016/options.rs @@ -1,5 +1,7 @@ use serde::Deserialize; +use crate::env::{can_enable_plugin, Versions}; + #[derive(Debug, Default, Clone, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2016Options { @@ -8,9 +10,19 @@ pub struct ES2016Options { } impl ES2016Options { - #[must_use] - pub fn with_exponentiation_operator(mut self, enable: bool) -> Self { + pub fn with_exponentiation_operator(&mut self, enable: bool) -> &mut Self { self.exponentiation_operator = enable; self } + + #[must_use] + pub fn from_targets_and_bugfixes(targets: Option<&Versions>, bugfixes: bool) -> Self { + Self { + exponentiation_operator: can_enable_plugin( + "transform-exponentiation-operator", + targets, + bugfixes, + ), + } + } } diff --git a/crates/oxc_transformer/src/es2018/options.rs b/crates/oxc_transformer/src/es2018/options.rs index 26dd5aa442394..a5d9688bfbecf 100644 --- a/crates/oxc_transformer/src/es2018/options.rs +++ b/crates/oxc_transformer/src/es2018/options.rs @@ -1,6 +1,9 @@ -use super::object_rest_spread::ObjectRestSpreadOptions; +use crate::env::{can_enable_plugin, Versions}; + use serde::Deserialize; +use super::ObjectRestSpreadOptions; + #[derive(Debug, Default, Clone, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2018Options { @@ -9,9 +12,23 @@ pub struct ES2018Options { } impl ES2018Options { - #[must_use] - pub fn with_object_rest_spread(mut self, option: Option) -> Self { + pub fn with_object_rest_spread( + &mut self, + option: Option, + ) -> &mut Self { self.object_rest_spread = option; self } + + #[must_use] + pub fn from_targets_and_bugfixes(targets: Option<&Versions>, bugfixes: bool) -> Self { + Self { + object_rest_spread: can_enable_plugin( + "transform-object-rest-spread", + targets, + bugfixes, + ) + .then(Default::default), + } + } } diff --git a/crates/oxc_transformer/src/es2019/options.rs b/crates/oxc_transformer/src/es2019/options.rs index 624c6a9b942d8..9bc3bf94e8aed 100644 --- a/crates/oxc_transformer/src/es2019/options.rs +++ b/crates/oxc_transformer/src/es2019/options.rs @@ -1,5 +1,7 @@ use serde::Deserialize; +use crate::env::{can_enable_plugin, Versions}; + #[derive(Debug, Default, Clone, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2019Options { @@ -8,9 +10,19 @@ pub struct ES2019Options { } impl ES2019Options { - #[must_use] - pub fn with_optional_catch_binding(mut self, enable: bool) -> Self { + pub fn with_optional_catch_binding(&mut self, enable: bool) -> &mut Self { self.optional_catch_binding = enable; self } + + #[must_use] + pub fn from_targets_and_bugfixes(targets: Option<&Versions>, bugfixes: bool) -> Self { + Self { + optional_catch_binding: can_enable_plugin( + "transform-optional-catch-binding", + targets, + bugfixes, + ), + } + } } diff --git a/crates/oxc_transformer/src/es2020/options.rs b/crates/oxc_transformer/src/es2020/options.rs index 1101637e6f1e1..3c0623d166579 100644 --- a/crates/oxc_transformer/src/es2020/options.rs +++ b/crates/oxc_transformer/src/es2020/options.rs @@ -1,5 +1,7 @@ use serde::Deserialize; +use crate::env::{can_enable_plugin, Versions}; + #[derive(Debug, Default, Clone, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2020Options { @@ -8,9 +10,19 @@ pub struct ES2020Options { } impl ES2020Options { - #[must_use] - pub fn with_nullish_coalescing_operator(mut self, enable: bool) -> Self { + pub fn with_nullish_coalescing_operator(&mut self, enable: bool) -> &mut Self { self.nullish_coalescing_operator = enable; self } + + #[must_use] + pub fn from_targets_and_bugfixes(targets: Option<&Versions>, bugfixes: bool) -> Self { + Self { + nullish_coalescing_operator: can_enable_plugin( + "transform-nullish-coalescing-operator", + targets, + bugfixes, + ), + } + } } diff --git a/crates/oxc_transformer/src/es2021/options.rs b/crates/oxc_transformer/src/es2021/options.rs index a1aa084ffe33c..3bccdae98e12b 100644 --- a/crates/oxc_transformer/src/es2021/options.rs +++ b/crates/oxc_transformer/src/es2021/options.rs @@ -1,5 +1,7 @@ use serde::Deserialize; +use crate::env::{can_enable_plugin, Versions}; + #[derive(Debug, Default, Clone, Deserialize)] #[serde(default, rename_all = "camelCase", deny_unknown_fields)] pub struct ES2021Options { @@ -8,9 +10,19 @@ pub struct ES2021Options { } impl ES2021Options { - #[must_use] - pub fn with_logical_assignment_operators(mut self, enable: bool) -> Self { + pub fn with_logical_assignment_operators(&mut self, enable: bool) -> &mut Self { self.logical_assignment_operators = enable; self } + + #[must_use] + pub fn from_targets_and_bugfixes(targets: Option<&Versions>, bugfixes: bool) -> Self { + Self { + logical_assignment_operators: can_enable_plugin( + "transform-logical-assignment-operators", + targets, + bugfixes, + ), + } + } } diff --git a/crates/oxc_transformer/src/options/transformer.rs b/crates/oxc_transformer/src/options/transformer.rs index 5f749a991f1da..b244d6d4671cf 100644 --- a/crates/oxc_transformer/src/options/transformer.rs +++ b/crates/oxc_transformer/src/options/transformer.rs @@ -52,36 +52,78 @@ pub struct TransformOptions { } impl TransformOptions { + fn from_targets_and_bugfixes(targets: Option<&Versions>, bugfixes: bool) -> Self { + Self { + es2015: ES2015Options::from_targets_and_bugfixes(targets, bugfixes), + es2016: ES2016Options::from_targets_and_bugfixes(targets, bugfixes), + es2018: ES2018Options::from_targets_and_bugfixes(targets, bugfixes), + es2019: ES2019Options::from_targets_and_bugfixes(targets, bugfixes), + es2020: ES2020Options::from_targets_and_bugfixes(targets, bugfixes), + es2021: ES2021Options::from_targets_and_bugfixes(targets, bugfixes), + ..Default::default() + } + } + + /// # Errors + /// + /// If there are any errors in the `options.targets``, they will be returned as a list of errors. + pub fn from_preset_env(env_options: &EnvOptions) -> Result> { + let mut errors = Vec::::new(); + + let targets = match env_options.get_targets() { + Ok(t) => Some(t), + Err(err) => { + errors.push(OxcDiagnostic::error(err.to_string()).into()); + None + } + }; + let bugfixes = env_options.bugfixes; + Ok(Self::from_targets_and_bugfixes(targets.as_ref(), bugfixes)) + } + /// # Errors /// + /// If the `options` contains any unknown fields, they will be returned as a list of errors. pub fn from_babel_options(options: &BabelOptions) -> Result> { let mut errors = Vec::::new(); let env_options = { let preset_name = "env"; - from_value::(get_preset_options(preset_name, options)).unwrap_or_else( - |err| { - report_error(preset_name, &err, true, &mut errors); - EnvOptions::default() - }, - ) + get_preset_options(preset_name, options).and_then(|value| { + match from_value::(value) { + Ok(res) => Some(res), + Err(err) => { + report_error(preset_name, &err, true, &mut errors); + None + } + } + }) }; - let targets = match env_options.get_targets() { - Ok(t) => t, + + let targets = env_options.as_ref().and_then(|env| match env.get_targets() { + Ok(res) => Some(res), Err(err) => { errors.push(OxcDiagnostic::error(err.to_string()).into()); - return Err(errors); + None } + }); + let bugfixes = env_options.as_ref().is_some_and(|o| o.bugfixes); + + let mut transformer_options = if env_options.is_some() { + TransformOptions::from_targets_and_bugfixes(targets.as_ref(), bugfixes) + } else { + TransformOptions::default() }; let preset_name = "react"; - let react = if options.has_preset(preset_name) { - from_value::(get_preset_options(preset_name, options)).unwrap_or_else( - |err| { + transformer_options.react = if let Some(value) = get_preset_options(preset_name, options) { + match from_value::(value) { + Ok(res) => res, + Err(err) => { report_error(preset_name, &err, true, &mut errors); ReactOptions::default() - }, - ) + } + } } else { let has_jsx_plugin = options.has_plugin("transform-react-jsx"); let has_jsx_development_plugin = options.has_plugin("transform-react-jsx-development"); @@ -109,47 +151,51 @@ impl TransformOptions { react_options }; - let es2015 = ES2015Options::default().with_arrow_function({ + transformer_options.es2015.with_arrow_function({ let plugin_name = "transform-arrow-functions"; - enable_plugin(plugin_name, options, &env_options, &targets).map(|options| { - from_value::(options).unwrap_or_else(|err| { - report_error(plugin_name, &err, false, &mut errors); - ArrowFunctionsOptions::default() - }) - }) + get_enabled_plugin_options(plugin_name, options, targets.as_ref(), bugfixes).map( + |options| { + from_value::(options).unwrap_or_else(|err| { + report_error(plugin_name, &err, false, &mut errors); + ArrowFunctionsOptions::default() + }) + }, + ) }); - let es2016 = ES2016Options::default().with_exponentiation_operator({ + transformer_options.es2016.with_exponentiation_operator({ let plugin_name = "transform-exponentiation-operator"; - enable_plugin(plugin_name, options, &env_options, &targets).is_some() + get_enabled_plugin_options(plugin_name, options, targets.as_ref(), bugfixes).is_some() }); - let es2018 = ES2018Options::default().with_object_rest_spread({ + transformer_options.es2018.with_object_rest_spread({ let plugin_name = "transform-object-rest-spread"; - enable_plugin(plugin_name, options, &env_options, &targets).map(|options| { - from_value::(options).unwrap_or_else(|err| { - report_error(plugin_name, &err, false, &mut errors); - ObjectRestSpreadOptions::default() - }) - }) + get_enabled_plugin_options(plugin_name, options, targets.as_ref(), bugfixes).map( + |options| { + from_value::(options).unwrap_or_else(|err| { + report_error(plugin_name, &err, false, &mut errors); + ObjectRestSpreadOptions::default() + }) + }, + ) }); - let es2019 = ES2019Options::default().with_optional_catch_binding({ + transformer_options.es2019.with_optional_catch_binding({ let plugin_name = "transform-optional-catch-binding"; - enable_plugin(plugin_name, options, &env_options, &targets).is_some() + get_enabled_plugin_options(plugin_name, options, targets.as_ref(), bugfixes).is_some() }); - let es2020 = ES2020Options::default().with_nullish_coalescing_operator({ + transformer_options.es2020.with_nullish_coalescing_operator({ let plugin_name = "transform-nullish-coalescing-operator"; - enable_plugin(plugin_name, options, &env_options, &targets).is_some() + get_enabled_plugin_options(plugin_name, options, targets.as_ref(), bugfixes).is_some() }); - let es2021 = ES2021Options::default().with_logical_assignment_operators({ + transformer_options.es2021.with_logical_assignment_operators({ let plugin_name = "transform-logical-assignment-operators"; - enable_plugin(plugin_name, options, &env_options, &targets).is_some() + get_enabled_plugin_options(plugin_name, options, targets.as_ref(), bugfixes).is_some() }); - let typescript = { + transformer_options.typescript = { let plugin_name = "transform-typescript"; from_value::(get_plugin_options(plugin_name, options)) .unwrap_or_else(|err| { @@ -158,7 +204,7 @@ impl TransformOptions { }) }; - let assumptions = if options.assumptions.is_null() { + transformer_options.assumptions = if options.assumptions.is_null() { CompilerAssumptions::default() } else { match serde_json::from_value::(options.assumptions.clone()) { @@ -170,22 +216,13 @@ impl TransformOptions { } }; + transformer_options.cwd = options.cwd.clone().unwrap_or_default(); + if !errors.is_empty() { return Err(errors); } - Ok(Self { - cwd: options.cwd.clone().unwrap_or_default(), - assumptions, - typescript, - react, - es2015, - es2016, - es2018, - es2019, - es2020, - es2021, - }) + Ok(transformer_options) } } @@ -194,19 +231,19 @@ fn get_plugin_options(name: &str, babel_options: &BabelOptions) -> Value { plugin.and_then(|options| options).unwrap_or_else(|| json!({})) } -fn get_preset_options(name: &str, babel_options: &BabelOptions) -> Value { +fn get_preset_options(name: &str, babel_options: &BabelOptions) -> Option { let preset = babel_options.get_preset(name); - preset.and_then(|options| options).unwrap_or_else(|| json!({})) + preset.and_then(|options| options) } -fn enable_plugin( +fn get_enabled_plugin_options( plugin_name: &str, babel_options: &BabelOptions, - env_options: &EnvOptions, - targets: &Versions, + targets: Option<&Versions>, + bugfixes: bool, ) -> Option { - let can_enable = can_enable_plugin(plugin_name, targets, env_options.bugfixes) - || babel_options.has_plugin(plugin_name); + let can_enable = + can_enable_plugin(plugin_name, targets, bugfixes) || babel_options.has_plugin(plugin_name); if can_enable { get_plugin_options(plugin_name, babel_options).into()