Bump the npm group with 2 updates (#1819)

* Bump the npm group with 2 updates

Bumps the npm group with 2 updates: [eslint](https://github.com/eslint/eslint) and [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import).


Updates `eslint` from 8.45.0 to 8.46.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.45.0...v8.46.0)

Updates `eslint-plugin-import` from 2.27.5 to 2.28.0
- [Release notes](https://github.com/import-js/eslint-plugin-import/releases)
- [Changelog](https://github.com/import-js/eslint-plugin-import/blob/main/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.27.5...v2.28.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: eslint-plugin-import
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update checked-in dependencies

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
dependabot[bot] 2023-08-01 03:35:02 -07:00 committed by GitHub
parent a6b0ced86b
commit e7e35baaf0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1408 changed files with 27215 additions and 9910 deletions

View file

@ -212,6 +212,38 @@ function assertIsObject(value) {
}
}
/**
* The error type when there's an eslintrc-style options in a flat config.
*/
class IncompatibleKeyError extends Error {
/**
* @param {string} key The invalid key.
*/
constructor(key) {
super("This appears to be in eslintrc format rather than flat config format.");
this.messageTemplate = "eslintrc-incompat";
this.messageData = { key };
}
}
/**
* The error type when there's an eslintrc-style plugins array found.
*/
class IncompatiblePluginsError extends Error {
/**
* Creates a new instance.
* @param {Array<string>} plugins The plugins array.
*/
constructor(plugins) {
super("This appears to be in eslintrc format (array of strings) rather than flat config format (object).");
this.messageTemplate = "eslintrc-plugins";
this.messageData = { plugins };
}
}
//-----------------------------------------------------------------------------
// Low-Level Schemas
//-----------------------------------------------------------------------------
@ -303,6 +335,11 @@ const pluginsSchema = {
throw new TypeError("Expected an object.");
}
// make sure it's not an array, which would mean eslintrc-style is used
if (Array.isArray(value)) {
throw new IncompatiblePluginsError(value);
}
// second check the keys to make sure they are objects
for (const key of Object.keys(value)) {
@ -438,11 +475,44 @@ const sourceTypeSchema = {
}
};
/**
* Creates a schema that always throws an error. Useful for warning
* about eslintrc-style keys.
* @param {string} key The eslintrc key to create a schema for.
* @returns {ObjectPropertySchema} The schema.
*/
function createEslintrcErrorSchema(key) {
return {
merge: "replace",
validate() {
throw new IncompatibleKeyError(key);
}
};
}
const eslintrcKeys = [
"env",
"extends",
"globals",
"ignorePatterns",
"noInlineConfig",
"overrides",
"parser",
"parserOptions",
"reportUnusedDisableDirectives",
"root"
];
//-----------------------------------------------------------------------------
// Full schema
//-----------------------------------------------------------------------------
exports.flatConfigSchema = {
// eslintrc-style keys that should always error
...Object.fromEntries(eslintrcKeys.map(key => [key, createEslintrcErrorSchema(key)])),
// flat config keys
settings: deepObjectAssignSchema,
linterOptions: {
schema: {

View file

@ -714,12 +714,10 @@ class FlatESLint {
}
const rule = getRuleFromConfig(ruleId, config);
// ensure the rule exists
if (!rule) {
throw new TypeError(`Could not find the rule "${ruleId}".`);
// ignore unknown rules
if (rule) {
resultRules.set(ruleId, rule);
}
resultRules.set(ruleId, rule);
}
}

View file

@ -14,6 +14,16 @@ const collector = new (class {
}
onPatternEnter() {
/*
* `RegExpValidator` may parse the pattern twice in one `validatePattern`.
* So `this._controlChars` should be cleared here as well.
*
* For example, the `/(?<a>\x1f)/` regex will parse the pattern twice.
* This is based on the content described in Annex B.
* If the regex contains a `GroupName` and the `u` flag is not used, `ParseText` will be called twice.
* See https://tc39.es/ecma262/2023/multipage/additional-ecmascript-features-for-web-browsers.html#sec-parsepattern-annexb
*/
this._controlChars = [];
}
@ -32,10 +42,13 @@ const collector = new (class {
collectControlChars(regexpStr, flags) {
const uFlag = typeof flags === "string" && flags.includes("u");
const vFlag = typeof flags === "string" && flags.includes("v");
this._controlChars = [];
this._source = regexpStr;
try {
this._source = regexpStr;
this._validator.validatePattern(regexpStr, void 0, void 0, uFlag); // Call onCharacter hook
this._validator.validatePattern(regexpStr, void 0, void 0, { unicode: uFlag, unicodeSets: vFlag }); // Call onCharacter hook
} catch {
// Ignore syntax errors in RegExp.

View file

@ -5,20 +5,18 @@
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/*
* plain-English description of the following regexp:
* 0. `^` fix the match at the beginning of the string
* 1. `([^\\[]|\\.|\[([^\\\]]|\\.)+\])*`: regexp contents; 0 or more of the following
* 1.0. `[^\\[]`: any character that's not a `\` or a `[` (anything but escape sequences and character classes)
* 1.1. `\\.`: an escape sequence
* 1.2. `\[([^\\\]]|\\.)+\]`: a character class that isn't empty
* 2. `$`: fix the match at the end of the string
*/
const regex = /^([^\\[]|\\.|\[([^\\\]]|\\.)+\])*$/u;
const parser = new RegExpParser();
const QUICK_TEST_REGEX = /\[\]/u;
//------------------------------------------------------------------------------
// Rule Definition
@ -45,9 +43,32 @@ module.exports = {
create(context) {
return {
"Literal[regex]"(node) {
if (!regex.test(node.regex.pattern)) {
context.report({ node, messageId: "unexpected" });
const { pattern, flags } = node.regex;
if (!QUICK_TEST_REGEX.test(pattern)) {
return;
}
let regExpAST;
try {
regExpAST = parser.parsePattern(pattern, 0, pattern.length, {
unicode: flags.includes("u"),
unicodeSets: flags.includes("v")
});
} catch {
// Ignore regular expressions that regexpp cannot parse
return;
}
visitRegExpAST(regExpAST, {
onCharacterClassEnter(characterClass) {
if (!characterClass.negate && characterClass.elements.length === 0) {
context.report({ node, messageId: "unexpected" });
}
}
});
}
};

View file

@ -4,6 +4,8 @@
*/
"use strict";
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
@ -19,7 +21,18 @@ module.exports = {
url: "https://eslint.org/docs/latest/rules/no-empty-pattern"
},
schema: [],
schema: [
{
type: "object",
properties: {
allowObjectPatternsAsParameters: {
type: "boolean",
default: false
}
},
additionalProperties: false
}
],
messages: {
unexpected: "Unexpected empty {{type}} pattern."
@ -27,11 +40,33 @@ module.exports = {
},
create(context) {
const options = context.options[0] || {},
allowObjectPatternsAsParameters = options.allowObjectPatternsAsParameters || false;
return {
ObjectPattern(node) {
if (node.properties.length === 0) {
context.report({ node, messageId: "unexpected", data: { type: "object" } });
if (node.properties.length > 0) {
return;
}
// Allow {} and {} = {} empty object patterns as parameters when allowObjectPatternsAsParameters is true
if (
allowObjectPatternsAsParameters &&
(
astUtils.isFunction(node.parent) ||
(
node.parent.type === "AssignmentPattern" &&
astUtils.isFunction(node.parent.parent) &&
node.parent.right.type === "ObjectExpression" &&
node.parent.right.properties.length === 0
)
)
) {
return;
}
context.report({ node, messageId: "unexpected", data: { type: "object" } });
},
ArrayPattern(node) {
if (node.elements.length === 0) {

View file

@ -10,7 +10,7 @@
const RegExpValidator = require("@eslint-community/regexpp").RegExpValidator;
const validator = new RegExpValidator();
const validFlags = /[dgimsuy]/gu;
const validFlags = /[dgimsuvy]/gu;
const undefined1 = void 0;
//------------------------------------------------------------------------------
@ -108,12 +108,14 @@ module.exports = {
/**
* Check syntax error in a given pattern.
* @param {string} pattern The RegExp pattern to validate.
* @param {boolean} uFlag The Unicode flag.
* @param {Object} flags The RegExp flags to validate.
* @param {boolean} [flags.unicode] The Unicode flag.
* @param {boolean} [flags.unicodeSets] The UnicodeSets flag.
* @returns {string|null} The syntax error.
*/
function validateRegExpPattern(pattern, uFlag) {
function validateRegExpPattern(pattern, flags) {
try {
validator.validatePattern(pattern, undefined1, undefined1, uFlag);
validator.validatePattern(pattern, undefined1, undefined1, flags);
return null;
} catch (err) {
return err.message;
@ -131,10 +133,19 @@ module.exports = {
}
try {
validator.validateFlags(flags);
return null;
} catch {
return `Invalid flags supplied to RegExp constructor '${flags}'`;
}
/*
* `regexpp` checks the combination of `u` and `v` flags when parsing `Pattern` according to `ecma262`,
* but this rule may check only the flag when the pattern is unidentifiable, so check it here.
* https://tc39.es/ecma262/multipage/text-processing.html#sec-parsepattern
*/
if (flags.includes("u") && flags.includes("v")) {
return "Regex 'u' and 'v' flags cannot be used together";
}
return null;
}
return {
@ -166,8 +177,12 @@ module.exports = {
// If flags are unknown, report the regex only if its pattern is invalid both with and without the "u" flag
flags === null
? validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
: validateRegExpPattern(pattern, flags.includes("u"))
? (
validateRegExpPattern(pattern, { unicode: true, unicodeSets: false }) &&
validateRegExpPattern(pattern, { unicode: false, unicodeSets: true }) &&
validateRegExpPattern(pattern, { unicode: false, unicodeSets: false })
)
: validateRegExpPattern(pattern, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") })
);
if (message) {

View file

@ -186,7 +186,7 @@ module.exports = {
}
const references = sourceCode.getScope(node).through;
const unsafeRefs = references.filter(r => !isSafe(loopNode, r)).map(r => r.identifier.name);
const unsafeRefs = references.filter(r => r.resolved && !isSafe(loopNode, r)).map(r => r.identifier.name);
if (unsafeRefs.length > 0) {
context.report({

View file

@ -18,7 +18,7 @@ const { isValidWithUnicodeFlag } = require("./utils/regular-expressions");
*
* CharacterClassRange syntax can steal a part of character sequence,
* so this function reverts CharacterClassRange syntax and restore the sequence.
* @param {regexpp.AST.CharacterClassElement[]} nodes The node list to iterate character sequences.
* @param {import('@eslint-community/regexpp').AST.CharacterClassElement[]} nodes The node list to iterate character sequences.
* @returns {IterableIterator<number[]>} The list of character sequences.
*/
function *iterateCharacterSequence(nodes) {
@ -37,6 +37,9 @@ function *iterateCharacterSequence(nodes) {
break;
case "CharacterSet":
case "CharacterClass": // [[]] nesting character class
case "ClassStringDisjunction": // \q{...}
case "ExpressionCharacterClass": // [A--B]
if (seq.length > 0) {
yield seq;
seq = [];
@ -144,7 +147,10 @@ module.exports = {
pattern,
0,
pattern.length,
flags.includes("u")
{
unicode: flags.includes("u"),
unicodeSets: flags.includes("v")
}
);
} catch {

View file

@ -77,7 +77,7 @@ module.exports = {
let regExpAST;
try {
regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") });
} catch {
// Ignore regular expressions with syntax errors
@ -155,13 +155,28 @@ module.exports = {
const regExpVar = astUtils.getVariableByName(scope, "RegExp");
const shadowed = regExpVar && regExpVar.defs.length > 0;
const patternNode = node.arguments[0];
const flagsNode = node.arguments[1];
if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(patternNode) && !shadowed) {
const pattern = patternNode.value;
const rawPattern = patternNode.raw.slice(1, -1);
const rawPatternStartRange = patternNode.range[0] + 1;
const flags = isString(flagsNode) ? flagsNode.value : "";
let flags;
if (node.arguments.length < 2) {
// It has no flags.
flags = "";
} else {
const flagsNode = node.arguments[1];
if (isString(flagsNode)) {
flags = flagsNode.value;
} else {
// The flags cannot be determined.
return;
}
}
checkRegex(
node,

View file

@ -1,6 +1,7 @@
/**
* @fileoverview Disallows unnecessary `return await`
* @author Jordan Harband
* @deprecated in ESLint v8.46.0
*/
"use strict";
@ -26,6 +27,10 @@ module.exports = {
fixable: null,
deprecated: true,
replacedBy: [],
schema: [
],

View file

@ -95,7 +95,7 @@ module.exports = {
let regExpAST;
try {
regExpAST = parser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
regExpAST = parser.parsePattern(pattern, 0, pattern.length, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") });
} catch {
// Ignore regular expressions with syntax errors

View file

@ -6,7 +6,12 @@
"use strict";
const astUtils = require("./utils/ast-utils");
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");
/**
* @typedef {import('@eslint-community/regexpp').AST.CharacterClass} CharacterClass
* @typedef {import('@eslint-community/regexpp').AST.ExpressionCharacterClass} ExpressionCharacterClass
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
@ -28,55 +33,17 @@ const VALID_STRING_ESCAPES = union(new Set("\\nrvtbfux"), astUtils.LINEBREAKS);
const REGEX_GENERAL_ESCAPES = new Set("\\bcdDfnpPrsStvwWxu0123456789]");
const REGEX_NON_CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("^/.$*+?[{}|()Bk"));
/**
* Parses a regular expression into a list of characters with character class info.
* @param {string} regExpText The raw text used to create the regular expression
* @returns {Object[]} A list of characters, each with info on escaping and whether they're in a character class.
* @example
*
* parseRegExp("a\\b[cd-]");
*
* // returns:
* [
* { text: "a", index: 0, escaped: false, inCharClass: false, startsCharClass: false, endsCharClass: false },
* { text: "b", index: 2, escaped: true, inCharClass: false, startsCharClass: false, endsCharClass: false },
* { text: "c", index: 4, escaped: false, inCharClass: true, startsCharClass: true, endsCharClass: false },
* { text: "d", index: 5, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false },
* { text: "-", index: 6, escaped: false, inCharClass: true, startsCharClass: false, endsCharClass: false }
* ];
*
/*
* Set of characters that require escaping in character classes in `unicodeSets` mode.
* ( ) [ ] { } / - \ | are ClassSetSyntaxCharacter
*/
function parseRegExp(regExpText) {
const charList = [];
const REGEX_CLASSSET_CHARACTER_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("q/[{}|()-"));
regExpText.split("").reduce((state, char, index) => {
if (!state.escapeNextChar) {
if (char === "\\") {
return Object.assign(state, { escapeNextChar: true });
}
if (char === "[" && !state.inCharClass) {
return Object.assign(state, { inCharClass: true, startingCharClass: true });
}
if (char === "]" && state.inCharClass) {
if (charList.length && charList[charList.length - 1].inCharClass) {
charList[charList.length - 1].endsCharClass = true;
}
return Object.assign(state, { inCharClass: false, startingCharClass: false });
}
}
charList.push({
text: char,
index,
escaped: state.escapeNextChar,
inCharClass: state.inCharClass,
startsCharClass: state.startingCharClass,
endsCharClass: false
});
return Object.assign(state, { escapeNextChar: false, startingCharClass: false });
}, { escapeNextChar: false, inCharClass: false, startingCharClass: false });
return charList;
}
/*
* A single character set of ClassSetReservedDoublePunctuator.
* && !! ## $$ %% ** ++ ,, .. :: ;; << == >> ?? @@ ^^ `` ~~ are ClassSetReservedDoublePunctuator
*/
const REGEX_CLASS_SET_RESERVED_DOUBLE_PUNCTUATOR = new Set("!#$%&*+,.:;<=>?@^`~");
/** @type {import('../shared/types').Rule} */
module.exports = {
@ -103,15 +70,17 @@ module.exports = {
create(context) {
const sourceCode = context.sourceCode;
const parser = new RegExpParser();
/**
* Reports a node
* @param {ASTNode} node The node to report
* @param {number} startOffset The backslash's offset from the start of the node
* @param {string} character The uselessly escaped character (not including the backslash)
* @param {boolean} [disableEscapeBackslashSuggest] `true` if escapeBackslash suggestion should be turned off.
* @returns {void}
*/
function report(node, startOffset, character) {
function report(node, startOffset, character, disableEscapeBackslashSuggest) {
const rangeStart = node.range[0] + startOffset;
const range = [rangeStart, rangeStart + 1];
const start = sourceCode.getLocFromIndex(rangeStart);
@ -134,12 +103,16 @@ module.exports = {
return fixer.removeRange(range);
}
},
{
messageId: "escapeBackslash",
fix(fixer) {
return fixer.insertTextBeforeRange(range, "\\");
}
}
...disableEscapeBackslashSuggest
? []
: [
{
messageId: "escapeBackslash",
fix(fixer) {
return fixer.insertTextBeforeRange(range, "\\");
}
}
]
]
});
}
@ -182,6 +155,133 @@ module.exports = {
}
}
/**
* Checks if the escape character in given regexp is unnecessary.
* @private
* @param {ASTNode} node node to validate.
* @returns {void}
*/
function validateRegExp(node) {
const { pattern, flags } = node.regex;
let patternNode;
const unicode = flags.includes("u");
const unicodeSets = flags.includes("v");
try {
patternNode = parser.parsePattern(pattern, 0, pattern.length, { unicode, unicodeSets });
} catch {
// Ignore regular expressions with syntax errors
return;
}
/** @type {(CharacterClass | ExpressionCharacterClass)[]} */
const characterClassStack = [];
visitRegExpAST(patternNode, {
onCharacterClassEnter: characterClassNode => characterClassStack.unshift(characterClassNode),
onCharacterClassLeave: () => characterClassStack.shift(),
onExpressionCharacterClassEnter: characterClassNode => characterClassStack.unshift(characterClassNode),
onExpressionCharacterClassLeave: () => characterClassStack.shift(),
onCharacterEnter(characterNode) {
if (!characterNode.raw.startsWith("\\")) {
// It's not an escaped character.
return;
}
const escapedChar = characterNode.raw.slice(1);
if (escapedChar !== String.fromCodePoint(characterNode.value)) {
// It's a valid escape.
return;
}
let allowedEscapes;
if (characterClassStack.length) {
allowedEscapes = unicodeSets ? REGEX_CLASSSET_CHARACTER_ESCAPES : REGEX_GENERAL_ESCAPES;
} else {
allowedEscapes = REGEX_NON_CHARCLASS_ESCAPES;
}
if (allowedEscapes.has(escapedChar)) {
return;
}
const reportedIndex = characterNode.start + 1;
let disableEscapeBackslashSuggest = false;
if (characterClassStack.length) {
const characterClassNode = characterClassStack[0];
if (escapedChar === "^") {
/*
* The '^' character is also a special case; it must always be escaped outside of character classes, but
* it only needs to be escaped in character classes if it's at the beginning of the character class. To
* account for this, consider it to be a valid escape character outside of character classes, and filter
* out '^' characters that appear at the start of a character class.
*/
if (characterClassNode.start + 1 === characterNode.start) {
return;
}
}
if (!unicodeSets) {
if (escapedChar === "-") {
/*
* The '-' character is a special case, because it's only valid to escape it if it's in a character
* class, and is not at either edge of the character class. To account for this, don't consider '-'
* characters to be valid in general, and filter out '-' characters that appear in the middle of a
* character class.
*/
if (characterClassNode.start + 1 !== characterNode.start && characterNode.end !== characterClassNode.end - 1) {
return;
}
}
} else { // unicodeSets mode
if (REGEX_CLASS_SET_RESERVED_DOUBLE_PUNCTUATOR.has(escapedChar)) {
// Escaping is valid if it is a ClassSetReservedDoublePunctuator.
if (pattern[characterNode.end] === escapedChar) {
return;
}
if (pattern[characterNode.start - 1] === escapedChar) {
if (escapedChar !== "^") {
return;
}
// If the previous character is a `negate` caret(`^`), escape to caret is unnecessary.
if (!characterClassNode.negate) {
return;
}
const negateCaretIndex = characterClassNode.start + 1;
if (negateCaretIndex < characterNode.start - 1) {
return;
}
}
}
if (characterNode.parent.type === "ClassIntersection" || characterNode.parent.type === "ClassSubtraction") {
disableEscapeBackslashSuggest = true;
}
}
}
report(
node,
reportedIndex,
escapedChar,
disableEscapeBackslashSuggest
);
}
});
}
/**
* Checks if a node has an escape.
* @param {ASTNode} node node to check.
@ -220,32 +320,7 @@ module.exports = {
validateString(node, match);
}
} else if (node.regex) {
parseRegExp(node.regex.pattern)
/*
* The '-' character is a special case, because it's only valid to escape it if it's in a character
* class, and is not at either edge of the character class. To account for this, don't consider '-'
* characters to be valid in general, and filter out '-' characters that appear in the middle of a
* character class.
*/
.filter(charInfo => !(charInfo.text === "-" && charInfo.inCharClass && !charInfo.startsCharClass && !charInfo.endsCharClass))
/*
* The '^' character is also a special case; it must always be escaped outside of character classes, but
* it only needs to be escaped in character classes if it's at the beginning of the character class. To
* account for this, consider it to be a valid escape character outside of character classes, and filter
* out '^' characters that appear at the start of a character class.
*/
.filter(charInfo => !(charInfo.text === "^" && charInfo.startsCharClass))
// Filter out characters that aren't escaped.
.filter(charInfo => charInfo.escaped)
// Filter out characters that are valid to escape, based on their position in the regular expression.
.filter(charInfo => !(charInfo.inCharClass ? REGEX_GENERAL_ESCAPES : REGEX_NON_CHARCLASS_ESCAPES).has(charInfo.text))
// Report all the remaining characters.
.forEach(charInfo => report(node, charInfo.index, charInfo.text));
validateRegExp(node);
}
}

View file

@ -112,14 +112,17 @@ module.exports = {
* @param {string} pattern The regular expression pattern to be checked.
* @param {ASTNode} node AST node which contains the regular expression or a call/new expression.
* @param {ASTNode} regexNode AST node which contains the regular expression.
* @param {boolean} uFlag Flag indicates whether unicode mode is enabled or not.
* @param {string|null} flags The regular expression flags to be checked.
* @returns {void}
*/
function checkRegex(pattern, node, regexNode, uFlag) {
function checkRegex(pattern, node, regexNode, flags) {
let ast;
try {
ast = parser.parsePattern(pattern, 0, pattern.length, uFlag);
ast = parser.parsePattern(pattern, 0, pattern.length, {
unicode: Boolean(flags && flags.includes("u")),
unicodeSets: Boolean(flags && flags.includes("v"))
});
} catch {
// ignore regex syntax errors
@ -148,7 +151,7 @@ module.exports = {
return {
Literal(node) {
if (node.regex) {
checkRegex(node.regex.pattern, node, node, node.regex.flags.includes("u"));
checkRegex(node.regex.pattern, node, node, node.regex.flags);
}
},
Program(node) {
@ -166,7 +169,7 @@ module.exports = {
const flags = getStringIfConstant(refNode.arguments[1]);
if (regex) {
checkRegex(regex, refNode, refNode.arguments[0], flags && flags.includes("u"));
checkRegex(regex, refNode, refNode.arguments[0], flags);
}
}
}

View file

@ -241,7 +241,7 @@ module.exports = {
/**
* Returns a ecmaVersion compatible for regexpp.
* @param {number} ecmaVersion The ecmaVersion to convert.
* @returns {import("regexpp/ecma-versions").EcmaVersion} The resulting ecmaVersion compatible for regexpp.
* @returns {import("@eslint-community/regexpp/ecma-versions").EcmaVersion} The resulting ecmaVersion compatible for regexpp.
*/
function getRegexppEcmaVersion(ecmaVersion) {
if (ecmaVersion <= 5) {
@ -297,7 +297,10 @@ module.exports = {
const validator = new RegExpValidator({ ecmaVersion: regexppEcmaVersion });
try {
validator.validatePattern(pattern, 0, pattern.length, flags ? flags.includes("u") : false);
validator.validatePattern(pattern, 0, pattern.length, {
unicode: flags ? flags.includes("u") : false,
unicodeSets: flags ? flags.includes("v") : false
});
if (flags) {
validator.validateFlags(flags);
}
@ -461,7 +464,10 @@ module.exports = {
if (regexContent && !noFix) {
let charIncrease = 0;
const ast = new RegExpParser({ ecmaVersion: regexppEcmaVersion }).parsePattern(regexContent, 0, regexContent.length, flags ? flags.includes("u") : false);
const ast = new RegExpParser({ ecmaVersion: regexppEcmaVersion }).parsePattern(regexContent, 0, regexContent.length, {
unicode: flags ? flags.includes("u") : false,
unicodeSets: flags ? flags.includes("v") : false
});
visitRegExpAST(ast, {
onCharacterEnter(characterNode) {

View file

@ -28,7 +28,7 @@ module.exports = {
type: "suggestion",
docs: {
description: "Enforce the use of `u` flag on RegExp",
description: "Enforce the use of `u` or `v` flag on RegExp",
recommended: false,
url: "https://eslint.org/docs/latest/rules/require-unicode-regexp"
},
@ -51,7 +51,7 @@ module.exports = {
"Literal[regex]"(node) {
const flags = node.regex.flags || "";
if (!flags.includes("u")) {
if (!flags.includes("u") && !flags.includes("v")) {
context.report({
messageId: "requireUFlag",
node,
@ -85,7 +85,7 @@ module.exports = {
const pattern = getStringIfConstant(patternNode, scope);
const flags = getStringIfConstant(flagsNode, scope);
if (!flagsNode || (typeof flags === "string" && !flags.includes("u"))) {
if (!flagsNode || (typeof flags === "string" && !flags.includes("u") && !flags.includes("v"))) {
context.report({
messageId: "requireUFlag",
node: refNode,

View file

@ -8,7 +8,7 @@
const { RegExpValidator } = require("@eslint-community/regexpp");
const REGEXPP_LATEST_ECMA_VERSION = 2022;
const REGEXPP_LATEST_ECMA_VERSION = 2024;
/**
* Checks if the given regular expression pattern would be valid with the `u` flag.
@ -28,7 +28,7 @@ function isValidWithUnicodeFlag(ecmaVersion, pattern) {
});
try {
validator.validatePattern(pattern, void 0, void 0, /* uFlag = */ true);
validator.validatePattern(pattern, void 0, void 0, { unicode: /* uFlag = */ true });
} catch {
return false;
}