1 |
|
2 |
|
3 |
|
4 |
|
5 | 'use strict';
|
6 |
|
7 | const variableUtil = require('../util/variable');
|
8 | const propsUtil = require('../util/props');
|
9 | const astUtil = require('../util/ast');
|
10 | const docsUrl = require('../util/docsUrl');
|
11 | const propWrapperUtil = require('../util/propWrapper');
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | const DEFAULTS = ['any', 'array', 'object'];
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | module.exports = {
|
24 | meta: {
|
25 | docs: {
|
26 | description: 'Forbid certain propTypes',
|
27 | category: 'Best Practices',
|
28 | recommended: false,
|
29 | url: docsUrl('forbid-prop-types')
|
30 | },
|
31 |
|
32 | schema: [{
|
33 | type: 'object',
|
34 | properties: {
|
35 | forbid: {
|
36 | type: 'array',
|
37 | items: {
|
38 | type: 'string'
|
39 | }
|
40 | },
|
41 | checkContextTypes: {
|
42 | type: 'boolean'
|
43 | },
|
44 | checkChildContextTypes: {
|
45 | type: 'boolean'
|
46 | }
|
47 | },
|
48 | additionalProperties: true
|
49 | }]
|
50 | },
|
51 |
|
52 | create(context) {
|
53 | const configuration = context.options[0] || {};
|
54 | const checkContextTypes = configuration.checkContextTypes || false;
|
55 | const checkChildContextTypes = configuration.checkChildContextTypes || false;
|
56 |
|
57 | function isForbidden(type) {
|
58 | const forbid = configuration.forbid || DEFAULTS;
|
59 | return forbid.indexOf(type) >= 0;
|
60 | }
|
61 |
|
62 | function reportIfForbidden(type, declaration, target) {
|
63 | if (isForbidden(type)) {
|
64 | context.report({
|
65 | node: declaration,
|
66 | message: `Prop type \`${target}\` is forbidden`
|
67 | });
|
68 | }
|
69 | }
|
70 |
|
71 | function shouldCheckContextTypes(node) {
|
72 | if (checkContextTypes && propsUtil.isContextTypesDeclaration(node)) {
|
73 | return true;
|
74 | }
|
75 | return false;
|
76 | }
|
77 |
|
78 | function shouldCheckChildContextTypes(node) {
|
79 | if (checkChildContextTypes && propsUtil.isChildContextTypesDeclaration(node)) {
|
80 | return true;
|
81 | }
|
82 | return false;
|
83 | }
|
84 |
|
85 | |
86 |
|
87 |
|
88 |
|
89 |
|
90 | function checkProperties(declarations) {
|
91 | if (declarations) {
|
92 | declarations.forEach((declaration) => {
|
93 | if (declaration.type !== 'Property') {
|
94 | return;
|
95 | }
|
96 | let target;
|
97 | let value = declaration.value;
|
98 | if (
|
99 | value.type === 'MemberExpression'
|
100 | && value.property
|
101 | && value.property.name
|
102 | && value.property.name === 'isRequired'
|
103 | ) {
|
104 | value = value.object;
|
105 | }
|
106 | if (value.type === 'CallExpression') {
|
107 | value.arguments.forEach((arg) => {
|
108 | reportIfForbidden(arg.name, declaration, target);
|
109 | });
|
110 | value = value.callee;
|
111 | }
|
112 | if (value.property) {
|
113 | target = value.property.name;
|
114 | } else if (value.type === 'Identifier') {
|
115 | target = value.name;
|
116 | }
|
117 | reportIfForbidden(target, declaration, target);
|
118 | });
|
119 | }
|
120 | }
|
121 |
|
122 | function checkNode(node) {
|
123 | switch (node && node.type) {
|
124 | case 'ObjectExpression':
|
125 | checkProperties(node.properties);
|
126 | break;
|
127 | case 'Identifier': {
|
128 | const propTypesObject = variableUtil.findVariableByName(context, node.name);
|
129 | if (propTypesObject && propTypesObject.properties) {
|
130 | checkProperties(propTypesObject.properties);
|
131 | }
|
132 | break;
|
133 | }
|
134 | case 'CallExpression': {
|
135 | const innerNode = node.arguments && node.arguments[0];
|
136 | if (propWrapperUtil.isPropWrapperFunction(context, context.getSource(node.callee)) && innerNode) {
|
137 | checkNode(innerNode);
|
138 | }
|
139 | break;
|
140 | }
|
141 | default:
|
142 | break;
|
143 | }
|
144 | }
|
145 |
|
146 | return {
|
147 | ClassProperty(node) {
|
148 | if (
|
149 | !propsUtil.isPropTypesDeclaration(node)
|
150 | && !shouldCheckContextTypes(node)
|
151 | && !shouldCheckChildContextTypes(node)
|
152 | ) {
|
153 | return;
|
154 | }
|
155 | checkNode(node.value);
|
156 | },
|
157 |
|
158 | MemberExpression(node) {
|
159 | if (
|
160 | !propsUtil.isPropTypesDeclaration(node)
|
161 | && !shouldCheckContextTypes(node)
|
162 | && !shouldCheckChildContextTypes(node)
|
163 | ) {
|
164 | return;
|
165 | }
|
166 |
|
167 | checkNode(node.parent.right);
|
168 | },
|
169 |
|
170 | CallExpression(node) {
|
171 | if (
|
172 | node.arguments.length > 0
|
173 | && (node.callee.name === 'shape' || astUtil.getPropertyName(node.callee) === 'shape')
|
174 | ) {
|
175 | checkProperties(node.arguments[0].properties);
|
176 | }
|
177 | },
|
178 |
|
179 | MethodDefinition(node) {
|
180 | if (
|
181 | !propsUtil.isPropTypesDeclaration(node)
|
182 | && !shouldCheckContextTypes(node)
|
183 | && !shouldCheckChildContextTypes(node)
|
184 | ) {
|
185 | return;
|
186 | }
|
187 |
|
188 | const returnStatement = astUtil.findReturnStatement(node);
|
189 |
|
190 | if (returnStatement && returnStatement.argument) {
|
191 | checkNode(returnStatement.argument);
|
192 | }
|
193 | },
|
194 |
|
195 | ObjectExpression(node) {
|
196 | node.properties.forEach((property) => {
|
197 | if (!property.key) {
|
198 | return;
|
199 | }
|
200 |
|
201 | if (
|
202 | !propsUtil.isPropTypesDeclaration(property)
|
203 | && !shouldCheckContextTypes(property)
|
204 | && !shouldCheckChildContextTypes(property)
|
205 | ) {
|
206 | return;
|
207 | }
|
208 | if (property.value.type === 'ObjectExpression') {
|
209 | checkProperties(property.value.properties);
|
210 | }
|
211 | });
|
212 | }
|
213 |
|
214 | };
|
215 | }
|
216 | };
|