Skip to content

Commit

Permalink
feat(transformer): add diagnostics to react transform (#2974)
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen authored Apr 14, 2024
1 parent ef602af commit c211f1e
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 25 deletions.
30 changes: 30 additions & 0 deletions crates/oxc_transformer/src/react/jsx/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_span::Span;

#[derive(Debug, Error, Diagnostic)]
#[error("pragma and pragmaFrag cannot be set when runtime is automatic.")]
#[diagnostic(severity(warning), help("Remove `pragma` and `pragmaFrag` options."))]
pub struct PragmaAndPragmaFragCannotBeSet;

#[derive(Debug, Error, Diagnostic)]
#[error("importSource cannot be set when runtime is classic.")]
#[diagnostic(severity(warning), help("Remove `importSource` option."))]
pub struct ImportSourceCannotBeSet;

#[derive(Debug, Error, Diagnostic)]
#[error("Namespace tags are not supported by default. React's JSX doesn't support namespace tags. You can set `throwIfNamespace: false` to bypass this warning.")]
#[diagnostic(severity(warning))]
pub struct NamespaceDoesNotSupport(#[label] pub Span);

#[derive(Debug, Error, Diagnostic)]
#[error("Please provide an explicit key value. Using \"key\" as a shorthand for \"key={{true}}\" is not allowed.")]
#[diagnostic(severity(warning))]
pub struct ValuelessKey(#[label] pub Span);

#[derive(Debug, Error, Diagnostic)]
#[error("Spread children are not supported in React.")]
#[diagnostic(severity(warning))]
pub struct SpreadChildrenAreNotSupported(#[label] pub Span);
29 changes: 18 additions & 11 deletions crates/oxc_transformer/src/react/jsx/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
mod diagnostics;

use std::rc::Rc;

use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder};
use oxc_span::{CompactStr, SPAN};
use oxc_span::{CompactStr, GetSpan, SPAN};
use oxc_syntax::{
identifier::{is_irregular_whitespace, is_line_terminator},
xml_entities::XML_ENTITIES,
Expand All @@ -16,6 +18,11 @@ pub use super::{
options::{ReactJsxRuntime, ReactOptions},
};

use self::diagnostics::{
ImportSourceCannotBeSet, NamespaceDoesNotSupport, PragmaAndPragmaFragCannotBeSet,
SpreadChildrenAreNotSupported, ValuelessKey,
};

/// [plugin-transform-react-jsx](https://babeljs.io/docs/babel-plugin-transform-react-jsx)
///
/// This plugin generates production-ready JS code.
Expand Down Expand Up @@ -99,15 +106,15 @@ 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(ImportSourceCannotBeSet);
self.ctx.error(ImportSourceCannotBeSet);
}
return;
}

if self.options.pragma != "React.createElement"
|| self.options.pragma_frag != "React.Fragment"
{
// self.ctx.error(PragmaAndPragmaFragCannotBeSet);
self.ctx.error(PragmaAndPragmaFragCannotBeSet);
return;
}

Expand Down Expand Up @@ -332,9 +339,9 @@ impl<'a> ReactJsx<'a> {
}
}
JSXAttributeItem::Attribute(attr) if attr.is_key() => {
// if attr.value.is_none() {
// self.ctx.error(ValuelessKey(attr.name.span()));
// }
if attr.value.is_none() {
self.ctx.error(ValuelessKey(attr.name.span()));
}
// In automatic mode, extract the key before spread prop,
// and add it to the third argument later.
if is_automatic && !has_key_after_props_spread {
Expand Down Expand Up @@ -431,9 +438,9 @@ impl<'a> ReactJsx<'a> {
self.transform_jsx_member_expression(member_expr)
}
JSXElementName::NamespacedName(name) => {
// if self.options.throw_if_namespace {
// self.ctx.error(NamespaceDoesNotSupport(name.span));
// }
if self.options.throw_if_namespace {
self.ctx.error(NamespaceDoesNotSupport(name.span));
}
let name = self.ast().new_atom(&name.to_string());
let string_literal = StringLiteral::new(SPAN, name);
self.ast().literal_string_expression(string_literal)
Expand Down Expand Up @@ -610,8 +617,8 @@ impl<'a> ReactJsx<'a> {
},
JSXChild::Element(e) => Some(self.transform_jsx(&JSXElementOrFragment::Element(e))),
JSXChild::Fragment(e) => Some(self.transform_jsx(&JSXElementOrFragment::Fragment(e))),
JSXChild::Spread(_e) => {
// self.ctx.error(SpreadChildrenAreNotSupported(e.span));
JSXChild::Spread(e) => {
self.ctx.error(SpreadChildrenAreNotSupported(e.span));
None
}
}
Expand Down
7 changes: 7 additions & 0 deletions crates/oxc_transformer/src/react/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ pub struct ReactOptions {
#[serde(default = "default_as_true")]
pub development: bool,

/// Toggles whether or not to throw an error if a XML namespaced tag name is used.
///
/// Though the JSX spec allows this, it is disabled by default since React's JSX does not currently have support for it.
#[serde(default = "default_as_true")]
pub throw_if_namespace: bool,

/// Enables `@babel/plugin-transform-react-pure-annotations`.
///
/// It will mark top-level React method calls as pure for tree shaking.
Expand Down Expand Up @@ -100,6 +106,7 @@ impl Default for ReactOptions {
jsx_source_plugin: true,
runtime: ReactJsxRuntime::default(),
development: default_as_true(),
throw_if_namespace: default_as_true(),
pure: default_as_true(),
import_source: default_for_import_source(),
pragma: default_for_pragma(),
Expand Down
16 changes: 2 additions & 14 deletions tasks/transform_conformance/babel.snap.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Passed: 191/415
Passed: 203/415

# All Passed:
* babel-plugin-transform-react-jsx-source
Expand Down Expand Up @@ -141,20 +141,17 @@ Passed: 191/415
* regression/another-preset-with-custom-jsx-keep-source-self/input.mjs
* regression/runtime-classic-allow-multiple-source-self/input.mjs

# babel-plugin-transform-react-jsx (100/163)
# babel-plugin-transform-react-jsx (112/163)
* autoImport/auto-import-react-source-type-module/input.js
* autoImport/complicated-scope-module/input.js
* autoImport/import-source-pragma/input.js
* autoImport/react-defined/input.js
* pure/false-pragma-comment-automatic-runtime/input.js
* pure/false-pragma-comment-classic-runtime/input.js
* pure/false-pragma-option-automatic-runtime/input.js
* pure/true-pragma-comment-automatic-runtime/input.js
* pure/true-pragma-comment-classic-runtime/input.js
* pure/true-pragma-option-automatic-runtime/input.js
* pure/unset-pragma-comment-automatic-runtime/input.js
* pure/unset-pragma-comment-classic-runtime/input.js
* pure/unset-pragma-option-automatic-runtime/input.js
* react/adds-appropriate-newlines-when-using-spread-attribute-babel-7/input.js
* react/arrow-functions/input.js
* react/assignment-babel-7/input.js
Expand All @@ -169,10 +166,6 @@ Passed: 191/415
* react/should-allow-jsx-docs-comment-with-pragma/input.js
* react/should-allow-no-pragmafrag-if-frag-unused/input.js
* react/should-allow-pragmafrag-and-frag/input.js
* react/should-disallow-spread-children/input.js
* react/should-disallow-valueless-key/input.js
* react/should-disallow-xml-namespacing/input.js
* react/should-throw-error-namespaces-if-not-flag/input.js
* react/should-warn-when-importSource-is-set/input.js
* react/should-warn-when-importSource-pragma-is-set/input.js
* react/wraps-props-in-react-spread-for-first-spread-attributes-babel-7/input.js
Expand All @@ -188,17 +181,12 @@ Passed: 191/415
* react-automatic/pragma-works-with-no-space-at-the-end/input.js
* react-automatic/should-add-quotes-es3/input.js
* react-automatic/should-allow-nested-fragments/input.js
* react-automatic/should-disallow-spread-children/input.js
* react-automatic/should-disallow-valueless-key/input.js
* react-automatic/should-disallow-xml-namespacing/input.js
* react-automatic/should-escape-xhtml-jsxtext/input.js
* react-automatic/should-escape-xhtml-jsxtext-babel-7/input.js
* react-automatic/should-handle-attributed-elements/input.js
* react-automatic/should-have-correct-comma-in-nested-children/input.js
* react-automatic/should-properly-handle-keys/input.js
* react-automatic/should-throw-error-namespaces-if-not-flag/input.js
* react-automatic/should-throw-when-filter-is-specified/input.js
* react-automatic/should-warn-when-pragma-or-pragmaFrag-is-set/input.js
* regression/issue-12478-automatic/input.js
* regression/issue-12478-classic/input.js
* regression/pragma-frag-set-default-classic-runtime/input.js
Expand Down

0 comments on commit c211f1e

Please sign in to comment.