diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs
index 03b63d909..0524b3cec 100644
--- a/crates/oxc_transformer/src/lib.rs
+++ b/crates/oxc_transformer/src/lib.rs
@@ -79,7 +79,6 @@ impl<'a> Transformer<'a> {
Self {
// TODO: pass verbatim_module_syntax from user config
typescript: source_type.is_typescript().then(|| TypeScript::new(Rc::clone(&ast), ctx.clone(), false)),
- react_jsx: options.react_jsx.map(|options| ReactJsx::new(Rc::clone(&ast), &ctx, options)),
regexp_flags: RegexpFlags::new(Rc::clone(&ast), &options),
es2022_class_static_block: es2022::ClassStaticBlock::new(Rc::clone(&ast), &options),
es2021_logical_assignment_operators: LogicalAssignmentOperators::new(Rc::clone(&ast), ctx.clone(), &options),
@@ -88,6 +87,7 @@ impl<'a> Transformer<'a> {
es2016_exponentiation_operator: ExponentiationOperator::new(Rc::clone(&ast), ctx.clone(), &options),
es2015_shorthand_properties: ShorthandProperties::new(Rc::clone(&ast), &options),
es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options),
+ react_jsx: options.react_jsx.map(|options| ReactJsx::new(Rc::clone(&ast), &ctx, options)),
}
}
diff --git a/crates/oxc_transformer/src/options.rs b/crates/oxc_transformer/src/options.rs
index 9fc19a8cf..e916c96c2 100644
--- a/crates/oxc_transformer/src/options.rs
+++ b/crates/oxc_transformer/src/options.rs
@@ -2,7 +2,7 @@ use oxc_syntax::assumptions::CompilerAssumptions;
use crate::{es2020::NullishCoalescingOperatorOptions, react_jsx::ReactJsxOptions};
-#[derive(Debug, Default, Clone, Copy)]
+#[derive(Debug, Default, Clone)]
pub struct TransformOptions {
pub target: TransformTarget,
pub assumptions: CompilerAssumptions,
diff --git a/crates/oxc_transformer/src/react_jsx/mod.rs b/crates/oxc_transformer/src/react_jsx/mod.rs
index 148fce8d1..6b5d39260 100644
--- a/crates/oxc_transformer/src/react_jsx/mod.rs
+++ b/crates/oxc_transformer/src/react_jsx/mod.rs
@@ -27,6 +27,8 @@ pub struct ReactJsx<'a> {
import_jsxs: bool,
import_fragment: bool,
import_create_element: bool,
+ // Will be store jsx runtime importer, like `react/jsx-runtime`
+ jsx_runtime_importer: Atom,
}
enum JSXElementOrFragment<'a, 'b> {
@@ -75,10 +77,19 @@ impl<'a> ReactJsx<'a> {
) -> Self {
let imports = ast.new_vec();
let options = options.with_comments(&ctx.semantic());
+
+ let jsx_runtime_importer =
+ if options.import_source == "react" || options.runtime.is_classic() {
+ Atom::new_inline("react/jsx-runtime")
+ } else {
+ Atom::from(format!("{}/jsx-runtime", options.import_source))
+ };
+
Self {
ast,
options,
imports,
+ jsx_runtime_importer,
import_jsx: false,
import_jsxs: false,
import_fragment: false,
@@ -111,6 +122,10 @@ impl<'a> ReactJsx<'a> {
program.body.splice(index..index, imports);
}
+ fn new_string_literal(name: &str) -> StringLiteral {
+ StringLiteral::new(SPAN, name.into())
+ }
+
fn add_import<'b>(
&mut self,
e: &JSXElementOrFragment<'a, 'b>,
@@ -133,21 +148,24 @@ impl<'a> ReactJsx<'a> {
fn add_import_jsx(&mut self) {
if !self.import_jsx {
self.import_jsx = true;
- self.add_import_statement("jsx", "_jsx", "react/jsx-runtime");
+ let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
+ self.add_import_statement("jsx", "_jsx", source);
}
}
fn add_import_jsxs(&mut self) {
if !self.import_jsxs {
self.import_jsxs = true;
- self.add_import_statement("jsxs", "_jsxs", "react/jsx-runtime");
+ let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
+ self.add_import_statement("jsxs", "_jsxs", source);
}
}
fn add_import_fragment(&mut self) {
if !self.import_fragment {
self.import_fragment = true;
- self.add_import_statement("Fragment", "_Fragment", "react/jsx-runtime");
+ let source = Self::new_string_literal(self.jsx_runtime_importer.as_str());
+ self.add_import_statement("Fragment", "_Fragment", source);
self.add_import_jsx();
}
}
@@ -155,11 +173,12 @@ impl<'a> ReactJsx<'a> {
fn add_import_create_element(&mut self) {
if !self.import_create_element {
self.import_create_element = true;
- self.add_import_statement("createElement", "_createElement", "react");
+ let source = Self::new_string_literal(self.options.import_source.as_ref());
+ self.add_import_statement("createElement", "_createElement", source);
}
}
- fn add_import_statement(&mut self, imported: &str, local: &str, source: &str) {
+ fn add_import_statement(&mut self, imported: &str, local: &str, source: StringLiteral) {
let mut specifiers = self.ast.new_vec_with_capacity(1);
specifiers.push(ImportDeclarationSpecifier::ImportSpecifier(ImportSpecifier {
span: SPAN,
@@ -167,7 +186,6 @@ impl<'a> ReactJsx<'a> {
local: BindingIdentifier::new(SPAN, local.into()),
import_kind: ImportOrExportKind::Value,
}));
- let source = StringLiteral::new(SPAN, source.into());
let import_statement = self.ast.import_declaration(
SPAN,
Some(specifiers),
diff --git a/crates/oxc_transformer/src/react_jsx/options.rs b/crates/oxc_transformer/src/react_jsx/options.rs
index 75cd93f57..73a1efd26 100644
--- a/crates/oxc_transformer/src/react_jsx/options.rs
+++ b/crates/oxc_transformer/src/react_jsx/options.rs
@@ -1,14 +1,23 @@
-use serde::Deserialize;
+use std::borrow::Cow;
use oxc_semantic::Semantic;
+use serde::Deserialize;
-#[derive(Debug, Default, Clone, Copy, Deserialize)]
+#[derive(Debug, Default, Clone, Deserialize)]
+#[serde(rename_all = "camelCase")]
pub struct ReactJsxOptions {
/// Decides which runtime to use.
pub runtime: ReactJsxRuntime,
/// Toggles whether or not to throw an error if an XML namespaced tag name is used. e.g. ``
/// Though the JSX spec allows this, it is disabled by default since React's JSX does not currently have support for it.
pub throw_if_namespace: Option,
+ /// Replaces the import source when importing functions. default to `react`
+ #[serde(default = "default_import_source")]
+ pub import_source: Cow<'static, str>,
+}
+
+fn default_import_source() -> Cow<'static, str> {
+ Cow::Borrowed("react")
}
#[derive(Debug, Default, Clone, Copy, Deserialize)]
diff --git a/crates/oxc_transformer/src/tester.rs b/crates/oxc_transformer/src/tester.rs
index ec80ee156..c3419945d 100644
--- a/crates/oxc_transformer/src/tester.rs
+++ b/crates/oxc_transformer/src/tester.rs
@@ -32,7 +32,8 @@ impl Tester {
let program = Parser::new(&self.allocator, source_text, self.source_type).parse().program;
let semantic = SemanticBuilder::new(source_text, self.source_type).build(&program).semantic;
let program = self.allocator.alloc(program);
- Transformer::new(&self.allocator, self.source_type, semantic, self.options).build(program);
+ Transformer::new(&self.allocator, self.source_type, semantic, self.options.clone())
+ .build(program);
Codegen::::new(source_text.len(), CodegenOptions).build(program)
}
diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md
index eebfd1e66..a61f6b26e 100644
--- a/tasks/transform_conformance/babel.snap.md
+++ b/tasks/transform_conformance/babel.snap.md
@@ -1,4 +1,4 @@
-Passed: 255/1113
+Passed: 256/1113
# All Passed:
* babel-plugin-transform-numeric-separator
@@ -825,14 +825,13 @@ Passed: 255/1113
* regression/11061/input.mjs
* variable-declaration/non-null-in-optional-chain/input.ts
-# babel-plugin-transform-react-jsx (93/170)
+# babel-plugin-transform-react-jsx (94/170)
* autoImport/after-polyfills-compiled-to-cjs/input.mjs
* autoImport/after-polyfills-script-not-supported/input.js
* autoImport/auto-import-react-source-type-module/input.js
* autoImport/auto-import-react-source-type-script/input.js
* autoImport/complicated-scope-module/input.js
* autoImport/complicated-scope-script/input.js
-* autoImport/import-source/input.js
* autoImport/import-source-pragma/input.js
* pure/false-default-pragma-automatic-runtime/input.js
* pure/false-pragma-comment-automatic-runtime/input.js