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_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}");

View file

@ -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 <https://www.typescriptlang.org/tsconfig#target>
#[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<TypeScript<'a>>,
react_jsx: Option<ReactJsx<'a>>,
// es2021
es2021_logical_assignment_operators: Option<LogicalAssignmentOperators<'a>>,
// 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));

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 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));
});
});
}

View file

@ -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<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());
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()