feat(napi/transform): return helpers information (#7737)

closes #7599
This commit is contained in:
Boshen 2024-12-09 14:07:40 +00:00
parent 065f7dcb9d
commit 2803aec521
5 changed files with 79 additions and 15 deletions

View file

@ -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",
@ -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> {
@ -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()),
}
}
}
@ -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)
@ -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 {
@ -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";

View file

@ -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},
@ -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> {
@ -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 }
}
}

View file

@ -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.
*

View file

@ -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;
@ -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
@ -461,6 +476,7 @@ struct Compiler {
define: Option<ReplaceGlobalDefinesConfig>,
inject: Option<InjectGlobalVariablesConfig>,
helpers_used: FxHashMap<String, String>,
errors: Vec<OxcDiagnostic>,
}
@ -527,6 +543,7 @@ impl Compiler {
declaration_map: None,
define,
inject,
helpers_used: FxHashMap::default(),
errors: vec![],
})
}
@ -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.
@ -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(),
}
}

View file

@ -10,6 +10,7 @@ describe('simple', () => {
expect(ret).toStrictEqual({
code: 'export class A {}\n',
errors: [],
helpersUsed: {},
map: {
mappings: 'AAAA,OAAO,MAAM,EAAK,CAAE',
names: [],
@ -117,6 +118,9 @@ describe('helpers', () => {
helpers: { mode },
});
expect(ret.code).toEqual(expected);
expect(ret.helpersUsed).toStrictEqual({
objectSpread2: '@babel/runtime/helpers/objectSpread2',
});
});
});