diff --git a/docs/rules/no-unused-prop-types.md b/docs/rules/no-unused-prop-types.md index 52e50bc6ed..f39eaf68e3 100644 --- a/docs/rules/no-unused-prop-types.md +++ b/docs/rules/no-unused-prop-types.md @@ -47,13 +47,14 @@ This rule can take one argument to ignore some specific props during validation. ```js ... -"react/no-unused-prop-types": [, { customValidators: , skipShapeProps: }] +"react/no-unused-prop-types": [, { customValidators: , skipShapeProps: , propWrapperFunctions: }] ... ``` * `enabled`: for enabling the rule. 0=off, 1=warn, 2=error. Defaults to 0. * `customValidators`: optional array of validators used for propTypes validation. * `skipShapeProps`: In some cases it is impossible to accurately detect whether or not a `PropTypes.shape`'s values are being used. Setting this option to `true` will skip validation of `PropTypes.shape` (`true` by default). +* `propWrapperFunctions`: The names of any functions used to wrap the propTypes object, such as `forbidExtraProps`. If this isn't set, any propTypes wrapped in a function will be skipped. ## Caveats diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js index 5ed0e998ce..afcb4df6c6 100644 --- a/lib/rules/no-unused-prop-types.js +++ b/lib/rules/no-unused-prop-types.js @@ -44,6 +44,13 @@ module.exports = { }, skipShapeProps: { type: 'boolean' + }, + propWrapperFunctions: { + type: 'array', + items: { + type: 'string' + }, + uniqueItems: true } }, additionalProperties: false @@ -56,6 +63,8 @@ module.exports = { var configuration = Object.assign({}, defaults, context.options[0] || {}); var skipShapeProps = configuration.skipShapeProps; var customValidators = configuration.customValidators || []; + var propWrapperFunctions = new Set(configuration.propWrapperFunctions || []); + // Used to track the type annotations in scope. // Necessary because babel's scopes do not track type annotations. var stack = null; @@ -722,6 +731,15 @@ module.exports = { } ignorePropsValidation = true; break; + case 'CallExpression': + if ( + propWrapperFunctions.has(propTypes.callee.name) && + propTypes.arguments && propTypes.arguments[0] + ) { + markPropTypesAsDeclared(node, propTypes.arguments[0]); + return; + } + break; case null: break; default: diff --git a/tests/lib/rules/no-unused-prop-types.js b/tests/lib/rules/no-unused-prop-types.js index fbad06f427..c74585487b 100644 --- a/tests/lib/rules/no-unused-prop-types.js +++ b/tests/lib/rules/no-unused-prop-types.js @@ -2779,6 +2779,47 @@ ruleTester.run('no-unused-prop-types', rule, { line: 10, column: 8 }] + }, { + code: [ + 'class Hello extends Component {', + ' componentDidUpdate (nextProps) {', + ' if (nextProps.foo) {', + ' return true;', + ' }', + ' }', + '}', + 'Hello.propTypes = forbidExtraProps({', + ' foo: PropTypes.string,', + ' bar: PropTypes.string,', + '});' + ].join('\n'), + errors: [{ + message: '\'bar\' PropType is defined but prop is never used', + line: 10, + column: 8 + }], + options: [{propWrapperFunctions: ['forbidExtraProps']}] + }, { + code: [ + 'class Hello extends Component {', + ' propTypes = forbidExtraProps({', + ' foo: PropTypes.string,', + ' bar: PropTypes.string', + ' });', + ' componentDidUpdate (nextProps) {', + ' if (nextProps.foo) {', + ' return true;', + ' }', + ' }', + '};' + ].join('\n'), + parser: 'babel-eslint', + errors: [{ + message: '\'bar\' PropType is defined but prop is never used', + line: 4, + column: 10 + }], + options: [{propWrapperFunctions: ['forbidExtraProps']}] } /* , { // Enable this when the following issue is fixed