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
5 changes: 5 additions & 0 deletions crates/oxc_transformer/src/react/jsx/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()])
Expand Down
59 changes: 35 additions & 24 deletions crates/oxc_transformer/src/react/jsx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,32 +130,45 @@ impl<'a> Pragma<'a> {
// Transforms
impl<'a> ReactJsx<'a> {
pub fn new(options: &Rc<ReactOptions>, 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)
}
};

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 {
Expand Down
19 changes: 6 additions & 13 deletions crates/oxc_transformer/src/react/options.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::borrow::Cow;

use serde::Deserialize;

use crate::Ctx;
Expand All @@ -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.
Expand Down Expand Up @@ -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<String>,

// React Classic Runtime
//
/// Replace the function used when compiling JSX expressions.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

Expand Down