From e8dc9c1c838735eed35e750cf9e98467dee52a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Nowak?= Date: Wed, 10 Apr 2019 23:44:56 +0200 Subject: [PATCH] Validate boolean prop names nested deeper --- lib/rules/boolean-prop-naming.js | 76 ++++++++++++++++++----- tests/lib/rules/boolean-prop-naming.js | 84 ++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 16 deletions(-) diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js index c464e13265..9ba632a2d0 100644 --- a/lib/rules/boolean-prop-naming.js +++ b/lib/rules/boolean-prop-naming.js @@ -104,6 +104,64 @@ module.exports = { return node.key.name; } + /** + * Checks if prop is declared in flow way + * @param {Object} prop Property object, single prop type declaration + * @returns {Boolean} + */ + function flowCheck(prop) { + return ( + prop.type === 'ObjectTypeProperty' && + prop.value.type === 'BooleanTypeAnnotation' && + rule.test(getPropName(prop)) === false + ); + } + + /** + * Checks if prop is declared in regular way + * @param {Object} prop Property object, single prop type declaration + * @returns {Boolean} + */ + function regularCheck(prop) { + const propKey = getPropKey(prop); + return ( + propKey && + propTypeNames.indexOf(propKey) >= 0 && + rule.test(getPropName(prop)) === false + ); + } + + /** + * Checks if prop is nested + * @param {Object} prop Property object, single prop type declaration + * @returns {Boolean} + */ + function nestedPropTypes(prop) { + return ( + prop.type === 'Property' && + prop.value.type === 'CallExpression' + ); + } + + /** + * Runs recursive check on all proptypes + * @param {Array} proptypes A list of Property object (for each proptype defined) + * @param {Function} addInvalidProp callback to run for each error + */ + function runCheck(proptypes, addInvalidProp) { + proptypes = proptypes || []; + + proptypes.forEach(prop => { + if (nestedPropTypes(prop)) { + runCheck(prop.value.arguments[0].properties, addInvalidProp); + return; + } + if (flowCheck(prop) || regularCheck(prop)) { + addInvalidProp(prop); + } + }); + } + /** * Checks and mark props with invalid naming * @param {Object} node The component node we're testing @@ -113,22 +171,8 @@ module.exports = { const component = components.get(node) || node; const invalidProps = component.invalidProps || []; - (proptypes || []).forEach(prop => { - const propKey = getPropKey(prop); - const flowCheck = ( - prop.type === 'ObjectTypeProperty' && - prop.value.type === 'BooleanTypeAnnotation' && - rule.test(getPropName(prop)) === false - ); - const regularCheck = ( - propKey && - propTypeNames.indexOf(propKey) >= 0 && - rule.test(getPropName(prop)) === false - ); - - if (flowCheck || regularCheck) { - invalidProps.push(prop); - } + runCheck(proptypes, prop => { + invalidProps.push(prop); }); components.set(node, { diff --git a/tests/lib/rules/boolean-prop-naming.js b/tests/lib/rules/boolean-prop-naming.js index a542b5f0ef..f234654939 100644 --- a/tests/lib/rules/boolean-prop-naming.js +++ b/tests/lib/rules/boolean-prop-naming.js @@ -373,6 +373,42 @@ ruleTester.run('boolean-prop-naming', rule, { rule: '^is[A-Z]([A-Za-z0-9]?)+' }], parser: 'babel-eslint' + }, { + code: ` + class Hello extends React.Component { + render() { + return ( +
+ ); + } + } + + Hello.propTypes = { + isSomething: PropTypes.bool.isRequired, + nested: PropTypes.shape({ + isWorking: PropTypes.bool + }) + }; + ` + }, { + code: ` + class Hello extends React.Component { + render() { + return ( +
+ ); + } + } + + Hello.propTypes = { + isSomething: PropTypes.bool.isRequired, + nested: PropTypes.shape({ + nested: PropTypes.shape({ + isWorking: PropTypes.bool + }) + }) + }; + ` }], invalid: [{ @@ -807,5 +843,53 @@ ruleTester.run('boolean-prop-naming', rule, { errors: [{ message: 'Prop name (something) doesn\'t match rule (^is[A-Z]([A-Za-z0-9]?)+)' }] + }, { + code: ` + class Hello extends React.Component { + render() { + return ( +
+ ); + } + } + + Hello.propTypes = { + isSomething: PropTypes.bool.isRequired, + nested: PropTypes.shape({ + failingItIs: PropTypes.bool + }) + }; + `, + options: [{ + rule: '^is[A-Z]([A-Za-z0-9]?)+' + }], + errors: [{ + message: 'Prop name (failingItIs) doesn\'t match rule (^is[A-Z]([A-Za-z0-9]?)+)' + }] + }, { + code: ` + class Hello extends React.Component { + render() { + return ( +
+ ); + } + } + + Hello.propTypes = { + isSomething: PropTypes.bool.isRequired, + nested: PropTypes.shape({ + nested: PropTypes.shape({ + failingItIs: PropTypes.bool + }) + }) + }; + `, + options: [{ + rule: '^is[A-Z]([A-Za-z0-9]?)+' + }], + errors: [{ + message: 'Prop name (failingItIs) doesn\'t match rule (^is[A-Z]([A-Za-z0-9]?)+)' + }] }] });