mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
fix(transformer): use UID for JSX source filename var (#3612)
React JSX Source transform generates a top-level variable `_jsxFileName` containing file path. Previously this var name was hard-coded. Generate a UID instead to avoid clash with existing var called `_jsxFileName`. Add this binding to scopes.
This commit is contained in:
parent
60cbdece65
commit
0fb4c35083
4 changed files with 74 additions and 28 deletions
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: `<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)
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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<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)
|
||||
};
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue