Skip to content

Commit 94954a7

Browse files
authored
feat: no-invalid-regexp support v flag (#17404)
* feat: `no-invalid-regexp` support `v` flag * fix: uv flags * feat: update regexpp * test: add test cases * fix: error message and update regexpp to ^4.6.1
1 parent 7fc3a2c commit 94954a7

3 files changed

Lines changed: 73 additions & 8 deletions

File tree

lib/rules/no-invalid-regexp.js

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
const RegExpValidator = require("@eslint-community/regexpp").RegExpValidator;
1212
const validator = new RegExpValidator();
13-
const validFlags = /[dgimsuy]/gu;
13+
const validFlags = /[dgimsuvy]/gu;
1414
const undefined1 = void 0;
1515

1616
//------------------------------------------------------------------------------
@@ -108,12 +108,14 @@ module.exports = {
108108
/**
109109
* Check syntax error in a given pattern.
110110
* @param {string} pattern The RegExp pattern to validate.
111-
* @param {boolean} uFlag The Unicode flag.
111+
* @param {Object} flags The RegExp flags to validate.
112+
* @param {boolean} [flags.unicode] The Unicode flag.
113+
* @param {boolean} [flags.unicodeSets] The UnicodeSets flag.
112114
* @returns {string|null} The syntax error.
113115
*/
114-
function validateRegExpPattern(pattern, uFlag) {
116+
function validateRegExpPattern(pattern, flags) {
115117
try {
116-
validator.validatePattern(pattern, undefined1, undefined1, uFlag);
118+
validator.validatePattern(pattern, undefined1, undefined1, flags);
117119
return null;
118120
} catch (err) {
119121
return err.message;
@@ -131,10 +133,19 @@ module.exports = {
131133
}
132134
try {
133135
validator.validateFlags(flags);
134-
return null;
135136
} catch {
136137
return `Invalid flags supplied to RegExp constructor '${flags}'`;
137138
}
139+
140+
/*
141+
* `regexpp` checks the combination of `u` and `v` flags when parsing `Pattern` according to `ecma262`,
142+
* but this rule may check only the flag when the pattern is unidentifiable, so check it here.
143+
* https://tc39.es/ecma262/multipage/text-processing.html#sec-parsepattern
144+
*/
145+
if (flags.includes("u") && flags.includes("v")) {
146+
return "Regex 'u' and 'v' flags cannot be used together";
147+
}
148+
return null;
138149
}
139150

140151
return {
@@ -166,8 +177,12 @@ module.exports = {
166177

167178
// If flags are unknown, report the regex only if its pattern is invalid both with and without the "u" flag
168179
flags === null
169-
? validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
170-
: validateRegExpPattern(pattern, flags.includes("u"))
180+
? (
181+
validateRegExpPattern(pattern, { unicode: true, unicodeSets: false }) &&
182+
validateRegExpPattern(pattern, { unicode: false, unicodeSets: true }) &&
183+
validateRegExpPattern(pattern, { unicode: false, unicodeSets: false })
184+
)
185+
: validateRegExpPattern(pattern, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") })
171186
);
172187

173188
if (message) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"bugs": "https://github.com/eslint/eslint/issues/",
6262
"dependencies": {
6363
"@eslint-community/eslint-utils": "^4.2.0",
64-
"@eslint-community/regexpp": "^4.4.0",
64+
"@eslint-community/regexpp": "^4.6.1",
6565
"@eslint/eslintrc": "^2.1.0",
6666
"@eslint/js": "8.44.0",
6767
"@humanwhocodes/config-array": "^0.11.10",

tests/lib/rules/no-invalid-regexp.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ ruleTester.run("no-invalid-regexp", rule, {
8181
"new RegExp('\\\\p{Script=Vith}', 'u')",
8282
"new RegExp('\\\\p{Script=Vithkuqi}', 'u')",
8383

84+
// ES2024
85+
"new RegExp('[A--B]', 'v')",
86+
"new RegExp('[A&&B]', 'v')",
87+
"new RegExp('[A--[0-9]]', 'v')",
88+
"new RegExp('[\\\\p{Basic_Emoji}--\\\\q{a|bc|def}]', 'v')",
89+
"new RegExp('[A--B]', flags)", // valid only with `v` flag
90+
"new RegExp('[[]\\\\u{0}*', flags)", // valid only with `u` flag
91+
8492
// allowConstructorFlags
8593
{
8694
code: "new RegExp('.', 'g')",
@@ -288,6 +296,48 @@ ruleTester.run("no-invalid-regexp", rule, {
288296
data: { message: "Invalid flags supplied to RegExp constructor 'z'" },
289297
type: "NewExpression"
290298
}]
299+
},
300+
301+
// ES2024
302+
{
303+
code: "new RegExp('[[]', 'v');",
304+
errors: [{
305+
messageId: "regexMessage",
306+
data: { message: "Invalid regular expression: /[[]/v: Unterminated character class" },
307+
type: "NewExpression"
308+
}]
309+
},
310+
{
311+
code: "new RegExp('.', 'uv');",
312+
errors: [{
313+
messageId: "regexMessage",
314+
data: { message: "Regex 'u' and 'v' flags cannot be used together" },
315+
type: "NewExpression"
316+
}]
317+
},
318+
{
319+
code: "new RegExp(pattern, 'uv');",
320+
errors: [{
321+
messageId: "regexMessage",
322+
data: { message: "Regex 'u' and 'v' flags cannot be used together" },
323+
type: "NewExpression"
324+
}]
325+
},
326+
{
327+
code: "new RegExp('[A--B]' /* valid only with `v` flag */, 'u')",
328+
errors: [{
329+
messageId: "regexMessage",
330+
data: { message: "Invalid regular expression: /[A--B]/u: Range out of order in character class" },
331+
type: "NewExpression"
332+
}]
333+
},
334+
{
335+
code: "new RegExp('[[]\\\\u{0}*' /* valid only with `u` flag */, 'v')",
336+
errors: [{
337+
messageId: "regexMessage",
338+
data: { message: "Invalid regular expression: /[[]\\u{0}*/v: Unterminated character class" },
339+
type: "NewExpression"
340+
}]
291341
}
292342
]
293343
});

0 commit comments

Comments
 (0)