Skip to content
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
20 changes: 15 additions & 5 deletions crates/oxc_transformer/src/helpers/module_imports.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
use indexmap::IndexMap;
use std::cell::RefCell;
use std::cell::{Cell, RefCell};

use oxc_allocator::{Allocator, Vec};
use oxc_ast::{ast::*, AstBuilder};
use oxc_span::{Atom, SPAN};
use oxc_syntax::symbol::SymbolId;

pub struct NamedImport<'a> {
imported: Atom<'a>,
local: Option<Atom<'a>>, // Not used in `require`
symbol_id: SymbolId,
}

impl<'a> NamedImport<'a> {
pub fn new(imported: Atom<'a>, local: Option<Atom<'a>>) -> Self {
Self { imported, local }
pub fn new(imported: Atom<'a>, local: Option<Atom<'a>>, symbol_id: SymbolId) -> Self {
Self { imported, local, symbol_id }
}
}

Expand Down Expand Up @@ -89,7 +91,11 @@ impl<'a> ModuleImports<'a> {
ImportDeclarationSpecifier::ImportSpecifier(self.ast.alloc(ImportSpecifier {
span: SPAN,
imported: ModuleExportName::Identifier(IdentifierName::new(SPAN, name.imported)),
local: BindingIdentifier::new(SPAN, local),
local: BindingIdentifier {
span: SPAN,
name: local,
symbol_id: Cell::new(Some(name.symbol_id)),
},
import_kind: ImportOrExportKind::Value,
}))
}));
Expand Down Expand Up @@ -120,7 +126,11 @@ impl<'a> ModuleImports<'a> {
};
let name = names.into_iter().next().unwrap();
let id = {
let ident = BindingIdentifier::new(SPAN, name.imported);
let ident = BindingIdentifier {
span: SPAN,
name: name.imported,
symbol_id: Cell::new(Some(name.symbol_id)),
};
self.ast.binding_pattern(self.ast.binding_pattern_identifier(ident), None, false)
};
let decl = {
Expand Down
87 changes: 45 additions & 42 deletions crates/oxc_transformer/src/react/jsx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use oxc_ast::{ast::*, AstBuilder};
use oxc_span::{Atom, GetSpan, Span, SPAN};
use oxc_syntax::{
identifier::{is_irregular_whitespace, is_line_terminator},
symbol::SymbolFlags,
symbol::{SymbolFlags, SymbolId},
xml_entities::XML_ENTITIES,
};
use oxc_traverse::TraverseCtx;
Expand Down Expand Up @@ -43,15 +43,20 @@ pub struct ReactJsx<'a> {
jsx_runtime_importer: Atom<'a>,

// Doubles as var name for require react
import_create_element: Option<Atom<'a>>,
import_create_element: Option<BoundIdentifier<'a>>,
// Doubles as var name for require JSX
import_jsx: Option<Atom<'a>>,
import_jsxs: Option<Atom<'a>>,
import_fragment: Option<Atom<'a>>,
import_jsx: Option<BoundIdentifier<'a>>,
import_jsxs: Option<BoundIdentifier<'a>>,
import_fragment: Option<BoundIdentifier<'a>>,

can_add_filename_statement: bool,
}

pub struct BoundIdentifier<'a> {
pub name: Atom<'a>,
pub symbol_id: SymbolId,
}

// Transforms
impl<'a> ReactJsx<'a> {
pub fn new(options: &Rc<ReactOptions>, ctx: &Ctx<'a>) -> Self {
Expand Down Expand Up @@ -182,9 +187,9 @@ impl<'a> ReactJsx<'a> {
if self.import_jsx.is_none() {
let var_name =
if self.options.development { "reactJsxDevRuntime" } else { "reactJsxRuntime" };
let var_name =
let id =
self.add_require_statement(var_name, self.jsx_runtime_importer.clone(), false, ctx);
self.import_jsx = Some(var_name);
self.import_jsx = Some(id);
}
}

Expand All @@ -194,8 +199,8 @@ impl<'a> ReactJsx<'a> {
} else if self.options.development {
self.add_import_jsx_dev(ctx);
} else if self.import_jsx.is_none() {
let var_name = self.add_import_statement("jsx", self.jsx_runtime_importer.clone(), ctx);
self.import_jsx = Some(var_name);
let id = self.add_import_statement("jsx", self.jsx_runtime_importer.clone(), ctx);
self.import_jsx = Some(id);
}
}

Expand All @@ -205,42 +210,39 @@ impl<'a> ReactJsx<'a> {
} else if self.options.development {
self.add_import_jsx_dev(ctx);
} else if self.import_jsxs.is_none() {
let var_name =
self.add_import_statement("jsxs", self.jsx_runtime_importer.clone(), ctx);
self.import_jsxs = Some(var_name);
let id = self.add_import_statement("jsxs", self.jsx_runtime_importer.clone(), ctx);
self.import_jsxs = Some(id);
}
}

fn add_import_jsx_dev(&mut self, ctx: &mut TraverseCtx<'a>) {
if self.is_script() {
self.add_require_jsx_runtime(ctx);
} else if self.import_jsx.is_none() {
let var_name =
self.add_import_statement("jsxDEV", self.jsx_runtime_importer.clone(), ctx);
self.import_jsx = Some(var_name);
let id = self.add_import_statement("jsxDEV", self.jsx_runtime_importer.clone(), ctx);
self.import_jsx = Some(id);
}
}

fn add_import_fragment(&mut self, ctx: &mut TraverseCtx<'a>) {
if self.is_script() {
self.add_require_jsx_runtime(ctx);
} else if self.import_fragment.is_none() {
let var_name =
self.add_import_statement("Fragment", self.jsx_runtime_importer.clone(), ctx);
self.import_fragment = Some(var_name);
let id = self.add_import_statement("Fragment", self.jsx_runtime_importer.clone(), ctx);
self.import_fragment = Some(id);
self.add_import_jsx(ctx);
}
}

fn add_import_create_element(&mut self, ctx: &mut TraverseCtx<'a>) {
if self.import_create_element.is_none() {
let source = ctx.ast.new_atom(&self.options.import_source);
let var_name = if self.is_script() {
let id = if self.is_script() {
self.add_require_statement("react", source, true, ctx)
} else {
self.add_import_statement("createElement", source, ctx)
};
self.import_create_element = Some(var_name);
self.import_create_element = Some(id);
}
}

Expand All @@ -249,14 +251,14 @@ impl<'a> ReactJsx<'a> {
name: &'static str,
source: Atom<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Atom<'a> {
) -> BoundIdentifier<'a> {
let root_scope_id = ctx.scopes().root_scope_id();
let symbol_id = ctx.generate_uid(name, root_scope_id, SymbolFlags::FunctionScopedVariable);
let local = ctx.ast.new_atom(&ctx.symbols().names[symbol_id]);

let import = NamedImport::new(Atom::from(name), Some(local.clone()));
let import = NamedImport::new(Atom::from(name), Some(local.clone()), symbol_id);
self.ctx.module_imports.add_import(source, import);
local
BoundIdentifier { name: local, symbol_id }
}

fn add_require_statement(
Expand All @@ -265,15 +267,15 @@ impl<'a> ReactJsx<'a> {
source: Atom<'a>,
front: bool,
ctx: &mut TraverseCtx<'a>,
) -> Atom<'a> {
) -> BoundIdentifier<'a> {
let root_scope_id = ctx.scopes().root_scope_id();
let symbol_id =
ctx.generate_uid(variable_name, root_scope_id, SymbolFlags::FunctionScopedVariable);
let variable_name = ctx.ast.new_atom(&ctx.symbols().names[symbol_id]);

let import = NamedImport::new(variable_name.clone(), None);
let import = NamedImport::new(variable_name.clone(), None, symbol_id);
self.ctx.module_imports.add_require(source, import, front);
variable_name
BoundIdentifier { name: variable_name, symbol_id }
}
}

Expand Down Expand Up @@ -615,16 +617,18 @@ impl<'a> ReactJsx<'a> {
return;
}

if self.is_script() {
let (id, local_id) = if self.is_script() {
let Argument::StaticMemberExpression(member_expr) = arg else { unreachable!() };
let Expression::Identifier(id) = &mut member_expr.object else {
unreachable!();
};
id.name = self.import_jsx.as_ref().unwrap().clone();
(id, self.import_jsx.as_ref().unwrap())
} else {
let Argument::Identifier(id) = arg else { unreachable!() };
id.name = self.import_fragment.as_ref().unwrap().clone();
}
(id, self.import_fragment.as_ref().unwrap())
};
id.name = local_id.name.clone();
// TODO: Set `reference_id`
}

fn get_create_element(&self, has_key_after_props_spread: bool, jsxs: bool) -> Expression<'a> {
Expand All @@ -640,11 +644,8 @@ impl<'a> ReactJsx<'a> {
}
ReactJsxRuntime::Automatic => {
if self.is_script() {
let (object_ident_name, property_name) = if has_key_after_props_spread {
(
self.import_create_element.as_ref().unwrap().clone(),
Atom::from("createElement"),
)
let (object_id, property_name) = if has_key_after_props_spread {
(self.import_create_element.as_ref().unwrap(), Atom::from("createElement"))
} else {
let property_name = if self.options.development {
Atom::from("jsxDEV")
Expand All @@ -653,18 +654,20 @@ impl<'a> ReactJsx<'a> {
} else {
Atom::from("jsx")
};
(self.import_jsx.as_ref().unwrap().clone(), property_name)
(self.import_jsx.as_ref().unwrap(), property_name)
};
self.get_static_member_expression(object_ident_name, property_name)
// TODO: Set `reference_id`
self.get_static_member_expression(object_id.name.clone(), property_name)
} else {
let name = if has_key_after_props_spread {
self.import_create_element.as_ref().unwrap().clone()
let id = if has_key_after_props_spread {
self.import_create_element.as_ref().unwrap()
} else if jsxs && !self.options.development {
self.import_jsxs.as_ref().unwrap().clone()
self.import_jsxs.as_ref().unwrap()
} else {
self.import_jsx.as_ref().unwrap().clone()
self.import_jsx.as_ref().unwrap()
};
let ident = IdentifierReference::new(SPAN, name);
// TODO: Set `reference_id`
let ident = IdentifierReference::new(SPAN, id.name.clone());
self.ast().identifier_reference_expression(ident)
}
}
Expand Down