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

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

Pred finalna verzija

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