From 477f36da0e60266820dac4f954042c5705de5fc5 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 14 Apr 2023 12:35:43 -0700 Subject: [PATCH 1/2] [Refactor] `forbid-component-props`: tweak formatting --- lib/rules/forbid-component-props.js | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/lib/rules/forbid-component-props.js b/lib/rules/forbid-component-props.js index 95ba3a5377..768099a176 100644 --- a/lib/rules/forbid-component-props.js +++ b/lib/rules/forbid-component-props.js @@ -39,26 +39,21 @@ module.exports = { forbid: { type: 'array', items: { - anyOf: [{ - type: 'string', - }, { - type: 'object', - properties: { - propName: { - type: 'string', - }, - allowedFor: { - type: 'array', - uniqueItems: true, - items: { - type: 'string', + anyOf: [ + { type: 'string' }, + { + type: 'object', + properties: { + propName: { type: 'string' }, + allowedFor: { + type: 'array', + uniqueItems: true, + items: { type: 'string' }, }, - }, - message: { - type: 'string', + message: { type: 'string' }, }, }, - }], + ], }, }, }, From 4a92667e4b99221013e1d2aa61e97296895cecc1 Mon Sep 17 00:00:00 2001 From: jilianfeng Date: Wed, 7 Sep 2022 17:32:48 +0800 Subject: [PATCH 2/2] [New] `forbid-component-props`: add `disallowedFor` option Co-authored-by: jilianfeng Co-authored-by: Ell Bradshaw --- CHANGELOG.md | 2 + docs/rules/forbid-component-props.md | 14 +- lib/rules/forbid-component-props.js | 29 +++- tests/lib/rules/forbid-component-props.js | 191 ++++++++++++++++++++++ 4 files changed, 232 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b264f6356c..60030fb736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [`display-name`]: add `checkContextObjects` option ([#3529][] @JulesBlm) * [`jsx-first-prop-new-line`]: add `multiprop` option ([#3533][] @haydncomley) * [`no-deprecated`]: add React 18 deprecations ([#3548][] @sergei-startsev) +* [`forbid-component-props`]: add `disallowedFor` option ([#3417][] @jacketwpbb) ### Fixed * [`no-array-index-key`]: consider flatMap ([#3530][] @k-yle) @@ -26,6 +27,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange [#3533]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3533 [#3530]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3530 [#3529]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3529 +[#3417]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3417 ## [7.32.2] - 2023.01.28 diff --git a/docs/rules/forbid-component-props.md b/docs/rules/forbid-component-props.md index ddcfc12eb7..2d624be31c 100644 --- a/docs/rules/forbid-component-props.md +++ b/docs/rules/forbid-component-props.md @@ -50,8 +50,18 @@ custom message, and a component allowlist: ```js { "propName": "someProp", - "allowedFor": [SomeComponent, AnotherComponent], - "message": "Avoid using someProp" + "allowedFor": ["SomeComponent", "AnotherComponent"], + "message": "Avoid using someProp except SomeComponent and AnotherComponent" +} +``` + +Use `disallowedFor` as an exclusion list to warn on props for specific components. `disallowedFor` must have at least one item. + +```js +{ + "propName": "someProp", + "disallowedFor": ["SomeComponent", "AnotherComponent"], + "message": "Avoid using someProp for SomeComponent and AnotherComponent" } ``` diff --git a/lib/rules/forbid-component-props.js b/lib/rules/forbid-component-props.js index 768099a176..cca553e123 100644 --- a/lib/rules/forbid-component-props.js +++ b/lib/rules/forbid-component-props.js @@ -52,6 +52,22 @@ module.exports = { }, message: { type: 'string' }, }, + additionalProperties: false, + }, + { + type: 'object', + properties: { + propName: { type: 'string' }, + disallowedFor: { + type: 'array', + uniqueItems: true, + minItems: 1, + items: { type: 'string' }, + }, + message: { type: 'string' }, + }, + required: ['disallowedFor'], + additionalProperties: false, }, ], }, @@ -66,6 +82,7 @@ module.exports = { const propName = typeof value === 'string' ? value : value.propName; const options = { allowList: typeof value === 'string' ? [] : (value.allowedFor || []), + disallowList: typeof value === 'string' ? [] : (value.disallowedFor || []), message: typeof value === 'string' ? null : value.message, }; return [propName, options]; @@ -73,9 +90,17 @@ module.exports = { function isForbidden(prop, tagName) { const options = forbid.get(prop); - const allowList = options ? options.allowList : undefined; + if (!options) { + return false; + } + + // disallowList should have a least one item (schema configuration) + const isTagForbidden = options.disallowList.length > 0 + ? options.disallowList.indexOf(tagName) !== -1 + : options.allowList.indexOf(tagName) === -1; + // if the tagName is undefined (``), we assume it's a forbidden element - return typeof allowList !== 'undefined' && (typeof tagName === 'undefined' || allowList.indexOf(tagName) === -1); + return typeof tagName === 'undefined' || isTagForbidden; } return { diff --git a/tests/lib/rules/forbid-component-props.js b/tests/lib/rules/forbid-component-props.js index ac27537fd4..5f1aa7e68d 100644 --- a/tests/lib/rules/forbid-component-props.js +++ b/tests/lib/rules/forbid-component-props.js @@ -151,12 +151,103 @@ ruleTester.run('forbid-component-props', rule, { }, ], }, + { + code: ` + const item = (); + `, + options: [ + { + forbid: [ + { + propName: 'className', + disallowedFor: ['ReactModal'], + }, + ], + }, + ], + }, + { + code: ` + const item = (); + `, + options: [ + { + forbid: [ + { + propName: 'className', + disallowedFor: ['ReactModal'], + }, + ], + }, + ], + }, { code: ` `, features: ['jsx namespace'], }, + { + code: ` + const item = ( + + + + ); + `, + options: [ + { + forbid: [ + { + propName: 'className', + disallowedFor: ['OtherModal', 'ReactModal'], + }, + { + propName: 'style', + disallowedFor: ['Foo'], + }, + ], + }, + ], + }, + { + code: ` + const item = ( + + + + ); + `, + options: [ + { + forbid: [ + { + propName: 'className', + disallowedFor: ['OtherModal', 'ReactModal'], + }, + { + propName: 'style', + allowedFor: ['ReactModal'], + }, + ], + }, + ], + }, + { + code: ` + const item = (); + `, + options: [ + { + forbid: [ + { + propName: 'className', + disallowedFor: ['ReactModal'], + }, + ], + }, + ], + }, ]), invalid: parsers.all([ @@ -235,6 +326,34 @@ ruleTester.run('forbid-component-props', rule, { }, ], }, + { + code: ` + var First = createReactClass({ + propTypes: externalPropTypes, + render: function() { + return ; + } + }); + `, + options: [ + { + forbid: [ + { + propName: 'style', + disallowedFor: ['Foo'], + }, + ], + }, + ], + errors: [ + { + messageId: 'propIsForbidden', + data: { prop: 'style' }, + line: 5, + type: 'JSXAttribute', + }, + ], + }, { code: ` const item = (); @@ -282,6 +401,78 @@ ruleTester.run('forbid-component-props', rule, { }, ], }, + { + code: ` + const item = (); + `, + options: [ + { + forbid: [ + { + propName: 'className', + disallowedFor: ['this.ReactModal'], + }, + ], + }, + ], + errors: [ + { + messageId: 'propIsForbidden', + data: { prop: 'className' }, + line: 2, + column: 40, + type: 'JSXAttribute', + }, + ], + }, + { + code: ` + const item = (); + `, + options: [ + { + forbid: [ + { + propName: 'className', + disallowedFor: ['ReactModal'], + }, + ], + }, + ], + errors: [ + { + messageId: 'propIsForbidden', + data: { prop: 'className' }, + line: 2, + column: 35, + type: 'JSXAttribute', + }, + ], + }, + { + code: ` + const item = (); + `, + options: [ + { + forbid: [ + { + propName: 'className', + disallowedFor: ['AntdLayout.Content'], + }, + ], + }, + ], + errors: [ + { + messageId: 'propIsForbidden', + data: { prop: 'className' }, + line: 2, + column: 43, + type: 'JSXAttribute', + }, + ], + }, { code: ` const item = ();