Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add rule no-regexp-duplicate-named-groups (and bump to ES2025) #32

Merged
merged 3 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ for configuration. Here's some examples:
- [no-private-class-fields](./docs/no-private-class-fields.md)
- [no-public-instance-class-fields](./docs/no-public-instance-class-fields.md)
- [no-public-static-class-fields](./docs/no-public-static-class-fields.md)
- [no-regexp-duplicate-named-groups](./docs/no-regexp-duplicate-named-groups.md)
- [no-regexp-lookbehind](./docs/no-regexp-lookbehind.md)
- [no-regexp-named-group](./docs/no-regexp-named-group.md)
- [no-regexp-s-flag](./docs/no-regexp-s-flag.md)
Expand Down
24 changes: 24 additions & 0 deletions docs/no-regexp-duplicate-named-groups.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# no-regexp-duplicate-named-groups

This prevents the use of the RegExp duplicate named groups feature

```js
/(?<year>\d{4})-(?<month>\d{2})|(?<month>\d{2})-(?<year>\d{4})/;

new RegExp('(?<year>\\d{4})-(?<month>\\d{2})|(?<month>\\d{2})-(?<year>\\d{4})')
```

These will not be allowed because they are not supported in the following browsers:

- Edge < 125
- Safari < 17
- Firefox < 129
- Chrome < 125


## What is the Fix?

You will have to avoid getting the same name out-of-the-box for an
alternative group.

This can be safely disabled if you intend to compile code with the `@babel/plugin-proposal-duplicate-named-capturing-groups-regex` Babel plugin.
4 changes: 2 additions & 2 deletions docs/no-regexp-named-group.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This prevents the use of the RegExp named groups feature
```js
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/

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

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

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

// Without named
const [_, year, month, day] = '2020-01-01'.match(/(\d{4})-(\d{2})-(\d{2})/) || []
Expand Down
12 changes: 10 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,14 @@ createRule(
{ ts: 2024 }
);

// ES2025
createRule(
"no-regexp-duplicate-named-groups",
"edge < 125, safari < 17, firefox < 129, chrome < 125",
"disallow the use of RegExp duplicate named groups",
{ ts: 2025 }
);

// Proposals...
createRule(
"no-do-expression",
Expand All @@ -247,7 +255,7 @@ createRule(

module.exports.configs.recommended = {
plugins: ["escompat"],
parserOptions: { ecmaVersion: 2024 },
parserOptions: { ecmaVersion: 2025 },
rules: Object.keys(module.exports.rules).reduce(
(o, r) => ((o["escompat/" + r] = ["error"]), o),
{}
Expand All @@ -260,7 +268,7 @@ module.exports.configs["flat/recommended"] = {
escompat: module.exports
},
languageOptions: {
ecmaVersion: 2024
ecmaVersion: 2025
},
rules: Object.keys(module.exports.rules).reduce(
(o, r) => ((o["escompat/" + r] = ["error"]), o),
Expand Down
30 changes: 30 additions & 0 deletions lib/rules/no-regexp-duplicate-named-groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict';

const hasDuplicateNamedGroups = s => /(\(\?<[_$\w]*?)>.*?\1>/.test(s)

module.exports = (context, badBrowser) => ({
'Literal[regex]'(node) {
if (hasDuplicateNamedGroups(node.regex.pattern)) {
context.report(node, `RegExp duplicate named groups are not supported in ${badBrowser}`)
}
},
'CallExpression[callee.name="RegExp"], NewExpression[callee.name="RegExp"]'(node) {
const [source] = node.arguments;
if (
source &&
(
(
source.type === 'Literal' &&
typeof source.value === 'string' &&
hasDuplicateNamedGroups(source.value)
) ||
(
source.type === 'TemplateLiteral' &&
source.quasis.some(({value: {raw}}) => hasDuplicateNamedGroups(raw))
)
)
) {
context.report(node, `RegExp duplicate named groups are not supported in ${badBrowser}`)
}
}
})
52 changes: 52 additions & 0 deletions test/no-regexp-duplicate-named-groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict';

const rule = require('../lib/index').rules['no-regexp-duplicate-named-groups']
const RuleTester = require('eslint').RuleTester

const ruleTester = new RuleTester({languageOptions: {ecmaVersion: 2025}})

ruleTester.run('no-regexp-duplicate-named-groups', rule, {
valid: [
{code: '/(?:a)/'},
{code: '/(?:a)/g'},
{code: 'RegExp("(?:a)b")'},
{code: 'RegExp("(?:a)b", "g")'},
{code: '/(?<name>a)/'},
{code: 'RegExp("(?<name>a)")'},
{code: '/(?<name>a)|(?<anotherName>a)/'},
],
invalid: [
{
code: '/(?<name>a)|(?<name>b)/',
errors: [
{
message: 'RegExp duplicate named groups are not supported in undefined'
}
]
},
{
code: 'new RegExp("(?<name>a)|(?<name>b)")',
errors: [
{
message: 'RegExp duplicate named groups are not supported in undefined'
}
]
},
{
code: '/(?<$name>a)|(?<$name>b)/',
errors: [
{
message: 'RegExp duplicate named groups are not supported in undefined'
}
]
},
{
code: '/(?<_name>)|(?<_name>)/',
errors: [
{
message: 'RegExp duplicate named groups are not supported in undefined'
}
]
},
]
})