1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | const variableUtil = require('../util/variable');
|
9 | const docsUrl = require('../util/docsUrl');
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | module.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 |
|
45 |
|
46 |
|
47 | function isNonNullaryLiteral(expression) {
|
48 | return expression.type === 'Literal' && expression.value !== null;
|
49 | }
|
50 |
|
51 | |
52 |
|
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 |
|
79 | const componentName = node.arguments[0].name;
|
80 |
|
81 |
|
82 | if (allowed.has(componentName)) {
|
83 |
|
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 |
|
108 | const parentElement = node.parent;
|
109 |
|
110 |
|
111 | if (parentElement && parentElement.type === 'JSXOpeningElement') {
|
112 |
|
113 | const name = parentElement.name && parentElement.name.name;
|
114 |
|
115 |
|
116 | if (allowed.has(name)) {
|
117 |
|
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 | };
|