feat(transformer): add transform callback methods (#2929)

For milestone 1, I think it's safe to just layout all the
transformations manually.


In TypeScript, the transformers are collected dynamically and ran on
each ast node; this achieves a single AST pass.
https://github.com/microsoft/TypeScript/blob/main/src/compiler/transformer.ts#L129-L145

To maximize performance and reduce confusion, I think it's safe to
layout all the transformations manually for milestone 1.

The next PR will add transformation context to all presets so we can
start adding "context".
This commit is contained in:
Boshen 2024-04-10 11:06:49 +08:00 committed by GitHub
parent 8662f4f613
commit 79ca6feca9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 60 additions and 8 deletions

View file

@ -1,5 +1,7 @@
use serde::Deserialize;
use oxc_ast::ast::*;
/// Only `"2023-11"` will be implemented because Babel 8 will only support "2023-11" and "legacy".
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -17,3 +19,8 @@ impl Decorators {
Self { options }
}
}
// Transformers
impl Decorators {
pub fn transform_statement(&mut self, _stmt: &mut Statement<'_>) {}
}

View file

@ -1,3 +1,5 @@
#![allow(clippy::wildcard_imports)]
//! Transformer / Transpiler
//!
//! References:
@ -13,7 +15,10 @@ mod react;
mod typescript;
use oxc_allocator::Allocator;
use oxc_ast::ast::Program;
use oxc_ast::{
ast::*,
visit::{walk_mut, VisitMut},
};
use oxc_diagnostics::Error;
use oxc_semantic::Semantic;
use oxc_span::SourceType;
@ -54,9 +59,10 @@ pub struct Transformer<'a> {
semantic: Semantic<'a>,
options: TransformOptions,
decorators: Decorators,
typescript: TypeScript,
react: React,
// NOTE: all callbacks must run in order.
x0_typescript: TypeScript,
x1_react: React,
x2_decorators: Decorators,
}
impl<'a> Transformer<'a> {
@ -71,16 +77,30 @@ impl<'a> Transformer<'a> {
source_type,
semantic,
options,
decorators: Decorators::default(),
typescript: TypeScript::default(),
react: React::default(),
x0_typescript: TypeScript::default(),
x1_react: React::default(),
x2_decorators: Decorators::default(),
}
}
/// # Errors
///
/// Returns `Vec<Error>` if any errors were collected during the transformation.
pub fn build(self, _program: &mut Program<'a>) -> Result<(), Vec<Error>> {
pub fn build(mut self, program: &mut Program<'a>) -> Result<(), Vec<Error>> {
self.visit_program(program);
Ok(())
}
}
impl<'a> VisitMut<'a> for Transformer<'a> {
fn visit_statement(&mut self, stmt: &mut Statement<'a>) {
self.x0_typescript.transform_statement(stmt);
self.x2_decorators.transform_statement(stmt);
walk_mut::walk_statement_mut(self, stmt);
}
fn visit_expression(&mut self, expr: &mut Expression<'a>) {
self.x1_react.transform_expression(expr);
walk_mut::walk_expression_mut(self, expr);
}
}

View file

@ -4,6 +4,8 @@ mod jsx_self;
mod jsx_source;
mod options;
use oxc_ast::ast::*;
pub use self::{
display_name::{ReactDisplayName, ReactDisplayNameOptions},
jsx::ReactJsx,
@ -28,6 +30,7 @@ pub struct React {
display_name: ReactDisplayName,
}
// Constructors
impl React {
pub fn new(&mut self, options: ReactOptions) -> &mut Self {
self.jsx = ReactJsx::new(options);
@ -49,3 +52,18 @@ impl React {
self
}
}
// Transformers
impl React {
pub fn transform_expression(&mut self, expr: &mut Expression<'_>) {
match expr {
Expression::JSXElement(_e) => {
// *expr = unimplemented!();
}
Expression::JSXFragment(_e) => {
// *expr = unimplemented!();
}
_ => {}
}
}
}

View file

@ -1,5 +1,7 @@
use serde::Deserialize;
use oxc_ast::ast::*;
#[derive(Debug, Default, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TypeScriptOptions;
@ -36,3 +38,8 @@ impl TypeScript {
Self { options }
}
}
// Transformers
impl TypeScript {
pub fn transform_statement(&mut self, _stmt: &mut Statement<'_>) {}
}