mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(transformer): implement key extraction for react automatic (#1077)
This commit is contained in:
parent
394ed358f6
commit
5fb27fbe8a
3 changed files with 52 additions and 42 deletions
|
|
@ -157,6 +157,12 @@ pub struct JSXAttribute<'a> {
|
|||
pub value: Option<JSXAttributeValue<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> JSXAttribute<'a> {
|
||||
pub fn is_key(&self) -> bool {
|
||||
matches!(&self.name, JSXAttributeName::Identifier(ident) if ident.name == "key")
|
||||
}
|
||||
}
|
||||
|
||||
/// JSX Spread Attribute
|
||||
#[derive(Debug, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
|
||||
|
|
|
|||
|
|
@ -53,14 +53,8 @@ impl<'a, 'b> JSXElementOrFragment<'a, 'b> {
|
|||
for attr in &e.opening_element.attributes {
|
||||
if matches!(attr, JSXAttributeItem::SpreadAttribute(_)) {
|
||||
spread = true;
|
||||
} else if spread {
|
||||
if let JSXAttributeItem::Attribute(a) = attr {
|
||||
if let JSXAttributeName::Identifier(ident) = &a.name {
|
||||
if ident.name == "key" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if spread && matches!(attr, JSXAttributeItem::Attribute(a) if a.is_key()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
|
@ -165,6 +159,8 @@ impl<'a> ReactJsx<'a> {
|
|||
}
|
||||
|
||||
fn transform_jsx<'b>(&mut self, e: &JSXElementOrFragment<'a, 'b>) -> Option<Expression<'a>> {
|
||||
let is_classic = self.options.runtime.is_classic();
|
||||
let is_automatic = self.options.runtime.is_automatic();
|
||||
let has_key_after_props_spread = e.has_key_after_props_spread();
|
||||
let callee = self.get_create_element(has_key_after_props_spread);
|
||||
let children = e.children();
|
||||
|
|
@ -179,6 +175,7 @@ impl<'a> ReactJsx<'a> {
|
|||
JSXElementOrFragment::Fragment(_) => self.get_fragment(),
|
||||
}));
|
||||
|
||||
let mut key = None;
|
||||
// TODO: compute the correct capacity for both runtimes
|
||||
let mut properties = self.ast.new_vec_with_capacity(0);
|
||||
if let Some(attributes) = e.attributes() {
|
||||
|
|
@ -186,34 +183,12 @@ impl<'a> ReactJsx<'a> {
|
|||
let kind = PropertyKind::Init;
|
||||
match attribute {
|
||||
JSXAttributeItem::Attribute(attr) => {
|
||||
if is_automatic && attr.is_key() && !has_key_after_props_spread {
|
||||
key = attr.value.as_ref();
|
||||
continue;
|
||||
}
|
||||
let key = self.get_attribute_name(&attr.name);
|
||||
let value = match &attr.value {
|
||||
Some(value) => {
|
||||
match value {
|
||||
JSXAttributeValue::StringLiteral(s) => {
|
||||
self.ast.literal_string_expression(s.clone())
|
||||
}
|
||||
JSXAttributeValue::Element(_)
|
||||
| JSXAttributeValue::Fragment(_) => {
|
||||
/* TODO */
|
||||
continue;
|
||||
}
|
||||
JSXAttributeValue::ExpressionContainer(c) => {
|
||||
match &c.expression {
|
||||
JSXExpression::Expression(e) => self.ast.copy(e),
|
||||
JSXExpression::EmptyExpression(_e) =>
|
||||
/* TODO */
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.ast.literal_boolean_expression(BooleanLiteral::new(SPAN, true))
|
||||
}
|
||||
};
|
||||
let value = self.transform_jsx_attribute_value(attr.value.as_ref())?;
|
||||
let object_property = self
|
||||
.ast
|
||||
.object_property(SPAN, kind, key, value, None, false, false, false);
|
||||
|
|
@ -236,12 +211,12 @@ impl<'a> ReactJsx<'a> {
|
|||
},
|
||||
}
|
||||
}
|
||||
} else if self.options.runtime.is_classic() {
|
||||
} else if is_classic {
|
||||
let null_expr = self.ast.literal_null_expression(NullLiteral::new(SPAN));
|
||||
arguments.push(Argument::Expression(null_expr));
|
||||
}
|
||||
|
||||
if self.options.runtime.is_automatic() && !children.is_empty() {
|
||||
if is_automatic && !children.is_empty() {
|
||||
let key =
|
||||
self.ast.property_key_identifier(IdentifierName::new(SPAN, "children".into()));
|
||||
let value = if children.len() == 1 {
|
||||
|
|
@ -268,12 +243,16 @@ impl<'a> ReactJsx<'a> {
|
|||
properties.push(ObjectPropertyKind::ObjectProperty(object_property));
|
||||
}
|
||||
|
||||
if !properties.is_empty() || self.options.runtime.is_automatic() {
|
||||
if !properties.is_empty() || is_automatic {
|
||||
let object_expression = self.ast.object_expression(SPAN, properties, None);
|
||||
arguments.push(Argument::Expression(object_expression));
|
||||
}
|
||||
|
||||
if self.options.runtime.is_classic() && !children.is_empty() {
|
||||
if is_automatic && key.is_some() {
|
||||
arguments.push(Argument::Expression(self.transform_jsx_attribute_value(key)?));
|
||||
}
|
||||
|
||||
if is_classic && !children.is_empty() {
|
||||
arguments.extend(
|
||||
children
|
||||
.iter()
|
||||
|
|
@ -373,6 +352,32 @@ impl<'a> ReactJsx<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn transform_jsx_attribute_value(
|
||||
&self,
|
||||
value: Option<&JSXAttributeValue<'a>>,
|
||||
) -> Option<Expression<'a>> {
|
||||
match value {
|
||||
Some(JSXAttributeValue::StringLiteral(s)) => {
|
||||
Some(self.ast.literal_string_expression(s.clone()))
|
||||
}
|
||||
Some(JSXAttributeValue::Element(_) | JSXAttributeValue::Fragment(_)) => {
|
||||
/* TODO */
|
||||
None
|
||||
}
|
||||
Some(JSXAttributeValue::ExpressionContainer(c)) => {
|
||||
match &c.expression {
|
||||
JSXExpression::Expression(e) => Some(self.ast.copy(e)),
|
||||
JSXExpression::EmptyExpression(_e) =>
|
||||
/* TODO */
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Some(self.ast.literal_boolean_expression(BooleanLiteral::new(SPAN, true))),
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_jsx_member_expression(&self, expr: &JSXMemberExpression<'a>) -> Expression<'a> {
|
||||
let object = match &expr.object {
|
||||
JSXMemberExpressionObject::Identifier(ident) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Passed: 214/1083
|
||||
Passed: 215/1083
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-numeric-separator
|
||||
|
|
@ -804,7 +804,7 @@ Passed: 214/1083
|
|||
* regression/11061/input.mjs
|
||||
* variable-declaration/non-null-in-optional-chain/input.ts
|
||||
|
||||
# babel-plugin-transform-react-jsx (65/172)
|
||||
# babel-plugin-transform-react-jsx (66/172)
|
||||
* autoImport/after-polyfills/input.mjs
|
||||
* autoImport/after-polyfills-2/input.mjs
|
||||
* autoImport/after-polyfills-compiled-to-cjs/input.mjs
|
||||
|
|
@ -895,7 +895,6 @@ Passed: 214/1083
|
|||
* react-automatic/should-properly-handle-keys/input.js
|
||||
* react-automatic/should-support-xml-namespaces-if-flag/input.js
|
||||
* react-automatic/should-throw-when-filter-is-specified/input.js
|
||||
* react-automatic/should-use-jsx-when-key-comes-before-spread/input.js
|
||||
* react-automatic/should-warn-when-pragma-or-pragmaFrag-is-set/input.js
|
||||
* react-automatic/weird-symbols/input.js
|
||||
* regression/issue-12478-automatic/input.js
|
||||
|
|
|
|||
Loading…
Reference in a new issue