Skip to content

Commit c69b406

Browse files
feat: report duplicate allowed flags in no-invalid-regexp (#18754)
* fix: duplicate allowed flags * apply suggestions * fix: error message * keep duplicate flags in error message * apply suggestion
1 parent a20c870 commit c69b406

2 files changed

Lines changed: 92 additions & 18 deletions

File tree

lib/rules/no-invalid-regexp.js

Lines changed: 29 additions & 18 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 = /[dgimsuvy]/gu;
13+
const validFlags = "dgimsuvy";
1414
const undefined1 = void 0;
1515

1616
//------------------------------------------------------------------------------
@@ -49,13 +49,13 @@ module.exports = {
4949
create(context) {
5050

5151
const options = context.options[0];
52-
let allowedFlags = null;
52+
let allowedFlags = [];
5353

5454
if (options && options.allowConstructorFlags) {
55-
const temp = options.allowConstructorFlags.join("").replace(validFlags, "");
55+
const temp = options.allowConstructorFlags.join("").replace(new RegExp(`[${validFlags}]`, "gu"), "");
5656

5757
if (temp) {
58-
allowedFlags = new RegExp(`[${temp}]`, "gu");
58+
allowedFlags = [...new Set(temp)];
5959
}
6060
}
6161

@@ -125,27 +125,34 @@ module.exports = {
125125
/**
126126
* Check syntax error in a given flags.
127127
* @param {string|null} flags The RegExp flags to validate.
128+
* @param {string|null} flagsToCheck The RegExp invalid flags.
128129
* @returns {string|null} The syntax error.
129130
*/
130-
function validateRegExpFlags(flags) {
131-
if (!flags) {
132-
return null;
133-
}
134-
try {
135-
validator.validateFlags(flags);
136-
} catch {
137-
return `Invalid flags supplied to RegExp constructor '${flags}'`;
131+
function validateRegExpFlags(flags, flagsToCheck) {
132+
const flagsToReport = [];
133+
134+
if (typeof flags === "string") {
135+
for (const flag of flags) {
136+
if (flagsToCheck.includes(flag)) {
137+
flagsToReport.push(flag);
138+
}
139+
}
138140
}
139141

140142
/*
141143
* `regexpp` checks the combination of `u` and `v` flags when parsing `Pattern` according to `ecma262`,
142144
* but this rule may check only the flag when the pattern is unidentifiable, so check it here.
143145
* https://tc39.es/ecma262/multipage/text-processing.html#sec-parsepattern
144146
*/
145-
if (flags.includes("u") && flags.includes("v")) {
147+
if (flags && flags.includes("u") && flags.includes("v")) {
146148
return "Regex 'u' and 'v' flags cannot be used together";
147149
}
148-
return null;
150+
151+
if (!flagsToCheck) {
152+
return null;
153+
}
154+
155+
return `Invalid flags supplied to RegExp constructor '${flagsToReport.join("")}'`;
149156
}
150157

151158
return {
@@ -154,13 +161,17 @@ module.exports = {
154161
return;
155162
}
156163

157-
let flags = getFlags(node);
164+
const flags = getFlags(node);
165+
let flagsToCheck = flags;
166+
const allFlags = allowedFlags.length > 0 ? validFlags.split("").concat(allowedFlags) : validFlags.split("");
158167

159-
if (flags && allowedFlags) {
160-
flags = flags.replace(allowedFlags, "");
168+
if (flags) {
169+
allFlags.forEach(flag => {
170+
flagsToCheck = flagsToCheck.replace(flag, "");
171+
});
161172
}
162173

163-
let message = validateRegExpFlags(flags);
174+
let message = validateRegExpFlags(flags, flagsToCheck);
164175

165176
if (message) {
166177
report(node, message);

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,69 @@ ruleTester.run("no-invalid-regexp", rule, {
213213
type: "NewExpression"
214214
}]
215215
},
216+
{
217+
code: "new RegExp('.', 'aa');",
218+
options: [{ allowConstructorFlags: ["a"] }],
219+
errors: [{
220+
messageId: "regexMessage",
221+
data: { message: "Invalid flags supplied to RegExp constructor 'aa'" },
222+
type: "NewExpression"
223+
}]
224+
},
225+
{
226+
code: "new RegExp('.', 'aa');",
227+
options: [{ allowConstructorFlags: ["a", "a"] }],
228+
errors: [{
229+
messageId: "regexMessage",
230+
data: { message: "Invalid flags supplied to RegExp constructor 'aa'" },
231+
type: "NewExpression"
232+
}]
233+
},
234+
{
235+
code: "new RegExp('.', 'aA');",
236+
options: [{ allowConstructorFlags: ["a"] }],
237+
errors: [{
238+
messageId: "regexMessage",
239+
data: { message: "Invalid flags supplied to RegExp constructor 'A'" },
240+
type: "NewExpression"
241+
}]
242+
},
243+
{
244+
code: "new RegExp('.', 'aaz');",
245+
options: [{ allowConstructorFlags: ["a", "z"] }],
246+
errors: [{
247+
messageId: "regexMessage",
248+
data: { message: "Invalid flags supplied to RegExp constructor 'aa'" },
249+
type: "NewExpression"
250+
}]
251+
},
252+
{
253+
code: "new RegExp('.', 'azz');",
254+
options: [{ allowConstructorFlags: ["a", "z"] }],
255+
errors: [{
256+
messageId: "regexMessage",
257+
data: { message: "Invalid flags supplied to RegExp constructor 'zz'" },
258+
type: "NewExpression"
259+
}]
260+
},
261+
{
262+
code: "new RegExp('.', 'aga');",
263+
options: [{ allowConstructorFlags: ["a"] }],
264+
errors: [{
265+
messageId: "regexMessage",
266+
data: { message: "Invalid flags supplied to RegExp constructor 'aa'" },
267+
type: "NewExpression"
268+
}]
269+
},
270+
{
271+
code: "new RegExp('.', 'uu');",
272+
options: [{ allowConstructorFlags: ["u"] }],
273+
errors: [{
274+
messageId: "regexMessage",
275+
data: { message: "Invalid flags supplied to RegExp constructor 'uu'" },
276+
type: "NewExpression"
277+
}]
278+
},
216279
{
217280
code: "new RegExp(')');",
218281
errors: [{

0 commit comments

Comments
 (0)