feat(transformer): add diagnostics to react transform (#2974)

This commit is contained in:
Boshen 2024-04-14 21:42:32 +08:00 committed by GitHub
parent ef602af4cc
commit c211f1e57f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 57 additions and 25 deletions

View file

@ -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);

View file

@ -1,8 +1,10 @@
mod diagnostics;
use std::rc::Rc; use std::rc::Rc;
use oxc_allocator::Vec; use oxc_allocator::Vec;
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::{ast::*, AstBuilder};
use oxc_span::{CompactStr, SPAN}; use oxc_span::{CompactStr, GetSpan, SPAN};
use oxc_syntax::{ use oxc_syntax::{
identifier::{is_irregular_whitespace, is_line_terminator}, identifier::{is_irregular_whitespace, is_line_terminator},
xml_entities::XML_ENTITIES, xml_entities::XML_ENTITIES,
@ -16,6 +18,11 @@ pub use super::{
options::{ReactJsxRuntime, ReactOptions}, options::{ReactJsxRuntime, ReactOptions},
}; };
use self::diagnostics::{
ImportSourceCannotBeSet, NamespaceDoesNotSupport, PragmaAndPragmaFragCannotBeSet,
SpreadChildrenAreNotSupported, ValuelessKey,
};
/// [plugin-transform-react-jsx](https://babeljs.io/docs/babel-plugin-transform-react-jsx) /// [plugin-transform-react-jsx](https://babeljs.io/docs/babel-plugin-transform-react-jsx)
/// ///
/// This plugin generates production-ready JS code. /// This plugin generates production-ready JS code.
@ -99,7 +106,7 @@ impl<'a> ReactJsx<'a> {
pub fn add_runtime_imports(&mut self, program: &mut Program<'a>) { pub fn add_runtime_imports(&mut self, program: &mut Program<'a>) {
if self.options.runtime.is_classic() { if self.options.runtime.is_classic() {
if self.options.import_source != "react" { if self.options.import_source != "react" {
// self.ctx.error(ImportSourceCannotBeSet); self.ctx.error(ImportSourceCannotBeSet);
} }
return; return;
} }
@ -107,7 +114,7 @@ impl<'a> ReactJsx<'a> {
if self.options.pragma != "React.createElement" if self.options.pragma != "React.createElement"
|| self.options.pragma_frag != "React.Fragment" || self.options.pragma_frag != "React.Fragment"
{ {
// self.ctx.error(PragmaAndPragmaFragCannotBeSet); self.ctx.error(PragmaAndPragmaFragCannotBeSet);
return; return;
} }
@ -332,9 +339,9 @@ impl<'a> ReactJsx<'a> {
} }
} }
JSXAttributeItem::Attribute(attr) if attr.is_key() => { JSXAttributeItem::Attribute(attr) if attr.is_key() => {
// if attr.value.is_none() { if attr.value.is_none() {
// self.ctx.error(ValuelessKey(attr.name.span())); self.ctx.error(ValuelessKey(attr.name.span()));
// } }
// In automatic mode, extract the key before spread prop, // In automatic mode, extract the key before spread prop,
// and add it to the third argument later. // and add it to the third argument later.
if is_automatic && !has_key_after_props_spread { if is_automatic && !has_key_after_props_spread {
@ -431,9 +438,9 @@ impl<'a> ReactJsx<'a> {
self.transform_jsx_member_expression(member_expr) self.transform_jsx_member_expression(member_expr)
} }
JSXElementName::NamespacedName(name) => { JSXElementName::NamespacedName(name) => {
// if self.options.throw_if_namespace { if self.options.throw_if_namespace {
// self.ctx.error(NamespaceDoesNotSupport(name.span)); self.ctx.error(NamespaceDoesNotSupport(name.span));
// } }
let name = self.ast().new_atom(&name.to_string()); let name = self.ast().new_atom(&name.to_string());
let string_literal = StringLiteral::new(SPAN, name); let string_literal = StringLiteral::new(SPAN, name);
self.ast().literal_string_expression(string_literal) self.ast().literal_string_expression(string_literal)
@ -610,8 +617,8 @@ impl<'a> ReactJsx<'a> {
}, },
JSXChild::Element(e) => Some(self.transform_jsx(&JSXElementOrFragment::Element(e))), JSXChild::Element(e) => Some(self.transform_jsx(&JSXElementOrFragment::Element(e))),
JSXChild::Fragment(e) => Some(self.transform_jsx(&JSXElementOrFragment::Fragment(e))), JSXChild::Fragment(e) => Some(self.transform_jsx(&JSXElementOrFragment::Fragment(e))),
JSXChild::Spread(_e) => { JSXChild::Spread(e) => {
// self.ctx.error(SpreadChildrenAreNotSupported(e.span)); self.ctx.error(SpreadChildrenAreNotSupported(e.span));
None None
} }
} }

View file

@ -46,6 +46,12 @@ pub struct ReactOptions {
#[serde(default = "default_as_true")] #[serde(default = "default_as_true")]
pub development: bool, 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`. /// Enables `@babel/plugin-transform-react-pure-annotations`.
/// ///
/// It will mark top-level React method calls as pure for tree shaking. /// It will mark top-level React method calls as pure for tree shaking.
@ -100,6 +106,7 @@ impl Default for ReactOptions {
jsx_source_plugin: true, jsx_source_plugin: true,
runtime: ReactJsxRuntime::default(), runtime: ReactJsxRuntime::default(),
development: default_as_true(), development: default_as_true(),
throw_if_namespace: default_as_true(),
pure: default_as_true(), pure: default_as_true(),
import_source: default_for_import_source(), import_source: default_for_import_source(),
pragma: default_for_pragma(), pragma: default_for_pragma(),

View file

@ -1,4 +1,4 @@
Passed: 191/415 Passed: 203/415
# All Passed: # All Passed:
* babel-plugin-transform-react-jsx-source * babel-plugin-transform-react-jsx-source
@ -141,20 +141,17 @@ Passed: 191/415
* regression/another-preset-with-custom-jsx-keep-source-self/input.mjs * regression/another-preset-with-custom-jsx-keep-source-self/input.mjs
* regression/runtime-classic-allow-multiple-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/auto-import-react-source-type-module/input.js
* autoImport/complicated-scope-module/input.js * autoImport/complicated-scope-module/input.js
* autoImport/import-source-pragma/input.js * autoImport/import-source-pragma/input.js
* autoImport/react-defined/input.js * autoImport/react-defined/input.js
* pure/false-pragma-comment-automatic-runtime/input.js * pure/false-pragma-comment-automatic-runtime/input.js
* pure/false-pragma-comment-classic-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-automatic-runtime/input.js
* pure/true-pragma-comment-classic-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-automatic-runtime/input.js
* pure/unset-pragma-comment-classic-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/adds-appropriate-newlines-when-using-spread-attribute-babel-7/input.js
* react/arrow-functions/input.js * react/arrow-functions/input.js
* react/assignment-babel-7/input.js * react/assignment-babel-7/input.js
@ -169,10 +166,6 @@ Passed: 191/415
* react/should-allow-jsx-docs-comment-with-pragma/input.js * react/should-allow-jsx-docs-comment-with-pragma/input.js
* react/should-allow-no-pragmafrag-if-frag-unused/input.js * react/should-allow-no-pragmafrag-if-frag-unused/input.js
* react/should-allow-pragmafrag-and-frag/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-is-set/input.js
* react/should-warn-when-importSource-pragma-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 * react/wraps-props-in-react-spread-for-first-spread-attributes-babel-7/input.js
@ -188,17 +181,12 @@ Passed: 191/415
* react-automatic/pragma-works-with-no-space-at-the-end/input.js * react-automatic/pragma-works-with-no-space-at-the-end/input.js
* react-automatic/should-add-quotes-es3/input.js * react-automatic/should-add-quotes-es3/input.js
* react-automatic/should-allow-nested-fragments/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/input.js
* react-automatic/should-escape-xhtml-jsxtext-babel-7/input.js * react-automatic/should-escape-xhtml-jsxtext-babel-7/input.js
* react-automatic/should-handle-attributed-elements/input.js * react-automatic/should-handle-attributed-elements/input.js
* react-automatic/should-have-correct-comma-in-nested-children/input.js * react-automatic/should-have-correct-comma-in-nested-children/input.js
* react-automatic/should-properly-handle-keys/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-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-automatic/input.js
* regression/issue-12478-classic/input.js * regression/issue-12478-classic/input.js
* regression/pragma-frag-set-default-classic-runtime/input.js * regression/pragma-frag-set-default-classic-runtime/input.js