Skip to content

Commit 8773a33

Browse files
committed
Add new dom-elements-no-danger-with-children rule (Fixes jsx-eslint#710)
Prevents dangerouslySetInnerHTML and children from being used at the same time
1 parent 8b8eba7 commit 8773a33

File tree

5 files changed

+209
-1
lines changed

5 files changed

+209
-1
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
8080
# List of supported rules
8181

8282
* [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
83+
* [react/dom-elements-no-danger-with-children](docs/rules/dom-elements-no-danger-with-children.md): Prevent problem with children and props.dangerouslySetInnerHTML
8384
* [react/forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
8485
* [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties
8586
* [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods
@@ -156,6 +157,7 @@ See [ESLint documentation](http://eslint.org/docs/user-guide/configuring#extendi
156157
The rules enabled in this configuration are:
157158

158159
* [react/display-name](docs/rules/display-name.md)
160+
* [react/dom-elements-no-danger-with-children](docs/rules/dom-elements-no-danger-with-children.md)
159161
* [react/jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md)
160162
* [react/jsx-no-undef](docs/rules/jsx-no-undef.md)
161163
* [react/jsx-uses-react](docs/rules/jsx-uses-react.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Prevent problem with children and props.dangerouslySetInnerHTML (dom-elements-no-danger-with-children)
2+
3+
This rule helps prevent problems caused by using children and the dangerouslySetInnerHTML prop at the same time
4+
5+
## Rule Details
6+
7+
The following patterns are considered warnings:
8+
9+
```js
10+
<div dangerouslySetInnerHTML={{ __html: "HTML" }}>
11+
Children
12+
</div>
13+
```
14+
15+
```js
16+
React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } }, "Children");
17+
```
18+
19+
20+
The following patterns are not considered warnings:
21+
22+
```js
23+
<div dangerouslySetInnerHTML={{ __html: "HTML" }} />
24+
```
25+
26+
```js
27+
<div>
28+
Children
29+
</div>
30+
```
31+
32+
```js
33+
React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } });
34+
```
35+
36+
```js
37+
React.createElement("div", {}, "Children");
38+
```
39+

index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ var rules = {
5050
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
5151
'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'),
5252
'require-optimization': require('./lib/rules/require-optimization'),
53-
'no-find-dom-node': require('./lib/rules/no-find-dom-node')
53+
'no-find-dom-node': require('./lib/rules/no-find-dom-node'),
54+
'dom-elements-no-danger-with-children': require('./lib/rules/dom-elements-no-danger-with-children')
5455
};
5556

5657
var ruleNames = Object.keys(rules);
@@ -70,6 +71,7 @@ module.exports = {
7071
},
7172
rules: {
7273
'react/display-name': 2,
74+
'react/dom-elements-no-danger-with-children': 2,
7375
'react/jsx-no-duplicate-props': 2,
7476
'react/jsx-no-undef': 2,
7577
'react/jsx-uses-react': 2,
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: true
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+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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/dom-elements-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('dom-elements-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: 'React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } });',
38+
parserOptions: parserOptions
39+
},
40+
{
41+
code: 'React.createElement("div", {}, "Children");',
42+
parserOptions: parserOptions
43+
}
44+
],
45+
invalid: [
46+
{
47+
code: [
48+
'<div dangerouslySetInnerHTML={{ __html: "HTML" }}>',
49+
' Children',
50+
'</div>'
51+
].join('\n'),
52+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
53+
parserOptions: parserOptions
54+
},
55+
{
56+
code: '<div dangerouslySetInnerHTML={{ __html: "HTML" }} children="Children" />',
57+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
58+
parserOptions: parserOptions
59+
},
60+
{
61+
code: [
62+
'React.createElement(',
63+
' "div",',
64+
' { dangerouslySetInnerHTML: { __html: "HTML" } },',
65+
' "Children"',
66+
');'
67+
].join('\n'),
68+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
69+
parserOptions: parserOptions
70+
},
71+
{
72+
code: [
73+
'React.createElement(',
74+
' "div",',
75+
' {',
76+
' dangerouslySetInnerHTML: { __html: "HTML" },',
77+
' children: "Children",',
78+
' }',
79+
');'
80+
].join('\n'),
81+
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
82+
parserOptions: parserOptions
83+
}
84+
]
85+
});

0 commit comments

Comments
 (0)