-
-
Notifications
You must be signed in to change notification settings - Fork 503
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(transformer): support helper loader
- Loading branch information
Showing
11 changed files
with
382 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
31
crates/oxc_transformer/src/common/helper_loader/options.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.