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