Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(transformer): use UID for JSX source filename var #3612

Merged
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
25 changes: 23 additions & 2 deletions crates/oxc_transformer/src/helpers/bindings.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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> {
Expand All @@ -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)),
}
}
}
16 changes: 6 additions & 10 deletions crates/oxc_transformer/src/react/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ pub struct ReactJsx<'a> {

// States
bindings: Bindings<'a>,
can_add_filename_statement: bool,
}

/// Bindings for different import options
Expand Down Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -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;
}
Expand All @@ -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() {
Expand Down Expand Up @@ -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),
);
}
}
Expand Down Expand Up @@ -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));
}
}
Expand Down
57 changes: 43 additions & 14 deletions crates/oxc_transformer/src/react/jsx_source.rs
Original file line number Diff line number Diff line change
@@ -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)
///
Expand All @@ -20,26 +21,32 @@ const FILE_NAME_VAR: &str = "_jsxFileName";
/// Out: `<sometag __source={ { fileName: 'this/file.js', lineNumber: 10, columnNumber: 1 } } />`
pub struct ReactJsxSource<'a> {
ctx: Ctx<'a>,
filename_var: Option<BoundIdentifier<'a>>,
}

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)
}
Expand All @@ -53,7 +60,11 @@ impl<'a> ReactJsxSource<'a> {
impl<'a> ReactJsxSource<'a> {
/// `<sometag __source={ { fileName: 'this/file.js', lineNumber: 10, columnNumber: 1 } } />`
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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 {
Expand All @@ -70,21 +81,26 @@ 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));
elem.attributes.push(JSXAttributeItem::Attribute(attribute_item));
}

#[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)
};
Expand Down Expand Up @@ -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<Statement<'a>> {
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)
};
Expand All @@ -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()
}
}
4 changes: 2 additions & 2 deletions crates/oxc_transformer/src/react/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}