feedback: disallow children prop with dangerouslySetInnerHTML prop
MODIFY
packages/@romejs/js-compiler/lint/rules/react/noDangerWithChildren.test.ts
packages/@romejs/js-compiler/lint/rules/react/noDangerWithChildren.test.ts
*/
import {test} from 'rome';
+
import {testLintMultiple} from '../testHelpers';
test(
@@ -16,22 +17,37 @@ test(
[
// INVALID
'<div dangerouslySetInnerHTML={{ __html: "HTML" }}>children</div>',
+ '<div dangerouslySetInnerHTML={{ __html: "HTML" }} children={"children"} />',
+ '<div dangerouslySetInnerHTML={{ __html: "HTML" }} children={["children"]} />',
'<Invalid dangerouslySetInnerHTML={{ __html: "HTML" }}>children</Invalid>',
+ '<Invalid dangerouslySetInnerHTML={{ __html: "HTML" }} children={"children"}/>',
'React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } }, "children")',
'React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } }, ["children"])',
+ 'React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" }, children: "children" })',
+ 'React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" }, children: ["children"] })',
+ 'React.createElement("Invalid", { dangerouslySetInnerHTML: { __html: "HTML" }, children: "children" })',
+ 'React.createElement("Invalid", { dangerouslySetInnerHTML: { __html: "HTML" }, children: ["children"] })',
'React.createElement("Invalid", { dangerouslySetInnerHTML: { __html: "HTML" } }, "children")',
'React.createElement("Invalid", { dangerouslySetInnerHTML: { __html: "HTML" } }, ["children"])',
// VALID
'<div dangerouslySetInnerHTML={{ __html: "HTML" }} />',
- '<Valid dangerouslySetInnerHTML={{ __html: "HTML" }} />',
'<div>children</div>',
+ '<div children={"children"} />',
+ '<div children={["children"]} />',
+ '<Valid dangerouslySetInnerHTML={{ __html: "HTML" }} />',
'<Valid>children</Valid>',
+ '<Valid children={"children"} />',
+ '<Valid children={["children"]} />',
'React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } })',
- 'React.createElement("Valid", { dangerouslySetInnerHTML: { __html: "HTML" } })',
'React.createElement("div", {}, "children")',
'React.createElement("div", {}, ["children"])',
+ 'React.createElement("div", { children: "children" })',
+ 'React.createElement("div", { children: ["children"] })',
+ 'React.createElement("Valid", { dangerouslySetInnerHTML: { __html: "HTML" } })',
'React.createElement("Valid", {}, "children")',
'React.createElement("Valid", {}, ["children"])',
+ 'React.createElement("Valid", { children: "children" })',
+ 'React.createElement("Valid", { children: ["children"] })',
],
{category: 'lint/noDangerWithChildren'},
);

MODIFY
packages/@romejs/js-compiler/lint/rules/react/noDangerWithChildren.ts
packages/@romejs/js-compiler/lint/rules/react/noDangerWithChildren.ts
*/
import {descriptions} from '@romejs/diagnostics';
-import {AnyNode} from '@romejs/js-ast';
+import {AnyNode, ObjectExpression} from '@romejs/js-ast';
import {Path} from '@romejs/js-compiler';
import {doesNodeMatchPattern} from '@romejs/js-ast-utils';
@@ -23,6 +23,23 @@ function jsxDangerWithChildren(node: AnyNode) {
return hasAttribute && node.children && node.children.length > 0;
}
+function jsxDangerWithPropChildren(node: AnyNode) {
+ if (node.type !== 'JSXElement') {
+ return false;
+ }
+
+ const hasDangerAttribute = !!node.attributes.find((attribute) =>
+ attribute.type === 'JSXAttribute' &&
+ attribute.name.name === 'dangerouslySetInnerHTML'
+ );
+
+ const hasChildrenAttribute = !!node.attributes.find((attribute) =>
+ attribute.type === 'JSXAttribute' && attribute.name.name === 'children'
+ );
+
+ return hasDangerAttribute && hasChildrenAttribute;
+}
+
function createElementDangerWithChildren(node: AnyNode): boolean {
if (node.type !== 'CallExpression') {
return false;
@@ -43,13 +60,51 @@ function createElementDangerWithChildren(node: AnyNode): boolean {
);
}
+function createElementDangerWithPropChildren(node: AnyNode): boolean {
+ if (node.type !== 'CallExpression') {
+ return false;
+ }
+
+ const propsArgument = node.arguments[1];
+
+ function hasDangerAttribute(node: ObjectExpression) {
+ return node.properties.some((prop) =>
+ prop.type === 'ObjectProperty' &&
+ prop.key.type === 'StaticPropertyKey' &&
+ prop.key.value.type === 'Identifier' &&
+ prop.key.value.name === 'dangerouslySetInnerHTML'
+ );
+ }
+
+ function hasChildrenAttribute(node: ObjectExpression) {
+ return node.properties.some((prop) =>
+ prop.type === 'ObjectProperty' &&
+ prop.key.type === 'StaticPropertyKey' &&
+ prop.key.value.type === 'Identifier' &&
+ prop.key.value.name === 'children'
+ );
+ }
+
+ return (
+ doesNodeMatchPattern(node.callee, 'React.createElement') &&
+ propsArgument.type === 'ObjectExpression' &&
+ hasDangerAttribute(propsArgument) &&
+ hasChildrenAttribute(propsArgument)
+ );
+}
+
export default {
name: 'noDangerWithChildren',
enter(path: Path): AnyNode {
const {node} = path;
- if (jsxDangerWithChildren(node) || createElementDangerWithChildren(node)) {
+ if (
+ jsxDangerWithChildren(node) ||
+ jsxDangerWithPropChildren(node) ||
+ createElementDangerWithChildren(node) ||
+ createElementDangerWithPropChildren(node)
+ ) {
path.context.addNodeDiagnostic(
node,
descriptions.LINT.NO_DANGER_WITH_CHILDREN,

feedback: disallow children prop with dangerouslySetInnerHTML prop