Permalink
Cannot retrieve contributors at this time
197 lines (164 sloc)
6.42 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
codeql-action/node_modules/eslint/lib/rules/no-regex-spaces.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @fileoverview Rule to count multiple spaces in regular expressions | |
* @author Matt DuVall <http://www.mattduvall.com/> | |
*/ | |
"use strict"; | |
//------------------------------------------------------------------------------ | |
// Requirements | |
//------------------------------------------------------------------------------ | |
const astUtils = require("./utils/ast-utils"); | |
const regexpp = require("@eslint-community/regexpp"); | |
//------------------------------------------------------------------------------ | |
// Helpers | |
//------------------------------------------------------------------------------ | |
const regExpParser = new regexpp.RegExpParser(); | |
const DOUBLE_SPACE = / {2}/u; | |
/** | |
* Check if node is a string | |
* @param {ASTNode} node node to evaluate | |
* @returns {boolean} True if its a string | |
* @private | |
*/ | |
function isString(node) { | |
return node && node.type === "Literal" && typeof node.value === "string"; | |
} | |
//------------------------------------------------------------------------------ | |
// Rule Definition | |
//------------------------------------------------------------------------------ | |
/** @type {import('../shared/types').Rule} */ | |
module.exports = { | |
meta: { | |
type: "suggestion", | |
docs: { | |
description: "Disallow multiple spaces in regular expressions", | |
recommended: true, | |
url: "https://eslint.org/docs/latest/rules/no-regex-spaces" | |
}, | |
schema: [], | |
fixable: "code", | |
messages: { | |
multipleSpaces: "Spaces are hard to count. Use {{{length}}}." | |
} | |
}, | |
create(context) { | |
const sourceCode = context.sourceCode; | |
/** | |
* Validate regular expression | |
* @param {ASTNode} nodeToReport Node to report. | |
* @param {string} pattern Regular expression pattern to validate. | |
* @param {string} rawPattern Raw representation of the pattern in the source code. | |
* @param {number} rawPatternStartRange Start range of the pattern in the source code. | |
* @param {string} flags Regular expression flags. | |
* @returns {void} | |
* @private | |
*/ | |
function checkRegex(nodeToReport, pattern, rawPattern, rawPatternStartRange, flags) { | |
// Skip if there are no consecutive spaces in the source code, to avoid reporting e.g., RegExp(' \ '). | |
if (!DOUBLE_SPACE.test(rawPattern)) { | |
return; | |
} | |
const characterClassNodes = []; | |
let regExpAST; | |
try { | |
regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") }); | |
} catch { | |
// Ignore regular expressions with syntax errors | |
return; | |
} | |
regexpp.visitRegExpAST(regExpAST, { | |
onCharacterClassEnter(ccNode) { | |
characterClassNodes.push(ccNode); | |
} | |
}); | |
const spacesPattern = /( {2,})(?: [+*{?]|[^+*{?]|$)/gu; | |
let match; | |
while ((match = spacesPattern.exec(pattern))) { | |
const { 1: { length }, index } = match; | |
// Report only consecutive spaces that are not in character classes. | |
if ( | |
characterClassNodes.every(({ start, end }) => index < start || end <= index) | |
) { | |
context.report({ | |
node: nodeToReport, | |
messageId: "multipleSpaces", | |
data: { length }, | |
fix(fixer) { | |
if (pattern !== rawPattern) { | |
return null; | |
} | |
return fixer.replaceTextRange( | |
[rawPatternStartRange + index, rawPatternStartRange + index + length], | |
` {${length}}` | |
); | |
} | |
}); | |
// Report only the first occurrence of consecutive spaces | |
return; | |
} | |
} | |
} | |
/** | |
* Validate regular expression literals | |
* @param {ASTNode} node node to validate | |
* @returns {void} | |
* @private | |
*/ | |
function checkLiteral(node) { | |
if (node.regex) { | |
const pattern = node.regex.pattern; | |
const rawPattern = node.raw.slice(1, node.raw.lastIndexOf("/")); | |
const rawPatternStartRange = node.range[0] + 1; | |
const flags = node.regex.flags; | |
checkRegex( | |
node, | |
pattern, | |
rawPattern, | |
rawPatternStartRange, | |
flags | |
); | |
} | |
} | |
/** | |
* Validate strings passed to the RegExp constructor | |
* @param {ASTNode} node node to validate | |
* @returns {void} | |
* @private | |
*/ | |
function checkFunction(node) { | |
const scope = sourceCode.getScope(node); | |
const regExpVar = astUtils.getVariableByName(scope, "RegExp"); | |
const shadowed = regExpVar && regExpVar.defs.length > 0; | |
const patternNode = node.arguments[0]; | |
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; | |
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, | |
pattern, | |
rawPattern, | |
rawPatternStartRange, | |
flags | |
); | |
} | |
} | |
return { | |
Literal: checkLiteral, | |
CallExpression: checkFunction, | |
NewExpression: checkFunction | |
}; | |
} | |
}; |