-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
/
prefer-exact-props.js
142 lines (126 loc) · 4.22 KB
/
prefer-exact-props.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
/**
* @fileoverview Prefer exact proptype definitions
*/
'use strict';
const Components = require('../util/Components');
const docsUrl = require('../util/docsUrl');
const propsUtil = require('../util/props');
const propWrapperUtil = require('../util/propWrapper');
const variableUtil = require('../util/variable');
const PROP_TYPES_MESSAGE = 'Component propTypes should be exact by using {{exactPropWrappers}}.';
const FLOW_MESSAGE = 'Component flow props should be set with exact objects.';
// -----------------------------------------------------------------------------
// Rule Definition
// -----------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: 'Prefer exact proptype definitions',
category: 'Possible Errors',
recommended: false,
url: docsUrl('prefer-exact-props')
},
schema: []
},
create: Components.detect((context, components, utils) => {
const exactWrappers = propWrapperUtil.getExactPropWrapperFunctions(context);
function getPropTypesErrorMessage() {
const formattedWrappers = propWrapperUtil.formatPropWrapperFunctions(exactWrappers);
const message = exactWrappers.size > 1 ? `one of ${formattedWrappers}` : formattedWrappers;
return {exactPropWrappers: message};
}
function isNonExactObjectTypeAnnotation(node) {
return (
node &&
node.type === 'ObjectTypeAnnotation' &&
node.properties.length > 0 &&
!node.exact
);
}
function hasNonExactObjectTypeAnnotation(node) {
const typeAnnotation = node.typeAnnotation;
return (
typeAnnotation &&
typeAnnotation.typeAnnotation &&
isNonExactObjectTypeAnnotation(typeAnnotation.typeAnnotation)
);
}
function hasGenericTypeAnnotation(node) {
const typeAnnotation = node.typeAnnotation;
return (
typeAnnotation &&
typeAnnotation.typeAnnotation &&
typeAnnotation.typeAnnotation.type === 'GenericTypeAnnotation'
);
}
function isNonEmptyObjectExpression(node) {
return (
node &&
node.type === 'ObjectExpression' &&
node.properties.length > 0
);
}
return {
ClassProperty: function(node) {
if (!propsUtil.isPropTypesDeclaration(node)) {
return;
}
if (hasNonExactObjectTypeAnnotation(node)) {
context.report({
node: node,
message: FLOW_MESSAGE
});
} else if (isNonEmptyObjectExpression(node.value) && exactWrappers.size > 0) {
context.report({
node: node,
message: PROP_TYPES_MESSAGE,
data: getPropTypesErrorMessage()
});
}
},
Identifier: function(node) {
if (!utils.getParentStatelessComponent(node)) {
return;
}
if (hasNonExactObjectTypeAnnotation(node)) {
context.report({
node: node,
message: FLOW_MESSAGE
});
} else if (hasGenericTypeAnnotation(node)) {
const identifier = node.typeAnnotation.typeAnnotation.id.name;
const propsDefinition = variableUtil.findVariableByName(context, identifier);
if (isNonExactObjectTypeAnnotation(propsDefinition)) {
context.report({
node: node,
message: FLOW_MESSAGE
});
}
}
},
MemberExpression: function(node) {
if (!propsUtil.isPropTypesDeclaration(node) || exactWrappers.size === 0) {
return;
}
const right = node.parent.right;
if (isNonEmptyObjectExpression(right)) {
context.report({
node: node,
message: PROP_TYPES_MESSAGE,
data: getPropTypesErrorMessage()
});
} else if (right.type === 'Identifier') {
const identifier = right.name;
const propsDefinition = variableUtil.findVariableByName(context, identifier);
if (isNonEmptyObjectExpression(propsDefinition)) {
context.report({
node: node,
message: PROP_TYPES_MESSAGE,
data: getPropTypesErrorMessage()
});
}
}
}
};
})
};