Skip to content

Commit

Permalink
feat(transformer): support helper loader
Browse files Browse the repository at this point in the history
  • Loading branch information
Dunqing committed Oct 11, 2024
1 parent 8fd72e6 commit bec456c
Show file tree
Hide file tree
Showing 11 changed files with 382 additions and 40 deletions.
156 changes: 156 additions & 0 deletions crates/oxc_transformer/src/common/helper_loader/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//! Utility import / inline / external helper function transform.
//!
//! TODO: add more details
pub mod options;

use std::{
borrow::Cow,
cell::{Cell, RefCell},
rc::Rc,
};

use options::{HelperLoaderMode, HelperLoaderOptions};
use oxc_allocator::Vec;
use oxc_ast::ast::{Argument, CallExpression, Expression, Program, TSTypeParameterInstantiation};
use oxc_semantic::{ReferenceFlags, SymbolFlags, SymbolId};
use oxc_span::{Atom, SPAN};
use oxc_traverse::{BoundIdentifier, Traverse, TraverseCtx};
use rustc_hash::FxHashMap;

use super::module_imports::ImportKind;
use crate::TransformCtx;

pub struct HelperLoader<'a, 'ctx> {
ctx: &'ctx TransformCtx<'a>,
}

impl<'a, 'ctx> Traverse<'a> for HelperLoader<'a, 'ctx> {
fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
self.insert_into_program();
}
}

impl<'a, 'ctx> HelperLoader<'a, 'ctx> {
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
Self { ctx }
}

fn insert_into_program(&self) {
self.ctx.helper_loader.loaded_helpers.borrow_mut().drain().for_each(
|(_, (source, import))| {
self.ctx.module_imports.add_import(
source,
ImportKind::new_default(import.name, import.symbol_id),
false,
);
},
);
}
}

// (helper_name, (path, bound_ident))
type LoadedHelper<'a> = FxHashMap<Atom<'a>, (Atom<'a>, BoundIdentifier<'a>)>;

pub struct HelperLoaderStore<'a> {
mode: HelperLoaderMode,
module_name: Cow<'static, str>,
babel_helper_symbol_id: Rc<Cell<Option<SymbolId>>>,
loaded_helpers: Rc<RefCell<LoadedHelper<'a>>>,
}

impl<'a> HelperLoaderStore<'a> {
pub fn new(options: &HelperLoaderOptions) -> Self {
Self {
mode: options.mode,
module_name: options.module_name.clone(),
loaded_helpers: Rc::new(RefCell::new(FxHashMap::default())),
babel_helper_symbol_id: Rc::new(Cell::new(None)),
}
}

fn add_default_import(&self, helper_name: Atom<'a>, ctx: &mut TraverseCtx<'a>) {
let source = ctx.ast.atom(&format!("{}/helpers/{helper_name}", self.module_name));
let bound_ident = ctx.generate_uid_in_root_scope(&helper_name, SymbolFlags::Import);
self.loaded_helpers.borrow_mut().insert(helper_name, (source, bound_ident));
}

fn transform_for_runtime_helper(
&self,
helper_name: &Atom<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
if !self.loaded_helpers.borrow().contains_key(helper_name) {
self.add_default_import(helper_name.clone(), ctx);
}
let bound_ident = self.loaded_helpers.borrow_mut()[helper_name].1.clone();
let ident = bound_ident.create_read_reference(ctx);
ctx.ast.expression_from_identifier_reference(ident)
}

fn transform_for_external_helper(
&self,
helper_name: Atom<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let symbol_id = self.babel_helper_symbol_id.get().or_else(|| {
let symbol_id = ctx.scopes().get_root_binding("babelHelpers");
self.babel_helper_symbol_id.set(symbol_id);
symbol_id
});

let ident = ctx.create_reference_id(
SPAN,
Atom::from("babelHelpers"),
symbol_id,
ReferenceFlags::Read,
);

let object = ctx.ast.expression_from_identifier_reference(ident);
let property = ctx.ast.identifier_name(SPAN, helper_name);
Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false))
}

#[allow(dead_code)]
pub fn call(
&mut self,
helper_name: Atom<'a>,
arguments: Vec<'a, Argument<'a>>,
ctx: &mut TraverseCtx<'a>,
) -> CallExpression<'a> {
let callee = self.get_callee(helper_name, ctx);
ctx.ast.call_expression(
SPAN,
callee,
None::<TSTypeParameterInstantiation<'a>>,
arguments,
false,
)
}

#[allow(dead_code)]
pub fn call_expr(
&mut self,
helper_name: Atom<'a>,
arguments: Vec<'a, Argument<'a>>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let callee = self.get_callee(helper_name, ctx);
ctx.ast.expression_call(
SPAN,
callee,
None::<TSTypeParameterInstantiation<'a>>,
arguments,
false,
)
}

pub fn get_callee(&self, helper_name: Atom<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
match self.mode {
HelperLoaderMode::Runtime => self.transform_for_runtime_helper(&helper_name, ctx),
HelperLoaderMode::External => self.transform_for_external_helper(helper_name, ctx),
HelperLoaderMode::Inline => {
panic!("Inline helpers are not supported yet");
}
}
}
}
31 changes: 31 additions & 0 deletions crates/oxc_transformer/src/common/helper_loader/options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use std::borrow::Cow;

use serde::Deserialize;

#[derive(Default, Clone, Copy, Debug, Deserialize)]
pub enum HelperLoaderMode {
/// Babel default mode
Inline,
/// Babel test default mode
External,
/// Like @babel/plugin-transform-runtime does
#[default]
Runtime,
}

#[derive(Clone, Debug, Deserialize)]
pub struct HelperLoaderOptions {
#[serde(default = "default_as_module_name")]
pub module_name: Cow<'static, str>,
pub mode: HelperLoaderMode,
}

impl Default for HelperLoaderOptions {
fn default() -> Self {
Self { module_name: default_as_module_name(), mode: HelperLoaderMode::default() }
}
}

fn default_as_module_name() -> Cow<'static, str> {
Cow::Borrowed("@babel/runtime")
}
5 changes: 5 additions & 0 deletions crates/oxc_transformer/src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//! Utility transforms which are in common between other transforms.
use helper_loader::HelperLoader;
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};

use crate::TransformCtx;

pub mod helper_loader;
pub mod module_imports;
pub mod top_level_statements;
pub mod var_declarations;
Expand All @@ -18,6 +20,7 @@ pub struct Common<'a, 'ctx> {
module_imports: ModuleImports<'a, 'ctx>,
var_declarations: VarDeclarations<'a, 'ctx>,
top_level_statements: TopLevelStatements<'a, 'ctx>,
helper_loader: HelperLoader<'a, 'ctx>,
}

impl<'a, 'ctx> Common<'a, 'ctx> {
Expand All @@ -26,12 +29,14 @@ impl<'a, 'ctx> Common<'a, 'ctx> {
module_imports: ModuleImports::new(ctx),
var_declarations: VarDeclarations::new(ctx),
top_level_statements: TopLevelStatements::new(ctx),
helper_loader: HelperLoader::new(ctx),
}
}
}

impl<'a, 'ctx> Traverse<'a> for Common<'a, 'ctx> {
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.helper_loader.exit_program(program, ctx);
self.module_imports.exit_program(program, ctx);
self.var_declarations.exit_program(program, ctx);
self.top_level_statements.exit_program(program, ctx);
Expand Down
7 changes: 5 additions & 2 deletions crates/oxc_transformer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use oxc_span::SourceType;

use crate::{
common::{
module_imports::ModuleImportsStore, top_level_statements::TopLevelStatementsStore,
var_declarations::VarDeclarationsStore,
helper_loader::HelperLoaderStore, module_imports::ModuleImportsStore,
top_level_statements::TopLevelStatementsStore, var_declarations::VarDeclarationsStore,
},
TransformOptions,
};
Expand All @@ -38,6 +38,8 @@ pub struct TransformCtx<'a> {
pub var_declarations: VarDeclarationsStore<'a>,
/// Manage inserting statements at top of program globally
pub top_level_statements: TopLevelStatementsStore<'a>,
/// Manage helper loading
pub helper_loader: HelperLoaderStore<'a>,
}

impl<'a> TransformCtx<'a> {
Expand Down Expand Up @@ -65,6 +67,7 @@ impl<'a> TransformCtx<'a> {
module_imports: ModuleImportsStore::new(),
var_declarations: VarDeclarationsStore::new(),
top_level_statements: TopLevelStatementsStore::new(),
helper_loader: HelperLoaderStore::new(&options.helper_loader),
}
}

Expand Down
13 changes: 8 additions & 5 deletions crates/oxc_transformer/src/es2018/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,28 @@ pub use options::ES2018Options;
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};

pub struct ES2018 {
use crate::context::TransformCtx;

pub struct ES2018<'a, 'ctx> {
options: ES2018Options,

// Plugins
object_rest_spread: ObjectRestSpread,
object_rest_spread: ObjectRestSpread<'a, 'ctx>,
}

impl ES2018 {
pub fn new(options: ES2018Options) -> Self {
impl<'a, 'ctx> ES2018<'a, 'ctx> {
pub fn new(options: ES2018Options, ctx: &'ctx TransformCtx<'a>) -> Self {
Self {
object_rest_spread: ObjectRestSpread::new(
options.object_rest_spread.unwrap_or_default(),
ctx,
),
options,
}
}
}

impl<'a> Traverse<'a> for ES2018 {
impl<'a, 'ctx> Traverse<'a> for ES2018<'a, 'ctx> {
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.object_rest_spread.is_some() {
self.object_rest_spread.enter_expression(expr, ctx);
Expand Down
14 changes: 8 additions & 6 deletions crates/oxc_transformer/src/es2018/object_rest_spread/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};
use serde::Deserialize;

use crate::context::TransformCtx;

mod object_rest;
mod object_spread;

Expand All @@ -43,27 +45,27 @@ pub struct ObjectRestSpreadOptions {
pub(crate) use_built_ins: bool,
}

pub struct ObjectRestSpread {
pub struct ObjectRestSpread<'a, 'ctx> {
#[allow(dead_code)]
options: ObjectRestSpreadOptions,

// Plugins
object_spread: ObjectSpread,
object_spread: ObjectSpread<'a, 'ctx>,
#[allow(dead_code)]
object_rest: ObjectRest,
}

impl ObjectRestSpread {
pub fn new(options: ObjectRestSpreadOptions) -> Self {
impl<'a, 'ctx> ObjectRestSpread<'a, 'ctx> {
pub fn new(options: ObjectRestSpreadOptions, ctx: &'ctx TransformCtx<'a>) -> Self {
Self {
object_spread: ObjectSpread::new(options),
object_spread: ObjectSpread::new(options, ctx),
object_rest: ObjectRest::new(options),
options,
}
}
}

impl<'a> Traverse<'a> for ObjectRestSpread {
impl<'a, 'ctx> Traverse<'a> for ObjectRestSpread<'a, 'ctx> {
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.object_spread.enter_expression(expr, ctx);
}
Expand Down
Loading

0 comments on commit bec456c

Please sign in to comment.