fix(rome_js_analyze): improve the logic of jsx_reference_identifier_is_fragment
MODIFY
crates/rome_js_analyze/src/react.rs
crates/rome_js_analyze/src/react.rs
@@ -4,8 +4,10 @@ pub mod hooks;
use rome_js_semantic::SemanticModel;
use rome_js_syntax::{
- JsAnyCallArgument, JsAnyExpression, JsCallExpression, JsIdentifierBinding, JsImport,
- JsObjectExpression, JsPropertyObjectMember, JsxMemberName, JsxReferenceIdentifier,
+ JsAnyCallArgument, JsAnyExpression, JsAnyNamedImportSpecifier, JsCallExpression,
+ JsIdentifierBinding, JsImport, JsImportNamedClause, JsNamedImportSpecifierList,
+ JsNamedImportSpecifiers, JsObjectExpression, JsPropertyObjectMember, JsxMemberName,
+ JsxReferenceIdentifier,
};
use rome_rowan::{AstNode, AstSeparatedList};
@@ -331,21 +333,38 @@ pub(crate) fn jsx_reference_identifier_is_fragment(
name: &JsxReferenceIdentifier,
model: &SemanticModel,
) -> Option<bool> {
- let value_token = name.value_token().ok()?;
- let mut maybe_react_fragment = value_token.text_trimmed() == "Fragment";
- if let Some(reference) = model.declaration(name) {
- if let Some(js_import) = reference
- .syntax()
- .ancestors()
- .find_map(|ancestor| JsImport::cast_ref(&ancestor))
- {
- let source_is_react = js_import.source_is("react").ok()?;
- maybe_react_fragment = source_is_react;
- } else {
- // `Fragment` is a binding g but it doesn't come from the "react" package
- maybe_react_fragment = false;
+ match model.declaration(name) {
+ Some(reference) => {
+ let ident = JsIdentifierBinding::cast_ref(reference.syntax())?;
+
+ let import_specifier = ident.parent::<JsAnyNamedImportSpecifier>()?;
+ let name_token = match &import_specifier {
+ JsAnyNamedImportSpecifier::JsNamedImportSpecifier(named_import) => {
+ named_import.name().ok()?.value().ok()?
}
+ JsAnyNamedImportSpecifier::JsShorthandNamedImportSpecifier(_) => {
+ ident.name_token().ok()?
}
+ JsAnyNamedImportSpecifier::JsUnknownNamedImportSpecifier(_) => {
+ return None;
+ }
+ };
- Some(maybe_react_fragment)
+ if name_token.text_trimmed() != "Fragment" {
+ return Some(false);
+ }
+
+ let import_specifier_list = import_specifier.parent::<JsNamedImportSpecifierList>()?;
+ let import_specifiers = import_specifier_list.parent::<JsNamedImportSpecifiers>()?;
+ let import_clause = import_specifiers.parent::<JsImportNamedClause>()?;
+ let import = import_clause.parent::<JsImport>()?;
+ import.source_is("react").ok()
+ }
+
+ None => {
+ let value_token = name.value_token().ok()?;
+ let is_fragment = value_token.text_trimmed() == "Fragment";
+ Some(is_fragment)
+ }
+ }
}

MODIFY
crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/fromImportRenameValid.jsx
crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/fromImportRenameValid.jsx
-import AwesomeReact, { Fragment as AwesomeFragment } from "noReact";
+import AwesomeNoReact, { Fragment as AwesomeFragment } from "noReact";
+import AwesomeReact, { StrictMode as AwesomeStrictMode } from "react";
<>
<AwesomeFragment></AwesomeFragment>
- <AwesomeReact.Fragment>foo</AwesomeReact.Fragment>
+ <AwesomeNoReact.Fragment>foo</AwesomeNoReact.Fragment>
+ <AwesomeStrictMode></AwesomeStrictMode>
+ <AwesomeReact.StrictMode>foo</AwesomeReact.StrictMode>
</>

MODIFY
crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/fromImportRenameValid.jsx.snap
crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/fromImportRenameValid.jsx.snap
---
source: crates/rome_js_analyze/tests/spec_tests.rs
+assertion_line: 99
expression: fromImportRenameValid.jsx
---
# Input
```js
-import AwesomeReact, { Fragment as AwesomeFragment } from "noReact";
+import AwesomeNoReact, { Fragment as AwesomeFragment } from "noReact";
+import AwesomeReact, { StrictMode as AwesomeStrictMode } from "react";
<>
<AwesomeFragment></AwesomeFragment>
- <AwesomeReact.Fragment>foo</AwesomeReact.Fragment>
+ <AwesomeNoReact.Fragment>foo</AwesomeNoReact.Fragment>
+ <AwesomeStrictMode></AwesomeStrictMode>
+ <AwesomeReact.StrictMode>foo</AwesomeReact.StrictMode>
</>
```

ADD
crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/notFragmentValid.jsx
+import React, { StrictMode } from "react";
+
+<>
+ <StrictMode></StrictMode>
+ <React.StrictMode></React.StrictMode>
+</>

ADD
crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/notFragmentValid.jsx.snap
+---
+source: crates/rome_js_analyze/tests/spec_tests.rs
+assertion_line: 99
+expression: notFragmentValid.jsx
+---
+# Input
+```js
+import React, { StrictMode } from "react";
+
+<>
+ <StrictMode></StrictMode>
+ <React.StrictMode></React.StrictMode>
+</>
+
+```
+
+

MODIFY
crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/userDefinedValid.jsx
crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/userDefinedValid.jsx
@@ -2,5 +2,5 @@ function Fragment() {}
let React = { Fragment };
<>
<Fragment>test</Fragment>
- <React>test</React>
+ <React.Fragment>test</React.Fragment>
</>

MODIFY
crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/userDefinedValid.jsx.snap
crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/userDefinedValid.jsx.snap
---
source: crates/rome_js_analyze/tests/spec_tests.rs
+assertion_line: 99
expression: userDefinedValid.jsx
---
# Input
@@ -8,7 +9,7 @@ function Fragment() {}
let React = { Fragment };
<>
<Fragment>test</Fragment>
- <React>test</React>
+ <React.Fragment>test</React.Fragment>
</>
```

fix(rome_js_analyze): improve the logic of jsx_reference_identifier_is_fragment (#3592)