From 5973e5aeded6bab7cc5d5eaef9c2c0b34d7e413b Mon Sep 17 00:00:00 2001 From: Boshen Date: Mon, 18 Sep 2023 16:56:41 +0800 Subject: [PATCH] feat(transformer): setup typescript and react transformers (#930) --- .../oxc_transformer/examples/transformer.rs | 9 +++-- crates/oxc_transformer/src/lib.rs | 40 +++++++++++-------- crates/oxc_transformer/src/options.rs | 28 +++++++++++++ crates/oxc_transformer/src/react_jsx/mod.rs | 21 ++++++++++ crates/oxc_transformer/src/typescript/mod.rs | 18 +++++++++ tasks/benchmark/benches/transformer.rs | 5 ++- tasks/transform_conformance/src/lib.rs | 19 ++++++--- 7 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 crates/oxc_transformer/src/options.rs create mode 100644 crates/oxc_transformer/src/react_jsx/mod.rs create mode 100644 crates/oxc_transformer/src/typescript/mod.rs diff --git a/crates/oxc_transformer/examples/transformer.rs b/crates/oxc_transformer/examples/transformer.rs index 8701cbb8d..21b046854 100644 --- a/crates/oxc_transformer/examples/transformer.rs +++ b/crates/oxc_transformer/examples/transformer.rs @@ -4,7 +4,7 @@ use oxc_allocator::Allocator; use oxc_formatter::{Formatter, FormatterOptions}; use oxc_parser::Parser; use oxc_span::SourceType; -use oxc_transformer::{TransformOptions, TransformTarget, Transformer}; +use oxc_transformer::{TransformOptions, TransformReactOptions, TransformTarget, Transformer}; // Instruction: // create a `test.js`, @@ -33,8 +33,11 @@ fn main() { println!("Original:\n"); println!("{printed}"); - let transform_options = TransformOptions { target: TransformTarget::ES2015 }; - Transformer::new(&allocator, &transform_options).build(program); + let transform_options = TransformOptions { + target: TransformTarget::ES2015, + react: Some(TransformReactOptions::default()), + }; + Transformer::new(&allocator, source_type, transform_options).build(program); let printed = Formatter::new(source_text.len(), formatter_options).build(program); println!("Transformed:\n"); println!("{printed}"); diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index ce9f27d5e..b771e9f9e 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -10,33 +10,29 @@ mod es2016; mod es2019; mod es2021; +mod options; +mod react_jsx; +mod typescript; use oxc_allocator::Allocator; use oxc_ast::{ast::*, AstBuilder, VisitMut}; +use oxc_span::SourceType; use std::rc::Rc; use es2016::ExponentiationOperator; use es2019::OptionalCatchBinding; use es2021::LogicalAssignmentOperators; +use react_jsx::ReactJsx; +use typescript::TypeScript; -#[derive(Debug, Default, Clone)] -pub struct TransformOptions { - pub target: TransformTarget, -} - -/// See -#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] -pub enum TransformTarget { - ES2015, - ES2016, - ES2019, - ES2021, - #[default] - ESNext, -} +pub use crate::options::{ + TransformOptions, TransformReactOptions, TransformReactRuntime, TransformTarget, +}; #[derive(Default)] pub struct Transformer<'a> { + typescript: Option>, + react_jsx: Option>, // es2021 es2021_logical_assignment_operators: Option>, // es2019 @@ -46,10 +42,20 @@ pub struct Transformer<'a> { } impl<'a> Transformer<'a> { - pub fn new(allocator: &'a Allocator, options: &TransformOptions) -> Self { + pub fn new( + allocator: &'a Allocator, + source_type: SourceType, + options: TransformOptions, + ) -> Self { let ast = Rc::new(AstBuilder::new(allocator)); let mut t = Self::default(); + if source_type.is_typescript() { + t.typescript.replace(TypeScript::new(Rc::clone(&ast))); + } + if let Some(react_options) = options.react { + t.react_jsx.replace(ReactJsx::new(Rc::clone(&ast), react_options)); + } if options.target < TransformTarget::ES2021 { t.es2021_logical_assignment_operators .replace(LogicalAssignmentOperators::new(Rc::clone(&ast))); @@ -70,6 +76,8 @@ impl<'a> Transformer<'a> { impl<'a, 'b> VisitMut<'a, 'b> for Transformer<'a> { fn visit_expression(&mut self, expr: &'b 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.es2021_logical_assignment_operators.as_mut().map(|t| t.transform_expression(expr)); self.es2016_exponentiation_operator.as_mut().map(|t| t.transform_expression(expr)); diff --git a/crates/oxc_transformer/src/options.rs b/crates/oxc_transformer/src/options.rs new file mode 100644 index 000000000..0059aaffb --- /dev/null +++ b/crates/oxc_transformer/src/options.rs @@ -0,0 +1,28 @@ +#[derive(Debug, Default, Clone)] +pub struct TransformOptions { + pub target: TransformTarget, + pub react: Option, +} + +/// See +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] +pub enum TransformTarget { + ES2015, + ES2016, + ES2019, + ES2021, + #[default] + ESNext, +} + +#[derive(Debug, Default, Clone)] +pub struct TransformReactOptions { + _runtime: TransformReactRuntime, +} + +#[derive(Debug, Default, Clone)] +pub enum TransformReactRuntime { + #[default] + Classic, + Automatic, +} diff --git a/crates/oxc_transformer/src/react_jsx/mod.rs b/crates/oxc_transformer/src/react_jsx/mod.rs new file mode 100644 index 000000000..a7fcedf78 --- /dev/null +++ b/crates/oxc_transformer/src/react_jsx/mod.rs @@ -0,0 +1,21 @@ +use std::rc::Rc; + +use oxc_ast::AstBuilder; + +use crate::TransformReactOptions; + +/// Transform React JSX +/// +/// References: +/// * +/// * +pub struct ReactJsx<'a> { + _ast: Rc>, + _options: TransformReactOptions, +} + +impl<'a> ReactJsx<'a> { + pub fn new(_ast: Rc>, _options: TransformReactOptions) -> Self { + Self { _ast, _options } + } +} diff --git a/crates/oxc_transformer/src/typescript/mod.rs b/crates/oxc_transformer/src/typescript/mod.rs new file mode 100644 index 000000000..17a6ce5b5 --- /dev/null +++ b/crates/oxc_transformer/src/typescript/mod.rs @@ -0,0 +1,18 @@ +use oxc_ast::AstBuilder; + +use std::rc::Rc; + +/// Transform TypeScript +/// +/// References: +/// * +/// * +pub struct TypeScript<'a> { + _ast: Rc>, +} + +impl<'a> TypeScript<'a> { + pub fn new(_ast: Rc>) -> Self { + Self { _ast } + } +} diff --git a/tasks/benchmark/benches/transformer.rs b/tasks/benchmark/benches/transformer.rs index 4473df86c..b5f92b6b8 100644 --- a/tasks/benchmark/benches/transformer.rs +++ b/tasks/benchmark/benches/transformer.rs @@ -30,9 +30,10 @@ fn bench_transformer(criterion: &mut Criterion) { let allocator = Allocator::default(); let ret = Parser::new(&allocator, source_text, source_type).parse(); let program = allocator.alloc(ret.program); - let transform_options = TransformOptions::default(); b.iter(|| { - Transformer::new(&allocator, &transform_options).build(black_box(program)); + let transform_options = TransformOptions::default(); + Transformer::new(&allocator, source_type, transform_options) + .build(black_box(program)); }); }); } diff --git a/tasks/transform_conformance/src/lib.rs b/tasks/transform_conformance/src/lib.rs index cde99b663..db5a8b099 100644 --- a/tasks/transform_conformance/src/lib.rs +++ b/tasks/transform_conformance/src/lib.rs @@ -10,7 +10,7 @@ use oxc_formatter::{Formatter, FormatterOptions}; use oxc_parser::Parser; use oxc_span::{SourceType, VALID_EXTENSIONS}; use oxc_tasks_common::{normalize_path, project_root}; -use oxc_transformer::{TransformOptions, TransformTarget, Transformer}; +use oxc_transformer::{TransformOptions, TransformReactOptions, TransformTarget, Transformer}; pub struct BabelOptions { pub filter: Option, @@ -132,19 +132,28 @@ fn babel_test(input_path: &Path, options: &BabelOptions) -> bool { let expected = output_path.and_then(|path| fs::read_to_string(path).ok()); if let Some(expected) = &expected { - let transform_options = TransformOptions { target: TransformTarget::ES2015 }; + let transform_options = TransformOptions { + target: TransformTarget::ES2015, + react: Some(TransformReactOptions::default()), + }; let program = allocator.alloc(ret.program); - Transformer::new(&allocator, &transform_options).build(program); + Transformer::new(&allocator, source_type, transform_options).build(program); let formatter_options = FormatterOptions::default(); let transformed = Formatter::new(source_text.len(), formatter_options).build(program); + let trim_transformed = remove_whitespace(&transformed); + let trim_expected = remove_whitespace(expected); + let passed = trim_transformed == trim_expected; if filtered { println!("Expected:\n"); println!("{expected:?}\n"); println!("Transformed:\n"); - println!("{transformed}"); + println!("{transformed}\n"); + println!("Diff:\n"); + println!("{trim_transformed:?}"); + println!("{trim_expected:?}"); } - return remove_whitespace(&transformed) == remove_whitespace(expected); + return passed; } ret.errors.is_empty()