diff --git a/crates/oxc_transformer/src/helpers/bindings.rs b/crates/oxc_transformer/src/helpers/bindings.rs
index 575c8428e..3d93786f9 100644
--- a/crates/oxc_transformer/src/helpers/bindings.rs
+++ b/crates/oxc_transformer/src/helpers/bindings.rs
@@ -1,6 +1,11 @@
-use oxc_ast::ast::IdentifierReference;
+use std::cell::Cell;
+
+use oxc_ast::ast::{BindingIdentifier, IdentifierReference};
use oxc_span::{Atom, SPAN};
-use oxc_syntax::{reference::ReferenceFlag, symbol::SymbolId};
+use oxc_syntax::{
+ reference::ReferenceFlag,
+ symbol::{SymbolFlags, SymbolId},
+};
use oxc_traverse::TraverseCtx;
/// Store for a created binding identifier
@@ -11,6 +16,13 @@ pub struct BoundIdentifier<'a> {
}
impl<'a> BoundIdentifier<'a> {
+ /// Create `BoundIdentifier` for new binding in root scope
+ pub fn new_root_uid(name: &str, flags: SymbolFlags, ctx: &mut TraverseCtx<'a>) -> Self {
+ let symbol_id = ctx.generate_uid_in_root_scope(name, flags);
+ let name = ctx.ast.new_atom(&ctx.symbols().names[symbol_id]);
+ Self { name, symbol_id }
+ }
+
/// Create `IdentifierReference` referencing this binding which is read from
/// in current scope
pub fn create_read_reference(&self, ctx: &mut TraverseCtx) -> IdentifierReference<'a> {
@@ -21,4 +33,13 @@ impl<'a> BoundIdentifier<'a> {
);
IdentifierReference::new_read(SPAN, self.name.clone(), Some(reference_id))
}
+
+ /// Create `BindingIdentifier` for this binding
+ pub fn create_binding_identifier(&self) -> BindingIdentifier<'a> {
+ BindingIdentifier {
+ span: SPAN,
+ name: self.name.clone(),
+ symbol_id: Cell::new(Some(self.symbol_id)),
+ }
+ }
}
diff --git a/crates/oxc_transformer/src/react/jsx.rs b/crates/oxc_transformer/src/react/jsx.rs
index afca9709e..33223ecf3 100644
--- a/crates/oxc_transformer/src/react/jsx.rs
+++ b/crates/oxc_transformer/src/react/jsx.rs
@@ -44,7 +44,6 @@ pub struct ReactJsx<'a> {
// States
bindings: Bindings<'a>,
- can_add_filename_statement: bool,
}
/// Bindings for different import options
@@ -363,7 +362,6 @@ impl<'a> ReactJsx<'a> {
jsx_self: ReactJsxSelf::new(Rc::clone(&ctx)),
jsx_source: ReactJsxSource::new(ctx),
bindings,
- can_add_filename_statement: false,
}
}
@@ -400,8 +398,8 @@ impl<'a> ReactJsx<'a> {
impl<'a> ReactJsx<'a> {
pub fn add_runtime_imports(&mut self, program: &mut Program<'a>) {
if self.bindings.is_classic() {
- if self.can_add_filename_statement {
- program.body.insert(0, self.jsx_source.get_var_file_name_statement());
+ if let Some(stmt) = self.jsx_source.get_var_file_name_statement() {
+ program.body.insert(0, stmt);
}
return;
}
@@ -413,8 +411,8 @@ impl<'a> ReactJsx<'a> {
.rposition(|stmt| matches!(stmt, Statement::ImportDeclaration(_)))
.map_or(0, |i| i + 1);
- if self.can_add_filename_statement {
- program.body.insert(index, self.jsx_source.get_var_file_name_statement());
+ if let Some(stmt) = self.jsx_source.get_var_file_name_statement() {
+ program.body.insert(index, stmt);
// If source type is module then we need to add the import statement after the var file name statement
// Follow the same behavior as babel
if !self.is_script() {
@@ -599,10 +597,9 @@ impl<'a> ReactJsx<'a> {
if let Some(span) = source_attr_span {
self.jsx_source.report_error(span);
} else {
- self.can_add_filename_statement = true;
let (line, column) = get_line_column(e.span().start, self.ctx.source_text);
properties.push(
- self.jsx_source.get_object_property_kind_for_jsx_plugin(line, column),
+ self.jsx_source.get_object_property_kind_for_jsx_plugin(line, column, ctx),
);
}
}
@@ -655,9 +652,8 @@ impl<'a> ReactJsx<'a> {
if let Some(span) = source_attr_span {
self.jsx_source.report_error(span);
} else {
- self.can_add_filename_statement = true;
let (line, column) = get_line_column(e.span().start, self.ctx.source_text);
- let expr = self.jsx_source.get_source_object(line, column);
+ let expr = self.jsx_source.get_source_object(line, column, ctx);
arguments.push(Argument::from(expr));
}
}
diff --git a/crates/oxc_transformer/src/react/jsx_source.rs b/crates/oxc_transformer/src/react/jsx_source.rs
index 8ca4f9d3b..14ecd0945 100644
--- a/crates/oxc_transformer/src/react/jsx_source.rs
+++ b/crates/oxc_transformer/src/react/jsx_source.rs
@@ -1,14 +1,15 @@
use oxc_ast::ast::*;
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{Span, SPAN};
-use oxc_syntax::number::NumberBase;
+use oxc_syntax::{number::NumberBase, symbol::SymbolFlags};
+use oxc_traverse::TraverseCtx;
-use crate::context::Ctx;
+use crate::{context::Ctx, helpers::bindings::BoundIdentifier};
use super::utils::get_line_column;
const SOURCE: &str = "__source";
-const FILE_NAME_VAR: &str = "_jsxFileName";
+const FILE_NAME_VAR: &str = "jsxFileName";
/// [plugin-transform-react-jsx-source](https://babeljs.io/docs/babel-plugin-transform-react-jsx-source)
///
@@ -20,26 +21,32 @@ const FILE_NAME_VAR: &str = "_jsxFileName";
/// Out: ``
pub struct ReactJsxSource<'a> {
ctx: Ctx<'a>,
+ filename_var: Option>,
}
impl<'a> ReactJsxSource<'a> {
pub fn new(ctx: Ctx<'a>) -> Self {
- Self { ctx }
+ Self { ctx, filename_var: None }
}
- pub fn transform_jsx_opening_element(&mut self, elem: &mut JSXOpeningElement<'a>) {
- self.add_source_attribute(elem);
+ pub fn transform_jsx_opening_element(
+ &mut self,
+ elem: &mut JSXOpeningElement<'a>,
+ ctx: &mut TraverseCtx<'a>,
+ ) {
+ self.add_source_attribute(elem, ctx);
}
pub fn get_object_property_kind_for_jsx_plugin(
&mut self,
line: usize,
column: usize,
+ ctx: &mut TraverseCtx<'a>,
) -> ObjectPropertyKind<'a> {
let kind = PropertyKind::Init;
let ident = IdentifierName::new(SPAN, SOURCE.into());
let key = self.ctx.ast.property_key_identifier(ident);
- let value = self.get_source_object(line, column);
+ let value = self.get_source_object(line, column, ctx);
let obj = self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false);
ObjectPropertyKind::ObjectProperty(obj)
}
@@ -53,7 +60,11 @@ impl<'a> ReactJsxSource<'a> {
impl<'a> ReactJsxSource<'a> {
/// ``
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- fn add_source_attribute(&mut self, elem: &mut JSXOpeningElement<'a>) {
+ fn add_source_attribute(
+ &mut self,
+ elem: &mut JSXOpeningElement<'a>,
+ ctx: &mut TraverseCtx<'a>,
+ ) {
// Check if `__source` attribute already exists
for item in &elem.attributes {
if let JSXAttributeItem::Attribute(attribute) = item {
@@ -70,7 +81,7 @@ impl<'a> ReactJsxSource<'a> {
self.ctx.ast.alloc(self.ctx.ast.jsx_identifier(SPAN, SOURCE.into())),
);
let (line, column) = get_line_column(elem.span.start, self.ctx.source_text);
- let object = self.get_source_object(line, column);
+ let object = self.get_source_object(line, column, ctx);
let expr = self.ctx.ast.jsx_expression_container(SPAN, JSXExpression::from(object));
let value = JSXAttributeValue::ExpressionContainer(expr);
let attribute_item = self.ctx.ast.jsx_attribute(SPAN, key, Some(value));
@@ -78,13 +89,18 @@ impl<'a> ReactJsxSource<'a> {
}
#[allow(clippy::cast_precision_loss)]
- pub fn get_source_object(&mut self, line: usize, column: usize) -> Expression<'a> {
+ pub fn get_source_object(
+ &mut self,
+ line: usize,
+ column: usize,
+ ctx: &mut TraverseCtx<'a>,
+ ) -> Expression<'a> {
let kind = PropertyKind::Init;
let filename = {
let name = IdentifierName::new(SPAN, "fileName".into());
let key = self.ctx.ast.property_key_identifier(name);
- let ident = self.ctx.ast.identifier_reference(SPAN, FILE_NAME_VAR);
+ let ident = self.get_filename_var(ctx).create_read_reference(ctx);
let value = self.ctx.ast.identifier_reference_expression(ident);
self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false)
};
@@ -122,10 +138,12 @@ impl<'a> ReactJsxSource<'a> {
self.ctx.ast.object_expression(SPAN, properties, None)
}
- pub fn get_var_file_name_statement(&self) -> Statement<'a> {
+ pub fn get_var_file_name_statement(&mut self) -> Option> {
+ let filename_var = self.filename_var.as_ref()?;
+
let var_kind = VariableDeclarationKind::Var;
let id = {
- let ident = BindingIdentifier::new(SPAN, FILE_NAME_VAR.into());
+ let ident = filename_var.create_binding_identifier();
let ident = self.ctx.ast.binding_pattern_identifier(ident);
self.ctx.ast.binding_pattern(ident, None, false)
};
@@ -136,6 +154,17 @@ impl<'a> ReactJsxSource<'a> {
self.ctx.ast.new_vec_single(decl)
};
let var_decl = self.ctx.ast.variable_declaration(SPAN, var_kind, decl, Modifiers::empty());
- Statement::VariableDeclaration(var_decl)
+ Some(Statement::VariableDeclaration(var_decl))
+ }
+
+ fn get_filename_var(&mut self, ctx: &mut TraverseCtx<'a>) -> BoundIdentifier<'a> {
+ if self.filename_var.is_none() {
+ self.filename_var = Some(BoundIdentifier::new_root_uid(
+ FILE_NAME_VAR,
+ SymbolFlags::FunctionScopedVariable,
+ ctx,
+ ));
+ }
+ self.filename_var.as_ref().unwrap().clone()
}
}
diff --git a/crates/oxc_transformer/src/react/mod.rs b/crates/oxc_transformer/src/react/mod.rs
index 0beacfc67..76cafb764 100644
--- a/crates/oxc_transformer/src/react/mod.rs
+++ b/crates/oxc_transformer/src/react/mod.rs
@@ -96,13 +96,13 @@ impl<'a> React<'a> {
pub fn transform_jsx_opening_element(
&mut self,
elem: &mut JSXOpeningElement<'a>,
- ctx: &TraverseCtx<'a>,
+ ctx: &mut TraverseCtx<'a>,
) {
if self.jsx_self_plugin && self.jsx.jsx_self.can_add_self_attribute(ctx) {
self.jsx.jsx_self.transform_jsx_opening_element(elem);
}
if self.jsx_source_plugin {
- self.jsx.jsx_source.transform_jsx_opening_element(elem);
+ self.jsx.jsx_source.transform_jsx_opening_element(elem, ctx);
}
}
}