UNPKG

5.9 kBJavaScriptView Raw
1/**
2 * @fileoverview Forbid certain propTypes
3 */
4
5'use strict';
6
7const variableUtil = require('../util/variable');
8const propsUtil = require('../util/props');
9const astUtil = require('../util/ast');
10const docsUrl = require('../util/docsUrl');
11const propWrapperUtil = require('../util/propWrapper');
12
13// ------------------------------------------------------------------------------
14// Constants
15// ------------------------------------------------------------------------------
16
17const DEFAULTS = ['any', 'array', 'object'];
18
19// ------------------------------------------------------------------------------
20// Rule Definition
21// ------------------------------------------------------------------------------
22
23module.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 * Checks if propTypes declarations are forbidden
87 * @param {Array} declarations The array of AST nodes being checked.
88 * @returns {void}
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};