| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 | /** * @fileoverview Rule to count multiple spaces in regular expressions * @author Matt DuVall <http://www.mattduvall.com/> */"use strict";const astUtils = require("../ast-utils");//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------module.exports = {    meta: {        docs: {            description: "disallow multiple spaces in regular expressions",            category: "Possible Errors",            recommended: true,            url: "https://eslint.org/docs/rules/no-regex-spaces"        },        schema: [],        fixable: "code"    },    create(context) {        const sourceCode = context.getSourceCode();        /**         * Validate regular expressions         * @param {ASTNode} node node to validate         * @param {string} value regular expression to validate         * @param {number} valueStart The start location of the regex/string literal. It will always be the case that         * `sourceCode.getText().slice(valueStart, valueStart + value.length) === value`         * @returns {void}         * @private         */        function checkRegex(node, value, valueStart) {            const multipleSpacesRegex = /( {2,})( [+*{?]|[^+*{?]|$)/,                regexResults = multipleSpacesRegex.exec(value);            if (regexResults !== null) {                const count = regexResults[1].length;                context.report({                    node,                    message: "Spaces are hard to count. Use {{{count}}}.",                    data: { count },                    fix(fixer) {                        return fixer.replaceTextRange(                            [valueStart + regexResults.index, valueStart + regexResults.index + count],                            ` {${count}}`                        );                    }                });                /*                 * TODO: (platinumazure) Fix message to use rule message                 * substitution when api.report is fixed in lib/eslint.js.                 */            }        }        /**         * Validate regular expression literals         * @param {ASTNode} node node to validate         * @returns {void}         * @private         */        function checkLiteral(node) {            const token = sourceCode.getFirstToken(node),                nodeType = token.type,                nodeValue = token.value;            if (nodeType === "RegularExpression") {                checkRegex(node, nodeValue, token.range[0]);            }        }        /**         * 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";        }        /**         * Validate strings passed to the RegExp constructor         * @param {ASTNode} node node to validate         * @returns {void}         * @private         */        function checkFunction(node) {            const scope = context.getScope();            const regExpVar = astUtils.getVariableByName(scope, "RegExp");            const shadowed = regExpVar && regExpVar.defs.length > 0;            if (node.callee.type === "Identifier" && node.callee.name === "RegExp" && isString(node.arguments[0]) && !shadowed) {                checkRegex(node, node.arguments[0].value, node.arguments[0].range[0] + 1);            }        }        return {            Literal: checkLiteral,            CallExpression: checkFunction,            NewExpression: checkFunction        };    }};
 |