UNPKG

3.8 kBJavaScriptView Raw
1/**
2 * @fileoverview Enforce style prop value is an object
3 * @author David Petersen
4 */
5
6'use strict';
7
8const variableUtil = require('../util/variable');
9const docsUrl = require('../util/docsUrl');
10
11// ------------------------------------------------------------------------------
12// Rule Definition
13// ------------------------------------------------------------------------------
14
15module.exports = {
16 meta: {
17 docs: {
18 description: 'Enforce style prop value is an object',
19 category: '',
20 recommended: false,
21 url: docsUrl('style-prop-object')
22 },
23 schema: [
24 {
25 type: 'object',
26 properties: {
27 allow: {
28 type: 'array',
29 items: {
30 type: 'string'
31 },
32 additionalItems: false,
33 uniqueItems: true
34 }
35 }
36 }
37 ]
38 },
39
40 create(context) {
41 const allowed = new Set(context.options.length > 0 && context.options[0].allow || []);
42
43 /**
44 * @param {ASTNode} expression An Identifier node
45 * @returns {boolean}
46 */
47 function isNonNullaryLiteral(expression) {
48 return expression.type === 'Literal' && expression.value !== null;
49 }
50
51 /**
52 * @param {object} node A Identifier node
53 */
54 function checkIdentifiers(node) {
55 const variable = variableUtil.variablesInScope(context).find((item) => item.name === node.name);
56
57 if (!variable || !variable.defs[0] || !variable.defs[0].node.init) {
58 return;
59 }
60
61 if (isNonNullaryLiteral(variable.defs[0].node.init)) {
62 context.report({
63 node,
64 message: 'Style prop value must be an object'
65 });
66 }
67 }
68
69 return {
70 CallExpression(node) {
71 if (
72 node.callee
73 && node.callee.type === 'MemberExpression'
74 && node.callee.property.name === 'createElement'
75 && node.arguments.length > 1
76 ) {
77 if (node.arguments[0].name) {
78 // store name of component
79 const componentName = node.arguments[0].name;
80
81 // allowed list contains the name
82 if (allowed.has(componentName)) {
83 // abort operation
84 return;
85 }
86 }
87 if (node.arguments[1].type === 'ObjectExpression') {
88 const style = node.arguments[1].properties.find((property) => property.key && property.key.name === 'style' && !property.computed);
89 if (style) {
90 if (style.value.type === 'Identifier') {
91 checkIdentifiers(style.value);
92 } else if (isNonNullaryLiteral(style.value)) {
93 context.report({
94 node: style.value,
95 message: 'Style prop value must be an object'
96 });
97 }
98 }
99 }
100 }
101 },
102
103 JSXAttribute(node) {
104 if (!node.value || node.name.name !== 'style') {
105 return;
106 }
107 // store parent element
108 const parentElement = node.parent;
109
110 // parent element is a JSXOpeningElement
111 if (parentElement && parentElement.type === 'JSXOpeningElement') {
112 // get the name of the JSX element
113 const name = parentElement.name && parentElement.name.name;
114
115 // allowed list contains the name
116 if (allowed.has(name)) {
117 // abort operation
118 return;
119 }
120 }
121
122 if (node.value.type !== 'JSXExpressionContainer' || isNonNullaryLiteral(node.value.expression)) {
123 context.report({
124 node,
125 message: 'Style prop value must be an object'
126 });
127 } else if (node.value.expression.type === 'Identifier') {
128 checkIdentifiers(node.value.expression);
129 }
130 }
131 };
132 }
133};