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
18 changes: 9 additions & 9 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ impl<'a> Transformer<'a> {
impl<'a> Traverse<'a> for Transformer<'a> {
fn enter_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.enter_program(program, ctx);
self.x1_react.transform_program(program, ctx);
self.x1_react.enter_program(program, ctx);
}

fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.x1_react.transform_program_on_exit(program, ctx);
self.x1_react.exit_program(program, ctx);
self.x0_typescript.exit_program(program, ctx);
self.x3_es2015.exit_program(program, ctx);
}
Expand All @@ -150,7 +150,7 @@ impl<'a> Traverse<'a> for Transformer<'a> {

fn enter_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.enter_call_expression(expr, ctx);
self.x1_react.transform_call_expression(expr, ctx);
self.x1_react.enter_call_expression(expr, ctx);
}

fn enter_class(&mut self, class: &mut Class<'a>, ctx: &mut TraverseCtx<'a>) {
Expand All @@ -175,7 +175,7 @@ impl<'a> Traverse<'a> for Transformer<'a> {

fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.enter_expression(expr, ctx);
self.x1_react.transform_expression(expr, ctx);
self.x1_react.enter_expression(expr, ctx);
self.x2_es2021.enter_expression(expr, ctx);
self.x2_es2020.enter_expression(expr, ctx);
self.x2_es2018.enter_expression(expr, ctx);
Expand All @@ -185,7 +185,7 @@ impl<'a> Traverse<'a> for Transformer<'a> {
}

fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
self.x1_react.transform_expression_on_exit(expr, ctx);
self.x1_react.exit_expression(expr, ctx);
self.x3_es2015.exit_expression(expr, ctx);
}

Expand Down Expand Up @@ -219,7 +219,7 @@ impl<'a> Traverse<'a> for Transformer<'a> {

fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.exit_function(func, ctx);
self.x1_react.transform_function_on_exit(func, ctx);
self.x1_react.exit_function(func, ctx);
self.x3_es2015.exit_function(func, ctx);
}

Expand All @@ -237,7 +237,7 @@ impl<'a> Traverse<'a> for Transformer<'a> {
ctx: &mut TraverseCtx<'a>,
) {
self.x0_typescript.enter_jsx_opening_element(elem, ctx);
self.x1_react.transform_jsx_opening_element(elem, ctx);
self.x1_react.enter_jsx_opening_element(elem, ctx);
}

fn enter_method_definition(
Expand Down Expand Up @@ -278,7 +278,7 @@ impl<'a> Traverse<'a> for Transformer<'a> {

fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.enter_statements(stmts, ctx);
self.x1_react.transform_statements(stmts, ctx);
self.x1_react.enter_statements(stmts, ctx);
self.x2_es2021.enter_statements(stmts, ctx);
self.x2_es2020.enter_statements(stmts, ctx);
self.x2_es2016.enter_statements(stmts, ctx);
Expand Down Expand Up @@ -308,7 +308,7 @@ impl<'a> Traverse<'a> for Transformer<'a> {

fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.exit_statements(stmts, ctx);
self.x1_react.transform_statements_on_exit(stmts, ctx);
self.x1_react.exit_statements(stmts, ctx);
self.x2_es2021.exit_statements(stmts, ctx);
self.x2_es2020.exit_statements(stmts, ctx);
self.x2_es2016.exit_statements(stmts, ctx);
Expand Down
11 changes: 5 additions & 6 deletions crates/oxc_transformer/src/react/display_name.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use oxc_allocator::Box;
use oxc_ast::ast::*;
use oxc_span::{Atom, SPAN};
use oxc_traverse::{Ancestor, TraverseCtx};
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};

use crate::context::Ctx;

Expand All @@ -23,12 +23,11 @@ impl<'a> ReactDisplayName<'a> {
}
}

// Transforms
impl<'a> ReactDisplayName<'a> {
pub fn transform_call_expression(
&self,
impl<'a> Traverse<'a> for ReactDisplayName<'a> {
fn enter_call_expression(
&mut self,
call_expr: &mut CallExpression<'a>,
ctx: &TraverseCtx<'a>,
ctx: &mut TraverseCtx<'a>,
) {
let Some(obj_expr) = Self::get_object_from_create_class(call_expr) else {
return;
Expand Down
127 changes: 60 additions & 67 deletions crates/oxc_transformer/src/react/jsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use oxc_syntax::{
symbol::SymbolFlags,
xml_entities::XML_ENTITIES,
};
use oxc_traverse::TraverseCtx;
use oxc_traverse::{Traverse, TraverseCtx};

use super::{diagnostics, utils::get_line_column};
pub use super::{
Expand Down Expand Up @@ -291,7 +291,6 @@ impl<'a> Pragma<'a> {
}
}

// Transforms
impl<'a> ReactJsx<'a> {
pub fn new(options: ReactOptions, ctx: Ctx<'a>) -> Self {
let bindings = match options.runtime {
Expand Down Expand Up @@ -364,43 +363,40 @@ impl<'a> ReactJsx<'a> {
bindings,
}
}
}

pub fn transform_program_on_exit(
&mut self,
program: &mut Program<'a>,
ctx: &mut TraverseCtx<'a>,
) {
impl<'a> Traverse<'a> for ReactJsx<'a> {
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.add_runtime_imports(program, ctx);
}

pub fn transform_jsx_element(
&mut self,
e: &JSXElement<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
self.transform_jsx(&JSXElementOrFragment::Element(e), ctx)
}
fn enter_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
let new_expr = match expr {
Expression::JSXElement(e) => {
Some(self.transform_jsx(&JSXElementOrFragment::Element(e), ctx))
}
Expression::JSXFragment(e) => {
Some(self.transform_jsx(&JSXElementOrFragment::Fragment(e), ctx))
}
_ => None,
};

pub fn transform_jsx_fragment(
&mut self,
e: &JSXFragment<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
self.transform_jsx(&JSXElementOrFragment::Fragment(e), ctx)
if let Some(new_expr) = new_expr {
*expr = new_expr;
}
}
}

impl<'a> ReactJsx<'a> {
fn is_script(&self) -> bool {
self.ctx.source_type.is_script()
}

fn ast(&self) -> AstBuilder<'a> {
self.ctx.ast
}
}

// Add imports
impl<'a> ReactJsx<'a> {
pub fn add_runtime_imports(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
fn add_runtime_imports(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.bindings.is_classic() {
if let Some(stmt) = self.jsx_source.get_var_file_name_statement() {
program.body.insert(0, stmt);
Expand All @@ -426,50 +422,7 @@ impl<'a> ReactJsx<'a> {

program.body.splice(index..index, imports);
}
}

enum JSXElementOrFragment<'a, 'b> {
Element(&'b JSXElement<'a>),
Fragment(&'b JSXFragment<'a>),
}

impl<'a, 'b> JSXElementOrFragment<'a, 'b> {
fn span(&self) -> Span {
match self {
Self::Element(e) => e.span,
Self::Fragment(e) => e.span,
}
}

fn children(&self) -> &'b Vec<'a, JSXChild<'a>> {
match self {
Self::Element(e) => &e.children,
Self::Fragment(e) => &e.children,
}
}

fn is_fragment(&self) -> bool {
matches!(self, Self::Fragment(_))
}

/// The react jsx/jsxs transform falls back to `createElement` when an explicit `key` argument comes after a spread
/// <https://github.com/microsoft/TypeScript/blob/6134091642f57c32f50e7b5604635e4d37dd19e8/src/compiler/transformers/jsx.ts#L264-L278>
fn has_key_after_props_spread(&self) -> bool {
let Self::Element(e) = self else { return false };
let mut spread = false;
for attr in &e.opening_element.attributes {
if matches!(attr, JSXAttributeItem::SpreadAttribute(_)) {
spread = true;
} else if spread && matches!(attr, JSXAttributeItem::Attribute(a) if a.is_key()) {
return true;
}
}
false
}
}

// Transform jsx
impl<'a> ReactJsx<'a> {
/// ## Automatic
/// ### Element
/// Builds JSX into:
Expand Down Expand Up @@ -998,6 +951,46 @@ impl<'a> ReactJsx<'a> {
}
}

enum JSXElementOrFragment<'a, 'b> {
Element(&'b JSXElement<'a>),
Fragment(&'b JSXFragment<'a>),
}

impl<'a, 'b> JSXElementOrFragment<'a, 'b> {
fn span(&self) -> Span {
match self {
Self::Element(e) => e.span,
Self::Fragment(e) => e.span,
}
}

fn children(&self) -> &'b Vec<'a, JSXChild<'a>> {
match self {
Self::Element(e) => &e.children,
Self::Fragment(e) => &e.children,
}
}

fn is_fragment(&self) -> bool {
matches!(self, Self::Fragment(_))
}

/// The react jsx/jsxs transform falls back to `createElement` when an explicit `key` argument comes after a spread
/// <https://github.com/microsoft/TypeScript/blob/6134091642f57c32f50e7b5604635e4d37dd19e8/src/compiler/transformers/jsx.ts#L264-L278>
fn has_key_after_props_spread(&self) -> bool {
let Self::Element(e) = self else { return false };
let mut spread = false;
for attr in &e.opening_element.attributes {
if matches!(attr, JSXAttributeItem::SpreadAttribute(_)) {
spread = true;
} else if spread && matches!(attr, JSXAttributeItem::Attribute(a) if a.is_key()) {
return true;
}
}
false
}
}

/// Create `IdentifierReference` for var name in current scope which is read from
fn get_read_identifier_reference<'a>(
span: Span,
Expand Down
32 changes: 19 additions & 13 deletions crates/oxc_transformer/src/react/jsx_self.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use oxc_ast::ast::*;
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{Span, SPAN};
use oxc_traverse::{Ancestor, TraverseCtx};
use oxc_traverse::{Ancestor, Traverse, TraverseCtx};

use crate::context::Ctx;

Expand All @@ -23,20 +23,19 @@ impl<'a> ReactJsxSelf<'a> {
pub fn new(ctx: Ctx<'a>) -> Self {
Self { ctx }
}
}

pub fn transform_jsx_opening_element(&self, elem: &mut JSXOpeningElement<'a>) {
impl<'a> Traverse<'a> for ReactJsxSelf<'a> {
fn enter_jsx_opening_element(
&mut self,
elem: &mut JSXOpeningElement<'a>,
_ctx: &mut TraverseCtx<'a>,
) {
self.add_self_this_attribute(elem);
}
}

pub fn get_object_property_kind_for_jsx_plugin(&self) -> ObjectPropertyKind<'a> {
let kind = PropertyKind::Init;
let key = self.ctx.ast.property_key_identifier_name(SPAN, SELF);
let value = self.ctx.ast.expression_this(SPAN);
self.ctx
.ast
.object_property_kind_object_property(SPAN, kind, key, value, None, false, false, false)
}

impl<'a> ReactJsxSelf<'a> {
pub fn report_error(&self, span: Span) {
let error = OxcDiagnostic::warn("Duplicate __self prop found.").with_label(span);
self.ctx.error(error);
Expand All @@ -63,12 +62,19 @@ impl<'a> ReactJsxSelf<'a> {
true
}

pub fn get_object_property_kind_for_jsx_plugin(&self) -> ObjectPropertyKind<'a> {
let kind = PropertyKind::Init;
let key = self.ctx.ast.property_key_identifier_name(SPAN, SELF);
let value = self.ctx.ast.expression_this(SPAN);
self.ctx
.ast
.object_property_kind_object_property(SPAN, kind, key, value, None, false, false, false)
}

pub fn can_add_self_attribute(&self, ctx: &TraverseCtx<'a>) -> bool {
!self.is_inside_constructor(ctx) || Self::has_no_super_class(ctx)
}
}

impl<'a> ReactJsxSelf<'a> {
/// `<div __self={this} />`
/// ^^^^^^^^^^^^^
fn add_self_this_attribute(&self, elem: &mut JSXOpeningElement<'a>) {
Expand Down
10 changes: 6 additions & 4 deletions crates/oxc_transformer/src/react/jsx_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use oxc_ast::ast::*;
use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{Span, SPAN};
use oxc_syntax::{number::NumberBase, symbol::SymbolFlags};
use oxc_traverse::TraverseCtx;
use oxc_traverse::{Traverse, TraverseCtx};

use super::utils::get_line_column;
use crate::{context::Ctx, helpers::bindings::BoundIdentifier};
Expand All @@ -27,15 +27,19 @@ impl<'a> ReactJsxSource<'a> {
pub fn new(ctx: Ctx<'a>) -> Self {
Self { ctx, filename_var: None }
}
}

pub fn transform_jsx_opening_element(
impl<'a> Traverse<'a> for ReactJsxSource<'a> {
fn enter_jsx_opening_element(
&mut self,
elem: &mut JSXOpeningElement<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.add_source_attribute(elem, ctx);
}
}

impl<'a> ReactJsxSource<'a> {
pub fn get_object_property_kind_for_jsx_plugin(
&mut self,
line: usize,
Expand All @@ -54,9 +58,7 @@ impl<'a> ReactJsxSource<'a> {
let error = OxcDiagnostic::warn("Duplicate __source prop found.").with_label(span);
self.ctx.error(error);
}
}

impl<'a> ReactJsxSource<'a> {
/// `<sometag __source={ { fileName: 'this/file.js', lineNumber: 10, columnNumber: 1 } } />`
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fn add_source_attribute(
Expand Down
Loading