Skip to content

Commit

Permalink
Add no-danger-with-children rule (fixes #710)
Browse files Browse the repository at this point in the history
  • Loading branch information
petersendidit authored and yannickcr committed Aug 14, 2016
1 parent b164805 commit 7d3dbfd
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
* [react/forbid-component-props](docs/rules/forbid-component-props.md): Forbid certain props on Components
* [react/forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
* [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties
* [react/no-danger-with-children](docs/rules/no-danger-with-children.md): Prevent problem with children and props.dangerouslySetInnerHTML
* [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods
* [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount`
* [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate`
Expand Down
53 changes: 53 additions & 0 deletions docs/rules/no-danger-with-children.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Prevent problem with children and props.dangerouslySetInnerHTML (no-danger-with-children)

This rule helps prevent problems caused by using children and the dangerouslySetInnerHTML prop at the same time.
React will throw a warning if this rule is ignored.

## Rule Details

The following patterns are considered warnings:

```jsx
<div dangerouslySetInnerHTML={{ __html: "HTML" }}>
Children
</div>

<Hello dangerouslySetInnerHTML={{ __html: "HTML" }}>
Children
</Hello>

```

```js
React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } }, "Children");

React.createElement("Hello", { dangerouslySetInnerHTML: { __html: "HTML" } }, "Children");
```

The following patterns are not considered warnings:

```jsx
<div dangerouslySetInnerHTML={{ __html: "HTML" }} />

<Hello dangerouslySetInnerHTML={{ __html: "HTML" }} />

<div>
Children
</div>

<Hello>
Children
</Hello>

```

```js
React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } });

React.createElement("Hello", { dangerouslySetInnerHTML: { __html: "HTML" } });

React.createElement("div", {}, "Children");

React.createElement("Hello", {}, "Children");
```

3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ var rules = {
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
'jsx-filename-extension': require('./lib/rules/jsx-filename-extension'),
'require-optimization': require('./lib/rules/require-optimization'),
'no-find-dom-node': require('./lib/rules/no-find-dom-node')
'no-find-dom-node': require('./lib/rules/no-find-dom-node'),
'no-danger-with-children': require('./lib/rules/no-danger-with-children')
};

var ruleNames = Object.keys(rules);
Expand Down
80 changes: 80 additions & 0 deletions lib/rules/no-danger-with-children.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* @fileoverview Report when a DOM element is using both children and dangerouslySetInnerHTML
* @author David Petersen
*/
'use strict';

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: 'Report when a DOM element is using both children and dangerouslySetInnerHTML',
category: '',
recommended: false
},
schema: [] // no options
},
create: function(context) {
return {
JSXElement: function (node) {
var hasChildren = false;
var attributes = node.openingElement.attributes;

if (node.children.length) {
hasChildren = true;
} else {
var childrenProp = attributes.find(function (attribute) {
return attribute.name.name === 'children';
});
if (childrenProp) {
hasChildren = true;
}
}

if (attributes && hasChildren) {
var jsxElement = attributes.find(function (attribute) {
return attribute.name.name === 'dangerouslySetInnerHTML';
});


if (jsxElement) {
context.report(node, 'Only set one of `children` or `props.dangerouslySetInnerHTML`');
}
}
},
CallExpression: function (node) {
if (
node.callee
&& node.callee.type === 'MemberExpression'
&& node.callee.property.name === 'createElement'
&& node.arguments.length > 1
) {
var hasChildren = false;

var props = node.arguments[1].properties;
var dangerously = props.find(function(prop) {
return prop.key.name === 'dangerouslySetInnerHTML';
});


if (node.arguments.length === 2) {
var childrenProp = props.find(function(prop) {
return prop.key.name === 'children';
});
if (childrenProp) {
hasChildren = true;
}
} else {
hasChildren = true;
}

if (dangerously && hasChildren) {
context.report(node, 'Only set one of `children` or `props.dangerouslySetInnerHTML`');
}
}
}
};
}
};
139 changes: 139 additions & 0 deletions tests/lib/rules/no-danger-with-children.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* @fileoverview Report when a DOM element is using both children and dangerouslySetInnerHTML
* @author David Petersen
*/
'use strict';

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

var rule = require('../../../lib/rules/no-danger-with-children');
var RuleTester = require('eslint').RuleTester;

var parserOptions = {
ecmaVersion: 6,
ecmaFeatures: {
jsx: true
}
};

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

var ruleTester = new RuleTester();
ruleTester.run('no-danger-with-children', rule, {
valid: [
{
code: '<div>Children</div>',
parserOptions: parserOptions
},
{
code: '<div dangerouslySetInnerHTML={{ __html: "HTML" }} />',
parserOptions: parserOptions
},
{
code: '<Hello>Children</Hello>',
parserOptions: parserOptions
},
{
code: '<Hello dangerouslySetInnerHTML={{ __html: "HTML" }} />',
parserOptions: parserOptions
},
{
code: 'React.createElement("div", { dangerouslySetInnerHTML: { __html: "HTML" } });',
parserOptions: parserOptions
},
{
code: 'React.createElement("div", {}, "Children");',
parserOptions: parserOptions
},
{
code: 'React.createElement("Hello", { dangerouslySetInnerHTML: { __html: "HTML" } });',
parserOptions: parserOptions
},
{
code: 'React.createElement("Hello", {}, "Children");',
parserOptions: parserOptions
}
],
invalid: [
{
code: [
'<div dangerouslySetInnerHTML={{ __html: "HTML" }}>',
' Children',
'</div>'
].join('\n'),
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
parserOptions: parserOptions
},
{
code: '<div dangerouslySetInnerHTML={{ __html: "HTML" }} children="Children" />',
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
parserOptions: parserOptions
},
{
code: [
'<Hello dangerouslySetInnerHTML={{ __html: "HTML" }}>',
' Children',
'</Hello>'
].join('\n'),
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
parserOptions: parserOptions
},
{
code: '<Hello dangerouslySetInnerHTML={{ __html: "HTML" }} children="Children" />',
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
parserOptions: parserOptions
},
{
code: [
'React.createElement(',
' "div",',
' { dangerouslySetInnerHTML: { __html: "HTML" } },',
' "Children"',
');'
].join('\n'),
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
parserOptions: parserOptions
},
{
code: [
'React.createElement(',
' "div",',
' {',
' dangerouslySetInnerHTML: { __html: "HTML" },',
' children: "Children",',
' }',
');'
].join('\n'),
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
parserOptions: parserOptions
},
{
code: [
'React.createElement(',
' "Hello",',
' { dangerouslySetInnerHTML: { __html: "HTML" } },',
' "Children"',
');'
].join('\n'),
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
parserOptions: parserOptions
},
{
code: [
'React.createElement(',
' "Hello",',
' {',
' dangerouslySetInnerHTML: { __html: "HTML" },',
' children: "Children",',
' }',
');'
].join('\n'),
errors: [{message: 'Only set one of `children` or `props.dangerouslySetInnerHTML`'}],
parserOptions: parserOptions
}
]
});

0 comments on commit 7d3dbfd

Please sign in to comment.