Skip to content

Commit 896c8b1

Browse files
committed
fix(transformer): use UID for JSX source filename var
1 parent 2cc4913 commit 896c8b1

File tree

4 files changed

+69
-28
lines changed

4 files changed

+69
-28
lines changed

crates/oxc_transformer/src/helpers/bindings.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use std::cell::Cell;
22

3-
use oxc_ast::ast::IdentifierReference;
3+
use oxc_ast::ast::{BindingIdentifier, IdentifierReference};
44
use oxc_span::{Atom, Span, SPAN};
55
use oxc_syntax::{
66
reference::{ReferenceFlag, ReferenceId},
7-
symbol::SymbolId,
7+
symbol::{SymbolFlags, SymbolId},
88
};
99
use oxc_traverse::TraverseCtx;
1010

@@ -16,6 +16,13 @@ pub struct BoundIdentifier<'a> {
1616
}
1717

1818
impl<'a> BoundIdentifier<'a> {
19+
/// Create `BoundIdentifier` for new binding in root scope
20+
pub fn new_root_uid(name: &str, flags: SymbolFlags, ctx: &mut TraverseCtx<'a>) -> Self {
21+
let symbol_id = ctx.generate_uid_in_root_scope(name, flags);
22+
let name = ctx.ast.new_atom(&ctx.symbols().names[symbol_id]);
23+
Self { name, symbol_id }
24+
}
25+
1926
/// Create `IdentifierReference` referencing this binding which is read from
2027
/// in current scope
2128
pub fn create_read_reference(&self, ctx: &mut TraverseCtx) -> IdentifierReference<'a> {
@@ -26,6 +33,15 @@ impl<'a> BoundIdentifier<'a> {
2633
);
2734
create_read_identifier_reference(SPAN, self.name.clone(), Some(reference_id))
2835
}
36+
37+
/// Create `BindingIdentifer` for this binding
38+
pub fn create_binding_identifier(&self) -> BindingIdentifier<'a> {
39+
BindingIdentifier {
40+
span: SPAN,
41+
name: self.name.clone(),
42+
symbol_id: Cell::new(Some(self.symbol_id)),
43+
}
44+
}
2945
}
3046

3147
/// Create `IdentifierReference` which is read from

crates/oxc_transformer/src/react/jsx.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ pub struct ReactJsx<'a> {
4747

4848
// States
4949
bindings: Bindings<'a>,
50-
can_add_filename_statement: bool,
5150
}
5251

5352
/// Bindings for different import options
@@ -366,7 +365,6 @@ impl<'a> ReactJsx<'a> {
366365
jsx_self: ReactJsxSelf::new(Rc::clone(&ctx)),
367366
jsx_source: ReactJsxSource::new(ctx),
368367
bindings,
369-
can_add_filename_statement: false,
370368
}
371369
}
372370

@@ -403,8 +401,8 @@ impl<'a> ReactJsx<'a> {
403401
impl<'a> ReactJsx<'a> {
404402
pub fn add_runtime_imports(&mut self, program: &mut Program<'a>) {
405403
if self.bindings.is_classic() {
406-
if self.can_add_filename_statement {
407-
program.body.insert(0, self.jsx_source.get_var_file_name_statement());
404+
if let Some(stmt) = self.jsx_source.get_var_file_name_statement() {
405+
program.body.insert(0, stmt);
408406
}
409407
return;
410408
}
@@ -416,8 +414,8 @@ impl<'a> ReactJsx<'a> {
416414
.rposition(|stmt| matches!(stmt, Statement::ImportDeclaration(_)))
417415
.map_or(0, |i| i + 1);
418416

419-
if self.can_add_filename_statement {
420-
program.body.insert(index, self.jsx_source.get_var_file_name_statement());
417+
if let Some(stmt) = self.jsx_source.get_var_file_name_statement() {
418+
program.body.insert(index, stmt);
421419
// If source type is module then we need to add the import statement after the var file name statement
422420
// Follow the same behavior as babel
423421
if !self.is_script() {
@@ -602,10 +600,9 @@ impl<'a> ReactJsx<'a> {
602600
if let Some(span) = source_attr_span {
603601
self.jsx_source.report_error(span);
604602
} else {
605-
self.can_add_filename_statement = true;
606603
let (line, column) = get_line_column(e.span().start, self.ctx.source_text);
607604
properties.push(
608-
self.jsx_source.get_object_property_kind_for_jsx_plugin(line, column),
605+
self.jsx_source.get_object_property_kind_for_jsx_plugin(line, column, ctx),
609606
);
610607
}
611608
}
@@ -658,9 +655,8 @@ impl<'a> ReactJsx<'a> {
658655
if let Some(span) = source_attr_span {
659656
self.jsx_source.report_error(span);
660657
} else {
661-
self.can_add_filename_statement = true;
662658
let (line, column) = get_line_column(e.span().start, self.ctx.source_text);
663-
let expr = self.jsx_source.get_source_object(line, column);
659+
let expr = self.jsx_source.get_source_object(line, column, ctx);
664660
arguments.push(Argument::from(expr));
665661
}
666662
}

crates/oxc_transformer/src/react/jsx_source.rs

+43-14
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use oxc_ast::ast::*;
22
use oxc_diagnostics::OxcDiagnostic;
33
use oxc_span::{Span, SPAN};
4-
use oxc_syntax::number::NumberBase;
4+
use oxc_syntax::{number::NumberBase, symbol::SymbolFlags};
5+
use oxc_traverse::TraverseCtx;
56

6-
use crate::context::Ctx;
7+
use crate::{context::Ctx, helpers::bindings::BoundIdentifier};
78

89
use super::utils::get_line_column;
910

1011
const SOURCE: &str = "__source";
11-
const FILE_NAME_VAR: &str = "_jsxFileName";
12+
const FILE_NAME_VAR: &str = "jsxFileName";
1213

1314
/// [plugin-transform-react-jsx-source](https://babeljs.io/docs/babel-plugin-transform-react-jsx-source)
1415
///
@@ -20,26 +21,32 @@ const FILE_NAME_VAR: &str = "_jsxFileName";
2021
/// Out: `<sometag __source={ { fileName: 'this/file.js', lineNumber: 10, columnNumber: 1 } } />`
2122
pub struct ReactJsxSource<'a> {
2223
ctx: Ctx<'a>,
24+
filename_var: Option<BoundIdentifier<'a>>,
2325
}
2426

2527
impl<'a> ReactJsxSource<'a> {
2628
pub fn new(ctx: Ctx<'a>) -> Self {
27-
Self { ctx }
29+
Self { ctx, filename_var: None }
2830
}
2931

30-
pub fn transform_jsx_opening_element(&mut self, elem: &mut JSXOpeningElement<'a>) {
31-
self.add_source_attribute(elem);
32+
pub fn transform_jsx_opening_element(
33+
&mut self,
34+
elem: &mut JSXOpeningElement<'a>,
35+
ctx: &mut TraverseCtx<'a>,
36+
) {
37+
self.add_source_attribute(elem, ctx);
3238
}
3339

3440
pub fn get_object_property_kind_for_jsx_plugin(
3541
&mut self,
3642
line: usize,
3743
column: usize,
44+
ctx: &mut TraverseCtx<'a>,
3845
) -> ObjectPropertyKind<'a> {
3946
let kind = PropertyKind::Init;
4047
let ident = IdentifierName::new(SPAN, SOURCE.into());
4148
let key = self.ctx.ast.property_key_identifier(ident);
42-
let value = self.get_source_object(line, column);
49+
let value = self.get_source_object(line, column, ctx);
4350
let obj = self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false);
4451
ObjectPropertyKind::ObjectProperty(obj)
4552
}
@@ -53,7 +60,11 @@ impl<'a> ReactJsxSource<'a> {
5360
impl<'a> ReactJsxSource<'a> {
5461
/// `<sometag __source={ { fileName: 'this/file.js', lineNumber: 10, columnNumber: 1 } } />`
5562
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
56-
fn add_source_attribute(&mut self, elem: &mut JSXOpeningElement<'a>) {
63+
fn add_source_attribute(
64+
&mut self,
65+
elem: &mut JSXOpeningElement<'a>,
66+
ctx: &mut TraverseCtx<'a>,
67+
) {
5768
// Check if `__source` attribute already exists
5869
for item in &elem.attributes {
5970
if let JSXAttributeItem::Attribute(attribute) = item {
@@ -70,21 +81,26 @@ impl<'a> ReactJsxSource<'a> {
7081
self.ctx.ast.alloc(self.ctx.ast.jsx_identifier(SPAN, SOURCE.into())),
7182
);
7283
let (line, column) = get_line_column(elem.span.start, self.ctx.source_text);
73-
let object = self.get_source_object(line, column);
84+
let object = self.get_source_object(line, column, ctx);
7485
let expr = self.ctx.ast.jsx_expression_container(SPAN, JSXExpression::from(object));
7586
let value = JSXAttributeValue::ExpressionContainer(expr);
7687
let attribute_item = self.ctx.ast.jsx_attribute(SPAN, key, Some(value));
7788
elem.attributes.push(JSXAttributeItem::Attribute(attribute_item));
7889
}
7990

8091
#[allow(clippy::cast_precision_loss)]
81-
pub fn get_source_object(&mut self, line: usize, column: usize) -> Expression<'a> {
92+
pub fn get_source_object(
93+
&mut self,
94+
line: usize,
95+
column: usize,
96+
ctx: &mut TraverseCtx<'a>,
97+
) -> Expression<'a> {
8298
let kind = PropertyKind::Init;
8399

84100
let filename = {
85101
let name = IdentifierName::new(SPAN, "fileName".into());
86102
let key = self.ctx.ast.property_key_identifier(name);
87-
let ident = self.ctx.ast.identifier_reference(SPAN, FILE_NAME_VAR);
103+
let ident = self.get_filename_var(ctx).create_read_reference(ctx);
88104
let value = self.ctx.ast.identifier_reference_expression(ident);
89105
self.ctx.ast.object_property(SPAN, kind, key, value, None, false, false, false)
90106
};
@@ -122,10 +138,12 @@ impl<'a> ReactJsxSource<'a> {
122138
self.ctx.ast.object_expression(SPAN, properties, None)
123139
}
124140

125-
pub fn get_var_file_name_statement(&self) -> Statement<'a> {
141+
pub fn get_var_file_name_statement(&mut self) -> Option<Statement<'a>> {
142+
let filename_var = self.filename_var.as_ref()?;
143+
126144
let var_kind = VariableDeclarationKind::Var;
127145
let id = {
128-
let ident = BindingIdentifier::new(SPAN, FILE_NAME_VAR.into());
146+
let ident = filename_var.create_binding_identifier();
129147
let ident = self.ctx.ast.binding_pattern_identifier(ident);
130148
self.ctx.ast.binding_pattern(ident, None, false)
131149
};
@@ -136,6 +154,17 @@ impl<'a> ReactJsxSource<'a> {
136154
self.ctx.ast.new_vec_single(decl)
137155
};
138156
let var_decl = self.ctx.ast.variable_declaration(SPAN, var_kind, decl, Modifiers::empty());
139-
Statement::VariableDeclaration(var_decl)
157+
Some(Statement::VariableDeclaration(var_decl))
158+
}
159+
160+
fn get_filename_var(&mut self, ctx: &mut TraverseCtx<'a>) -> BoundIdentifier<'a> {
161+
if self.filename_var.is_none() {
162+
self.filename_var = Some(BoundIdentifier::new_root_uid(
163+
FILE_NAME_VAR,
164+
SymbolFlags::FunctionScopedVariable,
165+
ctx,
166+
));
167+
}
168+
self.filename_var.as_ref().unwrap().clone()
140169
}
141170
}

crates/oxc_transformer/src/react/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,13 @@ impl<'a> React<'a> {
9696
pub fn transform_jsx_opening_element(
9797
&mut self,
9898
elem: &mut JSXOpeningElement<'a>,
99-
ctx: &TraverseCtx<'a>,
99+
ctx: &mut TraverseCtx<'a>,
100100
) {
101101
if self.jsx_self_plugin && self.jsx.jsx_self.can_add_self_attribute(ctx) {
102102
self.jsx.jsx_self.transform_jsx_opening_element(elem);
103103
}
104104
if self.jsx_source_plugin {
105-
self.jsx.jsx_source.transform_jsx_opening_element(elem);
105+
self.jsx.jsx_source.transform_jsx_opening_element(elem, ctx);
106106
}
107107
}
108108
}

0 commit comments

Comments
 (0)