Skip to content

Commit 09ee12d

Browse files
committed
fix(sort-regexp): skip sorting when alternatives contain unnamed captures
1 parent 3084606 commit 09ee12d

File tree

3 files changed

+93
-22
lines changed

3 files changed

+93
-22
lines changed

rules/sort-regexp.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
commonJsonSchemas,
1111
groupsJsonSchema,
1212
} from '../utils/common-json-schemas'
13+
import { alternativesContainUnnamedCapturingGroups } from './sort-regexp/alternatives-contain-unnamed-capturing-groups'
1314
import { buildGetCustomGroupOverriddenOptionsFunction } from '../utils/get-custom-groups-compare-options'
1415
import { validateGeneratedGroupsConfiguration } from '../utils/validate-generated-groups-configuration'
1516
import { getCharacterClassElementSortKey } from './sort-regexp/get-character-class-element-sort-key'
@@ -164,6 +165,14 @@ export default createEslintRule<Options, MessageId>({
164165
return
165166
}
166167

168+
if (
169+
alternativesContainUnnamedCapturingGroups(
170+
alternative.parent.alternatives,
171+
)
172+
) {
173+
return
174+
}
175+
167176
if (
168177
hasShadowingAlternatives({
169178
alternatives: alternative.parent.alternatives,
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import type { Alternative, Element } from '@eslint-community/regexpp/ast'
2+
3+
/**
4+
* Checks whether any alternative contains an unnamed capturing group.
5+
*
6+
* @param alternatives - Alternatives to inspect.
7+
* @returns True if at least one unnamed capturing group is found.
8+
*/
9+
export function alternativesContainUnnamedCapturingGroups(
10+
alternatives: Alternative[],
11+
): boolean {
12+
return alternatives.some(alternativeContainsUnnamedCapturingGroups)
13+
}
14+
15+
/**
16+
* Determines whether the given element (or any nested element) contains an
17+
* unnamed capturing group.
18+
*
19+
* @param element - Regex element to inspect.
20+
* @returns True when an unnamed capturing group is found.
21+
*/
22+
function elementContainsUnnamedCapturingGroups(element: Element): boolean {
23+
switch (element.type) {
24+
case 'CapturingGroup': {
25+
if (!element.name) {
26+
return true
27+
}
28+
29+
return element.alternatives.some(
30+
alternativeContainsUnnamedCapturingGroups,
31+
)
32+
}
33+
34+
case 'Quantifier': {
35+
return elementContainsUnnamedCapturingGroups(element.element)
36+
}
37+
38+
case 'Assertion': {
39+
if (element.kind === 'lookahead' || element.kind === 'lookbehind') {
40+
return element.alternatives.some(
41+
alternativeContainsUnnamedCapturingGroups,
42+
)
43+
}
44+
45+
return false
46+
}
47+
48+
case 'Group': {
49+
return element.alternatives.some(
50+
alternativeContainsUnnamedCapturingGroups,
51+
)
52+
}
53+
54+
default: {
55+
return false
56+
}
57+
}
58+
}
59+
60+
/**
61+
* Checks whether an alternative contains unnamed capturing groups by inspecting
62+
* all of its direct elements.
63+
*
64+
* @param alternative - Alternative to inspect.
65+
* @returns True when an unnamed capture appears within the alternative.
66+
*/
67+
function alternativeContainsUnnamedCapturingGroups(
68+
alternative: Alternative,
69+
): boolean {
70+
return alternative.elements.some(elementContainsUnnamedCapturingGroups)
71+
}

test/rules/sort-regexp.test.ts

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,15 @@ describe('sort-regexp', () => {
634634
})
635635
})
636636

637+
it('skips sorting when alternatives contain unnamed capturing groups', async () => {
638+
await valid({
639+
code: dedent`
640+
const regex = /\\(.)|(['"])/gsu;
641+
`,
642+
options: [options],
643+
})
644+
})
645+
637646
it('sorts alternatives with escaped characters', async () => {
638647
await invalid({
639648
errors: [
@@ -800,17 +809,8 @@ describe('sort-regexp', () => {
800809
})
801810
})
802811

803-
it('sorts alternatives with alternation groups', async () => {
804-
await invalid({
805-
errors: [
806-
{
807-
data: { right: '(a|b)', left: '(c|d)' },
808-
messageId: 'unexpectedRegExpOrder',
809-
},
810-
],
811-
output: dedent`
812-
/((a|b)|(c|d)|(e|f))/
813-
`,
812+
it('skips sorting when alternatives are unnamed capturing groups', async () => {
813+
await valid({
814814
code: dedent`
815815
/((c|d)|(a|b)|(e|f))/
816816
`,
@@ -2184,17 +2184,8 @@ describe('sort-regexp', () => {
21842184
})
21852185
})
21862186

2187-
it('sorts alternatives with alternation groups', async () => {
2188-
await invalid({
2189-
errors: [
2190-
{
2191-
data: { right: '(a|b)', left: '(c|d)' },
2192-
messageId: 'unexpectedRegExpOrder',
2193-
},
2194-
],
2195-
output: dedent`
2196-
/((a|b)|(c|d)|(e|f))/
2197-
`,
2187+
it('skips sorting unnamed capturing group alternatives in secondary suite', async () => {
2188+
await valid({
21982189
code: dedent`
21992190
/((c|d)|(a|b)|(e|f))/
22002191
`,

0 commit comments

Comments
 (0)