Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 21 additions & 12 deletions crates/oxc_transformer/src/common/helper_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ pub enum Helper {
}

impl Helper {
const fn name(self) -> &'static str {
pub const fn name(self) -> &'static str {
match self {
Self::AwaitAsyncGenerator => "awaitAsyncGenerator",
Self::AsyncGeneratorDelegate => "asyncGeneratorDelegate",
Expand Down Expand Up @@ -184,6 +184,7 @@ pub struct HelperLoaderStore<'a> {
mode: HelperLoaderMode,
/// Loaded helpers, determined what helpers are loaded and what imports should be added.
loaded_helpers: RefCell<FxHashMap<Helper, BoundIdentifier<'a>>>,
pub(crate) used_helpers: RefCell<FxHashMap<Helper, String>>,
}

impl<'a> HelperLoaderStore<'a> {
Expand All @@ -192,6 +193,7 @@ impl<'a> HelperLoaderStore<'a> {
module_name: options.module_name.clone(),
mode: options.mode,
loaded_helpers: RefCell::new(FxHashMap::default()),
used_helpers: RefCell::new(FxHashMap::default()),
}
}
}
Expand Down Expand Up @@ -238,9 +240,12 @@ impl<'a> TransformCtx<'a> {
/// Load a helper function and return a callee expression.
pub fn helper_load(&self, helper: Helper, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
let helper_loader = &self.helper_loader;
let source = helper_loader.get_runtime_source(helper, ctx);
helper_loader.used_helpers.borrow_mut().entry(helper).or_insert_with(|| source.to_string());

match helper_loader.mode {
HelperLoaderMode::Runtime => {
helper_loader.transform_for_runtime_helper(helper, self, ctx)
helper_loader.transform_for_runtime_helper(helper, source, self, ctx)
}
HelperLoaderMode::External => {
HelperLoaderStore::transform_for_external_helper(helper, ctx)
Expand All @@ -257,32 +262,25 @@ impl<'a> HelperLoaderStore<'a> {
fn transform_for_runtime_helper(
&self,
helper: Helper,
source: Atom<'a>,
transform_ctx: &TransformCtx<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let mut loaded_helpers = self.loaded_helpers.borrow_mut();
let binding = loaded_helpers
.entry(helper)
.or_insert_with(|| self.get_runtime_helper(helper, transform_ctx, ctx));
.or_insert_with(|| Self::get_runtime_helper(helper, source, transform_ctx, ctx));
binding.create_read_expression(ctx)
}

fn get_runtime_helper(
&self,
helper: Helper,
source: Atom<'a>,
transform_ctx: &TransformCtx<'a>,
ctx: &mut TraverseCtx<'a>,
) -> BoundIdentifier<'a> {
let helper_name = helper.name();

// Construct string directly in arena without an intermediate temp allocation
let len = self.module_name.len() + "/helpers/".len() + helper_name.len();
let mut source = ArenaString::with_capacity_in(len, ctx.ast.allocator);
source.push_str(&self.module_name);
source.push_str("/helpers/");
source.push_str(helper_name);
let source = Atom::from(source.into_bump_str());

let flag = if transform_ctx.source_type.is_module() {
SymbolFlags::Import
} else {
Expand All @@ -295,6 +293,17 @@ impl<'a> HelperLoaderStore<'a> {
binding
}

// Construct string directly in arena without an intermediate temp allocation
fn get_runtime_source(&self, helper: Helper, ctx: &mut TraverseCtx<'a>) -> Atom<'a> {
let helper_name = helper.name();
let len = self.module_name.len() + "/helpers/".len() + helper_name.len();
let mut source = ArenaString::with_capacity_in(len, ctx.ast.allocator);
source.push_str(&self.module_name);
source.push_str("/helpers/");
source.push_str(helper_name);
Atom::from(source.into_bump_str())
}

fn transform_for_external_helper(helper: Helper, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
static HELPER_VAR: &str = "babelHelpers";

Expand Down
11 changes: 9 additions & 2 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ use es2021::ES2021;
use es2022::ES2022;
use jsx::Jsx;
use regexp::RegExp;
use rustc_hash::FxHashMap;
use typescript::TypeScript;

pub use crate::{
common::helper_loader::{HelperLoaderMode, HelperLoaderOptions},
common::helper_loader::{Helper, HelperLoaderMode, HelperLoaderOptions},
compiler_assumptions::CompilerAssumptions,
es2015::{ArrowFunctionsOptions, ES2015Options},
jsx::{JsxOptions, JsxRuntime, ReactRefreshOptions},
Expand All @@ -63,10 +64,14 @@ pub use crate::{
typescript::{RewriteExtensionsMode, TypeScriptOptions},
};

#[non_exhaustive]
pub struct TransformerReturn {
pub errors: std::vec::Vec<OxcDiagnostic>,
pub symbols: SymbolTable,
pub scopes: ScopeTree,
/// Helpers used by this transform.
#[deprecated = "Internal usage only"]
pub helpers_used: FxHashMap<Helper, String>,
}

pub struct Transformer<'a> {
Expand Down Expand Up @@ -128,7 +133,9 @@ impl<'a> Transformer<'a> {
};

let (symbols, scopes) = traverse_mut(&mut transformer, allocator, program, symbols, scopes);
TransformerReturn { errors: self.ctx.take_errors(), symbols, scopes }
let helpers_used = self.ctx.helper_loader.used_helpers.borrow_mut().drain().collect();
#[allow(deprecated)]
TransformerReturn { errors: self.ctx.take_errors(), symbols, scopes, helpers_used }
}
}

Expand Down
12 changes: 12 additions & 0 deletions napi/transform/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,18 @@ export interface TransformResult {
* {@link TransformOptions#sourcemap sourcemap} are set to `true`.
*/
declarationMap?: SourceMap
/**
* Helpers used.
*
* @internal
*
* Example:
*
* ```text
* { "_objectSpread": "@babel/runtime/helpers/objectSpread2" }
* ```
*/
helpersUsed: Record<string, string>
/**
* Parse and transformation errors.
*
Expand Down
34 changes: 33 additions & 1 deletion napi/transform/src/transformer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// NOTE: Types must be aligned with [@types/babel__core](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b5dc32740d9b45d11cff9b025896dd333c795b39/types/babel__core/index.d.ts).
#![allow(rustdoc::bare_urls)]

use std::path::{Path, PathBuf};
use std::{
ops::ControlFlow,
path::{Path, PathBuf},
};

use napi::Either;
use napi_derive::napi;
Expand Down Expand Up @@ -50,6 +53,18 @@ pub struct TransformResult {
/// {@link TransformOptions#sourcemap sourcemap} are set to `true`.
pub declaration_map: Option<SourceMap>,

/// Helpers used.
///
/// @internal
///
/// Example:
///
/// ```text
/// { "_objectSpread": "@babel/runtime/helpers/objectSpread2" }
/// ```
#[napi(ts_type = "Record<string, string>")]
pub helpers_used: FxHashMap<String, String>,

/// Parse and transformation errors.
///
/// Oxc's parser recovers from common syntax errors, meaning that
Expand Down Expand Up @@ -461,6 +476,7 @@ struct Compiler {
define: Option<ReplaceGlobalDefinesConfig>,
inject: Option<InjectGlobalVariablesConfig>,

helpers_used: FxHashMap<String, String>,
errors: Vec<OxcDiagnostic>,
}

Expand Down Expand Up @@ -527,6 +543,7 @@ impl Compiler {
declaration_map: None,
define,
inject,
helpers_used: FxHashMap::default(),
errors: vec![],
})
}
Expand Down Expand Up @@ -568,6 +585,20 @@ impl CompilerInterface for Compiler {
self.declaration.replace(ret.code);
self.declaration_map = ret.map.map(SourceMap::from);
}

#[allow(deprecated)]
fn after_transform(
&mut self,
_program: &mut oxc::ast::ast::Program<'_>,
transformer_return: &mut oxc::transformer::TransformerReturn,
) -> ControlFlow<()> {
self.helpers_used = transformer_return
.helpers_used
.drain()
.map(|(helper, source)| (helper.name().to_string(), source))
.collect();
ControlFlow::Continue(())
}
}

/// Transpile a JavaScript or TypeScript into a target ECMAScript version.
Expand Down Expand Up @@ -629,6 +660,7 @@ pub fn transform(
map: compiler.printed_sourcemap,
declaration: compiler.declaration,
declaration_map: compiler.declaration_map,
helpers_used: compiler.helpers_used,
errors: compiler.errors.into_iter().map(Error::from).collect(),
}
}
4 changes: 4 additions & 0 deletions napi/transform/test/transform.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ describe('simple', () => {
expect(ret).toStrictEqual({
code: 'export class A {}\n',
errors: [],
helpersUsed: {},
map: {
mappings: 'AAAA,OAAO,MAAM,EAAK,CAAE',
names: [],
Expand Down Expand Up @@ -117,6 +118,9 @@ describe('helpers', () => {
helpers: { mode },
});
expect(ret.code).toEqual(expected);
expect(ret.helpersUsed).toStrictEqual({
objectSpread2: '@babel/runtime/helpers/objectSpread2',
});
});
});

Expand Down