source: imaps-frontend/node_modules/eslint-plugin-react/lib/rules/no-typos.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 8.7 KB
Line 
1/**
2 * @fileoverview Prevent common casing typos
3 */
4
5'use strict';
6
7const PROP_TYPES = Object.keys(require('prop-types'));
8const Components = require('../util/Components');
9const docsUrl = require('../util/docsUrl');
10const componentUtil = require('../util/componentUtil');
11const report = require('../util/report');
12const lifecycleMethods = require('../util/lifecycleMethods');
13
14// ------------------------------------------------------------------------------
15// Rule Definition
16// ------------------------------------------------------------------------------
17
18const STATIC_CLASS_PROPERTIES = ['propTypes', 'contextTypes', 'childContextTypes', 'defaultProps'];
19
20const messages = {
21 typoPropTypeChain: 'Typo in prop type chain qualifier: {{name}}',
22 typoPropType: 'Typo in declared prop type: {{name}}',
23 typoStaticClassProp: 'Typo in static class property declaration',
24 typoPropDeclaration: 'Typo in property declaration',
25 typoLifecycleMethod: 'Typo in component lifecycle method declaration: {{actual}} should be {{expected}}',
26 staticLifecycleMethod: 'Lifecycle method should be static: {{method}}',
27 noPropTypesBinding: '`\'prop-types\'` imported without a local `PropTypes` binding.',
28 noReactBinding: '`\'react\'` imported without a local `React` binding.',
29};
30
31/** @type {import('eslint').Rule.RuleModule} */
32module.exports = {
33 meta: {
34 docs: {
35 description: 'Disallow common typos',
36 category: 'Stylistic Issues',
37 recommended: false,
38 url: docsUrl('no-typos'),
39 },
40
41 messages,
42
43 schema: [],
44 },
45
46 create: Components.detect((context, components, utils) => {
47 let propTypesPackageName = null;
48 let reactPackageName = null;
49
50 function checkValidPropTypeQualifier(node) {
51 if (node.name !== 'isRequired') {
52 report(context, messages.typoPropTypeChain, 'typoPropTypeChain', {
53 node,
54 data: { name: node.name },
55 });
56 }
57 }
58
59 function checkValidPropType(node) {
60 if (node.name && !PROP_TYPES.some((propTypeName) => propTypeName === node.name)) {
61 report(context, messages.typoPropType, 'typoPropType', {
62 node,
63 data: { name: node.name },
64 });
65 }
66 }
67
68 function isPropTypesPackage(node) {
69 return (
70 node.type === 'Identifier'
71 && node.name === propTypesPackageName
72 ) || (
73 node.type === 'MemberExpression'
74 && node.property.name === 'PropTypes'
75 && node.object.name === reactPackageName
76 );
77 }
78
79 /* eslint-disable no-use-before-define */
80
81 function checkValidCallExpression(node) {
82 const callee = node.callee;
83 if (callee.type === 'MemberExpression' && callee.property.name === 'shape') {
84 checkValidPropObject(node.arguments[0]);
85 } else if (callee.type === 'MemberExpression' && callee.property.name === 'oneOfType') {
86 const args = node.arguments[0];
87 if (args && args.type === 'ArrayExpression') {
88 args.elements.forEach((el) => {
89 checkValidProp(el);
90 });
91 }
92 }
93 }
94
95 function checkValidProp(node) {
96 if ((!propTypesPackageName && !reactPackageName) || !node) {
97 return;
98 }
99
100 if (node.type === 'MemberExpression') {
101 if (
102 node.object.type === 'MemberExpression'
103 && isPropTypesPackage(node.object.object)
104 ) { // PropTypes.myProp.isRequired
105 checkValidPropType(node.object.property);
106 checkValidPropTypeQualifier(node.property);
107 } else if (
108 isPropTypesPackage(node.object)
109 && node.property.name !== 'isRequired'
110 ) { // PropTypes.myProp
111 checkValidPropType(node.property);
112 } else if (node.object.type === 'CallExpression') {
113 checkValidPropTypeQualifier(node.property);
114 checkValidCallExpression(node.object);
115 }
116 } else if (node.type === 'CallExpression') {
117 checkValidCallExpression(node);
118 }
119 }
120
121 /* eslint-enable no-use-before-define */
122
123 function checkValidPropObject(node) {
124 if (node && node.type === 'ObjectExpression') {
125 node.properties.forEach((prop) => checkValidProp(prop.value));
126 }
127 }
128
129 function reportErrorIfPropertyCasingTypo(propertyValue, propertyKey, isClassProperty) {
130 const propertyName = propertyKey.name;
131 if (propertyName === 'propTypes' || propertyName === 'contextTypes' || propertyName === 'childContextTypes') {
132 checkValidPropObject(propertyValue);
133 }
134 STATIC_CLASS_PROPERTIES.forEach((CLASS_PROP) => {
135 if (propertyName && CLASS_PROP.toLowerCase() === propertyName.toLowerCase() && CLASS_PROP !== propertyName) {
136 const messageId = isClassProperty
137 ? 'typoStaticClassProp'
138 : 'typoPropDeclaration';
139 report(context, messages[messageId], messageId, {
140 node: propertyKey,
141 });
142 }
143 });
144 }
145
146 function reportErrorIfLifecycleMethodCasingTypo(node) {
147 const key = node.key;
148 let nodeKeyName = key.name;
149 if (key.type === 'Literal') {
150 nodeKeyName = key.value;
151 }
152 if (key.type === 'PrivateName' || (node.computed && typeof nodeKeyName !== 'string')) {
153 return;
154 }
155
156 lifecycleMethods.static.forEach((method) => {
157 if (!node.static && nodeKeyName && nodeKeyName.toLowerCase() === method.toLowerCase()) {
158 report(context, messages.staticLifecycleMethod, 'staticLifecycleMethod', {
159 node,
160 data: {
161 method: nodeKeyName,
162 },
163 });
164 }
165 });
166
167 lifecycleMethods.instance.concat(lifecycleMethods.static).forEach((method) => {
168 if (nodeKeyName && method.toLowerCase() === nodeKeyName.toLowerCase() && method !== nodeKeyName) {
169 report(context, messages.typoLifecycleMethod, 'typoLifecycleMethod', {
170 node,
171 data: { actual: nodeKeyName, expected: method },
172 });
173 }
174 });
175 }
176
177 return {
178 ImportDeclaration(node) {
179 if (node.source && node.source.value === 'prop-types') { // import PropType from "prop-types"
180 if (node.specifiers.length > 0) {
181 propTypesPackageName = node.specifiers[0].local.name;
182 } else {
183 report(context, messages.noPropTypesBinding, 'noPropTypesBinding', {
184 node,
185 });
186 }
187 } else if (node.source && node.source.value === 'react') { // import { PropTypes } from "react"
188 if (node.specifiers.length > 0) {
189 reactPackageName = node.specifiers[0].local.name; // guard against accidental anonymous `import "react"`
190 } else {
191 report(context, messages.noReactBinding, 'noReactBinding', {
192 node,
193 });
194 }
195 if (node.specifiers.length >= 1) {
196 const propTypesSpecifier = node.specifiers.find((specifier) => (
197 specifier.imported && specifier.imported.name === 'PropTypes'
198 ));
199 if (propTypesSpecifier) {
200 propTypesPackageName = propTypesSpecifier.local.name;
201 }
202 }
203 }
204 },
205
206 'ClassProperty, PropertyDefinition'(node) {
207 if (!node.static || !componentUtil.isES6Component(node.parent.parent, context)) {
208 return;
209 }
210
211 reportErrorIfPropertyCasingTypo(node.value, node.key, true);
212 },
213
214 MemberExpression(node) {
215 const propertyName = node.property.name;
216
217 if (
218 !propertyName
219 || STATIC_CLASS_PROPERTIES.map((prop) => prop.toLocaleLowerCase()).indexOf(propertyName.toLowerCase()) === -1
220 ) {
221 return;
222 }
223
224 const relatedComponent = utils.getRelatedComponent(node);
225
226 if (
227 relatedComponent
228 && (componentUtil.isES6Component(relatedComponent.node, context) || (
229 relatedComponent.node.type !== 'ClassDeclaration' && utils.isReturningJSX(relatedComponent.node)))
230 && (node.parent && node.parent.type === 'AssignmentExpression' && node.parent.right)
231 ) {
232 reportErrorIfPropertyCasingTypo(node.parent.right, node.property, true);
233 }
234 },
235
236 MethodDefinition(node) {
237 if (!componentUtil.isES6Component(node.parent.parent, context)) {
238 return;
239 }
240
241 reportErrorIfLifecycleMethodCasingTypo(node);
242 },
243
244 ObjectExpression(node) {
245 const component = componentUtil.isES5Component(node, context) && components.get(node);
246
247 if (!component) {
248 return;
249 }
250
251 node.properties.filter((property) => property.type !== 'SpreadElement').forEach((property) => {
252 reportErrorIfPropertyCasingTypo(property.value, property.key, false);
253 reportErrorIfLifecycleMethodCasingTypo(property);
254 });
255 },
256 };
257 }),
258};
Note: See TracBrowser for help on using the repository browser.