Skip to content

Commit 7d3dbfd

Browse files
petersendidityannickcr
authored andcommitted
Add no-danger-with-children rule (fixes #710)
1 parent b164805 commit 7d3dbfd

File tree

5 files changed

+275
-1
lines changed

5 files changed

+275
-1
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
8383
* [react/forbid-component-props](docs/rules/forbid-component-props.md): Forbid certain props on Components
8484
* [react/forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
8585
* [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties
86+
* [react/no-danger-with-children](docs/rules/no-danger-with-children.md): Prevent problem with children and props.dangerouslySetInnerHTML
8687
* [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods
8788
* [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount`
8889
* [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate`

docs/rules/no-danger-with-children.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Prevent problem with children and props.dangerouslySetInnerHTML (no-danger-with-children)
2+
3+
This rule helps prevent problems caused by using children and the dangerouslySetInnerHTML prop at the same time.
4+
React will throw a warning if this rule is ignored.
5+
6+
## Rule Details
7+
8+
The following patterns are considered warnings:
9+
10+
```jsx
11+
<div dangerouslySetInnerHTML={{ __html: "HTML" }}>
12+
Children
13+
</div>
14+
15+
<Hello dangerouslySetInnerHTML={{ __html: "HTML" }}>
16+
Children
17+
</Hello>
18+
19+
```
20+
21+
```js
22+
React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } }, "Children");
23+
24+
React.createElement("Hello", { dangerouslySetInnerHTML: { __html: "HTML" } }, "Children");
25+
```
26+
27+
The following patterns are not considered warnings:
28+
29+
```jsx
30+
<div dangerouslySetInnerHTML={{ __html: "HTML" }} />
31+
32+
<Hello dangerouslySetInnerHTML={{ __html: "HTML" }} />
33+
34+
<div>
35+
Children
36+
</div>
37+
38+
<Hello>
39+
Children
40+
</Hello>
41+
42+
```
43+
44+
```js
45+
React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } });
46+
47+
React.createElement("Hello", { dangerouslySetInnerHTML: { __html: "HTML" } });
48+
49+
React.createElement("div", {}, "Children");
50+
51+
React.createElement("Hello", {}, "Children");
52+
```
53+

index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ var rules = {
5353
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
5454
'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'),
5555
'require-optimization': require('./lib/rules/require-optimization'),
56-
'no-find-dom-node': require('./lib/rules/no-find-dom-node')
56+
'no-find-dom-node': require('./lib/rules/no-find-dom-node'),
57+
'no-danger-with-children': require('./lib/rules/no-danger-with-children')
5758
};
5859

5960
var ruleNames = Object.keys(rules);

lib/rules/no-danger-with-children.js

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @fileoverview Report when a DOM element is using both children and dangerouslySetInnerHTML
3+
* @author David Petersen
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Rule Definition
9+
// ------------------------------------------------------------------------------
10+
module.exports = {
11+
meta: {
12+
docs: {
13+
description: 'Report when a DOM element is using both children and dangerouslySetInnerHTML',
14+
category: '',
15+
recommended: false
16+
},
17+
schema: [] // no options
18+
},
19+
create: function(context) {
20+
return {
21+
JSXElement: function (node) {
22+
var hasChildren = false;
23+
var attributes = node.openingElement.attributes;
24+
25+
if (node.children.length) {
26+
hasChildren = true;
27+
} else {
28+
var childrenProp = attributes.find(function (attribute) {
29+
return attribute.name.name === 'children';
30+
});
31+
if (childrenProp) {
32+
hasChildren = true;
33+
}
34+
}
35+
36+
if (attributes && hasChildren) {
37+
var jsxElement = attributes.find(function (attribute) {
38+
return attribute.name.name === 'dangerouslySetInnerHTML';
39+
});
40+
41+
42+
if (jsxElement) {
43+
context.report(node, 'Only set one of `children` or `props.dangerouslySetInnerHTML`');
44+
}
45+
}
46+
},
47+
CallExpression: function (node) {
48+
if (
49+
node.callee
50+
&& node.callee.type === 'MemberExpression'
51+
&& node.callee.property.name === 'createElement'
52+
&& node.arguments.length > 1
53+
) {
54+
var hasChildren = false;
55+
56+
var props = node.arguments[1].properties;
57+
var dangerously = props.find(function(prop) {
58+
return prop.key.name === 'dangerouslySetInnerHTML';
59+
});
60+
61+
62+
if (node.arguments.length === 2) {
63+
var childrenProp = props.find(function(prop) {
64+
return prop.key.name === 'children';
65+
});
66+
if (childrenProp) {
67+
hasChildren = true;
68+
}
69+
} else {
70+
hasChildren = true;
71+
}
72+
73+
if (dangerously && hasChildren) {
74+
context.report(node, 'Only set one of `children` or `props.dangerouslySetInnerHTML`');
75+
}
76+
}
77+
}
78+
};
79+
}
80+
};
+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/**
2+
* @fileoverview Report when a DOM element is using both children and dangerouslySetInnerHTML
3+
* @author David Petersen
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var rule = require('../../../lib/rules/no-danger-with-children');
12+
var RuleTester = require('eslint').RuleTester;
13+
14+
var parserOptions = {
15+
ecmaVersion: 6,
16+
ecmaFeatures: {
17+
jsx: true
18+
}
19+
};
20+
21+
// ------------------------------------------------------------------------------
22+
// Tests
23+
// ------------------------------------------------------------------------------
24+
25+
var ruleTester = new RuleTester();
26+
ruleTester.run('no-danger-with-children', rule, {
27+
valid: [
28+
{
29+
code: '<div>Children</div>',
30+
parserOptions: parserOptions
31+
},
32+
{
33+
code: '<div dangerouslySetInnerHTML={{ __html: "HTML" }} />',
34+
parserOptions: parserOptions
35+
},
36+
{
37+
code: '<Hello>Children</Hello>',
38+
parserOptions: parserOptions
39+
},
40+
{
41+
code: '<Hello dangerouslySetInnerHTML={{ __html: "HTML" }} />',
42+
parserOptions: parserOptions
43+
},
44+
{
45+
code: 'React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } });',
46+
parserOptions: parserOptions
47+
},
48+
{
49+
code: 'React.createElement("div", {}, "Children");',
50+
parserOptions: parserOptions
51+
},
52+
{
53+
code: 'React.createElement("Hello", { dangerouslySetInnerHTML: { __html: "HTML" } });',
54+
parserOptions: parserOptions
55+
},
56+
{
57+
code: 'React.createElement("Hello", {}, "Children");',
58+
parserOptions: parserOptions
59+
}
60+
],
61+
invalid: [
62+
{
63+
code: [
64+
'<div dangerouslySetInnerHTML={{ __html: "HTML" }}>',
65+
' Children',
66+
'</div>'
67+
].join('\n'),
68+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
69+
parserOptions: parserOptions
70+
},
71+
{
72+
code: '<div dangerouslySetInnerHTML={{ __html: "HTML" }} children="Children" />',
73+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
74+
parserOptions: parserOptions
75+
},
76+
{
77+
code: [
78+
'<Hello dangerouslySetInnerHTML={{ __html: "HTML" }}>',
79+
' Children',
80+
'</Hello>'
81+
].join('\n'),
82+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
83+
parserOptions: parserOptions
84+
},
85+
{
86+
code: '<Hello dangerouslySetInnerHTML={{ __html: "HTML" }} children="Children" />',
87+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
88+
parserOptions: parserOptions
89+
},
90+
{
91+
code: [
92+
'React.createElement(',
93+
' "div",',
94+
' { dangerouslySetInnerHTML: { __html: "HTML" } },',
95+
' "Children"',
96+
');'
97+
].join('\n'),
98+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
99+
parserOptions: parserOptions
100+
},
101+
{
102+
code: [
103+
'React.createElement(',
104+
' "div",',
105+
' {',
106+
' dangerouslySetInnerHTML: { __html: "HTML" },',
107+
' children: "Children",',
108+
' }',
109+
');'
110+
].join('\n'),
111+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
112+
parserOptions: parserOptions
113+
},
114+
{
115+
code: [
116+
'React.createElement(',
117+
' "Hello",',
118+
' { dangerouslySetInnerHTML: { __html: "HTML" } },',
119+
' "Children"',
120+
');'
121+
].join('\n'),
122+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
123+
parserOptions: parserOptions
124+
},
125+
{
126+
code: [
127+
'React.createElement(',
128+
' "Hello",',
129+
' {',
130+
' dangerouslySetInnerHTML: { __html: "HTML" },',
131+
' children: "Children",',
132+
' }',
133+
');'
134+
].join('\n'),
135+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
136+
parserOptions: parserOptions
137+
}
138+
]
139+
});

0 commit comments

Comments
 (0)