Skip to content

Commit bb973bc

Browse files
committed
Add new dom-elements-style-is-object rule (Fixes jsx-eslint#715)
1 parent b164805 commit bb973bc

File tree

5 files changed

+310
-1
lines changed

5 files changed

+310
-1
lines changed

README.md

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

8080
# List of supported rules
8181

82+
* [react/dom-elements-style-is-object](docs/rules/dom-elements-style-is-object.md): Enforce style prop value being an object
8283
* [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
8384
* [react/forbid-component-props](docs/rules/forbid-component-props.md): Forbid certain props on Components
8485
* [react/forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
@@ -157,6 +158,7 @@ See [ESLint documentation](http://eslint.org/docs/user-guide/configuring#extendi
157158

158159
The rules enabled in this configuration are:
159160

161+
* [react/dom-elements-style-is-object](docs/rules/dom-elements-style-is-object.md)
160162
* [react/display-name](docs/rules/display-name.md)
161163
* [react/jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md)
162164
* [react/jsx-no-undef](docs/rules/jsx-no-undef.md)
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Enforce style prop value being an object (dom-elements-style-is-object)
2+
3+
Require that the value of the prop `style` be an object or a variable that is
4+
an object.
5+
6+
## Rule Details
7+
8+
The following patterns are considered warnings:
9+
10+
```jsx
11+
<div style="color: 'red'" />
12+
13+
<div style={true} />
14+
15+
<Hello style={true} />
16+
17+
const styles = true;
18+
<div style={styles} />
19+
```
20+
21+
```js
22+
React.createElement("div", { style: "color: 'red'" });
23+
24+
React.createElement("div", { style: true });
25+
26+
React.createElement("Hello", { style: true });
27+
28+
const styles = true;
29+
React.createElement("div", { style: styles });
30+
```
31+
32+
33+
The following patterns are not considered warnings:
34+
35+
```jsx
36+
<div style={{ color: "red" }} />
37+
38+
<Hello style={{ color: "red" }} />
39+
40+
const styles = { color: "red" };
41+
<div style={styles} />
42+
```
43+
44+
```js
45+
React.createElement("div", { style: { color: 'red' }});
46+
47+
React.createElement("Hello", { style: { color: 'red' }});
48+
49+
const styles = { height: '100px' };
50+
React.createElement("div", { style: styles });
51+
```

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+
'dom-elements-style-is-object': require('./lib/rules/dom-elements-style-is-object')
5758
};
5859

5960
var ruleNames = Object.keys(rules);
+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @fileoverview Enforce style prop value is an object
3+
* @author David Petersen
4+
*/
5+
'use strict';
6+
7+
var variableUtil = require('../util/variable');
8+
9+
// ------------------------------------------------------------------------------
10+
// Rule Definition
11+
// ------------------------------------------------------------------------------
12+
13+
module.exports = {
14+
meta: {
15+
docs: {
16+
description: 'Enforce style prop value is an object',
17+
category: '',
18+
recommended: false
19+
},
20+
schema: []
21+
},
22+
23+
create: function(context) {
24+
/**
25+
* @param {object} node A Identifier node
26+
*/
27+
function checkIdentifiers(node) {
28+
var variable = variableUtil.variablesInScope(context).find(function (item) {
29+
return item.name === node.name;
30+
});
31+
32+
if (!variable || !variable.defs[0].node.init) {
33+
return;
34+
}
35+
36+
if (variable.defs[0].node.init.type === 'Literal') {
37+
context.report(node, 'Style prop value must be an object');
38+
}
39+
}
40+
41+
return {
42+
CallExpression: function(node) {
43+
if (
44+
node.callee
45+
&& node.callee.type === 'MemberExpression'
46+
&& node.callee.property.name === 'createElement'
47+
&& node.arguments.length > 1
48+
) {
49+
if (node.arguments[1].type === 'ObjectExpression') {
50+
var style = node.arguments[1].properties.find(function(property) {
51+
return property.key.name === 'style';
52+
});
53+
if (style) {
54+
if (style.value.type === 'Identifier') {
55+
checkIdentifiers(style.value);
56+
} else if (style.value.type === 'Literal') {
57+
context.report(style.value, 'Style prop value must be an object');
58+
}
59+
}
60+
}
61+
}
62+
},
63+
64+
JSXAttribute: function(node) {
65+
if (node.name.name !== 'style') {
66+
return;
67+
}
68+
69+
if (
70+
node.value.type !== 'JSXExpressionContainer'
71+
|| node.value.expression.type === 'Literal'
72+
) {
73+
context.report(node, 'Style prop value must be an object');
74+
} else if (node.value.expression.type === 'Identifier') {
75+
checkIdentifiers(node.value.expression);
76+
}
77+
}
78+
};
79+
}
80+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/**
2+
* @fileoverview Enforce style prop value is an object
3+
* @author David Petersen
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
var rule = require('../../../lib/rules/dom-elements-style-is-object');
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-style-is-object', rule, {
27+
valid: [
28+
{
29+
code: '<div style={{ color: "red" }} />',
30+
parserOptions: parserOptions
31+
},
32+
{
33+
code: '<Hello style={{ color: "red" }} />',
34+
parserOptions: parserOptions
35+
},
36+
{
37+
code: [
38+
'function redDiv() {',
39+
' const styles = { color: "red" };',
40+
' return <div style={styles} />;',
41+
'}'
42+
].join('\n'),
43+
parserOptions: parserOptions
44+
},
45+
{
46+
code: [
47+
'function redDiv() {',
48+
' const styles = { color: "red" };',
49+
' return <Hello style={styles} />;',
50+
'}'
51+
].join('\n'),
52+
parserOptions: parserOptions
53+
},
54+
{
55+
code: [
56+
'const styles = { color: "red" };',
57+
'function redDiv() {',
58+
' return <div style={styles} />;',
59+
'}'
60+
].join('\n'),
61+
parserOptions: parserOptions
62+
},
63+
{
64+
code: [
65+
'function redDiv(props) {',
66+
' return <div style={props.styles} />;',
67+
'}'
68+
].join('\n'),
69+
parserOptions: parserOptions
70+
},
71+
{
72+
code: [
73+
'import styles from \'./styles\';',
74+
'function redDiv() {',
75+
' return <div style={styles} />;',
76+
'}'
77+
].join('\n'),
78+
parserOptions: Object.assign({sourceType: 'module'}, parserOptions)
79+
},
80+
{
81+
code: [
82+
'import mystyles from \'./styles\';',
83+
'const styles = Object.assign({ color: \'red\' }, mystyles);',
84+
'function redDiv() {',
85+
' return <div style={styles} />;',
86+
'}'
87+
].join('\n'),
88+
parserOptions: Object.assign({sourceType: 'module'}, parserOptions)
89+
},
90+
{
91+
code: [
92+
'const styles = Object.assign({ color: \'red\' }, mystyles);',
93+
'React.createElement("div", { style: styles });'
94+
].join('\n'),
95+
parserOptions: Object.assign({sourceType: 'module'}, parserOptions)
96+
}
97+
],
98+
invalid: [
99+
{
100+
code: '<div style="color: \'red\'" />',
101+
errors: [{
102+
message: 'Style prop value must be an object',
103+
line: 1,
104+
column: 6,
105+
type: 'JSXAttribute'
106+
}],
107+
parserOptions: parserOptions
108+
},
109+
{
110+
code: '<Hello style="color: \'red\'" />',
111+
errors: [{
112+
message: 'Style prop value must be an object',
113+
line: 1,
114+
column: 8,
115+
type: 'JSXAttribute'
116+
}],
117+
parserOptions: parserOptions
118+
},
119+
{
120+
code: '<div style={true} />',
121+
errors: [{
122+
message: 'Style prop value must be an object',
123+
line: 1,
124+
column: 6,
125+
type: 'JSXAttribute'
126+
}],
127+
parserOptions: parserOptions
128+
},
129+
{
130+
code: [
131+
'const styles = \'color: "red"\';',
132+
'function redDiv2() {',
133+
' return <div style={styles} />;',
134+
'}'
135+
].join('\n'),
136+
errors: [{
137+
message: 'Style prop value must be an object',
138+
line: 3,
139+
column: 22,
140+
type: 'Identifier'
141+
}],
142+
parserOptions: parserOptions
143+
},
144+
{
145+
code: [
146+
'const styles = \'color: "red"\';',
147+
'function redDiv2() {',
148+
' return <Hello style={styles} />;',
149+
'}'
150+
].join('\n'),
151+
errors: [{
152+
message: 'Style prop value must be an object',
153+
line: 3,
154+
column: 24,
155+
type: 'Identifier'
156+
}],
157+
parserOptions: parserOptions
158+
},
159+
{
160+
code: [
161+
'const styles = true;',
162+
'function redDiv() {',
163+
' return <div style={styles} />;',
164+
'}'
165+
].join('\n'),
166+
errors: [{
167+
message: 'Style prop value must be an object',
168+
line: 3,
169+
column: 22,
170+
type: 'Identifier'
171+
}],
172+
parserOptions: parserOptions
173+
}
174+
]
175+
});

0 commit comments

Comments
 (0)