Skip to content

Allow custom PropType classes with no-typos rule #1671

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Mar 13, 2018
88 changes: 68 additions & 20 deletions lib/rules/no-typos.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ module.exports = {
},

create: Components.detect((context, components, utils) => {
let propTypesPackageName = null;
let reactPackageName = null;

function checkValidPropTypeQualfier(node) {
if (node.name !== 'isRequired') {
context.report({
Expand All @@ -54,38 +57,66 @@ module.exports = {
}
}

function isPropTypesPackage(node) {
return (
node.type === 'Identifier' &&
node.name === propTypesPackageName
) || (
node.type === 'MemberExpression' &&
node.property.name === 'PropTypes' &&
node.object.name === reactPackageName
);
}

/* eslint-disable no-use-before-define */

function checkValidCallExpression(node) {
const callee = node.callee;
if (callee.type === 'MemberExpression' && callee.property.name === 'shape') {
checkValidPropObject(node.arguments[0]);
} else if (callee.type === 'MemberExpression' && callee.property.name === 'oneOfType') {
const args = node.arguments[0];
if (args && args.type === 'ArrayExpression') {
args.elements.forEach(el => {
checkValidProp(el);
});
}
}
}

function checkValidProp(node) {
if (node && node.type === 'MemberExpression' && node.object.type === 'MemberExpression') {
checkValidPropType(node.object.property);
checkValidPropTypeQualfier(node.property);
} else if (node && node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.property.name !== 'isRequired') {
checkValidPropType(node.property);
} else if (node && (
node.type === 'MemberExpression' && node.object.type === 'CallExpression' || node.type === 'CallExpression'
)) {
if (node.type === 'MemberExpression') {
if ((!propTypesPackageName && !reactPackageName) || !node) {
return;
}

if (node.type === 'MemberExpression') {
if (
node.object.type === 'MemberExpression' &&
isPropTypesPackage(node.object.object)
) { // PropTypes.myProp.isRequired
checkValidPropType(node.object.property);
checkValidPropTypeQualfier(node.property);
node = node.object;
}
const callee = node.callee;
if (callee.type === 'MemberExpression' && callee.property.name === 'shape') {
checkValidPropObject(node.arguments[0]);
} else if (callee.type === 'MemberExpression' && callee.property.name === 'oneOfType') {
const args = node.arguments[0];
if (args && args.type === 'ArrayExpression') {
args.elements.forEach(el => checkValidProp(el));
}
} else if (
isPropTypesPackage(node.object) &&
node.property.name !== 'isRequired'
) { // PropTypes.myProp
checkValidPropType(node.property);
} else if (node.object.type === 'CallExpression') {
checkValidPropTypeQualfier(node.property);
checkValidCallExpression(node.object);
}
} else if (node.type === 'CallExpression') {
checkValidCallExpression(node);
}
}

/* eslint-enable no-use-before-define */

function checkValidPropObject (node) {
if (node && node.type === 'ObjectExpression') {
node.properties.forEach(prop => checkValidProp(prop.value));
}
}
/* eslint-enable no-use-before-define */

function reportErrorIfClassPropertyCasingTypo(node, propertyName) {
if (propertyName === 'propTypes' || propertyName === 'contextTypes' || propertyName === 'childContextTypes') {
Expand Down Expand Up @@ -114,6 +145,23 @@ module.exports = {
}

return {
ImportDeclaration: function(node) {
if (node.source && node.source.value === 'prop-types') { // import PropType from "prop-types"
propTypesPackageName = node.specifiers[0].local.name;
} else if (node.source && node.source.value === 'react') { // import { PropTypes } from "react"
reactPackageName = node.specifiers[0].local.name;

if (node.specifiers.length >= 1) {
const propTypesSpecifier = node.specifiers.find(specifier => (
specifier.imported && specifier.imported.name === 'PropTypes'
));
if (propTypesSpecifier) {
propTypesPackageName = propTypesSpecifier.local.name;
}
}
}
},

ClassProperty: function(node) {
if (!node.static || !utils.isES6Component(node.parent.parent)) {
return;
Expand Down
Loading