Repository: ota-meshi/eslint-plugin-regexp (ts)

Issue:
Add option to rule `no-unused-capturing-group` to allow unused named capturing groups
### Motivation

I have this regex:

```javascript
const REGEX_SEMANTIC_VERSION = /(?<majorVersion>\d+)\.(?<minorVersion>\d+)\.(?<patchVersion>\d+)(?:-(?<suffix>.+))?/;
```

The named capturing groups makes it clear what part of the regex is what.  
Currently I don't access these capturing groups, so [`no-unused-capturing-group`](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-unused-capturing-group.html) reports this as an error.

### Feature Request

Add an option to `no-unused-capturing-group` so that it does not report unused named capturing groups.

Pull request description:
Add `allowNamed` option to `no-unused-capturing-group` rule
Fixes #688.

Patch:
diff --git a/.changeset/lovely-bees-stare.md b/.changeset/lovely-bees-stare.md
new file mode 100644
index 0000000..f8da7ec
--- /dev/null
+++ b/.changeset/lovely-bees-stare.md
@@ -0,0 +1,5 @@
+---
+"eslint-plugin-regexp": minor
+---
+
+Add `allowNamed` option to `no-unused-capturing-group` rule to allow for unused named capturing groups.
diff --git a/docs/rules/no-unused-capturing-group.md b/docs/rules/no-unused-capturing-group.md
index 3b5cae2..1cd40f3 100644
--- a/docs/rules/no-unused-capturing-group.md
+++ b/docs/rules/no-unused-capturing-group.md
@@ -78,7 +78,8 @@ Using capturing groups only if the captured text is used makes their usage unamb
 ```json
 {
   "regexp/no-unused-capturing-group": ["error", {
-    "fixable": false
+    "fixable": false,
+    "allowNamed": false
   }]
 }
 ```
@@ -89,6 +90,12 @@ Using capturing groups only if the captured text is used makes their usage unamb
 
   This rule is not fixable by default. Unused capturing groups can indicate a mistake in the code that uses the regex, so changing the regex might not be the right fix. When enabling this option, be sure to carefully check its changes.
 
+- `allowNamed: true | false`
+
+  Whether unused named capturing groups are allowed. Defaults to `false`.
+
+  Sometimes named capturing groups can be used to document a regex. In such cases, it can be acceptable to have unused named capturing groups. This option is disabled by default to prevent mistakes.
+
 ## :couple: Related rules
 
 - [regexp/no-useless-dollar-replacements]
diff --git a/lib/rules/no-unused-capturing-group.ts b/lib/rules/no-unused-capturing-group.ts
index 1888b5c..e0a86fa 100644
--- a/lib/rules/no-unused-capturing-group.ts
+++ b/lib/rules/no-unused-capturing-group.ts
@@ -29,6 +29,7 @@ export default createRule("no-unused-capturing-group", {
                 type: "object",
                 properties: {
                     fixable: { type: "boolean" },
+                    allowNamed: { type: "boolean" },
                 },
                 additionalProperties: false,
             },
@@ -45,6 +46,7 @@ export default createRule("no-unused-capturing-group", {
     },
     create(context) {
         const fixable: boolean = context.options[0]?.fixable ?? false
+        const allowNamed: boolean = context.options[0]?.allowNamed ?? false
 
         function reportUnused(
             unused: Set<CapturingGroup>,
@@ -57,6 +59,14 @@ export default createRule("no-unused-capturing-group", {
                 getAllCapturingGroups,
             } = regexpContext
 
+            if (allowNamed) {
+                for (const cgNode of unused) {
+                    if (cgNode.name) {
+                        unused.delete(cgNode)
+                    }
+                }
+            }
+
             const fixableGroups = new Set<CapturingGroup>()
             for (const group of [...getAllCapturingGroups()].reverse()) {
                 if (unused.has(group)) {


Test patch:
diff --git a/tests/lib/rules/no-unused-capturing-group.ts b/tests/lib/rules/no-unused-capturing-group.ts
index 3b70848..b35c50d 100644
--- a/tests/lib/rules/no-unused-capturing-group.ts
+++ b/tests/lib/rules/no-unused-capturing-group.ts
@@ -162,6 +162,11 @@ tester.run("no-unused-capturing-group", rule as any, {
         String.raw`const re = /(foo)/d
         console.log(re.exec('foo').indices[unknown])
         `,
+
+        {
+            code: String.raw`const REGEX_SEMANTIC_VERSION = /(?<majorVersion>\d+)\.(?<minorVersion>\d+)\.(?<patchVersion>\d+)(?:-(?<suffix>.+))?/;`,
+            options: [{ allowNamed: true }],
+        },
     ],
     invalid: [
         {