diff --git a/crates/oxc_transformer/src/es2019/mod.rs b/crates/oxc_transformer/src/es2019/mod.rs new file mode 100644 index 00000000000000..81ec660d663d5e --- /dev/null +++ b/crates/oxc_transformer/src/es2019/mod.rs @@ -0,0 +1,35 @@ +mod optional_catch_binding; +mod options; + +pub use optional_catch_binding::OptionalCatchBinding; +pub use options::ES2019Options; +use oxc_ast::ast::*; +use oxc_traverse::TraverseCtx; +use std::rc::Rc; + +use crate::context::Ctx; + +#[allow(dead_code)] +pub struct ES2019<'a> { + ctx: Ctx<'a>, + options: ES2019Options, + + // Plugins + optional_catch_binding: OptionalCatchBinding<'a>, +} + +impl<'a> ES2019<'a> { + pub fn new(options: ES2019Options, ctx: Ctx<'a>) -> Self { + Self { optional_catch_binding: OptionalCatchBinding::new(Rc::clone(&ctx)), ctx, options } + } + + pub fn transform_catch_clause( + &mut self, + clause: &mut CatchClause<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + if self.options.optional_catch_binding { + self.optional_catch_binding.transform_catch_clause(clause, ctx); + } + } +} diff --git a/crates/oxc_transformer/src/es2019/optional_catch_binding.rs b/crates/oxc_transformer/src/es2019/optional_catch_binding.rs new file mode 100644 index 00000000000000..d54dcff94017fa --- /dev/null +++ b/crates/oxc_transformer/src/es2019/optional_catch_binding.rs @@ -0,0 +1,52 @@ +use std::cell::Cell; + +use oxc_ast::ast::*; +use oxc_semantic::SymbolFlags; +use oxc_span::SPAN; +use oxc_traverse::TraverseCtx; + +use crate::context::Ctx; + +/// ES2019: Optional Catch Binding +/// +/// References: +/// * +/// * +pub struct OptionalCatchBinding<'a> { + _ctx: Ctx<'a>, +} + +impl<'a> OptionalCatchBinding<'a> { + pub fn new(ctx: Ctx<'a>) -> Self { + Self { _ctx: ctx } + } + + /// If CatchClause has no param, add a parameter called `unused`. + /// + /// ```ts + /// try {} + /// catch {} + /// ``` + /// too + /// ```ts + /// try {} + /// catch (_unused) {} + /// ``` + #[allow(clippy::unused_self)] + pub fn transform_catch_clause(&self, clause: &mut CatchClause<'a>, ctx: &mut TraverseCtx<'a>) { + if clause.param.is_some() { + return; + } + let symbol_id = + ctx.generate_uid("unused", ctx.scoping.current_scope_id(), SymbolFlags::CatchVariable); + let name = ctx.ast.atom(ctx.symbols().get_name(symbol_id)); + let binding_identifier = + BindingIdentifier { span: SPAN, symbol_id: Cell::new(Some(symbol_id)), name }; + let binding_pattern_kind = + ctx.ast.binding_pattern_kind_from_binding_identifier(binding_identifier); + let binding_pattern = + ctx.ast.binding_pattern(binding_pattern_kind, None::>, false); + let param = ctx.ast.catch_parameter(SPAN, binding_pattern); + clause.param = Some(param); + } +} diff --git a/crates/oxc_transformer/src/es2019/options.rs b/crates/oxc_transformer/src/es2019/options.rs new file mode 100644 index 00000000000000..624c6a9b942d88 --- /dev/null +++ b/crates/oxc_transformer/src/es2019/options.rs @@ -0,0 +1,16 @@ +use serde::Deserialize; + +#[derive(Debug, Default, Clone, Deserialize)] +#[serde(default, rename_all = "camelCase", deny_unknown_fields)] +pub struct ES2019Options { + #[serde(skip)] + pub optional_catch_binding: bool, +} + +impl ES2019Options { + #[must_use] + pub fn with_optional_catch_binding(mut self, enable: bool) -> Self { + self.optional_catch_binding = enable; + self + } +} diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index c0f9251fe81c5a..23a8cbd1765b22 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -16,6 +16,7 @@ mod options; mod env; mod es2015; mod es2016; +mod es2019; mod es2020; mod react; mod typescript; @@ -28,6 +29,7 @@ mod helpers { use std::{path::Path, rc::Rc}; use es2016::ES2016; +use es2019::ES2019; use es2020::ES2020; use oxc_allocator::{Allocator, Vec}; use oxc_ast::{ast::*, AstBuilder, Trivias}; @@ -63,6 +65,7 @@ pub struct Transformer<'a> { x0_typescript: TypeScript<'a>, x1_react: React<'a>, x2_es2020: ES2020<'a>, + x2_es2019: ES2019<'a>, x2_es2016: ES2016<'a>, x3_es2015: ES2015<'a>, } @@ -88,8 +91,9 @@ impl<'a> Transformer<'a> { ctx: Rc::clone(&ctx), x0_typescript: TypeScript::new(options.typescript, Rc::clone(&ctx)), x1_react: React::new(options.react, Rc::clone(&ctx)), - x2_es2016: ES2016::new(options.es2016, Rc::clone(&ctx)), x2_es2020: ES2020::new(options.es2020, Rc::clone(&ctx)), + x2_es2019: ES2019::new(options.es2019, Rc::clone(&ctx)), + x2_es2016: ES2016::new(options.es2016, Rc::clone(&ctx)), x3_es2015: ES2015::new(options.es2015, ctx), } } @@ -317,6 +321,10 @@ impl<'a> Traverse<'a> for Transformer<'a> { self.x0_typescript.transform_for_in_statement(stmt, ctx); } + fn enter_catch_clause(&mut self, clause: &mut CatchClause<'a>, ctx: &mut TraverseCtx<'a>) { + self.x2_es2019.transform_catch_clause(clause, ctx); + } + fn enter_ts_export_assignment( &mut self, export_assignment: &mut TSExportAssignment<'a>, diff --git a/crates/oxc_transformer/src/options/transformer.rs b/crates/oxc_transformer/src/options/transformer.rs index 88cd6a3314a765..882cb8edc34f6d 100644 --- a/crates/oxc_transformer/src/options/transformer.rs +++ b/crates/oxc_transformer/src/options/transformer.rs @@ -8,6 +8,7 @@ use crate::{ env::{can_enable_plugin, EnvOptions, Versions}, es2015::{ArrowFunctionsOptions, ES2015Options}, es2016::ES2016Options, + es2019::ES2019Options, es2020::ES2020Options, options::babel::BabelOptions, react::ReactOptions, @@ -39,6 +40,8 @@ pub struct TransformOptions { pub es2016: ES2016Options, + pub es2019: ES2019Options, + pub es2020: ES2020Options, } @@ -115,6 +118,11 @@ impl TransformOptions { enable_plugin(plugin_name, options, &env_options, &targets).is_some() }); + let es2019 = ES2019Options::default().with_optional_catch_binding({ + let plugin_name = "transform-optional-catch-binding"; + enable_plugin(plugin_name, options, &env_options, &targets).is_some() + }); + let es2020 = ES2020Options::default().with_nullish_coalescing_operator({ let plugin_name = "transform-nullish-coalescing-operator"; enable_plugin(plugin_name, options, &env_options, &targets).is_some() @@ -152,6 +160,7 @@ impl TransformOptions { react, es2015, es2016, + es2019, es2020, }) } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index f1c2a0f7e5a2da..ce0993b21b637b 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,8 +1,9 @@ commit: 12619ffe -Passed: 458/943 +Passed: 462/947 # All Passed: +* babel-plugin-transform-optional-catch-binding * babel-preset-react * babel-plugin-transform-react-display-name * babel-plugin-transform-react-jsx-self diff --git a/tasks/transform_conformance/babel_exec.snap.md b/tasks/transform_conformance/babel_exec.snap.md index 1ee4d2cf1e4253..ee806af5135ffe 100644 --- a/tasks/transform_conformance/babel_exec.snap.md +++ b/tasks/transform_conformance/babel_exec.snap.md @@ -1,9 +1,10 @@ commit: 12619ffe -Passed: 12/18 +Passed: 13/19 # All Passed: * babel-plugin-transform-nullish-coalescing-operator +* babel-plugin-transform-optional-catch-binding * babel-plugin-transform-exponentiation-operator * babel-plugin-transform-arrow-functions diff --git a/tasks/transform_conformance/oxc.snap.md b/tasks/transform_conformance/oxc.snap.md index df9dd6b77b9aae..ed5308934bf4f4 100644 --- a/tasks/transform_conformance/oxc.snap.md +++ b/tasks/transform_conformance/oxc.snap.md @@ -1,8 +1,9 @@ commit: 12619ffe -Passed: 5/8 +Passed: 6/9 # All Passed: +* babel-plugin-transform-optional-catch-binding * babel-plugin-transform-react-jsx diff --git a/tasks/transform_conformance/src/constants.rs b/tasks/transform_conformance/src/constants.rs index 1fe595215f857e..81f06fb0044da0 100644 --- a/tasks/transform_conformance/src/constants.rs +++ b/tasks/transform_conformance/src/constants.rs @@ -20,7 +20,7 @@ pub(crate) const PLUGINS: &[&str] = &[ // // [Syntax] "babel-plugin-transform-syntax-dynamic-import", // // [Syntax] "babel-plugin-transform-syntax-import-meta", // // ES2019 - // "babel-plugin-transform-optional-catch-binding", + "babel-plugin-transform-optional-catch-binding", // "babel-plugin-transform-json-strings", // // ES2018 // "babel-plugin-transform-async-generator-functions",