diff --git a/crates/oxc_transformer/src/react/jsx/diagnostics.rs b/crates/oxc_transformer/src/react/jsx/diagnostics.rs index 2ec7dcd2cc08c..30f71e08a140a 100644 --- a/crates/oxc_transformer/src/react/jsx/diagnostics.rs +++ b/crates/oxc_transformer/src/react/jsx/diagnostics.rs @@ -16,6 +16,11 @@ pub fn import_source_cannot_be_set() -> OxcDiagnostic { .with_help("Remove `importSource` option.") } +pub fn invalid_import_source() -> OxcDiagnostic { + OxcDiagnostic::warn("import_source cannot be an empty string") + .with_help("Fix `importSource` option.") +} + pub fn namespace_does_not_support(span0: Span) -> OxcDiagnostic { OxcDiagnostic::warn("Namespace tags are not supported by default. React's JSX doesn't support namespace tags. You can set `throwIfNamespace: false` to bypass this warning.") .with_labels([span0.into()]) diff --git a/crates/oxc_transformer/src/react/jsx/mod.rs b/crates/oxc_transformer/src/react/jsx/mod.rs index 48b08a42803b1..061d32f7edf5f 100644 --- a/crates/oxc_transformer/src/react/jsx/mod.rs +++ b/crates/oxc_transformer/src/react/jsx/mod.rs @@ -130,32 +130,45 @@ impl<'a> Pragma<'a> { // Transforms impl<'a> ReactJsx<'a> { pub fn new(options: &Rc, ctx: &Ctx<'a>) -> Self { - let default_runtime = options.runtime; - let jsx_runtime_importer = - if options.import_source == "react" || default_runtime.is_classic() { - let source = - if options.development { "react/jsx-dev-runtime" } else { "react/jsx-runtime" }; - Atom::from(source) - } else { - ctx.ast.new_atom(&format!( - "{}/jsx-{}runtime", - options.import_source, - if options.development { "dev-" } else { "" } - )) - }; - - // Parse pragmas - let (pragma, pragma_frag) = match options.runtime { + // Parse pragmas + import source + let (pragma, pragma_frag, jsx_runtime_importer) = match options.runtime { ReactJsxRuntime::Classic => { + if options.import_source.is_some() { + ctx.error(diagnostics::import_source_cannot_be_set()); + } let pragma = Pragma::parse(options.pragma.as_ref(), "createElement", ctx); let pragma_frag = Pragma::parse(options.pragma_frag.as_ref(), "Fragment", ctx); - (Some(pragma), Some(pragma_frag)) + + (Some(pragma), Some(pragma_frag), Atom::empty()) } ReactJsxRuntime::Automatic => { if options.pragma.is_some() || options.pragma_frag.is_some() { ctx.error(diagnostics::pragma_and_pragma_frag_cannot_be_set()); } - (None, None) + + let jsx_runtime_importer = match options.import_source.as_ref() { + Some(import_source) => { + let mut import_source = import_source.as_str(); + if import_source.is_empty() { + ctx.error(diagnostics::invalid_import_source()); + import_source = "react"; + } + ctx.ast.new_atom(&format!( + "{}/jsx-{}runtime", + import_source, + if options.development { "dev-" } else { "" } + )) + } + None => { + if options.development { + Atom::from("react/jsx-dev-runtime") + } else { + Atom::from("react/jsx-runtime") + } + } + }; + + (None, None, jsx_runtime_importer) } }; @@ -208,14 +221,9 @@ impl<'a> ReactJsx<'a> { impl<'a> ReactJsx<'a> { pub fn add_runtime_imports(&mut self, program: &mut Program<'a>) { if self.options.runtime.is_classic() { - if self.options.import_source != "react" { - self.ctx.error(diagnostics::import_source_cannot_be_set()); - } - if self.can_add_filename_statement { program.body.insert(0, self.jsx_source.get_var_file_name_statement()); } - return; } @@ -316,7 +324,10 @@ impl<'a> ReactJsx<'a> { 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 source = match self.options.import_source.as_ref() { + Some(source) => ctx.ast.new_atom(source), + None => Atom::from("react"), + }; let id = if self.is_script() { self.add_require_statement("react", source, true, ctx) } else { diff --git a/crates/oxc_transformer/src/react/options.rs b/crates/oxc_transformer/src/react/options.rs index 66f2c46b4f359..92293914c491e 100644 --- a/crates/oxc_transformer/src/react/options.rs +++ b/crates/oxc_transformer/src/react/options.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use serde::Deserialize; use crate::Ctx; @@ -9,11 +7,6 @@ fn default_as_true() -> bool { true } -#[inline] -fn default_for_import_source() -> Cow<'static, str> { - Cow::Borrowed("react") -} - /// Decides which runtime to use. /// /// Auto imports the functions that JSX transpiles to. @@ -75,15 +68,15 @@ pub struct ReactOptions { /// Defaults to `true`. #[serde(default = "default_as_true")] pub pure: bool, - // + // React Automatic Runtime // /// Replaces the import source when importing functions. /// /// Defaults to `react`. - #[serde(default = "default_for_import_source")] - pub import_source: Cow<'static, str>, - // + #[serde(default)] + pub import_source: Option, + // React Classic Runtime // /// Replace the function used when compiling JSX expressions. @@ -124,7 +117,7 @@ impl Default for ReactOptions { development: false, throw_if_namespace: default_as_true(), pure: default_as_true(), - import_source: default_for_import_source(), + import_source: None, pragma: None, pragma_frag: None, use_built_ins: None, @@ -179,7 +172,7 @@ impl ReactOptions { // read jsxImportSource if let Some(import_source) = comment.strip_prefix("jsxImportSource").map(str::trim) { - self.import_source = Cow::Owned(import_source.to_string()); + self.import_source = Some(import_source.to_string()); continue; }