eslint
Advanced tools
Comparing version 8.28.0 to 8.29.0
@@ -55,3 +55,4 @@ /** | ||
enforceForNewInMemberExpressions: { type: "boolean" }, | ||
enforceForFunctionPrototypeMethods: { type: "boolean" } | ||
enforceForFunctionPrototypeMethods: { type: "boolean" }, | ||
allowParensAfterCommentPattern: { type: "string" } | ||
}, | ||
@@ -90,2 +91,3 @@ additionalProperties: false | ||
context.options[1].enforceForFunctionPrototypeMethods === false; | ||
const ALLOW_PARENS_AFTER_COMMENT_PATTERN = ALL_NODES && context.options[1] && context.options[1].allowParensAfterCommentPattern; | ||
@@ -407,2 +409,15 @@ const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" }); | ||
} | ||
if (ALLOW_PARENS_AFTER_COMMENT_PATTERN) { | ||
const commentsBeforeLeftParenToken = sourceCode.getCommentsBefore(leftParenToken); | ||
const totalCommentsBeforeLeftParenTokenCount = commentsBeforeLeftParenToken.length; | ||
const ignorePattern = new RegExp(ALLOW_PARENS_AFTER_COMMENT_PATTERN, "u"); | ||
if ( | ||
totalCommentsBeforeLeftParenTokenCount > 0 && | ||
ignorePattern.test(commentsBeforeLeftParenToken[totalCommentsBeforeLeftParenTokenCount - 1].value) | ||
) { | ||
return; | ||
} | ||
} | ||
} | ||
@@ -409,0 +424,0 @@ |
@@ -63,2 +63,16 @@ /** | ||
/** | ||
* Reports error with the provided message. | ||
* @param {ASTNode} node The node holding the invalid RegExp | ||
* @param {string} message The message to report. | ||
* @returns {void} | ||
*/ | ||
function report(node, message) { | ||
context.report({ | ||
node, | ||
messageId: "regexMessage", | ||
data: { message } | ||
}); | ||
} | ||
/** | ||
* Check if node is a string | ||
@@ -112,6 +126,9 @@ * @param {ASTNode} node node to evaluate | ||
* Check syntax error in a given flags. | ||
* @param {string} flags The RegExp flags to validate. | ||
* @param {string|null} flags The RegExp flags to validate. | ||
* @returns {string|null} The syntax error. | ||
*/ | ||
function validateRegExpFlags(flags) { | ||
if (!flags) { | ||
return null; | ||
} | ||
try { | ||
@@ -127,6 +144,6 @@ validator.validateFlags(flags); | ||
"CallExpression, NewExpression"(node) { | ||
if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp" || !isString(node.arguments[0])) { | ||
if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp") { | ||
return; | ||
} | ||
const pattern = node.arguments[0].value; | ||
let flags = getFlags(node); | ||
@@ -138,20 +155,25 @@ | ||
const message = | ||
( | ||
flags && validateRegExpFlags(flags) | ||
) || | ||
( | ||
let message = validateRegExpFlags(flags); | ||
// 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")) | ||
); | ||
if (message) { | ||
report(node, message); | ||
return; | ||
} | ||
if (!isString(node.arguments[0])) { | ||
return; | ||
} | ||
const pattern = node.arguments[0].value; | ||
message = ( | ||
// 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")) | ||
); | ||
if (message) { | ||
context.report({ | ||
node, | ||
messageId: "regexMessage", | ||
data: { message } | ||
}); | ||
report(node, message); | ||
} | ||
@@ -158,0 +180,0 @@ } |
@@ -26,2 +26,57 @@ /** | ||
/** | ||
* Creates fixer suggestions for the regex, if statically determinable. | ||
* @param {number} groupStart Starting index of the regex group. | ||
* @param {string} pattern The regular expression pattern to be checked. | ||
* @param {string} rawText Source text of the regexNode. | ||
* @param {ASTNode} regexNode AST node which contains the regular expression. | ||
* @returns {Array<SuggestionResult>} Fixer suggestions for the regex, if statically determinable. | ||
*/ | ||
function suggestIfPossible(groupStart, pattern, rawText, regexNode) { | ||
switch (regexNode.type) { | ||
case "Literal": | ||
if (typeof regexNode.value === "string" && rawText.includes("\\")) { | ||
return null; | ||
} | ||
break; | ||
case "TemplateLiteral": | ||
if (regexNode.expressions.length || rawText.slice(1, -1) !== pattern) { | ||
return null; | ||
} | ||
break; | ||
default: | ||
return null; | ||
} | ||
const start = regexNode.range[0] + groupStart + 2; | ||
return [ | ||
{ | ||
fix(fixer) { | ||
const existingTemps = pattern.match(/temp\d+/gu) || []; | ||
const highestTempCount = existingTemps.reduce( | ||
(previous, next) => | ||
Math.max(previous, Number(next.slice("temp".length))), | ||
0 | ||
); | ||
return fixer.insertTextBeforeRange( | ||
[start, start], | ||
`?<temp${highestTempCount + 1}>` | ||
); | ||
}, | ||
messageId: "addGroupName" | ||
}, | ||
{ | ||
fix(fixer) { | ||
return fixer.insertTextBeforeRange( | ||
[start, start], | ||
"?:" | ||
); | ||
}, | ||
messageId: "addNonCapture" | ||
} | ||
]; | ||
} | ||
//------------------------------------------------------------------------------ | ||
@@ -42,5 +97,9 @@ // Rule Definition | ||
hasSuggestions: true, | ||
schema: [], | ||
messages: { | ||
addGroupName: "Add name to capture group.", | ||
addNonCapture: "Convert group to non-capturing.", | ||
required: "Capture group '{{group}}' should be converted to a named or non-capturing group." | ||
@@ -51,11 +110,13 @@ } | ||
create(context) { | ||
const sourceCode = context.getSourceCode(); | ||
/** | ||
* Function to check regular expression. | ||
* @param {string} pattern The regular expression pattern to be check. | ||
* @param {ASTNode} node AST node which contains regular expression. | ||
* @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. | ||
* @returns {void} | ||
*/ | ||
function checkRegex(pattern, node, uFlag) { | ||
function checkRegex(pattern, node, regexNode, uFlag) { | ||
let ast; | ||
@@ -74,2 +135,5 @@ | ||
if (!group.name) { | ||
const rawText = sourceCode.getText(regexNode); | ||
const suggest = suggestIfPossible(group.start, pattern, rawText, regexNode); | ||
context.report({ | ||
@@ -80,3 +144,4 @@ node, | ||
group: group.raw | ||
} | ||
}, | ||
suggest | ||
}); | ||
@@ -91,3 +156,3 @@ } | ||
if (node.regex) { | ||
checkRegex(node.regex.pattern, node, node.regex.flags.includes("u")); | ||
checkRegex(node.regex.pattern, node, node, node.regex.flags.includes("u")); | ||
} | ||
@@ -110,3 +175,3 @@ }, | ||
if (regex) { | ||
checkRegex(regex, node, flags && flags.includes("u")); | ||
checkRegex(regex, node, node.arguments[0], flags && flags.includes("u")); | ||
} | ||
@@ -113,0 +178,0 @@ } |
{ | ||
"name": "eslint", | ||
"version": "8.28.0", | ||
"version": "8.29.0", | ||
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", | ||
@@ -5,0 +5,0 @@ "description": "An AST-based pattern checker for JavaScript.", |
2849250
67625