Skip to content

Commit 23b867e

Browse files
brettz9keithamus
andauthored
feat: add rule no-regexp-duplicate-named-groups (and bump to ES2025) (#32)
* feat: add `no-regexp-v-flag` (and bump to ES2024) Also: - fix: ensures no-hashbang-comment rule is in TS 2023 - docs: remove unneeded backticks in no-regexp-s-flag rule docs * feat: add rule `no-regexp-duplicate-named-groups` (and bump to ES2025) Also: - docs: add required escapes to `no-regexp-named-group` docs --------- Co-authored-by: Keith Cirkel <[email protected]>
1 parent 58260bf commit 23b867e

6 files changed

Lines changed: 119 additions & 4 deletions

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ for configuration. Here's some examples:
164164
- [no-private-class-fields](./docs/no-private-class-fields.md)
165165
- [no-public-instance-class-fields](./docs/no-public-instance-class-fields.md)
166166
- [no-public-static-class-fields](./docs/no-public-static-class-fields.md)
167+
- [no-regexp-duplicate-named-groups](./docs/no-regexp-duplicate-named-groups.md)
167168
- [no-regexp-lookbehind](./docs/no-regexp-lookbehind.md)
168169
- [no-regexp-named-group](./docs/no-regexp-named-group.md)
169170
- [no-regexp-s-flag](./docs/no-regexp-s-flag.md)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# no-regexp-duplicate-named-groups
2+
3+
This prevents the use of the RegExp duplicate named groups feature
4+
5+
```js
6+
/(?<year>\d{4})-(?<month>\d{2})|(?<month>\d{2})-(?<year>\d{4})/;
7+
8+
new RegExp('(?<year>\\d{4})-(?<month>\\d{2})|(?<month>\\d{2})-(?<year>\\d{4})')
9+
```
10+
11+
These will not be allowed because they are not supported in the following browsers:
12+
13+
- Edge < 125
14+
- Safari < 17
15+
- Firefox < 129
16+
- Chrome < 125
17+
18+
19+
## What is the Fix?
20+
21+
You will have to avoid getting the same name out-of-the-box for an
22+
alternative group.
23+
24+
This can be safely disabled if you intend to compile code with the `@babel/plugin-proposal-duplicate-named-capturing-groups-regex` Babel plugin.

docs/no-regexp-named-group.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This prevents the use of the RegExp named groups feature
55
```js
66
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
77

8-
new RegExp('(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})')
8+
new RegExp('(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})')
99
```
1010

1111
These will not be allowed because they are not supported in the following browsers:
@@ -22,7 +22,7 @@ If readability is the main concern, using non-named groups with array-destructur
2222

2323
```js
2424
// With named:
25-
const {year,month,day} = '2020-01-01'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/).groups
25+
const {year, month, day} = '2020-01-01'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/).groups
2626

2727
// Without named
2828
const [_, year, month, day] = '2020-01-01'.match(/(\d{4})-(\d{2})-(\d{2})/) || []

lib/index.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,14 @@ createRule(
228228
{ ts: 2024 }
229229
);
230230

231+
// ES2025
232+
createRule(
233+
"no-regexp-duplicate-named-groups",
234+
"edge < 125, safari < 17, firefox < 129, chrome < 125",
235+
"disallow the use of RegExp duplicate named groups",
236+
{ ts: 2025 }
237+
);
238+
231239
// Proposals...
232240
createRule(
233241
"no-do-expression",
@@ -247,7 +255,7 @@ createRule(
247255

248256
module.exports.configs.recommended = {
249257
plugins: ["escompat"],
250-
parserOptions: { ecmaVersion: 2024 },
258+
parserOptions: { ecmaVersion: 2025 },
251259
rules: Object.keys(module.exports.rules).reduce(
252260
(o, r) => ((o["escompat/" + r] = ["error"]), o),
253261
{}
@@ -260,7 +268,7 @@ module.exports.configs["flat/recommended"] = {
260268
escompat: module.exports
261269
},
262270
languageOptions: {
263-
ecmaVersion: 2024
271+
ecmaVersion: 2025
264272
},
265273
rules: Object.keys(module.exports.rules).reduce(
266274
(o, r) => ((o["escompat/" + r] = ["error"]), o),
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
3+
const hasDuplicateNamedGroups = s => /(\(\?<[_$\w]*?)>.*?\1>/.test(s)
4+
5+
module.exports = (context, badBrowser) => ({
6+
'Literal[regex]'(node) {
7+
if (hasDuplicateNamedGroups(node.regex.pattern)) {
8+
context.report(node, `RegExp duplicate named groups are not supported in ${badBrowser}`)
9+
}
10+
},
11+
'CallExpression[callee.name="RegExp"], NewExpression[callee.name="RegExp"]'(node) {
12+
const [source] = node.arguments;
13+
if (
14+
source &&
15+
(
16+
(
17+
source.type === 'Literal' &&
18+
typeof source.value === 'string' &&
19+
hasDuplicateNamedGroups(source.value)
20+
) ||
21+
(
22+
source.type === 'TemplateLiteral' &&
23+
source.quasis.some(({value: {raw}}) => hasDuplicateNamedGroups(raw))
24+
)
25+
)
26+
) {
27+
context.report(node, `RegExp duplicate named groups are not supported in ${badBrowser}`)
28+
}
29+
}
30+
})
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use strict';
2+
3+
const rule = require('../lib/index').rules['no-regexp-duplicate-named-groups']
4+
const RuleTester = require('eslint').RuleTester
5+
6+
const ruleTester = new RuleTester({languageOptions: {ecmaVersion: 2025}})
7+
8+
ruleTester.run('no-regexp-duplicate-named-groups', rule, {
9+
valid: [
10+
{code: '/(?:a)/'},
11+
{code: '/(?:a)/g'},
12+
{code: 'RegExp("(?:a)b")'},
13+
{code: 'RegExp("(?:a)b", "g")'},
14+
{code: '/(?<name>a)/'},
15+
{code: 'RegExp("(?<name>a)")'},
16+
{code: '/(?<name>a)|(?<anotherName>a)/'},
17+
],
18+
invalid: [
19+
{
20+
code: '/(?<name>a)|(?<name>b)/',
21+
errors: [
22+
{
23+
message: 'RegExp duplicate named groups are not supported in undefined'
24+
}
25+
]
26+
},
27+
{
28+
code: 'new RegExp("(?<name>a)|(?<name>b)")',
29+
errors: [
30+
{
31+
message: 'RegExp duplicate named groups are not supported in undefined'
32+
}
33+
]
34+
},
35+
{
36+
code: '/(?<$name>a)|(?<$name>b)/',
37+
errors: [
38+
{
39+
message: 'RegExp duplicate named groups are not supported in undefined'
40+
}
41+
]
42+
},
43+
{
44+
code: '/(?<_name>)|(?<_name>)/',
45+
errors: [
46+
{
47+
message: 'RegExp duplicate named groups are not supported in undefined'
48+
}
49+
]
50+
},
51+
]
52+
})

0 commit comments

Comments
 (0)