feat(transformer): setup typescript and react transformers (#930)

This commit is contained in:
Boshen 2023-09-18 16:56:41 +08:00 committed by GitHub
parent 46d2623c1f
commit 5973e5aede
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 114 additions and 26 deletions

View file

@ -4,7 +4,7 @@ use oxc_allocator::Allocator;
use oxc_formatter::{Formatter, FormatterOptions}; use oxc_formatter::{Formatter, FormatterOptions};
use oxc_parser::Parser; use oxc_parser::Parser;
use oxc_span::SourceType; use oxc_span::SourceType;
use oxc_transformer::{TransformOptions, TransformTarget, Transformer}; use oxc_transformer::{TransformOptions, TransformReactOptions, TransformTarget, Transformer};
// Instruction: // Instruction:
// create a `test.js`, // create a `test.js`,
@ -33,8 +33,11 @@ fn main() {
println!("Original:\n"); println!("Original:\n");
println!("{printed}"); println!("{printed}");
let transform_options = TransformOptions { target: TransformTarget::ES2015 }; let transform_options = TransformOptions {
Transformer::new(&allocator, &transform_options).build(program); 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); let printed = Formatter::new(source_text.len(), formatter_options).build(program);
println!("Transformed:\n"); println!("Transformed:\n");
println!("{printed}"); println!("{printed}");

View file

@ -10,33 +10,29 @@
mod es2016; mod es2016;
mod es2019; mod es2019;
mod es2021; mod es2021;
mod options;
mod react_jsx;
mod typescript;
use oxc_allocator::Allocator; use oxc_allocator::Allocator;
use oxc_ast::{ast::*, AstBuilder, VisitMut}; use oxc_ast::{ast::*, AstBuilder, VisitMut};
use oxc_span::SourceType;
use std::rc::Rc; use std::rc::Rc;
use es2016::ExponentiationOperator; use es2016::ExponentiationOperator;
use es2019::OptionalCatchBinding; use es2019::OptionalCatchBinding;
use es2021::LogicalAssignmentOperators; use es2021::LogicalAssignmentOperators;
use react_jsx::ReactJsx;
use typescript::TypeScript;
#[derive(Debug, Default, Clone)] pub use crate::options::{
pub struct TransformOptions { TransformOptions, TransformReactOptions, TransformReactRuntime, TransformTarget,
pub target: TransformTarget, };
}
/// See <https://www.typescriptlang.org/tsconfig#target>
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub enum TransformTarget {
ES2015,
ES2016,
ES2019,
ES2021,
#[default]
ESNext,
}
#[derive(Default)] #[derive(Default)]
pub struct Transformer<'a> { pub struct Transformer<'a> {
typescript: Option<TypeScript<'a>>,
react_jsx: Option<ReactJsx<'a>>,
// es2021 // es2021
es2021_logical_assignment_operators: Option<LogicalAssignmentOperators<'a>>, es2021_logical_assignment_operators: Option<LogicalAssignmentOperators<'a>>,
// es2019 // es2019
@ -46,10 +42,20 @@ pub struct Transformer<'a> {
} }
impl<'a> 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 ast = Rc::new(AstBuilder::new(allocator));
let mut t = Self::default(); 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 { if options.target < TransformTarget::ES2021 {
t.es2021_logical_assignment_operators t.es2021_logical_assignment_operators
.replace(LogicalAssignmentOperators::new(Rc::clone(&ast))); .replace(LogicalAssignmentOperators::new(Rc::clone(&ast)));
@ -70,6 +76,8 @@ impl<'a> Transformer<'a> {
impl<'a, 'b> VisitMut<'a, 'b> for Transformer<'a> { impl<'a, 'b> VisitMut<'a, 'b> for Transformer<'a> {
fn visit_expression(&mut self, expr: &'b mut Expression<'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.es2021_logical_assignment_operators.as_mut().map(|t| t.transform_expression(expr));
self.es2016_exponentiation_operator.as_mut().map(|t| t.transform_expression(expr)); self.es2016_exponentiation_operator.as_mut().map(|t| t.transform_expression(expr));

View file

@ -0,0 +1,28 @@
#[derive(Debug, Default, Clone)]
pub struct TransformOptions {
pub target: TransformTarget,
pub react: Option<TransformReactOptions>,
}
/// See <https://www.typescriptlang.org/tsconfig#target>
#[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,
}

View file

@ -0,0 +1,21 @@
use std::rc::Rc;
use oxc_ast::AstBuilder;
use crate::TransformReactOptions;
/// Transform React JSX
///
/// References:
/// * <https://babeljs.io/docs/babel-plugin-transform-react-jsx>
/// * <https://github.com/babel/babel/tree/main/packages/babel-helper-builder-react-jsx>
pub struct ReactJsx<'a> {
_ast: Rc<AstBuilder<'a>>,
_options: TransformReactOptions,
}
impl<'a> ReactJsx<'a> {
pub fn new(_ast: Rc<AstBuilder<'a>>, _options: TransformReactOptions) -> Self {
Self { _ast, _options }
}
}

View file

@ -0,0 +1,18 @@
use oxc_ast::AstBuilder;
use std::rc::Rc;
/// Transform TypeScript
///
/// References:
/// * <https://babeljs.io/docs/babel-plugin-transform-typescript>
/// * <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-typescript>
pub struct TypeScript<'a> {
_ast: Rc<AstBuilder<'a>>,
}
impl<'a> TypeScript<'a> {
pub fn new(_ast: Rc<AstBuilder<'a>>) -> Self {
Self { _ast }
}
}

View file

@ -30,9 +30,10 @@ fn bench_transformer(criterion: &mut Criterion) {
let allocator = Allocator::default(); let allocator = Allocator::default();
let ret = Parser::new(&allocator, source_text, source_type).parse(); let ret = Parser::new(&allocator, source_text, source_type).parse();
let program = allocator.alloc(ret.program); let program = allocator.alloc(ret.program);
let transform_options = TransformOptions::default();
b.iter(|| { 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));
}); });
}); });
} }

View file

@ -10,7 +10,7 @@ use oxc_formatter::{Formatter, FormatterOptions};
use oxc_parser::Parser; use oxc_parser::Parser;
use oxc_span::{SourceType, VALID_EXTENSIONS}; use oxc_span::{SourceType, VALID_EXTENSIONS};
use oxc_tasks_common::{normalize_path, project_root}; 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 struct BabelOptions {
pub filter: Option<String>, pub filter: Option<String>,
@ -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()); let expected = output_path.and_then(|path| fs::read_to_string(path).ok());
if let Some(expected) = &expected { 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); 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 formatter_options = FormatterOptions::default();
let transformed = Formatter::new(source_text.len(), formatter_options).build(program); 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 { if filtered {
println!("Expected:\n"); println!("Expected:\n");
println!("{expected:?}\n"); println!("{expected:?}\n");
println!("Transformed:\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() ret.errors.is_empty()