diff --git a/README.md b/README.md
index 46a9d595fb..3c7e742ec3 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
# List of supported rules
+* [react/dom-elements-style-is-object](docs/rules/dom-elements-style-is-object.md): Enforce style prop value being an object
* [react/display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
* [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
diff --git a/docs/rules/dom-elements-style-is-object.md b/docs/rules/dom-elements-style-is-object.md
new file mode 100644
index 0000000000..54261a1304
--- /dev/null
+++ b/docs/rules/dom-elements-style-is-object.md
@@ -0,0 +1,51 @@
+# Enforce style prop value being an object (dom-elements-style-is-object)
+
+Require that the value of the prop `style` be an object or a variable that is
+an object.
+
+## Rule Details
+
+The following patterns are considered warnings:
+
+```jsx
+
+
+
+
+
+
+const styles = true;
+
+```
+
+```js
+React.createElement("div", { style: "color: 'red'" });
+
+React.createElement("div", { style: true });
+
+React.createElement("Hello", { style: true });
+
+const styles = true;
+React.createElement("div", { style: styles });
+```
+
+
+The following patterns are not considered warnings:
+
+```jsx
+
+
+
+
+const styles = { color: "red" };
+
+```
+
+```js
+React.createElement("div", { style: { color: 'red' }});
+
+React.createElement("Hello", { style: { color: 'red' }});
+
+const styles = { height: '100px' };
+React.createElement("div", { style: styles });
+```
diff --git a/index.js b/index.js
index 0a25847325..a58e21d1e3 100644
--- a/index.js
+++ b/index.js
@@ -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'),
+ 'dom-elements-style-is-object': require('./lib/rules/dom-elements-style-is-object')
};
var ruleNames = Object.keys(rules);
diff --git a/lib/rules/dom-elements-style-is-object.js b/lib/rules/dom-elements-style-is-object.js
new file mode 100644
index 0000000000..11c9fb1e15
--- /dev/null
+++ b/lib/rules/dom-elements-style-is-object.js
@@ -0,0 +1,80 @@
+/**
+ * @fileoverview Enforce style prop value is an object
+ * @author David Petersen
+ */
+'use strict';
+
+var variableUtil = require('../util/variable');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'Enforce style prop value is an object',
+ category: '',
+ recommended: false
+ },
+ schema: []
+ },
+
+ create: function(context) {
+ /**
+ * @param {object} node A Identifier node
+ */
+ function checkIdentifiers(node) {
+ var variable = variableUtil.variablesInScope(context).find(function (item) {
+ return item.name === node.name;
+ });
+
+ if (!variable || !variable.defs[0].node.init) {
+ return;
+ }
+
+ if (variable.defs[0].node.init.type === 'Literal') {
+ context.report(node, 'Style prop value must be an object');
+ }
+ }
+
+ return {
+ CallExpression: function(node) {
+ if (
+ node.callee
+ && node.callee.type === 'MemberExpression'
+ && node.callee.property.name === 'createElement'
+ && node.arguments.length > 1
+ ) {
+ if (node.arguments[1].type === 'ObjectExpression') {
+ var style = node.arguments[1].properties.find(function(property) {
+ return property.key.name === 'style';
+ });
+ if (style) {
+ if (style.value.type === 'Identifier') {
+ checkIdentifiers(style.value);
+ } else if (style.value.type === 'Literal') {
+ context.report(style.value, 'Style prop value must be an object');
+ }
+ }
+ }
+ }
+ },
+
+ JSXAttribute: function(node) {
+ if (node.name.name !== 'style') {
+ return;
+ }
+
+ if (
+ node.value.type !== 'JSXExpressionContainer'
+ || node.value.expression.type === 'Literal'
+ ) {
+ context.report(node, 'Style prop value must be an object');
+ } else if (node.value.expression.type === 'Identifier') {
+ checkIdentifiers(node.value.expression);
+ }
+ }
+ };
+ }
+};
diff --git a/tests/lib/rules/dom-elements-style-is-object.js b/tests/lib/rules/dom-elements-style-is-object.js
new file mode 100644
index 0000000000..872dfda7eb
--- /dev/null
+++ b/tests/lib/rules/dom-elements-style-is-object.js
@@ -0,0 +1,175 @@
+/**
+ * @fileoverview Enforce style prop value is an object
+ * @author David Petersen
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require('../../../lib/rules/dom-elements-style-is-object');
+var RuleTester = require('eslint').RuleTester;
+
+var parserOptions = {
+ ecmaVersion: 6,
+ ecmaFeatures: {
+ jsx: true
+ }
+};
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+var ruleTester = new RuleTester();
+ruleTester.run('dom-elements-style-is-object', rule, {
+ valid: [
+ {
+ code: '',
+ parserOptions: parserOptions
+ },
+ {
+ code: '',
+ parserOptions: parserOptions
+ },
+ {
+ code: [
+ 'function redDiv() {',
+ ' const styles = { color: "red" };',
+ ' return ;',
+ '}'
+ ].join('\n'),
+ parserOptions: parserOptions
+ },
+ {
+ code: [
+ 'function redDiv() {',
+ ' const styles = { color: "red" };',
+ ' return ;',
+ '}'
+ ].join('\n'),
+ parserOptions: parserOptions
+ },
+ {
+ code: [
+ 'const styles = { color: "red" };',
+ 'function redDiv() {',
+ ' return ;',
+ '}'
+ ].join('\n'),
+ parserOptions: parserOptions
+ },
+ {
+ code: [
+ 'function redDiv(props) {',
+ ' return ;',
+ '}'
+ ].join('\n'),
+ parserOptions: parserOptions
+ },
+ {
+ code: [
+ 'import styles from \'./styles\';',
+ 'function redDiv() {',
+ ' return ;',
+ '}'
+ ].join('\n'),
+ parserOptions: Object.assign({sourceType: 'module'}, parserOptions)
+ },
+ {
+ code: [
+ 'import mystyles from \'./styles\';',
+ 'const styles = Object.assign({ color: \'red\' }, mystyles);',
+ 'function redDiv() {',
+ ' return ;',
+ '}'
+ ].join('\n'),
+ parserOptions: Object.assign({sourceType: 'module'}, parserOptions)
+ },
+ {
+ code: [
+ 'const styles = Object.assign({ color: \'red\' }, mystyles);',
+ 'React.createElement("div", { style: styles });'
+ ].join('\n'),
+ parserOptions: Object.assign({sourceType: 'module'}, parserOptions)
+ }
+ ],
+ invalid: [
+ {
+ code: '',
+ errors: [{
+ message: 'Style prop value must be an object',
+ line: 1,
+ column: 6,
+ type: 'JSXAttribute'
+ }],
+ parserOptions: parserOptions
+ },
+ {
+ code: '',
+ errors: [{
+ message: 'Style prop value must be an object',
+ line: 1,
+ column: 8,
+ type: 'JSXAttribute'
+ }],
+ parserOptions: parserOptions
+ },
+ {
+ code: '',
+ errors: [{
+ message: 'Style prop value must be an object',
+ line: 1,
+ column: 6,
+ type: 'JSXAttribute'
+ }],
+ parserOptions: parserOptions
+ },
+ {
+ code: [
+ 'const styles = \'color: "red"\';',
+ 'function redDiv2() {',
+ ' return ;',
+ '}'
+ ].join('\n'),
+ errors: [{
+ message: 'Style prop value must be an object',
+ line: 3,
+ column: 22,
+ type: 'Identifier'
+ }],
+ parserOptions: parserOptions
+ },
+ {
+ code: [
+ 'const styles = \'color: "red"\';',
+ 'function redDiv2() {',
+ ' return ;',
+ '}'
+ ].join('\n'),
+ errors: [{
+ message: 'Style prop value must be an object',
+ line: 3,
+ column: 24,
+ type: 'Identifier'
+ }],
+ parserOptions: parserOptions
+ },
+ {
+ code: [
+ 'const styles = true;',
+ 'function redDiv() {',
+ ' return ;',
+ '}'
+ ].join('\n'),
+ errors: [{
+ message: 'Style prop value must be an object',
+ line: 3,
+ column: 22,
+ type: 'Identifier'
+ }],
+ parserOptions: parserOptions
+ }
+ ]
+});