diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.spec.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.spec.ts
index 5be92153a3..aef1dd127a 100644
--- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.spec.ts
+++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.spec.ts
@@ -39,6 +39,30 @@ ruleTester.run(RULE_NAME, rule, {
code: tsx` `,
errors: [{ messageId: "noDangerouslySetInnerhtmlWithChildren" }],
},
+ {
+ // https://github.com/Rel1cx/eslint-react/issues/1163
+ code: tsx`
+ function Abc() {
+ return (
+ <>
+ {/* Error on div 1: A DOM component cannot use both 'children' and 'dangerouslySetInnerHTML'. eslint @eslint-react/dom/no-dangerously-set-innerhtml-with-children */}
+
+ Goodbye World
+
+
+ {/* No error on div 2 */}
+
+ >
+ );
+ }
+ `,
+ errors: [
+ { messageId: "noDangerouslySetInnerhtmlWithChildren" },
+ { messageId: "noDangerouslySetInnerhtmlWithChildren" },
+ ],
+ },
],
valid: [
...allValid,
@@ -60,6 +84,10 @@ ruleTester.run(RULE_NAME, rule, {
const { a, b, ...props } = otherProps
const div =
`,
+ tsx`
+
+
+ `,
"Children",
'',
'\n',
diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.ts
index e4c8212bef..87a2dfe754 100644
--- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.ts
+++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml-with-children.ts
@@ -1,10 +1,9 @@
import type { RuleContext, RuleFeature } from "@eslint-react/kit";
-import type { TSESTree } from "@typescript-eslint/types";
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
import type { CamelCase } from "string-ts";
-import * as AST from "@eslint-react/ast";
import * as ER from "@eslint-react/core";
+import { AST_NODE_TYPES, type TSESTree } from "@typescript-eslint/types";
import { createRule } from "../utils";
export const RULE_NAME = "no-dangerously-set-innerhtml-with-children";
@@ -40,7 +39,8 @@ export function create(context: RuleContext): RuleListener {
JSXElement(node) {
const attributes = node.openingElement.attributes;
const initialScope = context.sourceCode.getScope(node);
- const hasChildren = hasChildrenWithin(node) || ER.hasAttribute(context, "children", attributes, initialScope);
+ const hasChildren = node.children.some(isSignificantChildren)
+ || ER.hasAttribute(context, "children", attributes, initialScope);
if (hasChildren && ER.hasAttribute(context, dangerouslySetInnerHTML, attributes, initialScope)) {
context.report({
messageId: "noDangerouslySetInnerhtmlWithChildren",
@@ -51,8 +51,26 @@ export function create(context: RuleContext): RuleListener {
};
}
-function hasChildrenWithin(node: TSESTree.JSXElement): boolean {
- return node.children.length > 0
- && node.children[0] != null
- && !AST.isLineBreak(node.children[0]);
+/**
+ * Check if a Literal or JSXText node is whitespace
+ * @param node The AST node to check
+ * @returns boolean `true` if the node is whitespace
+ */
+function isWhiteSpace(node: TSESTree.JSXText | TSESTree.Literal) {
+ return typeof node.value === "string" && node.raw.trim() === "";
+}
+
+/**
+ * Check if a Literal or JSXText node is padding spaces
+ * @param node The AST node to check
+ * @returns boolean
+ */
+function isPaddingSpaces(node: TSESTree.Node) {
+ return ER.isJsxText(node)
+ && isWhiteSpace(node)
+ && node.raw.includes("\n");
+}
+
+function isSignificantChildren(node: TSESTree.JSXElement["children"][number]) {
+ return node.type !== AST_NODE_TYPES.JSXText || !isPaddingSpaces(node);
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fd20058a9d..fa52fe1da5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4151,6 +4151,7 @@ packages:
'@zod/mini@4.0.0-beta.20250505T195954':
resolution: {integrity: sha512-ioybPtU4w4TqwHvJv0gkAiYNaBkZ/BaGHBpK7viCIRSE8BiiZucVZ8vS0YE04Qy1R120nAnFy1d+tD9ByMO0yw==}
+ deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
@@ -6923,6 +6924,7 @@ packages:
source-map@0.8.0-beta.0:
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
engines: {node: '>= 8'}
+ deprecated: The work that was done in this beta branch won't be included in future versions
space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}