Skip to content

Commit

Permalink
feat: Add es-x/no-regexp-duplicate-named-capturing-groups rule (#146)
Browse files Browse the repository at this point in the history
Co-authored-by: 唯然 <[email protected]>
  • Loading branch information
ota-meshi and aladdin-add committed Jul 1, 2024
1 parent 87a91d7 commit 5b9a5a0
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 3 deletions.
30 changes: 30 additions & 0 deletions docs/rules/no-regexp-duplicate-named-capturing-groups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: "es-x/no-regexp-duplicate-named-capturing-groups"
description: "disallow RegExp duplicate named capturing groups"
---

# es-x/no-regexp-duplicate-named-capturing-groups
> disallow RegExp duplicate named capturing groups
- ❗ <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>

This rule reports ES2025 [RegExp duplicate named capture groups](https://github.com/tc39/proposal-duplicate-named-capturing-groups) as errors.

## 💡 Examples

⛔ Examples of **incorrect** code for this rule:

<eslint-playground type="bad">

```js
/*eslint es-x/no-regexp-duplicate-named-capturing-groups: error */
const r1 = /(?<year>\d{4})-\d{2}|\d{2}-(?<year>\d{4})/
const r2 = /(?<x>a)|(?<x>b)/
```

</eslint-playground>

## 📚 References

- [Rule source](https://github.com/eslint-community/eslint-plugin-es-x/blob/master/lib/rules/no-regexp-duplicate-named-capturing-groups.js)
- [Test source](https://github.com/eslint-community/eslint-plugin-es-x/blob/master/tests/lib/rules/no-regexp-duplicate-named-capturing-groups.js)
71 changes: 71 additions & 0 deletions lib/rules/no-regexp-duplicate-named-capturing-groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"use strict"

const { getSourceCode } = require("eslint-compat-utils")
const { defineRegExpHandler } = require("../util/define-regexp-handler")

module.exports = {
meta: {
docs: {
description: "disallow RegExp duplicate named capturing groups.",
category: "ES2025",
recommended: false,
url: "http://eslint-community.github.io/eslint-plugin-es-x/rules/no-regexp-duplicate-named-capturing-groups.html",
},
fixable: null,
messages: {
forbidden:
"ES2025 RegExp duplicate named capturing groups are forbidden.",
},
schema: [],
type: "problem",
},
create(context) {
return defineRegExpHandler(context, (node) => {
const found = new Map()
return {
onPatternEnter() {
found.clear()
},
onCapturingGroupLeave(start, end, name) {
if (!name) {
return
}
const list = found.get(name)
if (list) {
list.push({ start, end })
} else {
found.set(name, [{ start, end }])
}
},
onExit() {
for (const [, dupe] of found.values()) {
if (!dupe) {
continue
}
const { start, end } = dupe
const sourceCode = getSourceCode(context)
context.report({
node,
loc:
node.type === "Literal"
? {
start: sourceCode.getLocFromIndex(
node.range[0] +
1 /* slash */ +
start,
),
end: sourceCode.getLocFromIndex(
node.range[0] +
1 /* slash */ +
end,
),
}
: null,
messageId: "forbidden",
})
}
},
}
})
},
}
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,19 @@
},
"dependencies": {
"@eslint-community/eslint-utils": "^4.1.2",
"@eslint-community/regexpp": "^4.6.0",
"@eslint-community/regexpp": "^4.11.0",
"eslint-compat-utils": "^0.5.1"
},
"devDependencies": {
"@typescript-eslint/parser": "^7.0.2",
"acorn": "^8.7.0",
"env-cmd": "^10.1.0",
"eslint": "^9.1.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-eslint-plugin": "^6.0.0",
"eslint-plugin-n": "^17.8.1",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-vue": "^9.25.0",
"espree": "^10.0.1",
"espree": "^10.1.0",
"globals": "^15.0.0",
"jsdom": "^24.0.0",
"mocha": "^10.0.0",
Expand Down
77 changes: 77 additions & 0 deletions tests/lib/rules/no-regexp-duplicate-named-capturing-groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use strict"

const RuleTester = require("../../tester")
const rule = require("../../../lib/rules/no-regexp-duplicate-named-capturing-groups.js")

if (!RuleTester.isSupported(2025)) {
//eslint-disable-next-line no-console
console.log("Skip the tests of no-regexp-duplicate-named-capturing-groups.")
return
}

new RuleTester().run("no-regexp-duplicate-named-capturing-groups", rule, {
valid: [
String.raw`/(?<x>a)/`,
String.raw`/(a)/`,
String.raw`/(?<x>a)(?<y>b)/`,
String.raw`/(?<x>a)(b)/`,
String.raw`/(?<x>a)|(?<y>b)/`,
String.raw`new RegExp("(?<x>a)")`,
String.raw`new RegExp("(a)")`,
String.raw`new RegExp("(?<x>a)(?<y>b)")`,
String.raw`new RegExp("(?<x>a)(b)")`,
String.raw`new RegExp("(?<x>a)|(?<y>b)")`,
],
invalid: [
{
code: String.raw`/(?<x>a)|(?<x>b)/`,
errors: [
{
message:
"ES2025 RegExp duplicate named capturing groups are forbidden.",
column: 10,
},
],
},
{
code: String.raw`/(?<x>a)|(?<x>b)|(?<x>c)/`,
errors: [
{
message:
"ES2025 RegExp duplicate named capturing groups are forbidden.",
column: 10,
},
],
},
{
code: String.raw`new RegExp("(?<x>a)|(?<x>b)")`,
errors: [
{
message:
"ES2025 RegExp duplicate named capturing groups are forbidden.",
column: 1,
},
],
},
{
code: String.raw`new RegExp("(?<x>a)|(?<x>b)|(?<x>c)")`,
errors: [
{
message:
"ES2025 RegExp duplicate named capturing groups are forbidden.",
column: 1,
},
],
},
{
code: String.raw`/(?<x>a)|(?<y>b)|(?<x>c)/`,
errors: [
{
message:
"ES2025 RegExp duplicate named capturing groups are forbidden.",
column: 18,
},
],
},
],
})
1 change: 1 addition & 0 deletions tests/tester.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const RuleTester = getRuleTester()
const eslintVersion = new Linter().version
const ecmaVersion =
/*eslint-disable prettier/prettier */
semver.gte(eslintVersion, "9.6.0") ? 2025 :
semver.gte(eslintVersion, "8.44.0") ? 2024 :
semver.gte(eslintVersion, "8.23.0") ? 2023 :
semver.gte(eslintVersion, "8.0.0") ? 2022 :
Expand Down

0 comments on commit 5b9a5a0

Please sign in to comment.