-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
/
style-prop-object.js
145 lines (126 loc) · 4.04 KB
/
style-prop-object.js
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
134
135
136
137
138
139
140
141
142
143
144
145
/**
* @fileoverview Enforce style prop value is an object
* @author David Petersen
*/
'use strict';
const variableUtil = require('../util/variable');
const docsUrl = require('../util/docsUrl');
const isCreateElement = require('../util/isCreateElement');
const report = require('../util/report');
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
const messages = {
stylePropNotObject: 'Style prop value must be an object',
};
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
docs: {
description: 'Enforce style prop value is an object',
category: 'Possible Errors',
recommended: false,
url: docsUrl('style-prop-object'),
},
messages,
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.getVariableFromContext(context, node, node.name);
if (!variable || !variable.defs[0] || !variable.defs[0].node.init) {
return;
}
if (isNonNullaryLiteral(variable.defs[0].node.init)) {
report(context, messages.stylePropNotObject, 'stylePropNotObject', {
node,
});
}
}
return {
CallExpression(node) {
if (
isCreateElement(context, node)
&& node.arguments.length > 1
) {
if ('name' in node.arguments[0] && 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) => (
'key' in property
&& property.key
&& 'name' in property.key
&& property.key.name === 'style'
&& !property.computed
));
if (style && 'value' in style) {
if (style.value.type === 'Identifier') {
checkIdentifiers(style.value);
} else if (isNonNullaryLiteral(style.value)) {
report(context, messages.stylePropNotObject, 'stylePropNotObject', {
node: style.value,
});
}
}
}
}
},
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)) {
report(context, messages.stylePropNotObject, 'stylePropNotObject', {
node,
});
} else if (node.value.expression.type === 'Identifier') {
checkIdentifiers(node.value.expression);
}
},
};
},
};