From 55f93e44126a587595b9c7510cd0775e434875ac Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Wed, 9 Nov 2022 13:56:37 +0100 Subject: [PATCH] fix(rome_js_analyze): Assert `React` symbols resolve to `react` module ## Summary The `is_react_call_api` in some situations didn't test if the method name is the tested for method name. For example, `is_react_call_api(..., "cloneElement")` returned true for `React.clone` or just `clone`. I fixed the same issue in `jsx_member_name_is_react_fragment` ## Test Plan I added some new tests veryfing that the logic correctly verifies the member name. --- crates/rome_js_analyze/src/react.rs | 119 ++++--- .../correctness/no_array_index_key.rs | 32 +- .../reactCreateElementInvalid.js | 2 + .../reactCreateElementInvalid.js.snap | 44 +-- .../specs/a11y/useButtonType/inObject.js | 2 + .../specs/a11y/useButtonType/inObject.js.snap | 52 ++-- .../specs/correctness/noArrayIndexKey.jsx | 4 +- .../correctness/noArrayIndexKey.jsx.snap | 290 +++++++----------- .../noChildrenProp/noChildrenPropInvalid.jsx | 3 + .../noChildrenPropInvalid.jsx.snap | 35 ++- .../noChildrenProp/noChildrenPropValid.jsx | 6 + .../noChildrenPropValid.jsx.snap | 6 + .../noUselessFragments/noChildren.jsx | 3 +- .../noUselessFragments/noChildren.jsx.snap | 41 +-- .../noUselessFragments/withChildren.jsx | 2 + .../noUselessFragments/withChildren.jsx.snap | 42 +-- .../createElement.js | 4 +- .../createElement.js.snap | 30 +- .../reactCreateElement.js | 4 +- .../reactCreateElement.js.snap | 12 +- .../createElement.js | 4 +- .../createElement.js.snap | 53 ++-- 22 files changed, 382 insertions(+), 408 deletions(-) diff --git a/crates/rome_js_analyze/src/react.rs b/crates/rome_js_analyze/src/react.rs index e105e11e9e6b..a688b2b2e581 100644 --- a/crates/rome_js_analyze/src/react.rs +++ b/crates/rome_js_analyze/src/react.rs @@ -2,7 +2,7 @@ pub mod hooks; -use rome_js_semantic::SemanticModel; +use rome_js_semantic::{Binding, SemanticModel}; use rome_js_syntax::{ JsAnyCallArgument, JsAnyExpression, JsAnyNamedImportSpecifier, JsCallExpression, JsIdentifierBinding, JsImport, JsImportNamedClause, JsNamedImportSpecifierList, @@ -247,46 +247,42 @@ pub(crate) fn is_react_call_api( ) -> Option { // we bail straight away if the API doesn't exists in React debug_assert!(VALID_REACT_API.contains(&api_name)); + Some(match expression { JsAnyExpression::JsStaticMemberExpression(node) => { - let object = node.object().ok()?; let member = node.member().ok()?; let member = member.as_js_name()?; + + if member.value_token().ok()?.text_trimmed() != api_name { + return Some(false); + } + + let object = node.object().ok()?; let identifier = object.as_js_identifier_expression()?.name().ok()?; - let mut maybe_from_react = identifier.syntax().text_trimmed() == "React" - && member.syntax().text_trimmed() == api_name; + return model.declaration(&identifier).and_then(|binding| { + let binding_identifier = JsIdentifierBinding::cast_ref(binding.syntax())?; - if let Some(binding_identifier) = model.declaration(&identifier) { - let binding_identifier = - JsIdentifierBinding::cast_ref(binding_identifier.syntax())?; if let Some(js_import) = binding_identifier .syntax() .ancestors() .find_map(|ancestor| JsImport::cast_ref(&ancestor)) { - maybe_from_react = js_import.source_is("react").ok()?; + js_import.source_is("react").ok() + } else { + Some(false) } - } - maybe_from_react + }); } JsAnyExpression::JsIdentifierExpression(identifier) => { let name = identifier.name().ok()?; - let mut maybe_react = identifier.syntax().text_trimmed() == api_name; - if let Some(identifier_binding) = model.declaration(&name) { - let binding_identifier = - JsIdentifierBinding::cast_ref(identifier_binding.syntax())?; - if let Some(js_import) = binding_identifier - .syntax() - .ancestors() - .find_map(|ancestor| JsImport::cast_ref(&ancestor)) - { - maybe_react = js_import.source_is("react").ok()?; - } - } - maybe_react + + model + .declaration(&name) + .and_then(|binding| is_react_export(binding, api_name)) + .unwrap_or(false) } - _ => return None, + _ => false, }) } @@ -303,24 +299,22 @@ pub(crate) fn jsx_member_name_is_react_fragment( let object = member_name.object().ok()?; let member = member_name.member().ok()?; let object = object.as_jsx_reference_identifier()?; - let mut maybe_react_fragment = object.value_token().ok()?.text_trimmed() == "React" - && member.value_token().ok()?.text_trimmed() == "Fragment"; - if let Some(reference) = model.declaration(object) { - if let Some(js_import) = reference + + if member.value_token().ok()?.text_trimmed() != "Fragment" { + return Some(false); + } + + model.declaration(object).and_then(|declaration| { + if let Some(js_import) = declaration .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 && member.value_token().ok()?.text_trimmed() == "Fragment"; + js_import.source_is("react").ok() } else { - // `React.Fragment` is a binding but it doesn't come from the "react" package - maybe_react_fragment = false; + Some(false) } - } - - Some(maybe_react_fragment) + }) } /// Checks if the node `JsxReferenceIdentifier` is a react fragment. @@ -334,33 +328,7 @@ pub(crate) fn jsx_reference_identifier_is_fragment( model: &SemanticModel, ) -> Option { match model.declaration(name) { - Some(reference) => { - let ident = JsIdentifierBinding::cast_ref(reference.syntax())?; - - let import_specifier = ident.parent::()?; - 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; - } - }; - - if name_token.text_trimmed() != "Fragment" { - return Some(false); - } - - let import_specifier_list = import_specifier.parent::()?; - let import_specifiers = import_specifier_list.parent::()?; - let import_clause = import_specifiers.parent::()?; - let import = import_clause.parent::()?; - import.source_is("react").ok() - } - + Some(reference) => is_react_export(reference, "Fragment"), None => { let value_token = name.value_token().ok()?; let is_fragment = value_token.text_trimmed() == "Fragment"; @@ -368,3 +336,28 @@ pub(crate) fn jsx_reference_identifier_is_fragment( } } } + +fn is_react_export(binding: Binding, name: &str) -> Option { + let ident = JsIdentifierBinding::cast_ref(binding.syntax())?; + let import_specifier = ident.parent::()?; + 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 Some(false); + } + }; + + if name_token.text_trimmed() != name { + return Some(false); + } + + let import_specifier_list = import_specifier.parent::()?; + let import_specifiers = import_specifier_list.parent::()?; + let import_clause = import_specifiers.parent::()?; + let import = import_clause.parent::()?; + + import.source_is("react").ok() +} diff --git a/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_array_index_key.rs b/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_array_index_key.rs index 04ad77e429c2..1ca2a3c22574 100644 --- a/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_array_index_key.rs +++ b/crates/rome_js_analyze/src/semantic_analyzers/correctness/no_array_index_key.rs @@ -5,8 +5,8 @@ use rome_analyze::{declare_rule, Rule, RuleDiagnostic}; use rome_console::markup; use rome_js_semantic::SemanticModel; use rome_js_syntax::{ - JsArrowFunctionExpression, JsCallExpression, JsExpressionStatement, JsFunctionDeclaration, - JsFunctionExpression, JsIdentifierBinding, JsIdentifierExpression, JsMethodClassMember, + JsAnyCallArgument, JsArrowFunctionExpression, JsCallExpression, JsExpressionStatement, + JsFunctionDeclaration, JsFunctionExpression, JsIdentifierBinding, JsMethodClassMember, JsMethodObjectMember, JsParameterList, JsPropertyObjectMember, JsReferenceIdentifier, JsxAttribute, JsxOpeningElement, JsxSelfClosingElement, }; @@ -326,28 +326,24 @@ fn find_react_children_function_argument( "forEach" | "map" ); - let object = member_expression.object().ok()?; - - let mut is_react_children = false; - // case we have `Children` - if let Some(identifier) = JsIdentifierExpression::cast_ref(object.syntax()) { - if identifier.name().ok()?.value_token().ok()?.text_trimmed() == "Children" { - is_react_children = array_call; - } - } else { - // case we have `React.Children` - is_react_children = is_react_call_api(&object, model, "Children")? && array_call; + if !array_call { + return None; } - if is_react_children { + let object = member_expression.object().ok()?; + + // React.Children.forEach/map or Children.forEach/map + if is_react_call_api(&object, model, "Children")? { let arguments = call_expression.arguments().ok()?; let arguments = arguments.args(); let mut arguments = arguments.into_iter(); - let _ = arguments.next()?.ok()?; - let second_argument = arguments.next()?.ok()?; - let second_argument = second_argument.as_js_any_expression()?; - FunctionLike::cast(second_argument.clone().into_syntax()) + match (arguments.next(), arguments.next(), arguments.next()) { + (Some(_), Some(Ok(JsAnyCallArgument::JsAnyExpression(second_argument))), None) => { + FunctionLike::cast(second_argument.into_syntax()) + } + _ => None, + } } else { None } diff --git a/crates/rome_js_analyze/tests/specs/a11y/noPositiveTabindex/reactCreateElementInvalid.js b/crates/rome_js_analyze/tests/specs/a11y/noPositiveTabindex/reactCreateElementInvalid.js index 46f5363f95c7..214915e85808 100644 --- a/crates/rome_js_analyze/tests/specs/a11y/noPositiveTabindex/reactCreateElementInvalid.js +++ b/crates/rome_js_analyze/tests/specs/a11y/noPositiveTabindex/reactCreateElementInvalid.js @@ -1,3 +1,5 @@ +import React from "react"; + React.createElement("div", { tabIndex: '1' }) React.createElement("div", { tabIndex: 1 }) React.createElement("div", { tabIndex: +1 }) diff --git a/crates/rome_js_analyze/tests/specs/a11y/noPositiveTabindex/reactCreateElementInvalid.js.snap b/crates/rome_js_analyze/tests/specs/a11y/noPositiveTabindex/reactCreateElementInvalid.js.snap index 3550c9842a05..ef28022dc82f 100644 --- a/crates/rome_js_analyze/tests/specs/a11y/noPositiveTabindex/reactCreateElementInvalid.js.snap +++ b/crates/rome_js_analyze/tests/specs/a11y/noPositiveTabindex/reactCreateElementInvalid.js.snap @@ -4,6 +4,8 @@ expression: reactCreateElementInvalid.js --- # Input ```js +import React from "react"; + React.createElement("div", { tabIndex: '1' }) React.createElement("div", { tabIndex: 1 }) React.createElement("div", { tabIndex: +1 }) @@ -13,14 +15,16 @@ React.createElement("div", { tabIndex: +01 }) # Diagnostics ``` -reactCreateElementInvalid.js:1:40 lint/a11y/noPositiveTabindex ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +reactCreateElementInvalid.js:3:40 lint/a11y/noPositiveTabindex ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid positive values for the tabIndex prop. - > 1 │ React.createElement("div", { tabIndex: '1' }) + 1 │ import React from "react"; + 2 │ + > 3 │ React.createElement("div", { tabIndex: '1' }) │ ^^^ - 2 │ React.createElement("div", { tabIndex: 1 }) - 3 │ React.createElement("div", { tabIndex: +1 }) + 4 │ React.createElement("div", { tabIndex: 1 }) + 5 │ React.createElement("div", { tabIndex: +1 }) i Elements with a positive tabIndex override natural page content order. This causes elements without a positive tab index to come last when navigating using a keyboard. @@ -28,15 +32,15 @@ reactCreateElementInvalid.js:1:40 lint/a11y/noPositiveTabindex ━━━━━ ``` ``` -reactCreateElementInvalid.js:2:40 lint/a11y/noPositiveTabindex ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +reactCreateElementInvalid.js:4:40 lint/a11y/noPositiveTabindex ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid positive values for the tabIndex prop. - 1 │ React.createElement("div", { tabIndex: '1' }) - > 2 │ React.createElement("div", { tabIndex: 1 }) + 3 │ React.createElement("div", { tabIndex: '1' }) + > 4 │ React.createElement("div", { tabIndex: 1 }) │ ^ - 3 │ React.createElement("div", { tabIndex: +1 }) - 4 │ React.createElement("div", { tabIndex: +01 }) + 5 │ React.createElement("div", { tabIndex: +1 }) + 6 │ React.createElement("div", { tabIndex: +01 }) i Elements with a positive tabIndex override natural page content order. This causes elements without a positive tab index to come last when navigating using a keyboard. @@ -44,16 +48,16 @@ reactCreateElementInvalid.js:2:40 lint/a11y/noPositiveTabindex ━━━━━ ``` ``` -reactCreateElementInvalid.js:3:40 lint/a11y/noPositiveTabindex ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +reactCreateElementInvalid.js:5:40 lint/a11y/noPositiveTabindex ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid positive values for the tabIndex prop. - 1 │ React.createElement("div", { tabIndex: '1' }) - 2 │ React.createElement("div", { tabIndex: 1 }) - > 3 │ React.createElement("div", { tabIndex: +1 }) + 3 │ React.createElement("div", { tabIndex: '1' }) + 4 │ React.createElement("div", { tabIndex: 1 }) + > 5 │ React.createElement("div", { tabIndex: +1 }) │ ^^ - 4 │ React.createElement("div", { tabIndex: +01 }) - 5 │ + 6 │ React.createElement("div", { tabIndex: +01 }) + 7 │ i Elements with a positive tabIndex override natural page content order. This causes elements without a positive tab index to come last when navigating using a keyboard. @@ -61,15 +65,15 @@ reactCreateElementInvalid.js:3:40 lint/a11y/noPositiveTabindex ━━━━━ ``` ``` -reactCreateElementInvalid.js:4:40 lint/a11y/noPositiveTabindex ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +reactCreateElementInvalid.js:6:40 lint/a11y/noPositiveTabindex ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid positive values for the tabIndex prop. - 2 │ React.createElement("div", { tabIndex: 1 }) - 3 │ React.createElement("div", { tabIndex: +1 }) - > 4 │ React.createElement("div", { tabIndex: +01 }) + 4 │ React.createElement("div", { tabIndex: 1 }) + 5 │ React.createElement("div", { tabIndex: +1 }) + > 6 │ React.createElement("div", { tabIndex: +01 }) │ ^^^ - 5 │ + 7 │ i Elements with a positive tabIndex override natural page content order. This causes elements without a positive tab index to come last when navigating using a keyboard. diff --git a/crates/rome_js_analyze/tests/specs/a11y/useButtonType/inObject.js b/crates/rome_js_analyze/tests/specs/a11y/useButtonType/inObject.js index 05afca94d109..90bf0e5cbe41 100644 --- a/crates/rome_js_analyze/tests/specs/a11y/useButtonType/inObject.js +++ b/crates/rome_js_analyze/tests/specs/a11y/useButtonType/inObject.js @@ -1,3 +1,5 @@ +import React from "react"; + // invalid React.createElement('button'); React.createElement('button', { diff --git a/crates/rome_js_analyze/tests/specs/a11y/useButtonType/inObject.js.snap b/crates/rome_js_analyze/tests/specs/a11y/useButtonType/inObject.js.snap index 527aebe073ab..bded5ef61d5b 100644 --- a/crates/rome_js_analyze/tests/specs/a11y/useButtonType/inObject.js.snap +++ b/crates/rome_js_analyze/tests/specs/a11y/useButtonType/inObject.js.snap @@ -4,6 +4,8 @@ expression: inObject.js --- # Input ```js +import React from "react"; + // invalid React.createElement('button'); React.createElement('button', { @@ -23,15 +25,15 @@ React.createElement('button', { # Diagnostics ``` -inObject.js:2:21 lint/a11y/useButtonType ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +inObject.js:4:21 lint/a11y/useButtonType ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Provide an explicit type prop for the button element. - 1 │ // invalid - > 2 │ React.createElement('button'); + 3 │ // invalid + > 4 │ React.createElement('button'); │ ^^^^^^^^ - 3 │ React.createElement('button', { - 4 │ "type": "bar" + 5 │ React.createElement('button', { + 6 │ "type": "bar" i The default type of a button is submit, which causes the submission of a form when placed inside a `form` element. This is likely not the behaviour that you want inside a React application. @@ -41,16 +43,16 @@ inObject.js:2:21 lint/a11y/useButtonType ━━━━━━━━━━━━━ ``` ``` -inObject.js:4:13 lint/a11y/useButtonType ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +inObject.js:6:13 lint/a11y/useButtonType ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Provide a valid type prop for the button element. - 2 │ React.createElement('button'); - 3 │ React.createElement('button', { - > 4 │ "type": "bar" + 4 │ React.createElement('button'); + 5 │ React.createElement('button', { + > 6 │ "type": "bar" │ ^^^^^ - 5 │ }); - 6 │ React.createElement('button', { + 7 │ }); + 8 │ React.createElement('button', { i The default type of a button is submit, which causes the submission of a form when placed inside a `form` element. This is likely not the behaviour that you want inside a React application. @@ -60,19 +62,19 @@ inObject.js:4:13 lint/a11y/useButtonType ━━━━━━━━━━━━━ ``` ``` -inObject.js:6:31 lint/a11y/useButtonType ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +inObject.js:8:31 lint/a11y/useButtonType ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Provide a valid type prop for the button element. - 4 │ "type": "bar" - 5 │ }); - > 6 │ React.createElement('button', { + 6 │ "type": "bar" + 7 │ }); + > 8 │ React.createElement('button', { │ ^ - > 7 │ "style": "background: red" - > 8 │ }); + > 9 │ "style": "background: red" + > 10 │ }); │ ^ - 9 │ React.createElement('button', {}); - 10 │ + 11 │ React.createElement('button', {}); + 12 │ i The default type of a button is submit, which causes the submission of a form when placed inside a `form` element. This is likely not the behaviour that you want inside a React application. @@ -82,16 +84,16 @@ inObject.js:6:31 lint/a11y/useButtonType ━━━━━━━━━━━━━ ``` ``` -inObject.js:9:31 lint/a11y/useButtonType ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +inObject.js:11:31 lint/a11y/useButtonType ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Provide a valid type prop for the button element. - 7 │ "style": "background: red" - 8 │ }); - > 9 │ React.createElement('button', {}); + 9 │ "style": "background: red" + 10 │ }); + > 11 │ React.createElement('button', {}); │ ^^ - 10 │ - 11 │ // valid + 12 │ + 13 │ // valid i The default type of a button is submit, which causes the submission of a form when placed inside a `form` element. This is likely not the behaviour that you want inside a React application. diff --git a/crates/rome_js_analyze/tests/specs/correctness/noArrayIndexKey.jsx b/crates/rome_js_analyze/tests/specs/correctness/noArrayIndexKey.jsx index 488f9ace4bbd..356719c35746 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noArrayIndexKey.jsx +++ b/crates/rome_js_analyze/tests/specs/correctness/noArrayIndexKey.jsx @@ -1,3 +1,5 @@ +import React, {Children} from "react"; + // invalid something.forEach((Element, index) => { foo @@ -57,4 +59,4 @@ something.forEach((element, index) => { something.forEach((element, index) => { -}); \ No newline at end of file +}); diff --git a/crates/rome_js_analyze/tests/specs/correctness/noArrayIndexKey.jsx.snap b/crates/rome_js_analyze/tests/specs/correctness/noArrayIndexKey.jsx.snap index 702ceaa1d110..d9ec0ad04048 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noArrayIndexKey.jsx.snap +++ b/crates/rome_js_analyze/tests/specs/correctness/noArrayIndexKey.jsx.snap @@ -4,6 +4,8 @@ expression: noArrayIndexKey.jsx --- # Input ```js +import React, {Children} from "react"; + // invalid something.forEach((Element, index) => { foo @@ -64,28 +66,29 @@ something.forEach((element, index) => { }); + ``` # Diagnostics ``` -noArrayIndexKey.jsx:3:21 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noArrayIndexKey.jsx:5:21 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 1 │ // invalid - 2 │ something.forEach((Element, index) => { - > 3 │ foo + 3 │ // invalid + 4 │ something.forEach((Element, index) => { + > 5 │ foo │ ^^^^^ - 4 │ }); - 5 │ something.forEach((element, index, array) => { + 6 │ }); + 7 │ something.forEach((element, index, array) => { i This is the source of the key value. - 1 │ // invalid - > 2 │ something.forEach((Element, index) => { + 3 │ // invalid + > 4 │ something.forEach((Element, index) => { │ ^^^^^ - 3 │ foo - 4 │ }); + 5 │ foo + 6 │ }); i The order of the items may change, and this also affects performances and component state. @@ -95,25 +98,25 @@ noArrayIndexKey.jsx:3:21 lint/correctness/noArrayIndexKey ━━━━━━━ ``` ``` -noArrayIndexKey.jsx:6:21 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noArrayIndexKey.jsx:8:21 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 4 │ }); - 5 │ something.forEach((element, index, array) => { - > 6 │ foo - │ ^^^^^ - 7 │ }); - 8 │ things.filter((thing, index) => { + 6 │ }); + 7 │ something.forEach((element, index, array) => { + > 8 │ foo + │ ^^^^^ + 9 │ }); + 10 │ things.filter((thing, index) => { i This is the source of the key value. - 3 │ foo - 4 │ }); - > 5 │ something.forEach((element, index, array) => { + 5 │ foo + 6 │ }); + > 7 │ something.forEach((element, index, array) => { │ ^^^^^ - 6 │ foo - 7 │ }); + 8 │ foo + 9 │ }); i The order of the items may change, and this also affects performances and component state. @@ -123,25 +126,25 @@ noArrayIndexKey.jsx:6:21 lint/correctness/noArrayIndexKey ━━━━━━━ ``` ``` -noArrayIndexKey.jsx:9:34 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noArrayIndexKey.jsx:11:34 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 7 │ }); - 8 │ things.filter((thing, index) => { - > 9 │ otherThings.push(foo); + 9 │ }); + 10 │ things.filter((thing, index) => { + > 11 │ otherThings.push(foo); │ ^^^^^ - 10 │ }); - 11 │ + 12 │ }); + 13 │ i This is the source of the key value. - 6 │ foo - 7 │ }); - > 8 │ things.filter((thing, index) => { + 8 │ foo + 9 │ }); + > 10 │ things.filter((thing, index) => { │ ^^^^^ - 9 │ otherThings.push(foo); - 10 │ }); + 11 │ otherThings.push(foo); + 12 │ }); i The order of the items may change, and this also affects performances and component state. @@ -151,24 +154,24 @@ noArrayIndexKey.jsx:9:34 lint/correctness/noArrayIndexKey ━━━━━━━ ``` ``` -noArrayIndexKey.jsx:13:21 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noArrayIndexKey.jsx:15:21 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 12 │ something.forEach((Element, index) => { - > 13 │ + 14 │ something.forEach((Element, index) => { + > 15 │ │ ^^^^^ - 14 │ }); - 15 │ something.forEach((element, index, array) => { + 16 │ }); + 17 │ something.forEach((element, index, array) => { i This is the source of the key value. - 10 │ }); - 11 │ - > 12 │ something.forEach((Element, index) => { + 12 │ }); + 13 │ + > 14 │ something.forEach((Element, index) => { │ ^^^^^ - 13 │ - 14 │ }); + 15 │ + 16 │ }); i The order of the items may change, and this also affects performances and component state. @@ -178,25 +181,25 @@ noArrayIndexKey.jsx:13:21 lint/correctness/noArrayIndexKey ━━━━━━━ ``` ``` -noArrayIndexKey.jsx:16:21 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noArrayIndexKey.jsx:18:21 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 14 │ }); - 15 │ something.forEach((element, index, array) => { - > 16 │ + 16 │ }); + 17 │ something.forEach((element, index, array) => { + > 18 │ │ ^^^^^ - 17 │ }); - 18 │ things.filter((thing, index) => { + 19 │ }); + 20 │ things.filter((thing, index) => { i This is the source of the key value. - 13 │ - 14 │ }); - > 15 │ something.forEach((element, index, array) => { + 15 │ + 16 │ }); + > 17 │ something.forEach((element, index, array) => { │ ^^^^^ - 16 │ - 17 │ }); + 18 │ + 19 │ }); i The order of the items may change, and this also affects performances and component state. @@ -206,25 +209,25 @@ noArrayIndexKey.jsx:16:21 lint/correctness/noArrayIndexKey ━━━━━━━ ``` ``` -noArrayIndexKey.jsx:19:34 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noArrayIndexKey.jsx:21:34 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 17 │ }); - 18 │ things.filter((thing, index) => { - > 19 │ otherThings.push(); + 19 │ }); + 20 │ things.filter((thing, index) => { + > 21 │ otherThings.push(); │ ^^^^^ - 20 │ }); - 21 │ things.reduce((collection, thing, index) => ( + 22 │ }); + 23 │ things.reduce((collection, thing, index) => ( i This is the source of the key value. - 16 │ - 17 │ }); - > 18 │ things.filter((thing, index) => { + 18 │ + 19 │ }); + > 20 │ things.filter((thing, index) => { │ ^^^^^ - 19 │ otherThings.push(); - 20 │ }); + 21 │ otherThings.push(); + 22 │ }); i The order of the items may change, and this also affects performances and component state. @@ -234,25 +237,25 @@ noArrayIndexKey.jsx:19:34 lint/correctness/noArrayIndexKey ━━━━━━━ ``` ``` -noArrayIndexKey.jsx:22:35 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noArrayIndexKey.jsx:24:35 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 20 │ }); - 21 │ things.reduce((collection, thing, index) => ( - > 22 │ collection.concat() + 22 │ }); + 23 │ things.reduce((collection, thing, index) => ( + > 24 │ collection.concat() │ ^^^^^ - 23 │ ), []); - 24 │ + 25 │ ), []); + 26 │ i This is the source of the key value. - 19 │ otherThings.push(); - 20 │ }); - > 21 │ things.reduce((collection, thing, index) => ( + 21 │ otherThings.push(); + 22 │ }); + > 23 │ things.reduce((collection, thing, index) => ( │ ^^^^^ - 22 │ collection.concat() - 23 │ ), []); + 24 │ collection.concat() + 25 │ ), []); i The order of the items may change, and this also affects performances and component state. @@ -262,24 +265,24 @@ noArrayIndexKey.jsx:22:35 lint/correctness/noArrayIndexKey ━━━━━━━ ``` ``` -noArrayIndexKey.jsx:26:38 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noArrayIndexKey.jsx:28:38 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 25 │ React.Children.map(this.props.children, (child, index) => ( - > 26 │ React.cloneElement(child, { key: index }) + 27 │ React.Children.map(this.props.children, (child, index) => ( + > 28 │ React.cloneElement(child, { key: index }) │ ^^^^^ - 27 │ )) - 28 │ + 29 │ )) + 30 │ i This is the source of the key value. - 23 │ ), []); - 24 │ - > 25 │ React.Children.map(this.props.children, (child, index) => ( + 25 │ ), []); + 26 │ + > 27 │ React.Children.map(this.props.children, (child, index) => ( │ ^^^^^ - 26 │ React.cloneElement(child, { key: index }) - 27 │ )) + 28 │ React.cloneElement(child, { key: index }) + 29 │ )) i The order of the items may change, and this also affects performances and component state. @@ -289,103 +292,24 @@ noArrayIndexKey.jsx:26:38 lint/correctness/noArrayIndexKey ━━━━━━━ ``` ``` -noArrayIndexKey.jsx:30:45 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noArrayIndexKey.jsx:32:45 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 29 │ React.Children.forEach(this.props.children, function (child, index) { - > 30 │ return React.cloneElement(child, { key: index }) + 31 │ React.Children.forEach(this.props.children, function (child, index) { + > 32 │ return React.cloneElement(child, { key: index }) │ ^^^^^ - 31 │ }) - 32 │ + 33 │ }) + 34 │ i This is the source of the key value. - 27 │ )) - 28 │ - > 29 │ React.Children.forEach(this.props.children, function (child, index) { + 29 │ )) + 30 │ + > 31 │ React.Children.forEach(this.props.children, function (child, index) { │ ^^^^^ - 30 │ return React.cloneElement(child, { key: index }) - 31 │ }) - - i The order of the items may change, and this also affects performances and component state. - - i Check the React documentation. - - -``` - -``` -noArrayIndexKey.jsx:35:32 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Avoid using the index of an array as key property in an element. - - 34 │ Children.map(this.props.children, (child, index) => ( - > 35 │ cloneElement(child, { key: index }) - │ ^^^^^ - 36 │ )) - 37 │ - - i This is the source of the key value. - - > 34 │ Children.map(this.props.children, (child, index) => ( - │ ^^^^^ - 35 │ cloneElement(child, { key: index }) - 36 │ )) - - i The order of the items may change, and this also affects performances and component state. - - i Check the React documentation. - - -``` - -``` -noArrayIndexKey.jsx:39:39 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Avoid using the index of an array as key property in an element. - - 38 │ Children.forEach(this.props.children, function (child, index) { - > 39 │ return cloneElement(child, { key: index }) - │ ^^^^^ - 40 │ }) - 41 │ - - i This is the source of the key value. - - 36 │ )) - 37 │ - > 38 │ Children.forEach(this.props.children, function (child, index) { - │ ^^^^^ - 39 │ return cloneElement(child, { key: index }) - 40 │ }) - - i The order of the items may change, and this also affects performances and component state. - - i Check the React documentation. - - -``` - -``` -noArrayIndexKey.jsx:43:44 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Avoid using the index of an array as key property in an element. - - 42 │ Children.forEach(this.props.children, function (child, index) { - > 43 │ const foo = cloneElement(child, { key: index }) - │ ^^^^^ - 44 │ return foo; - 45 │ }) - - i This is the source of the key value. - - 40 │ }) - 41 │ - > 42 │ Children.forEach(this.props.children, function (child, index) { - │ ^^^^^ - 43 │ const foo = cloneElement(child, { key: index }) - 44 │ return foo; + 32 │ return React.cloneElement(child, { key: index }) + 33 │ }) i The order of the items may change, and this also affects performances and component state. @@ -395,22 +319,22 @@ noArrayIndexKey.jsx:43:44 lint/correctness/noArrayIndexKey ━━━━━━━ ``` ``` -noArrayIndexKey.jsx:49:38 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noArrayIndexKey.jsx:51:38 lint/correctness/noArrayIndexKey ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using the index of an array as key property in an element. - 48 │ things.map((thing, index) => ( - > 49 │ React.cloneElement(thing, { key: index }) + 50 │ things.map((thing, index) => ( + > 51 │ React.cloneElement(thing, { key: index }) │ ^^^^^ - 50 │ )); - 51 │ + 52 │ )); + 53 │ i This is the source of the key value. - > 48 │ things.map((thing, index) => ( + > 50 │ things.map((thing, index) => ( │ ^^^^^ - 49 │ React.cloneElement(thing, { key: index }) - 50 │ )); + 51 │ React.cloneElement(thing, { key: index }) + 52 │ )); i The order of the items may change, and this also affects performances and component state. diff --git a/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropInvalid.jsx b/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropInvalid.jsx index 02c39406f0ed..2a0b829b2bc7 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropInvalid.jsx +++ b/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropInvalid.jsx @@ -1,3 +1,6 @@ +import {createElement} from "react"; +import React from "react"; + <> diff --git a/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropInvalid.jsx.snap b/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropInvalid.jsx.snap index f35e7d29f7e0..7c5df246fe4d 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropInvalid.jsx.snap +++ b/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropInvalid.jsx.snap @@ -4,6 +4,9 @@ expression: noChildrenPropInvalid.jsx --- # Input ```js +import {createElement} from "react"; +import React from "react"; + <> @@ -20,15 +23,15 @@ React.createElement('div', { # Diagnostics ``` -noChildrenPropInvalid.jsx:2:16 lint/correctness/noChildrenProp ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noChildrenPropInvalid.jsx:5:16 lint/correctness/noChildrenProp ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid passing children using a prop - 1 │ <> - > 2 │ + 4 │ <> + > 5 │ │ ^^^^^^^^ - 3 │ - 4 │ + 6 │ + 7 │ i The canonical way to pass children in React is to use JSX elements @@ -36,15 +39,15 @@ noChildrenPropInvalid.jsx:2:16 lint/correctness/noChildrenProp ━━━━━ ``` ``` -noChildrenPropInvalid.jsx:6:5 lint/correctness/noChildrenProp ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noChildrenPropInvalid.jsx:9:5 lint/correctness/noChildrenProp ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid passing children using a prop - 5 │ createElement('div', { - > 6 │ children: 'foo' - │ ^^^^^^^^ - 7 │ }) - 8 │ + 8 │ createElement('div', { + > 9 │ children: 'foo' + │ ^^^^^^^^ + 10 │ }) + 11 │ i The canonical way to pass children in React is to use additional arguments to React.createElement @@ -52,15 +55,15 @@ noChildrenPropInvalid.jsx:6:5 lint/correctness/noChildrenProp ━━━━━━ ``` ``` -noChildrenPropInvalid.jsx:10:5 lint/correctness/noChildrenProp ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noChildrenPropInvalid.jsx:13:5 lint/correctness/noChildrenProp ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid passing children using a prop - 9 │ React.createElement('div', { - > 10 │ children: 'foo' + 12 │ React.createElement('div', { + > 13 │ children: 'foo' │ ^^^^^^^^ - 11 │ }) - 12 │ + 14 │ }) + 15 │ i The canonical way to pass children in React is to use additional arguments to React.createElement diff --git a/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropValid.jsx b/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropValid.jsx index b5ea51268e05..bf0efd8dbd58 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropValid.jsx +++ b/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropValid.jsx @@ -1,3 +1,6 @@ +import { cloneElement } from "react"; +import React from "react"; + <> @@ -7,3 +10,6 @@ createElement('div', {}, 'foo') + +cloneElement('div', { children:
}); +React.cloneElement('div', { children:
}); diff --git a/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropValid.jsx.snap b/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropValid.jsx.snap index 4523817da33c..d193e41a1ee4 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropValid.jsx.snap +++ b/crates/rome_js_analyze/tests/specs/correctness/noChildrenProp/noChildrenPropValid.jsx.snap @@ -4,6 +4,9 @@ expression: noChildrenPropValid.jsx --- # Input ```js +import { cloneElement } from "react"; +import React from "react"; + <> @@ -14,6 +17,9 @@ expression: noChildrenPropValid.jsx createElement('div', {}, 'foo') +cloneElement('div', { children:
}); +React.cloneElement('div', { children:
}); + ``` diff --git a/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/noChildren.jsx b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/noChildren.jsx index 2be0a35fa56b..845a15dd0fe1 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/noChildren.jsx +++ b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/noChildren.jsx @@ -1,7 +1,8 @@ +import React, { Fragment } from "react"; // invalid <> <> - \ No newline at end of file + diff --git a/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/noChildren.jsx.snap b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/noChildren.jsx.snap index 0c8caa8df96d..64ac87635e4c 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/noChildren.jsx.snap +++ b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/noChildren.jsx.snap @@ -4,6 +4,7 @@ expression: noChildren.jsx --- # Input ```js +import React, { Fragment } from "react"; // invalid <> @@ -11,60 +12,62 @@ expression: noChildren.jsx + ``` # Diagnostics ``` -noChildren.jsx:4:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noChildren.jsx:5:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using unnecessary Fragment. - 3 │ <> - > 4 │ <> + 4 │ <> + > 5 │ <> │ ^^^^^ - 5 │ - 6 │ + 6 │ + 7 │ i Suggested fix: Remove the Fragment - 4 │ ····<> + 5 │ ····<> │ ----- ``` ``` -noChildren.jsx:5:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noChildren.jsx:6:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using unnecessary Fragment. - 3 │ <> - 4 │ <> - > 5 │ + 4 │ <> + 5 │ <> + > 6 │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 6 │ - 7 │ + 7 │ + 8 │ i Suggested fix: Remove the Fragment - 5 │ ···· + 6 │ ···· │ --------------------------------- ``` ``` -noChildren.jsx:6:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +noChildren.jsx:7:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using unnecessary Fragment. - 4 │ <> - 5 │ - > 6 │ + 5 │ <> + 6 │ + > 7 │ │ ^^^^^^^^^^^^^^^^^^^^^ - 7 │ + 8 │ + 9 │ i Suggested fix: Remove the Fragment - 6 │ ···· + 7 │ ···· │ --------------------- ``` diff --git a/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/withChildren.jsx b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/withChildren.jsx index 6f662e944351..e352600cb884 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/withChildren.jsx +++ b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/withChildren.jsx @@ -1,3 +1,5 @@ +import React, { Fragment, StrictMode } from "react"; + <> <>foo foo diff --git a/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/withChildren.jsx.snap b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/withChildren.jsx.snap index 054ae2b1867f..7ec989c0277e 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/withChildren.jsx.snap +++ b/crates/rome_js_analyze/tests/specs/correctness/noUselessFragments/withChildren.jsx.snap @@ -4,6 +4,8 @@ expression: withChildren.jsx --- # Input ```js +import React, { Fragment, StrictMode } from "react"; + <> <>foo foo @@ -16,57 +18,57 @@ expression: withChildren.jsx # Diagnostics ``` -withChildren.jsx:2:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +withChildren.jsx:4:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using unnecessary Fragment. - 1 │ <> - > 2 │ <>foo + 3 │ <> + > 4 │ <>foo │ ^^^^^^^^ - 3 │ foo - 4 │ foo + 5 │ foo + 6 │ foo i Suggested fix: Remove the Fragment - 2 │ ····<>foo + 4 │ ····<>foo │ -- --- ``` ``` -withChildren.jsx:3:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +withChildren.jsx:5:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using unnecessary Fragment. - 1 │ <> - 2 │ <>foo - > 3 │ foo + 3 │ <> + 4 │ <>foo + > 5 │ foo │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 4 │ foo - 5 │ {/* valid */} + 6 │ foo + 7 │ {/* valid */} i Suggested fix: Remove the Fragment - 3 │ ····foo + 5 │ ····foo │ ---------------- ----------------- ``` ``` -withChildren.jsx:4:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +withChildren.jsx:6:5 lint/correctness/noUselessFragments FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid using unnecessary Fragment. - 2 │ <>foo - 3 │ foo - > 4 │ foo + 4 │ <>foo + 5 │ foo + > 6 │ foo │ ^^^^^^^^^^^^^^^^^^^^^^^^ - 5 │ {/* valid */} - 6 │ + 7 │ {/* valid */} + 8 │ i Suggested fix: Remove the Fragment - 4 │ ····foo + 6 │ ····foo │ ---------- ----------- ``` diff --git a/crates/rome_js_analyze/tests/specs/correctness/noVoidElementsWithChildren/createElement.js b/crates/rome_js_analyze/tests/specs/correctness/noVoidElementsWithChildren/createElement.js index 45fd0486666e..bec5c1d08acf 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noVoidElementsWithChildren/createElement.js +++ b/crates/rome_js_analyze/tests/specs/correctness/noVoidElementsWithChildren/createElement.js @@ -1,4 +1,4 @@ - +import React from "react"; React.createElement('img', { someProp: "bar" @@ -6,4 +6,4 @@ React.createElement('img', { React.createElement('img', { dangerouslySetInnerHTML: "text" -}) \ No newline at end of file +}) diff --git a/crates/rome_js_analyze/tests/specs/correctness/noVoidElementsWithChildren/createElement.js.snap b/crates/rome_js_analyze/tests/specs/correctness/noVoidElementsWithChildren/createElement.js.snap index 51e6774c7a8b..b3db1f65883d 100644 --- a/crates/rome_js_analyze/tests/specs/correctness/noVoidElementsWithChildren/createElement.js.snap +++ b/crates/rome_js_analyze/tests/specs/correctness/noVoidElementsWithChildren/createElement.js.snap @@ -1,11 +1,10 @@ --- source: crates/rome_js_analyze/tests/spec_tests.rs -assertion_line: 99 expression: createElement.js --- # Input ```js - +import React from "react"; React.createElement('img', { someProp: "bar" @@ -14,6 +13,7 @@ React.createElement('img', { React.createElement('img', { dangerouslySetInnerHTML: "text" }) + ``` # Diagnostics @@ -22,6 +22,8 @@ createElement.js:3:1 lint/correctness/noVoidElementsWithChildren FIXABLE ━ ! img is a void element tag and must not have children. + 1 │ import React from "react"; + 2 │ > 3 │ React.createElement('img', { │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > 4 │ someProp: "bar" @@ -42,20 +44,22 @@ createElement.js:7:1 lint/correctness/noVoidElementsWithChildren FIXABLE ━ ! img is a void element tag and must not have the dangerouslySetInnerHTML prop. - 5 │ }, 'child') - 6 │ - > 7 │ React.createElement('img', { - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - > 8 │ dangerouslySetInnerHTML: "text" - > 9 │ }) - │ ^^ + 5 │ }, 'child') + 6 │ + > 7 │ React.createElement('img', { + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + > 8 │ dangerouslySetInnerHTML: "text" + > 9 │ }) + │ ^^ + 10 │ i Suggested fix: Remove the dangerouslySetInnerHTML prop. - 6 6 │ - 7 7 │ React.createElement('img', { - 8 │ - ····dangerouslySetInnerHTML:·"text" - 9 8 │ }) + 6 6 │ + 7 7 │ React.createElement('img', { + 8 │ - ····dangerouslySetInnerHTML:·"text" + 9 8 │ }) + 10 9 │ ``` diff --git a/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtml/reactCreateElement.js b/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtml/reactCreateElement.js index bfd30c586cb2..86af9c2d220a 100644 --- a/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtml/reactCreateElement.js +++ b/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtml/reactCreateElement.js @@ -1,3 +1,5 @@ +import React from "react"; + React.createElement('div', { dangerouslySetInnerHTML: { __html: 'child' } -}); \ No newline at end of file +}); diff --git a/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtml/reactCreateElement.js.snap b/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtml/reactCreateElement.js.snap index f697f49b28e6..74f486dfde4a 100644 --- a/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtml/reactCreateElement.js.snap +++ b/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtml/reactCreateElement.js.snap @@ -4,21 +4,25 @@ expression: reactCreateElement.js --- # Input ```js +import React from "react"; + React.createElement('div', { dangerouslySetInnerHTML: { __html: 'child' } }); + ``` # Diagnostics ``` -reactCreateElement.js:2:5 lint/security/noDangerouslySetInnerHtml ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +reactCreateElement.js:4:5 lint/security/noDangerouslySetInnerHtml ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid passing content using the dangerouslySetInnerHTML prop. - 1 │ React.createElement('div', { - > 2 │ dangerouslySetInnerHTML: { __html: 'child' } + 3 │ React.createElement('div', { + > 4 │ dangerouslySetInnerHTML: { __html: 'child' } │ ^^^^^^^^^^^^^^^^^^^^^^^ - 3 │ }); + 5 │ }); + 6 │ ! Setting content using code can expose users to cross-site scripting (XSS) attacks diff --git a/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtmlWithChildren/createElement.js b/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtmlWithChildren/createElement.js index 5cbae1955541..bf7bdb69b30e 100644 --- a/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtmlWithChildren/createElement.js +++ b/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtmlWithChildren/createElement.js @@ -1,3 +1,5 @@ +import React from "react"; + React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') -React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) \ No newline at end of file +React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) diff --git a/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtmlWithChildren/createElement.js.snap b/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtmlWithChildren/createElement.js.snap index aa21f4378da5..0586ad56fa54 100644 --- a/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtmlWithChildren/createElement.js.snap +++ b/crates/rome_js_analyze/tests/specs/security/noDangerouslySetInnerHtmlWithChildren/createElement.js.snap @@ -4,28 +4,35 @@ expression: createElement.js --- # Input ```js +import React from "react"; + React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) + ``` # Diagnostics ``` -createElement.js:1:30 lint/security/noDangerouslySetInnerHtmlWithChildren ━━━━━━━━━━━━━━━━━━━━━━━━━━ +createElement.js:3:30 lint/security/noDangerouslySetInnerHtmlWithChildren ━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid passing both children and the dangerouslySetInnerHTML prop. - > 1 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) + 1 │ import React from "react"; + 2 │ + > 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 2 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') - 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) + 4 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') + 5 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) i This is the source of the children prop - > 1 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) + 1 │ import React from "react"; + 2 │ + > 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) │ ^^^^^^^^^^^^ - 2 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') - 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) + 4 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') + 5 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) i Setting HTML content will inadvertently override any passed children in React @@ -33,21 +40,23 @@ createElement.js:1:30 lint/security/noDangerouslySetInnerHtmlWithChildren ━━ ``` ``` -createElement.js:2:30 lint/security/noDangerouslySetInnerHtmlWithChildren ━━━━━━━━━━━━━━━━━━━━━━━━━━ +createElement.js:4:30 lint/security/noDangerouslySetInnerHtmlWithChildren ━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid passing both children and the dangerouslySetInnerHTML prop. - 1 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) - > 2 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') + 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) + > 4 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) + 5 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) + 6 │ i This is the source of the children prop - 1 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) - > 2 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') + 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) + > 4 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') │ ^^^^^^^^^^ - 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) + 5 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) + 6 │ i Setting HTML content will inadvertently override any passed children in React @@ -55,21 +64,23 @@ createElement.js:2:30 lint/security/noDangerouslySetInnerHtmlWithChildren ━━ ``` ``` -createElement.js:3:30 lint/security/noDangerouslySetInnerHtmlWithChildren ━━━━━━━━━━━━━━━━━━━━━━━━━━ +createElement.js:5:30 lint/security/noDangerouslySetInnerHtmlWithChildren ━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Avoid passing both children and the dangerouslySetInnerHTML prop. - 1 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) - 2 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') - > 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) + 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) + 4 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') + > 5 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 6 │ i This is the source of the children prop - 1 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) - 2 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') - > 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) + 3 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, ['children']) + 4 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' } }, 'children') + > 5 │ React.createElement('div', { dangerouslySetInnerHTML: { __html: 'HTML' }, children: 'children' }) │ ^^^^^^^^^^^^^^^^^^^^ + 6 │ i Setting HTML content will inadvertently override any passed children in React