From d9b450414bc5758f252effd127ded574433fa756 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Fri, 10 Nov 2023 18:17:31 +0800 Subject: [PATCH] feat(transformer/react-jsx): when the source type is a script, use require to import the react (#1207) --- crates/oxc_transformer/src/context.rs | 6 +- crates/oxc_transformer/src/react_jsx/mod.rs | 89 +++++++++++++-------- tasks/transform_conformance/babel.snap.md | 1 - 3 files changed, 61 insertions(+), 35 deletions(-) diff --git a/crates/oxc_transformer/src/context.rs b/crates/oxc_transformer/src/context.rs index d234d966d..7b14d2df5 100644 --- a/crates/oxc_transformer/src/context.rs +++ b/crates/oxc_transformer/src/context.rs @@ -7,7 +7,7 @@ use std::{ use oxc_ast::AstBuilder; use oxc_diagnostics::Error; use oxc_semantic::{ScopeId, ScopeTree, Semantic, SymbolId, SymbolTable}; -use oxc_span::Atom; +use oxc_span::{Atom, SourceType}; #[derive(Clone)] pub struct TransformerCtx<'a> { @@ -42,6 +42,10 @@ impl<'a> TransformerCtx<'a> { self.scopes_mut().add_binding(ScopeId::new(0), name, SymbolId::new(0)); } + pub fn source_type(&self) -> Ref<'_, SourceType> { + Ref::map(self.semantic.borrow(), |semantic| semantic.source_type()) + } + pub fn errors(&self) -> Vec { mem::take(&mut self.errors.borrow_mut()) } diff --git a/crates/oxc_transformer/src/react_jsx/mod.rs b/crates/oxc_transformer/src/react_jsx/mod.rs index e6cac8f46..2620c45d4 100644 --- a/crates/oxc_transformer/src/react_jsx/mod.rs +++ b/crates/oxc_transformer/src/react_jsx/mod.rs @@ -165,12 +165,16 @@ impl<'a> ReactJsx<'a> { fn add_require_jsx_runtime(&mut self) { if !self.require_jsx_runtime { self.require_jsx_runtime = true; - self.require_jsx_runtime(Self::new_string_literal(self.jsx_runtime_importer.as_str())); + self.add_require_statement( + "_reactJsxRuntime", + Self::new_string_literal(self.jsx_runtime_importer.as_str()), + false, + ); } } fn add_import_jsx(&mut self) { - if self.ctx.semantic().source_type().is_script() { + if self.ctx.source_type().is_script() { self.add_require_jsx_runtime(); } else if !self.import_jsx { self.import_jsx = true; @@ -183,7 +187,7 @@ impl<'a> ReactJsx<'a> { } fn add_import_jsxs(&mut self) { - if self.ctx.semantic().source_type().is_script() { + if self.ctx.source_type().is_script() { self.add_require_jsx_runtime(); } else if !self.import_jsxs { self.import_jsxs = true; @@ -193,7 +197,7 @@ impl<'a> ReactJsx<'a> { } fn add_import_fragment(&mut self) { - if self.ctx.semantic().source_type().is_script() { + if self.ctx.source_type().is_script() { self.add_require_jsx_runtime(); } else if !self.import_fragment { self.import_fragment = true; @@ -206,8 +210,17 @@ impl<'a> ReactJsx<'a> { fn add_import_create_element(&mut self) { if !self.import_create_element { self.import_create_element = true; - let source = Self::new_string_literal(self.options.import_source.as_ref()); - self.add_import_statement("createElement", "_createElement", source); + + if self.ctx.source_type().is_script() { + self.add_require_statement( + "_react", + Self::new_string_literal(self.options.import_source.as_ref()), + true, + ); + } else { + let source = Self::new_string_literal(self.options.import_source.as_ref()); + self.add_import_statement("createElement", "_createElement", source); + } } } @@ -231,7 +244,7 @@ impl<'a> ReactJsx<'a> { self.imports.push(decl); } - fn require_jsx_runtime(&mut self, source: StringLiteral) { + fn add_require_statement(&mut self, variable_name: &str, source: StringLiteral, front: bool) { let callee = self .ast .identifier_reference_expression(IdentifierReference::new(SPAN, "require".into())); @@ -240,10 +253,7 @@ impl<'a> ReactJsx<'a> { .new_vec_single(Argument::Expression(self.ast.literal_string_expression(source))); let init = self.ast.call_expression(SPAN, callee, arguments, false, None); let id = self.ast.binding_pattern( - self.ast.binding_pattern_identifier(BindingIdentifier::new( - SPAN, - "_reactJsxRuntime".into(), - )), + self.ast.binding_pattern_identifier(BindingIdentifier::new(SPAN, variable_name.into())), None, false, ); @@ -263,7 +273,11 @@ impl<'a> ReactJsx<'a> { ); let stmt = Statement::Declaration(Declaration::VariableDeclaration(variable_declaration)); - self.imports.push(stmt); + if front { + self.imports.insert(0, stmt); + } else { + self.imports.push(stmt); + } } fn transform_jsx<'b>(&mut self, e: &JSXElementOrFragment<'a, 'b>) -> Expression<'a> { @@ -389,6 +403,17 @@ impl<'a> ReactJsx<'a> { self.ast.identifier_reference_expression(ident) } + fn get_static_member_expression( + &self, + object_ident_name: &str, + property_name: &str, + ) -> Expression<'a> { + let property = IdentifierName::new(SPAN, property_name.into()); + let ident = IdentifierReference::new(SPAN, object_ident_name.into()); + let object = self.ast.identifier_reference_expression(ident); + self.ast.static_member_expression(SPAN, object, property, false) + } + /// Get the callee from `pragma` and `pragmaFrag` fn get_call_expression_callee(&self, literal_callee: &str) -> Expression<'a> { let mut callee = literal_callee.split('.'); @@ -399,12 +424,7 @@ impl<'a> ReactJsx<'a> { let ident = IdentifierReference::new(SPAN, member.into()); self.ast.identifier_reference_expression(ident) }, - |property_name| { - let property = IdentifierName::new(SPAN, property_name.into()); - let ident = IdentifierReference::new(SPAN, member.into()); - let object = self.ast.identifier_reference_expression(ident); - self.ast.static_member_expression(SPAN, object, property, false) - }, + |property_name| self.get_static_member_expression(member, property_name), ) } @@ -424,31 +444,30 @@ impl<'a> ReactJsx<'a> { self.get_call_expression_callee(self.options.pragma.as_ref()) } ReactJsxRuntime::Automatic => { - let name = if has_key_after_props_spread { - "_createElement" - } else if self.require_jsx_runtime { - if jsxs { + let is_script = self.ctx.source_type().is_script(); + let name = if is_script { + if has_key_after_props_spread { + "createElement" + } else if jsxs { "jsxs" } else { "jsx" } + } else if has_key_after_props_spread { + "_createElement" } else if jsxs { "_jsxs" } else { "_jsx" }; - if has_key_after_props_spread || !self.require_jsx_runtime { + if is_script { + let object_ident_name = + if has_key_after_props_spread { "_react" } else { "_reactJsxRuntime" }; + self.get_static_member_expression(object_ident_name, name) + } else { let ident = IdentifierReference::new(SPAN, name.into()); self.ast.identifier_reference_expression(ident) - } else { - let ident = IdentifierReference::new(SPAN, "_reactJsxRuntime".into()); - self.ast.static_member_expression( - SPAN, - self.ast.identifier_reference_expression(ident), - IdentifierName::new(SPAN, name.into()), - false, - ) } } } @@ -466,8 +485,12 @@ impl<'a> ReactJsx<'a> { self.get_call_expression_callee(self.options.pragma_frag.as_ref()) } ReactJsxRuntime::Automatic => { - let ident = IdentifierReference::new(SPAN, "_Fragment".into()); - self.ast.identifier_reference_expression(ident) + if self.ctx.source_type().is_script() { + self.get_static_member_expression("_reactJsxRuntime", "Fragment") + } else { + let ident = IdentifierReference::new(SPAN, "_Fragment".into()); + self.ast.identifier_reference_expression(ident) + } } } } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index e042738b4..9de7228c0 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -854,7 +854,6 @@ Passed: 264/1113 # babel-plugin-transform-react-jsx (129/170) * autoImport/after-polyfills-compiled-to-cjs/input.mjs -* autoImport/auto-import-react-source-type-script/input.js * autoImport/complicated-scope-module/input.js * react/adds-appropriate-newlines-when-using-spread-attribute-babel-7/input.js * react/arrow-functions/input.js