oxc/crates/oxc_transformer/src/lib.rs

287 lines
11 KiB
Rust

#![allow(clippy::wildcard_imports, clippy::option_map_unit_fn)]
//! Transformer / Transpiler
//!
//! References:
//! * <https://www.typescriptlang.org/tsconfig#target>
//! * <https://babel.dev/docs/presets>
//! * <https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformer.ts>
mod context;
mod es2015;
mod es2016;
mod es2019;
mod es2020;
mod es2021;
mod es2022;
mod es3;
mod options;
mod react_jsx;
mod regexp;
#[cfg(test)]
mod tester;
mod typescript;
mod utils;
use std::{cell::RefCell, rc::Rc, sync::Arc};
use es2015::TemplateLiterals;
use oxc_allocator::Allocator;
use oxc_ast::{ast::*, AstBuilder, AstKind, VisitMut};
use oxc_diagnostics::Error;
use oxc_semantic::{ScopeFlags, Semantic};
use oxc_span::SourceType;
use crate::{
context::TransformerCtx, es2015::*, es2016::ExponentiationOperator,
es2019::OptionalCatchBinding, es2020::NullishCoalescingOperator,
es2021::LogicalAssignmentOperators, es2022::ClassStaticBlock, es3::PropertyLiteral,
react_jsx::ReactJsx, regexp::RegexpFlags, typescript::TypeScript, utils::CreateVars,
};
pub use crate::{
es2015::ArrowFunctionsOptions,
es2020::NullishCoalescingOperatorOptions,
options::{TransformOptions, TransformTarget},
react_jsx::{ReactJsxOptions, ReactJsxRuntime, ReactJsxRuntimeOption},
typescript::TypescriptOptions,
};
pub struct Transformer<'a> {
ctx: TransformerCtx<'a>,
#[allow(unused)]
typescript: Option<TypeScript<'a>>,
react_jsx: Option<ReactJsx<'a>>,
regexp_flags: Option<RegexpFlags<'a>>,
// es2022
es2022_class_static_block: Option<ClassStaticBlock<'a>>,
// es2021
es2021_logical_assignment_operators: Option<LogicalAssignmentOperators<'a>>,
// es2020
es2020_nullish_coalescing_operators: Option<NullishCoalescingOperator<'a>>,
// es2019
es2019_optional_catch_binding: Option<OptionalCatchBinding<'a>>,
// es2016
es2016_exponentiation_operator: Option<ExponentiationOperator<'a>>,
// es2015
es2015_function_name: Option<FunctionName<'a>>,
es2015_arrow_functions: Option<ArrowFunctions<'a>>,
es2015_shorthand_properties: Option<ShorthandProperties<'a>>,
es2015_template_literals: Option<TemplateLiterals<'a>>,
es2015_duplicate_keys: Option<DuplicateKeys<'a>>,
es2015_instanceof: Option<Instanceof<'a>>,
es2015_new_target: Option<NewTarget<'a>>,
es3_property_literal: Option<PropertyLiteral<'a>>,
}
impl<'a> Transformer<'a> {
#[rustfmt::skip]
pub fn new(
allocator: &'a Allocator,
source_type: SourceType,
semantic: Semantic<'a>,
options: TransformOptions,
) -> Self {
let ast = Rc::new(AstBuilder::new(allocator));
let ctx = TransformerCtx::new(
Rc::clone(&ast),
Rc::new(RefCell::new(semantic)),
);
Self {
ctx: ctx.clone(),
// TODO: pass verbatim_module_syntax from user config
typescript: source_type.is_typescript().then(|| TypeScript::new(Rc::clone(&ast), ctx.clone(), false, &options)),
regexp_flags: RegexpFlags::new(Rc::clone(&ast), &options),
// es2022
es2022_class_static_block: es2022::ClassStaticBlock::new(Rc::clone(&ast), &options),
// es2021
es2021_logical_assignment_operators: LogicalAssignmentOperators::new(Rc::clone(&ast), ctx.clone(), &options),
// es2020
es2020_nullish_coalescing_operators: NullishCoalescingOperator::new(Rc::clone(&ast), ctx.clone(), &options),
// es2019
es2019_optional_catch_binding: OptionalCatchBinding::new(Rc::clone(&ast), &options),
// es2016
es2016_exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ast), ctx.clone(), &options),
// es2015
es2015_function_name: FunctionName::new(Rc::clone(&ast), ctx.clone(), &options),
es2015_arrow_functions: ArrowFunctions::new(Rc::clone(&ast), ctx.clone(), &options),
es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options),
es2015_duplicate_keys: DuplicateKeys::new(Rc::clone(&ast), &options),
es2015_instanceof: Instanceof::new(Rc::clone(&ast), ctx.clone(), &options),
es2015_new_target: NewTarget::new(Rc::clone(&ast),ctx.clone(), &options),
// other
es3_property_literal: PropertyLiteral::new(Rc::clone(&ast), &options),
react_jsx: ReactJsx::new(Rc::clone(&ast), ctx.clone(), options)
}
}
/// # Errors
/// Returns `Vec<Error>` if any errors were collected during the transformation.
pub fn build(mut self, program: &mut Program<'a>) -> Result<(), Vec<Error>> {
self.visit_program(program);
let errors: Vec<_> = self
.ctx
.errors()
.into_iter()
.map(|e| e.with_source_code(Arc::new(self.ctx.semantic().source_text().to_string())))
.collect();
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
}
impl<'a> VisitMut<'a> for Transformer<'a> {
fn enter_node(&mut self, kind: oxc_ast::AstKind<'a>) {
self.es2015_new_target.as_mut().map(|t| t.enter_node(kind));
}
fn leave_node(&mut self, kind: oxc_ast::AstKind<'a>) {
self.es2015_new_target.as_mut().map(|t| t.leave_node(kind));
}
fn visit_program(&mut self, program: &mut Program<'a>) {
let kind = AstKind::Program(self.alloc(program));
self.enter_scope({
let mut flags = ScopeFlags::Top;
if program.is_strict() {
flags |= ScopeFlags::StrictMode;
}
flags
});
self.enter_node(kind);
for directive in program.directives.iter_mut() {
self.visit_directive(directive);
}
self.typescript.as_mut().map(|t| t.transform_program(program));
self.visit_statements(&mut program.body);
self.react_jsx.as_mut().map(|t| t.add_react_jsx_runtime_imports(program));
self.leave_node(kind);
self.leave_scope();
}
fn visit_assignment_expression(&mut self, expr: &mut AssignmentExpression<'a>) {
let kind = AstKind::AssignmentExpression(self.alloc(expr));
self.enter_node(kind);
self.es2015_function_name.as_mut().map(|t| t.transform_assignment_expression(expr));
self.visit_assignment_target(&mut expr.left);
self.visit_expression(&mut expr.right);
self.leave_node(kind);
}
fn visit_statements(&mut self, stmts: &mut oxc_allocator::Vec<'a, Statement<'a>>) {
self.typescript.as_mut().map(|t| t.transform_statements(stmts));
for stmt in stmts.iter_mut() {
self.visit_statement(stmt);
}
// TODO: we need scope id to insert the vars into the correct statements
self.es2021_logical_assignment_operators.as_mut().map(|t| t.add_vars_to_statements(stmts));
self.es2020_nullish_coalescing_operators.as_mut().map(|t| t.add_vars_to_statements(stmts));
self.es2016_exponentiation_operator.as_mut().map(|t| t.add_vars_to_statements(stmts));
self.es2015_arrow_functions.as_mut().map(|t| t.transform_statements(stmts));
}
fn visit_statement(&mut self, stmt: &mut Statement<'a>) {
self.typescript.as_mut().map(|t| t.transform_statement(stmt));
self.visit_statement_match(stmt);
}
fn visit_declaration(&mut self, decl: &mut Declaration<'a>) {
self.visit_declaration_match(decl);
self.typescript.as_mut().map(|t| t.transform_declaration(decl));
}
fn visit_expression(&mut self, expr: &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.regexp_flags.as_mut().map(|t| t.transform_expression(expr));
self.es2021_logical_assignment_operators.as_mut().map(|t| t.transform_expression(expr));
self.es2020_nullish_coalescing_operators.as_mut().map(|t| t.transform_expression(expr));
self.es2015_arrow_functions.as_mut().map(|t| t.transform_expression(expr));
self.es2015_instanceof.as_mut().map(|t| t.transform_expression(expr));
self.es2016_exponentiation_operator.as_mut().map(|t| t.transform_expression(expr));
self.es2015_template_literals.as_mut().map(|t| t.transform_expression(expr));
self.es2015_new_target.as_mut().map(|t| t.transform_expression(expr));
self.visit_expression_match(expr);
}
fn visit_catch_clause(&mut self, clause: &mut CatchClause<'a>) {
let kind = AstKind::CatchClause(self.alloc(clause));
self.enter_scope(ScopeFlags::empty());
self.enter_node(kind);
self.es2019_optional_catch_binding.as_mut().map(|t| t.transform_catch_clause(clause));
if let Some(param) = &mut clause.param {
self.visit_binding_pattern(param);
}
self.visit_statements(&mut clause.body.body);
self.leave_node(kind);
self.leave_scope();
}
fn visit_object_expression(&mut self, expr: &mut ObjectExpression<'a>) {
let kind = AstKind::ObjectExpression(self.alloc(expr));
self.enter_node(kind);
self.es2015_function_name.as_mut().map(|t| t.transform_object_expression(expr));
self.es2015_duplicate_keys.as_mut().map(|t| t.transform_object_expression(expr));
for property in expr.properties.iter_mut() {
self.visit_object_property_kind(property);
}
self.leave_node(kind);
}
fn visit_object_property(&mut self, prop: &mut ObjectProperty<'a>) {
let kind = AstKind::ObjectProperty(self.alloc(prop));
self.enter_node(kind);
self.es2015_shorthand_properties.as_mut().map(|t| t.transform_object_property(prop));
self.es3_property_literal.as_mut().map(|t| t.transform_object_property(prop));
self.visit_property_key(&mut prop.key);
self.visit_expression(&mut prop.value);
if let Some(init) = &mut prop.init {
self.visit_expression(init);
}
self.leave_node(kind);
}
fn visit_class_body(&mut self, class_body: &mut ClassBody<'a>) {
self.es2022_class_static_block.as_mut().map(|t| t.transform_class_body(class_body));
class_body.body.iter_mut().for_each(|class_element| {
self.visit_class_element(class_element);
});
}
fn visit_variable_declarator(&mut self, declarator: &mut VariableDeclarator<'a>) {
let kind = AstKind::VariableDeclarator(self.alloc(declarator));
self.enter_node(kind);
self.es2015_function_name.as_mut().map(|t| t.transform_variable_declarator(declarator));
self.visit_binding_pattern(&mut declarator.id);
if let Some(init) = &mut declarator.init {
self.visit_expression(init);
}
self.leave_node(kind);
}
}