Skip to content

Commit 06ed294

Browse files
authored
Merge pull request jsx-eslint#1671 from brettdh/no-typos-react-import
Allow custom PropType classes with no-typos rule
2 parents 34633a4 + fc06f85 commit 06ed294

File tree

2 files changed

+648
-82
lines changed

2 files changed

+648
-82
lines changed

lib/rules/no-typos.js

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ module.exports = {
3636
},
3737

3838
create: Components.detect((context, components, utils) => {
39+
let propTypesPackageName = null;
40+
let reactPackageName = null;
41+
3942
function checkValidPropTypeQualfier(node) {
4043
if (node.name !== 'isRequired') {
4144
context.report({
@@ -54,38 +57,66 @@ module.exports = {
5457
}
5558
}
5659

60+
function isPropTypesPackage(node) {
61+
return (
62+
node.type === 'Identifier' &&
63+
node.name === propTypesPackageName
64+
) || (
65+
node.type === 'MemberExpression' &&
66+
node.property.name === 'PropTypes' &&
67+
node.object.name === reactPackageName
68+
);
69+
}
70+
5771
/* eslint-disable no-use-before-define */
72+
73+
function checkValidCallExpression(node) {
74+
const callee = node.callee;
75+
if (callee.type === 'MemberExpression' && callee.property.name === 'shape') {
76+
checkValidPropObject(node.arguments[0]);
77+
} else if (callee.type === 'MemberExpression' && callee.property.name === 'oneOfType') {
78+
const args = node.arguments[0];
79+
if (args && args.type === 'ArrayExpression') {
80+
args.elements.forEach(el => {
81+
checkValidProp(el);
82+
});
83+
}
84+
}
85+
}
86+
5887
function checkValidProp(node) {
59-
if (node && node.type === 'MemberExpression' && node.object.type === 'MemberExpression') {
60-
checkValidPropType(node.object.property);
61-
checkValidPropTypeQualfier(node.property);
62-
} else if (node && node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.property.name !== 'isRequired') {
63-
checkValidPropType(node.property);
64-
} else if (node && (
65-
node.type === 'MemberExpression' && node.object.type === 'CallExpression' || node.type === 'CallExpression'
66-
)) {
67-
if (node.type === 'MemberExpression') {
88+
if ((!propTypesPackageName && !reactPackageName) || !node) {
89+
return;
90+
}
91+
92+
if (node.type === 'MemberExpression') {
93+
if (
94+
node.object.type === 'MemberExpression' &&
95+
isPropTypesPackage(node.object.object)
96+
) { // PropTypes.myProp.isRequired
97+
checkValidPropType(node.object.property);
6898
checkValidPropTypeQualfier(node.property);
69-
node = node.object;
70-
}
71-
const callee = node.callee;
72-
if (callee.type === 'MemberExpression' && callee.property.name === 'shape') {
73-
checkValidPropObject(node.arguments[0]);
74-
} else if (callee.type === 'MemberExpression' && callee.property.name === 'oneOfType') {
75-
const args = node.arguments[0];
76-
if (args && args.type === 'ArrayExpression') {
77-
args.elements.forEach(el => checkValidProp(el));
78-
}
99+
} else if (
100+
isPropTypesPackage(node.object) &&
101+
node.property.name !== 'isRequired'
102+
) { // PropTypes.myProp
103+
checkValidPropType(node.property);
104+
} else if (node.object.type === 'CallExpression') {
105+
checkValidPropTypeQualfier(node.property);
106+
checkValidCallExpression(node.object);
79107
}
108+
} else if (node.type === 'CallExpression') {
109+
checkValidCallExpression(node);
80110
}
81111
}
82112

113+
/* eslint-enable no-use-before-define */
114+
83115
function checkValidPropObject (node) {
84116
if (node && node.type === 'ObjectExpression') {
85117
node.properties.forEach(prop => checkValidProp(prop.value));
86118
}
87119
}
88-
/* eslint-enable no-use-before-define */
89120

90121
function reportErrorIfClassPropertyCasingTypo(node, propertyName) {
91122
if (propertyName === 'propTypes' || propertyName === 'contextTypes' || propertyName === 'childContextTypes') {
@@ -114,6 +145,23 @@ module.exports = {
114145
}
115146

116147
return {
148+
ImportDeclaration: function(node) {
149+
if (node.source && node.source.value === 'prop-types') { // import PropType from "prop-types"
150+
propTypesPackageName = node.specifiers[0].local.name;
151+
} else if (node.source && node.source.value === 'react') { // import { PropTypes } from "react"
152+
reactPackageName = node.specifiers[0].local.name;
153+
154+
if (node.specifiers.length >= 1) {
155+
const propTypesSpecifier = node.specifiers.find(specifier => (
156+
specifier.imported && specifier.imported.name === 'PropTypes'
157+
));
158+
if (propTypesSpecifier) {
159+
propTypesPackageName = propTypesSpecifier.local.name;
160+
}
161+
}
162+
}
163+
},
164+
117165
ClassProperty: function(node) {
118166
if (!node.static || !utils.isES6Component(node.parent.parent)) {
119167
return;

0 commit comments

Comments
 (0)