diff --git a/lib/rules/require-default-props.js b/lib/rules/require-default-props.js index ea39df0ec2..410b8e0ff2 100644 --- a/lib/rules/require-default-props.js +++ b/lib/rules/require-default-props.js @@ -25,13 +25,33 @@ module.exports = { }, create: Components.detect(function(context, components, utils) { + + /** + * Get properties name + * @param {Object} node - Property. + * @returns {String} Property name. + */ + function getPropertyName(node) { + if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) { + return node.key.name; + } else if (node.type === 'MemberExpression') { + return node.property.name; + // Special case for class properties + // (babel-eslint@5 does not expose property name so we have to rely on tokens) + } else if (node.type === 'ClassProperty') { + var tokens = context.getFirstTokens(node, 2); + return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value; + } + return ''; + } + /** * Checks if the Identifier node passed in looks like a propTypes declaration. * @param {ASTNode} node The node to check. Must be an Identifier node. * @returns {Boolean} `true` if the node is a propTypes declaration, `false` if not */ function isPropTypesDeclaration(node) { - return node.type === 'Identifier' && node.name === 'propTypes'; + return getPropertyName(node) === 'propTypes'; } /** @@ -40,8 +60,7 @@ module.exports = { * @returns {Boolean} `true` if the node is a defaultProps declaration, `false` if not */ function isDefaultPropsDeclaration(node) { - return node.type === 'Identifier' && - (node.name === 'defaultProps' || node.name === 'getDefaultProps'); + return (getPropertyName(node) === 'defaultProps' || getPropertyName(node) === 'getDefaultProps'); } /** @@ -289,7 +308,7 @@ module.exports = { } function isPropTypeAnnotation(node) { - return (node.key.name === 'props' && !!node.typeAnnotation); + return (getPropertyName(node) === 'props' && !!node.typeAnnotation); } /** @@ -328,8 +347,8 @@ module.exports = { return { MemberExpression: function(node) { - var isPropType = isPropTypesDeclaration(node.property); - var isDefaultProp = isDefaultPropsDeclaration(node.property); + var isPropType = isPropTypesDeclaration(node); + var isDefaultProp = isDefaultPropsDeclaration(node); if (!isPropType && !isDefaultProp) { return; @@ -412,8 +431,8 @@ module.exports = { return; } - var isPropType = isPropTypesDeclaration(node.key); - var isDefaultProp = isDefaultPropsDeclaration(node.key); + var isPropType = isPropTypesDeclaration(node); + var isDefaultProp = isDefaultPropsDeclaration(node); if (!isPropType && !isDefaultProp) { return; @@ -468,8 +487,8 @@ module.exports = { return; } - var isPropType = isPropTypesDeclaration(node.key); - var isDefaultProp = isDefaultPropsDeclaration(node.key); + var isPropType = getPropertyName(node) === 'propTypes'; + var isDefaultProp = getPropertyName(node) === 'defaultProps' || getPropertyName(node) === 'getDefaultProps'; if (!isPropType && !isDefaultProp) { return; @@ -520,8 +539,8 @@ module.exports = { return; } - var isPropType = isPropTypesDeclaration(property.key); - var isDefaultProp = isDefaultPropsDeclaration(property.key); + var isPropType = isPropTypesDeclaration(property); + var isDefaultProp = isDefaultPropsDeclaration(property); if (!isPropType && !isDefaultProp) { return; diff --git a/tests/lib/rules/require-default-props.js b/tests/lib/rules/require-default-props.js index 991913e88b..c277477427 100644 --- a/tests/lib/rules/require-default-props.js +++ b/tests/lib/rules/require-default-props.js @@ -1718,6 +1718,26 @@ ruleTester.run('require-default-props', rule, { column: 3 } ] + }, + { + code: [ + 'class Hello extends React.Component {', + ' static propTypes = {', + ' foo: PropTypes.string', + ' };', + ' render() {', + ' return
Hello {this.props.foo}
;', + ' }', + '}' + ].join('\n'), + parser: 'babel-eslint', + errors: [ + { + message: 'propType "foo" is not required, but has no corresponding defaultProp declaration.', + line: 3, + column: 5 + } + ] } ] });