Repository: jsx-eslint/eslint-plugin-react (js)

Issue:
`jsx-handler-names` support skipping prop naming checks
My project has many components from other dependency packages, like:
```tsx
<ComponentFromOtherLibraryFoo customPropNameFoo={handleSomething} />;
<ComponentFromOtherLibraryBar customPropNameBar={handleSomething} />;
```
Every time I come across them, I need to add an extra line of boring comments:
```tsx
// eslint-disable-next-line jsx-handler-names
<ComponentFromOtherLibraryFoo customPropNameFoo={handleSomething} />;
// eslint-disable-next-line jsx-handler-names
<ComponentFromOtherLibraryBar customPropNameBar={handleSomething} />;
```

To be honest, I don't care about the names of these PROPS(even the components in my own project, I just want it to check my handler function's name)

To make matters worse, if the prop's name is not camelCase (e.g., all lowercase letters: "foofoo"), the option "eventHandlerPropPrefix" is not valid either, because it only applies to CamelCase naming:
```tsx
/* with config: 
{
  eventHandlerPrefix: '(on|handle)',
  eventHandlerPropPrefix: '(on|foo)',
}
*/
// fine
<ComponentFromOtherLibraryFoo fooPropName={handleSomething} />;

// not work, still has error
<ComponentFromOtherLibraryFoo foo={handleSomething} />;
```
So, it would be nice if there were an option like "checkPropName". For those who don't care about prop naming, simply turn it off.
like:
```tsx
{
  eventHandlerPrefix: '(on|handle)',
  eventHandlerPropPrefix: '(on|foo)',
  // ...
  checkPropName: false,
}
```

Anyway, I want to confirm if this option is necessary (and if anyone else has experienced this), and if so, I can create a PR to do it.



Pull request:
[New] `jsx-handler-names`: support ignoring component names
Closes #3722 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 54b106d9..d138fc53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,8 +10,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
 * export flat configs from plugin root and fix flat config crash ([#3694][] @bradzacher @mdjermanovic)
 * add [`jsx-props-no-spread-multi`] ([#3724][] @SimonSchick)
 * [`forbid-component-props`]: add `propNamePattern` to allow / disallow prop name patterns ([#3774][] @akulsr0)
+* [`jsx-handler-names`]: support ignoring component names ([#3772][] @akulsr0)
 
 [#3774]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3774
+[#3772]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3772
 [#3759]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3759
 [#3724]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3724
 [#3694]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3694
diff --git a/docs/rules/jsx-handler-names.md b/docs/rules/jsx-handler-names.md
index ced349f3..8e93a167 100644
--- a/docs/rules/jsx-handler-names.md
+++ b/docs/rules/jsx-handler-names.md
@@ -34,7 +34,8 @@ Examples of **correct** code for this rule:
   "eventHandlerPrefix": <eventHandlerPrefix>,
   "eventHandlerPropPrefix": <eventHandlerPropPrefix>,
   "checkLocalVariables": <boolean>,
-  "checkInlineFunction": <boolean>
+  "checkInlineFunction": <boolean>,
+  "ignoreComponentNames": Array<string>
 }]
 ...
 ```
@@ -43,6 +44,7 @@ Examples of **correct** code for this rule:
 - `eventHandlerPropPrefix`: Prefix for props that are used as event handlers. Defaults to `on`
 - `checkLocalVariables`: Determines whether event handlers stored as local variables are checked. Defaults to `false`
 - `checkInlineFunction`: Determines whether event handlers set as inline functions are checked. Defaults to `false`
+- `ignoreComponentNames`: Array of glob strings, when matched with component name, ignores the rule on that component. Defaults to `[]`
 
 ## When Not To Use It
 
diff --git a/lib/rules/jsx-handler-names.js b/lib/rules/jsx-handler-names.js
index d839073a..652e125d 100644
--- a/lib/rules/jsx-handler-names.js
+++ b/lib/rules/jsx-handler-names.js
@@ -5,6 +5,7 @@
 
 'use strict';
 
+const minimatch = require('minimatch');
 const docsUrl = require('../util/docsUrl');
 const getText = require('../util/eslint').getText;
 const report = require('../util/report');
@@ -39,6 +40,11 @@ module.exports = {
             eventHandlerPropPrefix: { type: 'string' },
             checkLocalVariables: { type: 'boolean' },
             checkInlineFunction: { type: 'boolean' },
+            ignoreComponentNames: {
+              type: 'array',
+              uniqueItems: true,
+              items: { type: 'string' },
+            },
           },
           additionalProperties: false,
         }, {
@@ -51,6 +57,11 @@ module.exports = {
             },
             checkLocalVariables: { type: 'boolean' },
             checkInlineFunction: { type: 'boolean' },
+            ignoreComponentNames: {
+              type: 'array',
+              uniqueItems: true,
+              items: { type: 'string' },
+            },
           },
           additionalProperties: false,
         }, {
@@ -63,6 +74,11 @@ module.exports = {
             eventHandlerPropPrefix: { type: 'string' },
             checkLocalVariables: { type: 'boolean' },
             checkInlineFunction: { type: 'boolean' },
+            ignoreComponentNames: {
+              type: 'array',
+              uniqueItems: true,
+              items: { type: 'string' },
+            },
           },
           additionalProperties: false,
         }, {
@@ -78,6 +94,16 @@ module.exports = {
           },
           additionalProperties: false,
         },
+        {
+          type: 'object',
+          properties: {
+            ignoreComponentNames: {
+              type: 'array',
+              uniqueItems: true,
+              items: { type: 'string' },
+            },
+          },
+        },
       ],
     }],
   },
@@ -111,8 +137,17 @@ module.exports = {
 
     const checkInlineFunction = !!configuration.checkInlineFunction;
 
+    const ignoreComponentNames = configuration.ignoreComponentNames || [];
+
     return {
       JSXAttribute(node) {
+        const componentName = node.parent.name.name;
+
+        const isComponentNameIgnored = ignoreComponentNames.some((ignoredComponentNamePattern) => {
+          const isIgnored = minimatch(componentName, ignoredComponentNamePattern);
+          return isIgnored;
+        });
+
         if (
           !node.value
           || !node.value.expression
@@ -124,6 +159,7 @@ module.exports = {
               : !node.value.expression.object
             )
           )
+          || isComponentNameIgnored
         ) {
           return;
         }


Test patch:
diff --git a/tests/lib/rules/jsx-handler-names.js b/tests/lib/rules/jsx-handler-names.js
index 47adbe85..3ad3378a 100644
--- a/tests/lib/rules/jsx-handler-names.js
+++ b/tests/lib/rules/jsx-handler-names.js
@@ -181,6 +181,24 @@ ruleTester.run('jsx-handler-names', rule, {
       code: '<TestComponent someProp={props.onChange} />',
       options: [{ eventHandlerPropPrefix: false }],
     },
+    {
+      code: '<ComponentFromOtherLibraryBar customPropNameBar={handleSomething} />;',
+      options: [{ checkLocalVariables: true, ignoreComponentNames: ['ComponentFromOtherLibraryBar'] }],
+    },
+    {
+      code: `
+        function App() {
+          return (
+            <div>
+              <MyLibInput customPropNameBar={handleSomething} />;
+              <MyLibCheckbox customPropNameBar={handleSomething} />;
+              <MyLibButtom customPropNameBar={handleSomething} />;
+            </div>
+          )
+        }
+      `,
+      options: [{ checkLocalVariables: true, ignoreComponentNames: ['MyLib*'] }],
+    },
   ]),
 
   invalid: parsers.all([
@@ -369,5 +387,33 @@ ruleTester.run('jsx-handler-names', rule, {
         },
       ],
     },
+    {
+      code: `
+        function App() {
+          return (
+            <div>
+              <MyLibInput customPropNameBar={handleInput} />;
+              <MyLibCheckbox customPropNameBar={handleCheckbox} />;
+              <MyLibButtom customPropNameBar={handleButton} />;
+            </div>
+          )
+        }
+      `,
+      options: [{ checkLocalVariables: true, ignoreComponentNames: ['MyLibrary*'] }],
+      errors: [
+        {
+          messageId: 'badPropKey',
+          data: { propValue: 'handleInput', handlerPropPrefix: 'on' },
+        },
+        {
+          messageId: 'badPropKey',
+          data: { propValue: 'handleCheckbox', handlerPropPrefix: 'on' },
+        },
+        {
+          messageId: 'badPropKey',
+          data: { propValue: 'handleButton', handlerPropPrefix: 'on' },
+        },
+      ],
+    },
   ]),
 });