code:
/**
 * @fileoverview A rule to suggest using of const declaration for variables that are never reassigned after declared.
 * @author Toru Nagashima
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const FixTracker = require("./utils/fix-tracker");
const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const PATTERN_TYPE = /^(?:.+?Pattern|RestElement|SpreadProperty|ExperimentalRestProperty|Property)$/u;
const DECLARATION_HOST_TYPE = /^(?:Program|BlockStatement|StaticBlock|SwitchCase)$/u;
const DESTRUCTURING_HOST_TYPE = /^(?:VariableDeclarator|AssignmentExpression)$/u;

/**
 * Checks whether a given node is located at `ForStatement.init` or not.
 * @param {ASTNode} node A node to check.
 * @returns {boolean} `true` if the node is located at `ForStatement.init`.
 */
function isInitOfForStatement(node) {
    return node.parent.type === "ForStatement" && node.parent.init === node;
}

/**
 * Checks whether a given Identifier node becomes a VariableDeclaration or not.
 * @param {ASTNode} identifier An Identifier node to check.
 * @returns {boolean} `true` if the node can become a VariableDeclaration.
 */
function canBecomeVariableDeclaration(identifier) {
    let node = identifier.parent;

    while (PATTERN_TYPE.test(node.type)) {
        node = node.parent;
    }

    return (
        node.type === "VariableDeclarator" ||
        (
            node.type === "AssignmentExpression" &&
            node.parent.type === "ExpressionStatement" &&
            DECLARATION_HOST_TYPE.test(node.parent.parent.type)
        )
    );
}

/**
 * Checks if an property or element is from outer scope or function parameters
 * in destructing pattern.
 * @param {string} name A variable name to be checked.
 * @param {eslint-scope.Scope} initScope A scope to start find.
 * @returns {boolean} Indicates if the variable is from outer scope or function parameters.
 */
function isOuterVariableInDestructing(name, initScope) {

    if (initScope.through.some(ref => ref.resolved && ref.resolved.name === name)) {
        return true;
    }

    const variable = astUtils.getVariableByName(initScope, name);

    if (variable !== null) {
        return variable.defs.some(def => def.type === "Parameter");
    }

    return false;
}

/**
 * Gets the VariableDeclarator/AssignmentExpression node that a given reference
 * belongs to.
 * This is used to detect a mix of reassigned and never reassigned in a
 * destructuring.
 * @param {eslint-scope.Reference} reference A reference to get.
 * @returns {ASTNode|null} A VariableDeclarator/AssignmentExpression node or
 *      null.
 */
function getDestructuringHost(reference) {
    if (!reference.isWrite()) {
        return null;
    }
    let node = reference.identifier.parent;

    while (PATTERN_TYPE.test(node.type)) {
        node = node.parent;
    }

    if (!DESTRUCTURING_HOST_TYPE.test(node.type)) {
        return null;
    }
    return node;
}

/**
 * Determines if a destructuring assignment node contains
 * any MemberExpression nodes. This is used to determine if a
 * variable that is only written once using destructuring can be
 * safely converted into a const declaration.
 * @param {ASTNode} node The ObjectPattern or ArrayPattern node to check.
 * @returns {boolean} True if the destructuring pattern contains
 *      a MemberExpression, false if not.
 */
function hasMemberExpressionAssignment(node) {
    switch (node.type) {
        case "ObjectPattern":
            return node.properties.some(prop => {
                if (prop) {

                    /*
                     * Spread elements have an argument property while
                     * others have a value property. Because different
                     * parsers use different node types for spread elements,
                     * we just check if there is an argument property.
                     */
                    return hasMemberExpressionAssignment(prop.argument || prop.value);
                }

                return false;
            });

        case "ArrayPattern":
            return node.elements.some(element => {
                if (element) {
                    return hasMemberExpressionAssignment(element);
                }

                return false;
            });

        case "AssignmentPattern":
            return hasMemberExpressionAssignment(node.left);

        case "MemberExpression":
            return true;

        // no default
    }

    return false;
}

/**
 * Gets an identifier node of a given variable.
 *
 * If the initialization exists or one or more reading references exist before
 * the first assignment, the identifier node is the node of the declaration.
 * Otherwise, the identifier node is the node of the first assignment.
 *
 * If the variable should not change to const, this function returns null.
 * - If the variable is reassigned.
 * - If the variable is never initialized nor assigned.
 * - If the variable is initialized in a different scope from the declaration.
 * - If the unique assignment of the variable cannot change to a declaration.
 *   e.g. `if (a) b = 1` / `return (b = 1)`
 * - If the variable is declared in the global scope and `eslintUsed` is `true`.
 *   `/*exported foo` directive comment makes such variables. This rule does not
 *   warn such variables because this rule cannot distinguish whether the
 *   exported variables are reassigned or not.
 * @param {eslint-scope.Variable} variable A variable to get.
 * @param {boolean} ignoreReadBeforeAssign
 *      The value of `ignoreReadBeforeAssign` option.
 * @returns {ASTNode|null}
 *      An Identifier node if the variable should change to const.
 *      Otherwise, null.
 */
function getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign) {
    if (variable.eslintUsed && variable.scope.type === "global") {
        return null;
    }

    // Finds the unique WriteReference.
    let writer = null;
    let isReadBeforeInit = false;
    const references = variable.references;

    for (let i = 0; i < references.length; ++i) {
        const reference = references[i];

        if (reference.isWrite()) {
            const isReassigned = (
                writer !== null &&
                writer.identifier !== reference.identifier
            );

            if (isReassigned) {
                return null;
            }

            const destructuringHost = getDestructuringHost(reference);

            if (destructuringHost !== null && destructuringHost.left !== void 0) {
                const leftNode = destructuringHost.left;
                let hasOuterVariables = false,
                    hasNonIdentifiers = false;

                if (leftNode.type === "ObjectPattern") {
                    const properties = leftNode.properties;

                    hasOuterVariables = properties
                        .filter(prop => prop.value)
                        .map(prop => prop.value.name)
                        .some(name => isOuterVariableInDestructing(name, variable.scope));

                    hasNonIdentifiers = hasMemberExpressionAssignment(leftNode);

                } else if (leftNode.type === "ArrayPattern") {
                    const elements = leftNode.elements;

                    hasOuterVariables = elements
                        .map(element => element && element.name)
                        .some(name => isOuterVariableInDestructing(name, variable.scope));

                    hasNonIdentifiers = hasMemberExpressionAssignment(leftNode);
                }

                if (hasOuterVariables || hasNonIdentifiers) {
                    return null;
                }

            }

            writer = reference;

        } else if (reference.isRead() && writer === null) {
            if (ignoreReadBeforeAssign) {
                return null;
            }
            isReadBeforeInit = true;
        }
    }

    /*
     * If the assignment is from a different scope, ignore it.
     * If the assignment cannot change to a declaration, ignore it.
     */
    const shouldBeConst = (
        writer !== null &&
        writer.from === variable.scope &&
        canBecomeVariableDeclaration(writer.identifier)
    );

    if (!shouldBeConst) {
        return null;
    }

    if (isReadBeforeInit) {
        return variable.defs[0].name;
    }

    return writer.identifier;
}

/**
 * Groups by the VariableDeclarator/AssignmentExpression node that each
 * reference of given variables belongs to.
 * This is used to detect a mix of reassigned and never reassigned in a
 * destructuring.
 * @param {eslint-scope.Variable[]} variables Variables to group by destructuring.
 * @param {boolean} ignoreReadBeforeAssign
 *      The value of `ignoreReadBeforeAssign` option.
 * @returns {Map<ASTNode, ASTNode[]>} Grouped identifier nodes.
 */
function groupByDestructuring(variables, ignoreReadBeforeAssign) {
    const identifierMap = new Map();

    for (let i = 0; i < variables.length; ++i) {
        const variable = variables[i];
        const references = variable.references;
        const identifier = getIdentifierIfShouldBeConst(variable, ignoreReadBeforeAssign);
        let prevId = null;

        for (let j = 0; j < references.length; ++j) {
            const reference = references[j];
            const id = reference.identifier;

            /*
             * Avoid counting a reference twice or more for default values of
             * destructuring.
             */
            if (id === prevId) {
                continue;
            }
            prevId = id;

            // Add the identifier node into the destructuring group.
            const group = getDestructuringHost(reference);

            if (group) {
                if (identifierMap.has(group)) {
                    identifierMap.get(group).push(identifier);
                } else {
                    identifierMap.set(group, [identifier]);
                }
            }
        }
    }

    return identifierMap;
}

/**
 * Finds the nearest parent of node with a given type.
 * @param {ASTNode} node The node to search from.
 * @param {string} type The type field of the parent node.
 * @param {Function} shouldStop A predicate that returns true if the traversal should stop, and false otherwise.
 * @returns {ASTNode} The closest ancestor with the specified type; null if no such ancestor exists.
 */
function findUp(node, type, shouldStop) {
    if (!node || shouldStop(node)) {
        return null;
    }
    if (node.type === type) {
        return node;
    }
    return findUp(node.parent, type, shouldStop);
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Require `const` declarations for variables that are never reassigned after declared",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-const"
        },

        fixable: "code",

        schema: [
            {
                type: "object",
                properties: {
                    destructuring: { enum: ["any", "all"], default: "any" },
                    ignoreReadBeforeAssign: { type: "boolean", default: false }
                },
                additionalProperties: false
            }
        ],
        messages: {
            useConst: "'{{name}}' is never reassigned. Use 'const' instead."
        }
    },

    create(context) {
        const options = context.options[0] || {};
        const sourceCode = context.sourceCode;
        const shouldMatchAnyDestructuredVariable = options.destructuring !== "all";
        const ignoreReadBeforeAssign = options.ignoreReadBeforeAssign === true;
        const variables = [];
        let reportCount = 0;
        let checkedId = null;
        let checkedName = "";

        /**
         * Reports given identifier nodes if all of the nodes should be declared
         * as const.
         *
         * The argument 'nodes' is an array of Identifier nodes.
         * This node is the result of 'getIdentifierIfShouldBeConst()', so it's
         * nullable. In simple declaration or assignment cases, the length of
         * the array is 1. In destructuring cases, the length of the array can
         * be 2 or more.
         * @param {(eslint-scope.Reference|null)[]} nodes
         *      References which are grouped by destructuring to report.
         * @returns {void}
         */
        function checkGroup(nodes) {
            const nodesToReport = nodes.filter(Boolean);

            if (nodes.length && (shouldMatchAnyDestructuredVariable || nodesToReport.length === nodes.length)) {
                const varDeclParent = findUp(nodes[0], "VariableDeclaration", parentNode => parentNode.type.endsWith("Statement"));
                const isVarDecParentNull = varDeclParent === null;

                if (!isVarDecParentNull && varDeclParent.declarations.length > 0) {
                    const firstDeclaration = varDeclParent.declarations[0];

                    if (firstDeclaration.init) {
                        const firstDecParent = firstDeclaration.init.parent;

                        /*
                         * First we check the declaration type and then depending on
                         * if the type is a "VariableDeclarator" or its an "ObjectPattern"
                         * we compare the name and id from the first identifier, if the names are different
                         * we assign the new name, id and reset the count of reportCount and nodeCount in
                         * order to check each block for the number of reported errors and base our fix
                         * based on comparing nodes.length and nodesToReport.length.
                         */

                        if (firstDecParent.type === "VariableDeclarator") {

                            if (firstDecParent.id.name !== checkedName) {
                                checkedName = firstDecParent.id.name;
                                reportCount = 0;
                            }

                            if (firstDecParent.id.type === "ObjectPattern") {
                                if (firstDecParent.init.name !== checkedName) {
                                    checkedName = firstDecParent.init.name;
                                    reportCount = 0;
                                }
                            }

                            if (firstDecParent.id !== checkedId) {
                                checkedId = firstDecParent.id;
                                reportCount = 0;
                            }
                        }
                    }
                }

                let shouldFix = varDeclParent &&

                    // Don't do a fix unless all variables in the declarations are initialized (or it's in a for-in or for-of loop)
                    (varDeclParent.parent.type === "ForInStatement" || varDeclParent.parent.type === "ForOfStatement" ||
                        varDeclParent.declarations.every(declaration => declaration.init)) &&

                    /*
                     * If options.destructuring is "all", then this warning will not occur unless
                     * every assignment in the destructuring should be const. In that case, it's safe
                     * to apply the fix.
                     */
                    nodesToReport.length === nodes.length;

                if (!isVarDecParentNull && varDeclParent.declarations && varDeclParent.declarations.length !== 1) {

                    if (varDeclParent && varDeclParent.declarations && varDeclParent.declarations.length >= 1) {

                        /*
                         * Add nodesToReport.length to a count, then comparing the count to the length
                         * of the declarations in the current block.
                         */

                        reportCount += nodesToReport.length;

                        let totalDeclarationsCount = 0;

                        varDeclParent.declarations.forEach(declaration => {
                            if (declaration.id.type === "ObjectPattern") {
                                totalDeclarationsCount += declaration.id.properties.length;
                            } else if (declaration.id.type === "ArrayPattern") {
                                totalDeclarationsCount += declaration.id.elements.length;
                            } else {
                                totalDeclarationsCount += 1;
                            }
                        });

                        shouldFix = shouldFix && (reportCount === totalDeclarationsCount);
                    }
                }

                nodesToReport.forEach(node => {
                    context.report({
                        node,
                        messageId: "useConst",
                        data: node,
                        fix: shouldFix
                            ? fixer => {
                                const letKeywordToken = sourceCode.getFirstToken(varDeclParent, t => t.value === varDeclParent.kind);

                                /**
                                 * Extend the replacement range to the whole declaration,
                                 * in order to prevent other fixes in the same pass
                                 * https://github.com/eslint/eslint/issues/13899
                                 */
                                return new FixTracker(fixer, sourceCode)
                                    .retainRange(varDeclParent.range)
                                    .replaceTextRange(letKeywordToken.range, "const");
                            }
                            : null
                    });
                });
            }
        }

        return {
            "Program:exit"() {
                groupByDestructuring(variables, ignoreReadBeforeAssign).forEach(checkGroup);
            },

            VariableDeclaration(node) {
                if (node.kind === "let" && !isInitOfForStatement(node)) {
                    variables.push(...sourceCode.getDeclaredVariables(node));
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Rule to disallow Math.pow in favor of the ** operator
 * @author Milos Djermanovic
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");
const { CALL, ReferenceTracker } = require("@eslint-community/eslint-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const PRECEDENCE_OF_EXPONENTIATION_EXPR = astUtils.getPrecedence({ type: "BinaryExpression", operator: "**" });

/**
 * Determines whether the given node needs parens if used as the base in an exponentiation binary expression.
 * @param {ASTNode} base The node to check.
 * @returns {boolean} `true` if the node needs to be parenthesised.
 */
function doesBaseNeedParens(base) {
    return (

        // '**' is right-associative, parens are needed when Math.pow(a ** b, c) is converted to (a ** b) ** c
        astUtils.getPrecedence(base) <= PRECEDENCE_OF_EXPONENTIATION_EXPR ||

        // An unary operator cannot be used immediately before an exponentiation expression
        base.type === "AwaitExpression" ||
        base.type === "UnaryExpression"
    );
}

/**
 * Determines whether the given node needs parens if used as the exponent in an exponentiation binary expression.
 * @param {ASTNode} exponent The node to check.
 * @returns {boolean} `true` if the node needs to be parenthesised.
 */
function doesExponentNeedParens(exponent) {

    // '**' is right-associative, there is no need for parens when Math.pow(a, b ** c) is converted to a ** b ** c
    return astUtils.getPrecedence(exponent) < PRECEDENCE_OF_EXPONENTIATION_EXPR;
}

/**
 * Determines whether an exponentiation binary expression at the place of the given node would need parens.
 * @param {ASTNode} node A node that would be replaced by an exponentiation binary expression.
 * @param {SourceCode} sourceCode A SourceCode object.
 * @returns {boolean} `true` if the expression needs to be parenthesised.
 */
function doesExponentiationExpressionNeedParens(node, sourceCode) {
    const parent = node.parent.type === "ChainExpression" ? node.parent.parent : node.parent;

    const parentPrecedence = astUtils.getPrecedence(parent);
    const needsParens = (
        parent.type === "ClassDeclaration" ||
        (
            parent.type.endsWith("Expression") &&
            (parentPrecedence === -1 || parentPrecedence >= PRECEDENCE_OF_EXPONENTIATION_EXPR) &&
            !(parent.type === "BinaryExpression" && parent.operator === "**" && parent.right === node) &&
            !((parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node)) &&
            !(parent.type === "MemberExpression" && parent.computed && parent.property === node) &&
            !(parent.type === "ArrayExpression")
        )
    );

    return needsParens && !astUtils.isParenthesised(sourceCode, node);
}

/**
 * Optionally parenthesizes given text.
 * @param {string} text The text to parenthesize.
 * @param {boolean} shouldParenthesize If `true`, the text will be parenthesised.
 * @returns {string} parenthesised or unchanged text.
 */
function parenthesizeIfShould(text, shouldParenthesize) {
    return shouldParenthesize ? `(${text})` : text;
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow the use of `Math.pow` in favor of the `**` operator",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-exponentiation-operator"
        },

        schema: [],
        fixable: "code",

        messages: {
            useExponentiation: "Use the '**' operator instead of 'Math.pow'."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;

        /**
         * Reports the given node.
         * @param {ASTNode} node 'Math.pow()' node to report.
         * @returns {void}
         */
        function report(node) {
            context.report({
                node,
                messageId: "useExponentiation",
                fix(fixer) {
                    if (
                        node.arguments.length !== 2 ||
                        node.arguments.some(arg => arg.type === "SpreadElement") ||
                        sourceCode.getCommentsInside(node).length > 0
                    ) {
                        return null;
                    }

                    const base = node.arguments[0],
                        exponent = node.arguments[1],
                        baseText = sourceCode.getText(base),
                        exponentText = sourceCode.getText(exponent),
                        shouldParenthesizeBase = doesBaseNeedParens(base),
                        shouldParenthesizeExponent = doesExponentNeedParens(exponent),
                        shouldParenthesizeAll = doesExponentiationExpressionNeedParens(node, sourceCode);

                    let prefix = "",
                        suffix = "";

                    if (!shouldParenthesizeAll) {
                        if (!shouldParenthesizeBase) {
                            const firstReplacementToken = sourceCode.getFirstToken(base),
                                tokenBefore = sourceCode.getTokenBefore(node);

                            if (
                                tokenBefore &&
                                tokenBefore.range[1] === node.range[0] &&
                                !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken)
                            ) {
                                prefix = " "; // a+Math.pow(++b, c) -> a+ ++b**c
                            }
                        }
                        if (!shouldParenthesizeExponent) {
                            const lastReplacementToken = sourceCode.getLastToken(exponent),
                                tokenAfter = sourceCode.getTokenAfter(node);

                            if (
                                tokenAfter &&
                                node.range[1] === tokenAfter.range[0] &&
                                !astUtils.canTokensBeAdjacent(lastReplacementToken, tokenAfter)
                            ) {
                                suffix = " "; // Math.pow(a, b)in c -> a**b in c
                            }
                        }
                    }

                    const baseReplacement = parenthesizeIfShould(baseText, shouldParenthesizeBase),
                        exponentReplacement = parenthesizeIfShould(exponentText, shouldParenthesizeExponent),
                        replacement = parenthesizeIfShould(`${baseReplacement}**${exponentReplacement}`, shouldParenthesizeAll);

                    return fixer.replaceText(node, `${prefix}${replacement}${suffix}`);
                }
            });
        }

        return {
            Program(node) {
                const scope = sourceCode.getScope(node);
                const tracker = new ReferenceTracker(scope);
                const trackMap = {
                    Math: {
                        pow: { [CALL]: true }
                    }
                };

                for (const { node: refNode } of tracker.iterateGlobalReferences(trackMap)) {
                    report(refNode);
                }
            }
        };
    }
};

license: mit


code:
/**
 * @fileoverview Rule to enforce requiring named capture groups in regular expression.
 * @author Pig Fang <https://github.com/g-plane>
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const {
    CALL,
    CONSTRUCT,
    ReferenceTracker,
    getStringIfConstant
} = require("@eslint-community/eslint-utils");
const regexpp = require("@eslint-community/regexpp");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const parser = new regexpp.RegExpParser();

/**
 * 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"
        }
    ];
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Enforce using named capture group in regular expression",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-named-capture-group"
        },

        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."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;

        /**
         * Function to check 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 {string|null} flags The regular expression flags to be checked.
         * @returns {void}
         */
        function checkRegex(pattern, node, regexNode, flags) {
            let ast;

            try {
                ast = parser.parsePattern(pattern, 0, pattern.length, {
                    unicode: Boolean(flags && flags.includes("u")),
                    unicodeSets: Boolean(flags && flags.includes("v"))
                });
            } catch {

                // ignore regex syntax errors
                return;
            }

            regexpp.visitRegExpAST(ast, {
                onCapturingGroupEnter(group) {
                    if (!group.name) {
                        const rawText = sourceCode.getText(regexNode);
                        const suggest = suggestIfPossible(group.start, pattern, rawText, regexNode);

                        context.report({
                            node,
                            messageId: "required",
                            data: {
                                group: group.raw
                            },
                            suggest
                        });
                    }
                }
            });
        }

        return {
            Literal(node) {
                if (node.regex) {
                    checkRegex(node.regex.pattern, node, node, node.regex.flags);
                }
            },
            Program(node) {
                const scope = sourceCode.getScope(node);
                const tracker = new ReferenceTracker(scope);
                const traceMap = {
                    RegExp: {
                        [CALL]: true,
                        [CONSTRUCT]: true
                    }
                };

                for (const { node: refNode } of tracker.iterateGlobalReferences(traceMap)) {
                    const regex = getStringIfConstant(refNode.arguments[0]);
                    const flags = getStringIfConstant(refNode.arguments[1]);

                    if (regex) {
                        checkRegex(regex, refNode, refNode.arguments[0], flags);
                    }
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Rule to disallow `parseInt()` in favor of binary, octal, and hexadecimal literals
 * @author Annie Zhang, Henry Zhu
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const radixMap = new Map([
    [2, { system: "binary", literalPrefix: "0b" }],
    [8, { system: "octal", literalPrefix: "0o" }],
    [16, { system: "hexadecimal", literalPrefix: "0x" }]
]);

/**
 * Checks to see if a CallExpression's callee node is `parseInt` or
 * `Number.parseInt`.
 * @param {ASTNode} calleeNode The callee node to evaluate.
 * @returns {boolean} True if the callee is `parseInt` or `Number.parseInt`,
 * false otherwise.
 */
function isParseInt(calleeNode) {
    return (
        astUtils.isSpecificId(calleeNode, "parseInt") ||
        astUtils.isSpecificMemberAccess(calleeNode, "Number", "parseInt")
    );
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow `parseInt()` and `Number.parseInt()` in favor of binary, octal, and hexadecimal literals",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-numeric-literals"
        },

        schema: [],

        messages: {
            useLiteral: "Use {{system}} literals instead of {{functionName}}()."
        },

        fixable: "code"
    },

    create(context) {
        const sourceCode = context.sourceCode;

        //----------------------------------------------------------------------
        // Public
        //----------------------------------------------------------------------

        return {

            "CallExpression[arguments.length=2]"(node) {
                const [strNode, radixNode] = node.arguments,
                    str = astUtils.getStaticStringValue(strNode),
                    radix = radixNode.value;

                if (
                    str !== null &&
                    astUtils.isStringLiteral(strNode) &&
                    radixNode.type === "Literal" &&
                    typeof radix === "number" &&
                    radixMap.has(radix) &&
                    isParseInt(node.callee)
                ) {

                    const { system, literalPrefix } = radixMap.get(radix);

                    context.report({
                        node,
                        messageId: "useLiteral",
                        data: {
                            system,
                            functionName: sourceCode.getText(node.callee)
                        },
                        fix(fixer) {
                            if (sourceCode.getCommentsInside(node).length) {
                                return null;
                            }

                            const replacement = `${literalPrefix}${str}`;

                            if (+replacement !== parseInt(str, radix)) {

                                /*
                                 * If the newly-produced literal would be invalid, (e.g. 0b1234),
                                 * or it would yield an incorrect parseInt result for some other reason, don't make a fix.
                                 *
                                 * If `str` had numeric separators, `+replacement` will evaluate to `NaN` because unary `+`
                                 * per the specification doesn't support numeric separators. Thus, the above condition will be `true`
                                 * (`NaN !== anything` is always `true`) regardless of the `parseInt(str, radix)` value.
                                 * Consequently, no autofixes will be made. This is correct behavior because `parseInt` also
                                 * doesn't support numeric separators, but it does parse part of the string before the first `_`,
                                 * so the autofix would be invalid:
                                 *
                                 *   parseInt("1_1", 2) // === 1
                                 *   0b1_1 // === 3
                                 */
                                return null;
                            }

                            const tokenBefore = sourceCode.getTokenBefore(node),
                                tokenAfter = sourceCode.getTokenAfter(node);
                            let prefix = "",
                                suffix = "";

                            if (
                                tokenBefore &&
                                tokenBefore.range[1] === node.range[0] &&
                                !astUtils.canTokensBeAdjacent(tokenBefore, replacement)
                            ) {
                                prefix = " ";
                            }

                            if (
                                tokenAfter &&
                                node.range[1] === tokenAfter.range[0] &&
                                !astUtils.canTokensBeAdjacent(replacement, tokenAfter)
                            ) {
                                suffix = " ";
                            }

                            return fixer.replaceText(node, `${prefix}${replacement}${suffix}`);
                        }
                    });
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Prefers Object.hasOwn() instead of Object.prototype.hasOwnProperty.call()
 * @author Nitin Kumar
 * @author Gautam Arora
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Checks if the given node is considered to be an access to a property of `Object.prototype`.
 * @param {ASTNode} node `MemberExpression` node to evaluate.
 * @returns {boolean} `true` if `node.object` is `Object`, `Object.prototype`, or `{}` (empty 'ObjectExpression' node).
 */
function hasLeftHandObject(node) {

    /*
     * ({}).hasOwnProperty.call(obj, prop) - `true`
     * ({ foo }.hasOwnProperty.call(obj, prop)) - `false`, object literal should be empty
     */
    if (node.object.type === "ObjectExpression" && node.object.properties.length === 0) {
        return true;
    }

    const objectNodeToCheck = node.object.type === "MemberExpression" && astUtils.getStaticPropertyName(node.object) === "prototype" ? node.object.object : node.object;

    if (objectNodeToCheck.type === "Identifier" && objectNodeToCheck.name === "Object") {
        return true;
    }

    return false;
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",
        docs: {
            description:
                "Disallow use of `Object.prototype.hasOwnProperty.call()` and prefer use of `Object.hasOwn()`",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-object-has-own"
        },
        schema: [],
        messages: {
            useHasOwn: "Use 'Object.hasOwn()' instead of 'Object.prototype.hasOwnProperty.call()'."
        },
        fixable: "code"
    },
    create(context) {

        const sourceCode = context.sourceCode;

        return {
            CallExpression(node) {
                if (!(node.callee.type === "MemberExpression" && node.callee.object.type === "MemberExpression")) {
                    return;
                }

                const calleePropertyName = astUtils.getStaticPropertyName(node.callee);
                const objectPropertyName = astUtils.getStaticPropertyName(node.callee.object);
                const isObject = hasLeftHandObject(node.callee.object);

                // check `Object` scope
                const scope = sourceCode.getScope(node);
                const variable = astUtils.getVariableByName(scope, "Object");

                if (
                    calleePropertyName === "call" &&
                    objectPropertyName === "hasOwnProperty" &&
                    isObject &&
                    variable && variable.scope.type === "global"
                ) {
                    context.report({
                        node,
                        messageId: "useHasOwn",
                        fix(fixer) {

                            if (sourceCode.getCommentsInside(node.callee).length > 0) {
                                return null;
                            }

                            const tokenJustBeforeNode = sourceCode.getTokenBefore(node.callee, { includeComments: true });

                            // for https://github.com/eslint/eslint/pull/15346#issuecomment-991417335
                            if (
                                tokenJustBeforeNode &&
                                tokenJustBeforeNode.range[1] === node.callee.range[0] &&
                                !astUtils.canTokensBeAdjacent(tokenJustBeforeNode, "Object.hasOwn")
                            ) {
                                return fixer.replaceText(node.callee, " Object.hasOwn");
                            }

                            return fixer.replaceText(node.callee, "Object.hasOwn");
                        }
                    });
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Prefers object spread property over Object.assign
 * @author Sharmila Jesupaul
 */

"use strict";

const { CALL, ReferenceTracker } = require("@eslint-community/eslint-utils");
const {
    isCommaToken,
    isOpeningParenToken,
    isClosingParenToken,
    isParenthesised
} = require("./utils/ast-utils");

const ANY_SPACE = /\s/u;

/**
 * Helper that checks if the Object.assign call has array spread
 * @param {ASTNode} node The node that the rule warns on
 * @returns {boolean} - Returns true if the Object.assign call has array spread
 */
function hasArraySpread(node) {
    return node.arguments.some(arg => arg.type === "SpreadElement");
}

/**
 * Determines whether the given node is an accessor property (getter/setter).
 * @param {ASTNode} node Node to check.
 * @returns {boolean} `true` if the node is a getter or a setter.
 */
function isAccessorProperty(node) {
    return node.type === "Property" &&
        (node.kind === "get" || node.kind === "set");
}

/**
 * Determines whether the given object expression node has accessor properties (getters/setters).
 * @param {ASTNode} node `ObjectExpression` node to check.
 * @returns {boolean} `true` if the node has at least one getter/setter.
 */
function hasAccessors(node) {
    return node.properties.some(isAccessorProperty);
}

/**
 * Determines whether the given call expression node has object expression arguments with accessor properties (getters/setters).
 * @param {ASTNode} node `CallExpression` node to check.
 * @returns {boolean} `true` if the node has at least one argument that is an object expression with at least one getter/setter.
 */
function hasArgumentsWithAccessors(node) {
    return node.arguments
        .filter(arg => arg.type === "ObjectExpression")
        .some(hasAccessors);
}

/**
 * Helper that checks if the node needs parentheses to be valid JS.
 * The default is to wrap the node in parentheses to avoid parsing errors.
 * @param {ASTNode} node The node that the rule warns on
 * @param {Object} sourceCode in context sourcecode object
 * @returns {boolean} - Returns true if the node needs parentheses
 */
function needsParens(node, sourceCode) {
    const parent = node.parent;

    switch (parent.type) {
        case "VariableDeclarator":
        case "ArrayExpression":
        case "ReturnStatement":
        case "CallExpression":
        case "Property":
            return false;
        case "AssignmentExpression":
            return parent.left === node && !isParenthesised(sourceCode, node);
        default:
            return !isParenthesised(sourceCode, node);
    }
}

/**
 * Determines if an argument needs parentheses. The default is to not add parens.
 * @param {ASTNode} node The node to be checked.
 * @param {Object} sourceCode in context sourcecode object
 * @returns {boolean} True if the node needs parentheses
 */
function argNeedsParens(node, sourceCode) {
    switch (node.type) {
        case "AssignmentExpression":
        case "ArrowFunctionExpression":
        case "ConditionalExpression":
            return !isParenthesised(sourceCode, node);
        default:
            return false;
    }
}

/**
 * Get the parenthesis tokens of a given ObjectExpression node.
 * This includes the braces of the object literal and enclosing parentheses.
 * @param {ASTNode} node The node to get.
 * @param {Token} leftArgumentListParen The opening paren token of the argument list.
 * @param {SourceCode} sourceCode The source code object to get tokens.
 * @returns {Token[]} The parenthesis tokens of the node. This is sorted by the location.
 */
function getParenTokens(node, leftArgumentListParen, sourceCode) {
    const parens = [sourceCode.getFirstToken(node), sourceCode.getLastToken(node)];
    let leftNext = sourceCode.getTokenBefore(node);
    let rightNext = sourceCode.getTokenAfter(node);

    // Note: don't include the parens of the argument list.
    while (
        leftNext &&
        rightNext &&
        leftNext.range[0] > leftArgumentListParen.range[0] &&
        isOpeningParenToken(leftNext) &&
        isClosingParenToken(rightNext)
    ) {
        parens.push(leftNext, rightNext);
        leftNext = sourceCode.getTokenBefore(leftNext);
        rightNext = sourceCode.getTokenAfter(rightNext);
    }

    return parens.sort((a, b) => a.range[0] - b.range[0]);
}

/**
 * Get the range of a given token and around whitespaces.
 * @param {Token} token The token to get range.
 * @param {SourceCode} sourceCode The source code object to get tokens.
 * @returns {number} The end of the range of the token and around whitespaces.
 */
function getStartWithSpaces(token, sourceCode) {
    const text = sourceCode.text;
    let start = token.range[0];

    // If the previous token is a line comment then skip this step to avoid commenting this token out.
    {
        const prevToken = sourceCode.getTokenBefore(token, { includeComments: true });

        if (prevToken && prevToken.type === "Line") {
            return start;
        }
    }

    // Detect spaces before the token.
    while (ANY_SPACE.test(text[start - 1] || "")) {
        start -= 1;
    }

    return start;
}

/**
 * Get the range of a given token and around whitespaces.
 * @param {Token} token The token to get range.
 * @param {SourceCode} sourceCode The source code object to get tokens.
 * @returns {number} The start of the range of the token and around whitespaces.
 */
function getEndWithSpaces(token, sourceCode) {
    const text = sourceCode.text;
    let end = token.range[1];

    // Detect spaces after the token.
    while (ANY_SPACE.test(text[end] || "")) {
        end += 1;
    }

    return end;
}

/**
 * Autofixes the Object.assign call to use an object spread instead.
 * @param {ASTNode|null} node The node that the rule warns on, i.e. the Object.assign call
 * @param {string} sourceCode sourceCode of the Object.assign call
 * @returns {Function} autofixer - replaces the Object.assign with a spread object.
 */
function defineFixer(node, sourceCode) {
    return function *(fixer) {
        const leftParen = sourceCode.getTokenAfter(node.callee, isOpeningParenToken);
        const rightParen = sourceCode.getLastToken(node);

        // Remove everything before the opening paren: callee `Object.assign`, type arguments, and whitespace between the callee and the paren.
        yield fixer.removeRange([node.range[0], leftParen.range[0]]);

        // Replace the parens of argument list to braces.
        if (needsParens(node, sourceCode)) {
            yield fixer.replaceText(leftParen, "({");
            yield fixer.replaceText(rightParen, "})");
        } else {
            yield fixer.replaceText(leftParen, "{");
            yield fixer.replaceText(rightParen, "}");
        }

        // Process arguments.
        for (const argNode of node.arguments) {
            const innerParens = getParenTokens(argNode, leftParen, sourceCode);
            const left = innerParens.shift();
            const right = innerParens.pop();

            if (argNode.type === "ObjectExpression") {
                const maybeTrailingComma = sourceCode.getLastToken(argNode, 1);
                const maybeArgumentComma = sourceCode.getTokenAfter(right);

                /*
                 * Make bare this object literal.
                 * And remove spaces inside of the braces for better formatting.
                 */
                for (const innerParen of innerParens) {
                    yield fixer.remove(innerParen);
                }
                const leftRange = [left.range[0], getEndWithSpaces(left, sourceCode)];
                const rightRange = [
                    Math.max(getStartWithSpaces(right, sourceCode), leftRange[1]), // Ensure ranges don't overlap
                    right.range[1]
                ];

                yield fixer.removeRange(leftRange);
                yield fixer.removeRange(rightRange);

                // Remove the comma of this argument if it's duplication.
                if (
                    (argNode.properties.length === 0 || isCommaToken(maybeTrailingComma)) &&
                    isCommaToken(maybeArgumentComma)
                ) {
                    yield fixer.remove(maybeArgumentComma);
                }
            } else {

                // Make spread.
                if (argNeedsParens(argNode, sourceCode)) {
                    yield fixer.insertTextBefore(left, "...(");
                    yield fixer.insertTextAfter(right, ")");
                } else {
                    yield fixer.insertTextBefore(left, "...");
                }
            }
        }
    };
}

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description:
                "Disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-object-spread"
        },

        schema: [],
        fixable: "code",

        messages: {
            useSpreadMessage: "Use an object spread instead of `Object.assign` eg: `{ ...foo }`.",
            useLiteralMessage: "Use an object literal instead of `Object.assign`. eg: `{ foo: bar }`."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;

        return {
            Program(node) {
                const scope = sourceCode.getScope(node);
                const tracker = new ReferenceTracker(scope);
                const trackMap = {
                    Object: {
                        assign: { [CALL]: true }
                    }
                };

                // Iterate all calls of `Object.assign` (only of the global variable `Object`).
                for (const { node: refNode } of tracker.iterateGlobalReferences(trackMap)) {
                    if (
                        refNode.arguments.length >= 1 &&
                        refNode.arguments[0].type === "ObjectExpression" &&
                        !hasArraySpread(refNode) &&
                        !(
                            refNode.arguments.length > 1 &&
                            hasArgumentsWithAccessors(refNode)
                        )
                    ) {
                        const messageId = refNode.arguments.length === 1
                            ? "useLiteralMessage"
                            : "useSpreadMessage";
                        const fix = defineFixer(refNode, sourceCode);

                        context.report({ node: refNode, messageId, fix });
                    }
                }
            }
        };
    }
};


code:
/**
 * @fileoverview restrict values that can be used as Promise rejection reasons
 * @author Teddy Katz
 */
"use strict";

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Require using Error objects as Promise rejection reasons",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-promise-reject-errors"
        },

        fixable: null,

        schema: [
            {
                type: "object",
                properties: {
                    allowEmptyReject: { type: "boolean", default: false }
                },
                additionalProperties: false
            }
        ],

        messages: {
            rejectAnError: "Expected the Promise rejection reason to be an Error."
        }
    },

    create(context) {

        const ALLOW_EMPTY_REJECT = context.options.length && context.options[0].allowEmptyReject;
        const sourceCode = context.sourceCode;

        //----------------------------------------------------------------------
        // Helpers
        //----------------------------------------------------------------------

        /**
         * Checks the argument of a reject() or Promise.reject() CallExpression, and reports it if it can't be an Error
         * @param {ASTNode} callExpression A CallExpression node which is used to reject a Promise
         * @returns {void}
         */
        function checkRejectCall(callExpression) {
            if (!callExpression.arguments.length && ALLOW_EMPTY_REJECT) {
                return;
            }
            if (
                !callExpression.arguments.length ||
                !astUtils.couldBeError(callExpression.arguments[0]) ||
                callExpression.arguments[0].type === "Identifier" && callExpression.arguments[0].name === "undefined"
            ) {
                context.report({
                    node: callExpression,
                    messageId: "rejectAnError"
                });
            }
        }

        /**
         * Determines whether a function call is a Promise.reject() call
         * @param {ASTNode} node A CallExpression node
         * @returns {boolean} `true` if the call is a Promise.reject() call
         */
        function isPromiseRejectCall(node) {
            return astUtils.isSpecificMemberAccess(node.callee, "Promise", "reject");
        }

        //----------------------------------------------------------------------
        // Public
        //----------------------------------------------------------------------

        return {

            // Check `Promise.reject(value)` calls.
            CallExpression(node) {
                if (isPromiseRejectCall(node)) {
                    checkRejectCall(node);
                }
            },

            /*
             * Check for `new Promise((resolve, reject) => {})`, and check for reject() calls.
             * This function is run on "NewExpression:exit" instead of "NewExpression" to ensure that
             * the nodes in the expression already have the `parent` property.
             */
            "NewExpression:exit"(node) {
                if (
                    node.callee.type === "Identifier" && node.callee.name === "Promise" &&
                    node.arguments.length && astUtils.isFunction(node.arguments[0]) &&
                    node.arguments[0].params.length > 1 && node.arguments[0].params[1].type === "Identifier"
                ) {
                    sourceCode.getDeclaredVariables(node.arguments[0])

                        /*
                         * Find the first variable that matches the second parameter's name.
                         * If the first parameter has the same name as the second parameter, then the variable will actually
                         * be "declared" when the first parameter is evaluated, but then it will be immediately overwritten
                         * by the second parameter. It's not possible for an expression with the variable to be evaluated before
                         * the variable is overwritten, because functions with duplicate parameters cannot have destructuring or
                         * default assignments in their parameter lists. Therefore, it's not necessary to explicitly account for
                         * this case.
                         */
                        .find(variable => variable.name === node.arguments[0].params[1].name)

                        // Get the references to that variable.
                        .references

                        // Only check the references that read the parameter's value.
                        .filter(ref => ref.isRead())

                        // Only check the references that are used as the callee in a function call, e.g. `reject(foo)`.
                        .filter(ref => ref.identifier.parent.type === "CallExpression" && ref.identifier === ref.identifier.parent.callee)

                        // Check the argument of the function call to determine whether it's an Error.
                        .forEach(ref => checkRejectCall(ref.identifier.parent));
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Rule to suggest using "Reflect" api over Function/Object methods
 * @author Keith Cirkel <http://keithcirkel.co.uk>
 * @deprecated in ESLint v3.9.0
 */
"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Require `Reflect` methods where applicable",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-reflect"
        },

        deprecated: true,

        replacedBy: [],

        schema: [
            {
                type: "object",
                properties: {
                    exceptions: {
                        type: "array",
                        items: {
                            enum: [
                                "apply",
                                "call",
                                "delete",
                                "defineProperty",
                                "getOwnPropertyDescriptor",
                                "getPrototypeOf",
                                "setPrototypeOf",
                                "isExtensible",
                                "getOwnPropertyNames",
                                "preventExtensions"
                            ]
                        },
                        uniqueItems: true
                    }
                },
                additionalProperties: false
            }
        ],

        messages: {
            preferReflect: "Avoid using {{existing}}, instead use {{substitute}}."
        }
    },

    create(context) {
        const existingNames = {
            apply: "Function.prototype.apply",
            call: "Function.prototype.call",
            defineProperty: "Object.defineProperty",
            getOwnPropertyDescriptor: "Object.getOwnPropertyDescriptor",
            getPrototypeOf: "Object.getPrototypeOf",
            setPrototypeOf: "Object.setPrototypeOf",
            isExtensible: "Object.isExtensible",
            getOwnPropertyNames: "Object.getOwnPropertyNames",
            preventExtensions: "Object.preventExtensions"
        };

        const reflectSubstitutes = {
            apply: "Reflect.apply",
            call: "Reflect.apply",
            defineProperty: "Reflect.defineProperty",
            getOwnPropertyDescriptor: "Reflect.getOwnPropertyDescriptor",
            getPrototypeOf: "Reflect.getPrototypeOf",
            setPrototypeOf: "Reflect.setPrototypeOf",
            isExtensible: "Reflect.isExtensible",
            getOwnPropertyNames: "Reflect.getOwnPropertyNames",
            preventExtensions: "Reflect.preventExtensions"
        };

        const exceptions = (context.options[0] || {}).exceptions || [];

        /**
         * Reports the Reflect violation based on the `existing` and `substitute`
         * @param {Object} node The node that violates the rule.
         * @param {string} existing The existing method name that has been used.
         * @param {string} substitute The Reflect substitute that should be used.
         * @returns {void}
         */
        function report(node, existing, substitute) {
            context.report({
                node,
                messageId: "preferReflect",
                data: {
                    existing,
                    substitute
                }
            });
        }

        return {
            CallExpression(node) {
                const methodName = (node.callee.property || {}).name;
                const isReflectCall = (node.callee.object || {}).name === "Reflect";
                const hasReflectSubstitute = Object.prototype.hasOwnProperty.call(reflectSubstitutes, methodName);
                const userConfiguredException = exceptions.includes(methodName);

                if (hasReflectSubstitute && !isReflectCall && !userConfiguredException) {
                    report(node, existingNames[methodName], reflectSubstitutes[methodName]);
                }
            },
            UnaryExpression(node) {
                const isDeleteOperator = node.operator === "delete";
                const targetsIdentifier = node.argument.type === "Identifier";
                const userConfiguredException = exceptions.includes("delete");

                if (isDeleteOperator && !targetsIdentifier && !userConfiguredException) {
                    report(node, "the delete keyword", "Reflect.deleteProperty");
                }
            }
        };

    }
};


code:
/**
 * @fileoverview Rule to disallow use of the `RegExp` constructor in favor of regular expression literals
 * @author Milos Djermanovic
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");
const { CALL, CONSTRUCT, ReferenceTracker, findVariable } = require("@eslint-community/eslint-utils");
const { RegExpValidator, visitRegExpAST, RegExpParser } = require("@eslint-community/regexpp");
const { canTokensBeAdjacent } = require("./utils/ast-utils");
const { REGEXPP_LATEST_ECMA_VERSION } = require("./utils/regular-expressions");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Determines whether the given node is a string literal.
 * @param {ASTNode} node Node to check.
 * @returns {boolean} True if the node is a string literal.
 */
function isStringLiteral(node) {
    return node.type === "Literal" && typeof node.value === "string";
}

/**
 * Determines whether the given node is a regex literal.
 * @param {ASTNode} node Node to check.
 * @returns {boolean} True if the node is a regex literal.
 */
function isRegexLiteral(node) {
    return node.type === "Literal" && Object.prototype.hasOwnProperty.call(node, "regex");
}

const validPrecedingTokens = new Set([
    "(",
    ";",
    "[",
    ",",
    "=",
    "+",
    "*",
    "-",
    "?",
    "~",
    "%",
    "**",
    "!",
    "typeof",
    "instanceof",
    "&&",
    "||",
    "??",
    "return",
    "...",
    "delete",
    "void",
    "in",
    "<",
    ">",
    "<=",
    ">=",
    "==",
    "===",
    "!=",
    "!==",
    "<<",
    ">>",
    ">>>",
    "&",
    "|",
    "^",
    ":",
    "{",
    "=>",
    "*=",
    "<<=",
    ">>=",
    ">>>=",
    "^=",
    "|=",
    "&=",
    "??=",
    "||=",
    "&&=",
    "**=",
    "+=",
    "-=",
    "/=",
    "%=",
    "/",
    "do",
    "break",
    "continue",
    "debugger",
    "case",
    "throw"
]);

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow use of the `RegExp` constructor in favor of regular expression literals",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-regex-literals"
        },

        hasSuggestions: true,

        schema: [
            {
                type: "object",
                properties: {
                    disallowRedundantWrapping: {
                        type: "boolean",
                        default: false
                    }
                },
                additionalProperties: false
            }
        ],

        messages: {
            unexpectedRegExp: "Use a regular expression literal instead of the 'RegExp' constructor.",
            replaceWithLiteral: "Replace with an equivalent regular expression literal.",
            replaceWithLiteralAndFlags: "Replace with an equivalent regular expression literal with flags '{{ flags }}'.",
            replaceWithIntendedLiteralAndFlags: "Replace with a regular expression literal with flags '{{ flags }}'.",
            unexpectedRedundantRegExp: "Regular expression literal is unnecessarily wrapped within a 'RegExp' constructor.",
            unexpectedRedundantRegExpWithFlags: "Use regular expression literal with flags instead of the 'RegExp' constructor."
        }
    },

    create(context) {
        const [{ disallowRedundantWrapping = false } = {}] = context.options;
        const sourceCode = context.sourceCode;

        /**
         * Determines whether the given identifier node is a reference to a global variable.
         * @param {ASTNode} node `Identifier` node to check.
         * @returns {boolean} True if the identifier is a reference to a global variable.
         */
        function isGlobalReference(node) {
            const scope = sourceCode.getScope(node);
            const variable = findVariable(scope, node);

            return variable !== null && variable.scope.type === "global" && variable.defs.length === 0;
        }

        /**
         * Determines whether the given node is a String.raw`` tagged template expression
         * with a static template literal.
         * @param {ASTNode} node Node to check.
         * @returns {boolean} True if the node is String.raw`` with a static template.
         */
        function isStringRawTaggedStaticTemplateLiteral(node) {
            return node.type === "TaggedTemplateExpression" &&
                astUtils.isSpecificMemberAccess(node.tag, "String", "raw") &&
                isGlobalReference(astUtils.skipChainExpression(node.tag).object) &&
                astUtils.isStaticTemplateLiteral(node.quasi);
        }

        /**
         * Gets the value of a string
         * @param {ASTNode} node The node to get the string of.
         * @returns {string|null} The value of the node.
         */
        function getStringValue(node) {
            if (isStringLiteral(node)) {
                return node.value;
            }

            if (astUtils.isStaticTemplateLiteral(node)) {
                return node.quasis[0].value.cooked;
            }

            if (isStringRawTaggedStaticTemplateLiteral(node)) {
                return node.quasi.quasis[0].value.raw;
            }

            return null;
        }

        /**
         * Determines whether the given node is considered to be a static string by the logic of this rule.
         * @param {ASTNode} node Node to check.
         * @returns {boolean} True if the node is a static string.
         */
        function isStaticString(node) {
            return isStringLiteral(node) ||
                astUtils.isStaticTemplateLiteral(node) ||
                isStringRawTaggedStaticTemplateLiteral(node);
        }

        /**
         * Determines whether the relevant arguments of the given are all static string literals.
         * @param {ASTNode} node Node to check.
         * @returns {boolean} True if all arguments are static strings.
         */
        function hasOnlyStaticStringArguments(node) {
            const args = node.arguments;

            if ((args.length === 1 || args.length === 2) && args.every(isStaticString)) {
                return true;
            }

            return false;
        }

        /**
         * Determines whether the arguments of the given node indicate that a regex literal is unnecessarily wrapped.
         * @param {ASTNode} node Node to check.
         * @returns {boolean} True if the node already contains a regex literal argument.
         */
        function isUnnecessarilyWrappedRegexLiteral(node) {
            const args = node.arguments;

            if (args.length === 1 && isRegexLiteral(args[0])) {
                return true;
            }

            if (args.length === 2 && isRegexLiteral(args[0]) && isStaticString(args[1])) {
                return true;
            }

            return false;
        }

        /**
         * Returns a ecmaVersion compatible for regexpp.
         * @param {number} ecmaVersion The ecmaVersion to convert.
         * @returns {import("@eslint-community/regexpp/ecma-versions").EcmaVersion} The resulting ecmaVersion compatible for regexpp.
         */
        function getRegexppEcmaVersion(ecmaVersion) {
            if (ecmaVersion <= 5) {
                return 5;
            }
            return Math.min(ecmaVersion, REGEXPP_LATEST_ECMA_VERSION);
        }

        const regexppEcmaVersion = getRegexppEcmaVersion(context.languageOptions.ecmaVersion);

        /**
         * Makes a character escaped or else returns null.
         * @param {string} character The character to escape.
         * @returns {string} The resulting escaped character.
         */
        function resolveEscapes(character) {
            switch (character) {
                case "\n":
                case "\\\n":
                    return "\\n";

                case "\r":
                case "\\\r":
                    return "\\r";

                case "\t":
                case "\\\t":
                    return "\\t";

                case "\v":
                case "\\\v":
                    return "\\v";

                case "\f":
                case "\\\f":
                    return "\\f";

                case "/":
                    return "\\/";

                default:
                    return null;
            }
        }

        /**
         * Checks whether the given regex and flags are valid for the ecma version or not.
         * @param {string} pattern The regex pattern to check.
         * @param {string | undefined} flags The regex flags to check.
         * @returns {boolean} True if the given regex pattern and flags are valid for the ecma version.
         */
        function isValidRegexForEcmaVersion(pattern, flags) {
            const validator = new RegExpValidator({ ecmaVersion: regexppEcmaVersion });

            try {
                validator.validatePattern(pattern, 0, pattern.length, {
                    unicode: flags ? flags.includes("u") : false,
                    unicodeSets: flags ? flags.includes("v") : false
                });
                if (flags) {
                    validator.validateFlags(flags);
                }
                return true;
            } catch {
                return false;
            }
        }

        /**
         * Checks whether two given regex flags contain the same flags or not.
         * @param {string} flagsA The regex flags.
         * @param {string} flagsB The regex flags.
         * @returns {boolean} True if two regex flags contain same flags.
         */
        function areFlagsEqual(flagsA, flagsB) {
            return [...flagsA].sort().join("") === [...flagsB].sort().join("");
        }

        /**
         * Merges two regex flags.
         * @param {string} flagsA The regex flags.
         * @param {string} flagsB The regex flags.
         * @returns {string} The merged regex flags.
         */
        function mergeRegexFlags(flagsA, flagsB) {
            const flagsSet = new Set([
                ...flagsA,
                ...flagsB
            ]);

            return [...flagsSet].join("");
        }

        /**
         * Checks whether a give node can be fixed to the given regex pattern and flags.
         * @param {ASTNode} node The node to check.
         * @param {string} pattern The regex pattern to check.
         * @param {string} flags The regex flags
         * @returns {boolean} True if a node can be fixed to the given regex pattern and flags.
         */
        function canFixTo(node, pattern, flags) {
            const tokenBefore = sourceCode.getTokenBefore(node);

            return sourceCode.getCommentsInside(node).length === 0 &&
                (!tokenBefore || validPrecedingTokens.has(tokenBefore.value)) &&
                isValidRegexForEcmaVersion(pattern, flags);
        }

        /**
         * Returns a safe output code considering the before and after tokens.
         * @param {ASTNode} node The regex node.
         * @param {string} newRegExpValue The new regex expression value.
         * @returns {string} The output code.
         */
        function getSafeOutput(node, newRegExpValue) {
            const tokenBefore = sourceCode.getTokenBefore(node);
            const tokenAfter = sourceCode.getTokenAfter(node);

            return (tokenBefore && !canTokensBeAdjacent(tokenBefore, newRegExpValue) && tokenBefore.range[1] === node.range[0] ? " " : "") +
                newRegExpValue +
            (tokenAfter && !canTokensBeAdjacent(newRegExpValue, tokenAfter) && node.range[1] === tokenAfter.range[0] ? " " : "");

        }

        return {
            Program(node) {
                const scope = sourceCode.getScope(node);
                const tracker = new ReferenceTracker(scope);
                const traceMap = {
                    RegExp: {
                        [CALL]: true,
                        [CONSTRUCT]: true
                    }
                };

                for (const { node: refNode } of tracker.iterateGlobalReferences(traceMap)) {
                    if (disallowRedundantWrapping && isUnnecessarilyWrappedRegexLiteral(refNode)) {
                        const regexNode = refNode.arguments[0];

                        if (refNode.arguments.length === 2) {
                            const suggests = [];

                            const argFlags = getStringValue(refNode.arguments[1]) || "";

                            if (canFixTo(refNode, regexNode.regex.pattern, argFlags)) {
                                suggests.push({
                                    messageId: "replaceWithLiteralAndFlags",
                                    pattern: regexNode.regex.pattern,
                                    flags: argFlags
                                });
                            }

                            const literalFlags = regexNode.regex.flags || "";
                            const mergedFlags = mergeRegexFlags(literalFlags, argFlags);

                            if (
                                !areFlagsEqual(mergedFlags, argFlags) &&
                                canFixTo(refNode, regexNode.regex.pattern, mergedFlags)
                            ) {
                                suggests.push({
                                    messageId: "replaceWithIntendedLiteralAndFlags",
                                    pattern: regexNode.regex.pattern,
                                    flags: mergedFlags
                                });
                            }

                            context.report({
                                node: refNode,
                                messageId: "unexpectedRedundantRegExpWithFlags",
                                suggest: suggests.map(({ flags, pattern, messageId }) => ({
                                    messageId,
                                    data: {
                                        flags
                                    },
                                    fix(fixer) {
                                        return fixer.replaceText(refNode, getSafeOutput(refNode, `/${pattern}/${flags}`));
                                    }
                                }))
                            });
                        } else {
                            const outputs = [];

                            if (canFixTo(refNode, regexNode.regex.pattern, regexNode.regex.flags)) {
                                outputs.push(sourceCode.getText(regexNode));
                            }

                            context.report({
                                node: refNode,
                                messageId: "unexpectedRedundantRegExp",
                                suggest: outputs.map(output => ({
                                    messageId: "replaceWithLiteral",
                                    fix(fixer) {
                                        return fixer.replaceText(
                                            refNode,
                                            getSafeOutput(refNode, output)
                                        );
                                    }
                                }))
                            });
                        }
                    } else if (hasOnlyStaticStringArguments(refNode)) {
                        let regexContent = getStringValue(refNode.arguments[0]);
                        let noFix = false;
                        let flags;

                        if (refNode.arguments[1]) {
                            flags = getStringValue(refNode.arguments[1]);
                        }

                        if (!canFixTo(refNode, regexContent, flags)) {
                            noFix = true;
                        }

                        if (!/^[-a-zA-Z0-9\\[\](){} \t\r\n\v\f!@#$%^&*+^_=/~`.><?,'"|:;]*$/u.test(regexContent)) {
                            noFix = true;
                        }

                        if (regexContent && !noFix) {
                            let charIncrease = 0;

                            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) {
                                    const escaped = resolveEscapes(characterNode.raw);

                                    if (escaped) {
                                        regexContent =
                                            regexContent.slice(0, characterNode.start + charIncrease) +
                                            escaped +
                                            regexContent.slice(characterNode.end + charIncrease);

                                        if (characterNode.raw.length === 1) {
                                            charIncrease += 1;
                                        }
                                    }
                                }
                            });
                        }

                        const newRegExpValue = `/${regexContent || "(?:)"}/${flags || ""}`;

                        context.report({
                            node: refNode,
                            messageId: "unexpectedRegExp",
                            suggest: noFix ? [] : [{
                                messageId: "replaceWithLiteral",
                                fix(fixer) {
                                    return fixer.replaceText(refNode, getSafeOutput(refNode, newRegExpValue));
                                }
                            }]
                        });
                    }
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Rule to
 * @author Toru Nagashima
 */

"use strict";

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Gets the variable object of `arguments` which is defined implicitly.
 * @param {eslint-scope.Scope} scope A scope to get.
 * @returns {eslint-scope.Variable} The found variable object.
 */
function getVariableOfArguments(scope) {
    const variables = scope.variables;

    for (let i = 0; i < variables.length; ++i) {
        const variable = variables[i];

        if (variable.name === "arguments") {

            /*
             * If there was a parameter which is named "arguments", the implicit "arguments" is not defined.
             * So does fast return with null.
             */
            return (variable.identifiers.length === 0) ? variable : null;
        }
    }

    /* c8 ignore next */
    return null;
}

/**
 * Checks if the given reference is not normal member access.
 *
 * - arguments         .... true    // not member access
 * - arguments[i]      .... true    // computed member access
 * - arguments[0]      .... true    // computed member access
 * - arguments.length  .... false   // normal member access
 * @param {eslint-scope.Reference} reference The reference to check.
 * @returns {boolean} `true` if the reference is not normal member access.
 */
function isNotNormalMemberAccess(reference) {
    const id = reference.identifier;
    const parent = id.parent;

    return !(
        parent.type === "MemberExpression" &&
        parent.object === id &&
        !parent.computed
    );
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Require rest parameters instead of `arguments`",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-rest-params"
        },

        schema: [],

        messages: {
            preferRestParams: "Use the rest parameters instead of 'arguments'."
        }
    },

    create(context) {

        const sourceCode = context.sourceCode;

        /**
         * Reports a given reference.
         * @param {eslint-scope.Reference} reference A reference to report.
         * @returns {void}
         */
        function report(reference) {
            context.report({
                node: reference.identifier,
                loc: reference.identifier.loc,
                messageId: "preferRestParams"
            });
        }

        /**
         * Reports references of the implicit `arguments` variable if exist.
         * @param {ASTNode} node The node representing the function.
         * @returns {void}
         */
        function checkForArguments(node) {
            const argumentsVar = getVariableOfArguments(sourceCode.getScope(node));

            if (argumentsVar) {
                argumentsVar
                    .references
                    .filter(isNotNormalMemberAccess)
                    .forEach(report);
            }
        }

        return {
            "FunctionDeclaration:exit": checkForArguments,
            "FunctionExpression:exit": checkForArguments
        };
    }
};


code:
/**
 * @fileoverview A rule to suggest using of the spread operator instead of `.apply()`.
 * @author Toru Nagashima
 */

"use strict";

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Checks whether or not a node is a `.apply()` for variadic.
 * @param {ASTNode} node A CallExpression node to check.
 * @returns {boolean} Whether or not the node is a `.apply()` for variadic.
 */
function isVariadicApplyCalling(node) {
    return (
        astUtils.isSpecificMemberAccess(node.callee, null, "apply") &&
        node.arguments.length === 2 &&
        node.arguments[1].type !== "ArrayExpression" &&
        node.arguments[1].type !== "SpreadElement"
    );
}

/**
 * Checks whether or not `thisArg` is not changed by `.apply()`.
 * @param {ASTNode|null} expectedThis The node that is the owner of the applied function.
 * @param {ASTNode} thisArg The node that is given to the first argument of the `.apply()`.
 * @param {RuleContext} context The ESLint rule context object.
 * @returns {boolean} Whether or not `thisArg` is not changed by `.apply()`.
 */
function isValidThisArg(expectedThis, thisArg, context) {
    if (!expectedThis) {
        return astUtils.isNullOrUndefined(thisArg);
    }
    return astUtils.equalTokens(expectedThis, thisArg, context);
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Require spread operators instead of `.apply()`",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-spread"
        },

        schema: [],
        fixable: null,

        messages: {
            preferSpread: "Use the spread operator instead of '.apply()'."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;

        return {
            CallExpression(node) {
                if (!isVariadicApplyCalling(node)) {
                    return;
                }

                const applied = astUtils.skipChainExpression(astUtils.skipChainExpression(node.callee).object);
                const expectedThis = (applied.type === "MemberExpression") ? applied.object : null;
                const thisArg = node.arguments[0];

                if (isValidThisArg(expectedThis, thisArg, sourceCode)) {
                    context.report({
                        node,
                        messageId: "preferSpread"
                    });
                }
            }
        };
    }
};


code:
/**
 * @fileoverview A rule to suggest using template literals instead of string concatenation.
 * @author Toru Nagashima
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Checks whether or not a given node is a concatenation.
 * @param {ASTNode} node A node to check.
 * @returns {boolean} `true` if the node is a concatenation.
 */
function isConcatenation(node) {
    return node.type === "BinaryExpression" && node.operator === "+";
}

/**
 * Gets the top binary expression node for concatenation in parents of a given node.
 * @param {ASTNode} node A node to get.
 * @returns {ASTNode} the top binary expression node in parents of a given node.
 */
function getTopConcatBinaryExpression(node) {
    let currentNode = node;

    while (isConcatenation(currentNode.parent)) {
        currentNode = currentNode.parent;
    }
    return currentNode;
}

/**
 * Checks whether or not a node contains a string literal with an octal or non-octal decimal escape sequence
 * @param {ASTNode} node A node to check
 * @returns {boolean} `true` if at least one string literal within the node contains
 * an octal or non-octal decimal escape sequence
 */
function hasOctalOrNonOctalDecimalEscapeSequence(node) {
    if (isConcatenation(node)) {
        return (
            hasOctalOrNonOctalDecimalEscapeSequence(node.left) ||
            hasOctalOrNonOctalDecimalEscapeSequence(node.right)
        );
    }

    // No need to check TemplateLiterals – would throw parsing error
    if (node.type === "Literal" && typeof node.value === "string") {
        return astUtils.hasOctalOrNonOctalDecimalEscapeSequence(node.raw);
    }

    return false;
}

/**
 * Checks whether or not a given binary expression has string literals.
 * @param {ASTNode} node A node to check.
 * @returns {boolean} `true` if the node has string literals.
 */
function hasStringLiteral(node) {
    if (isConcatenation(node)) {

        // `left` is deeper than `right` normally.
        return hasStringLiteral(node.right) || hasStringLiteral(node.left);
    }
    return astUtils.isStringLiteral(node);
}

/**
 * Checks whether or not a given binary expression has non string literals.
 * @param {ASTNode} node A node to check.
 * @returns {boolean} `true` if the node has non string literals.
 */
function hasNonStringLiteral(node) {
    if (isConcatenation(node)) {

        // `left` is deeper than `right` normally.
        return hasNonStringLiteral(node.right) || hasNonStringLiteral(node.left);
    }
    return !astUtils.isStringLiteral(node);
}

/**
 * Determines whether a given node will start with a template curly expression (`${}`) when being converted to a template literal.
 * @param {ASTNode} node The node that will be fixed to a template literal
 * @returns {boolean} `true` if the node will start with a template curly.
 */
function startsWithTemplateCurly(node) {
    if (node.type === "BinaryExpression") {
        return startsWithTemplateCurly(node.left);
    }
    if (node.type === "TemplateLiteral") {
        return node.expressions.length && node.quasis.length && node.quasis[0].range[0] === node.quasis[0].range[1];
    }
    return node.type !== "Literal" || typeof node.value !== "string";
}

/**
 * Determines whether a given node end with a template curly expression (`${}`) when being converted to a template literal.
 * @param {ASTNode} node The node that will be fixed to a template literal
 * @returns {boolean} `true` if the node will end with a template curly.
 */
function endsWithTemplateCurly(node) {
    if (node.type === "BinaryExpression") {
        return startsWithTemplateCurly(node.right);
    }
    if (node.type === "TemplateLiteral") {
        return node.expressions.length && node.quasis.length && node.quasis[node.quasis.length - 1].range[0] === node.quasis[node.quasis.length - 1].range[1];
    }
    return node.type !== "Literal" || typeof node.value !== "string";
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Require template literals instead of string concatenation",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-template"
        },

        schema: [],
        fixable: "code",

        messages: {
            unexpectedStringConcatenation: "Unexpected string concatenation."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;
        let done = Object.create(null);

        /**
         * Gets the non-token text between two nodes, ignoring any other tokens that appear between the two tokens.
         * @param {ASTNode} node1 The first node
         * @param {ASTNode} node2 The second node
         * @returns {string} The text between the nodes, excluding other tokens
         */
        function getTextBetween(node1, node2) {
            const allTokens = [node1].concat(sourceCode.getTokensBetween(node1, node2)).concat(node2);
            const sourceText = sourceCode.getText();

            return allTokens.slice(0, -1).reduce((accumulator, token, index) => accumulator + sourceText.slice(token.range[1], allTokens[index + 1].range[0]), "");
        }

        /**
         * Returns a template literal form of the given node.
         * @param {ASTNode} currentNode A node that should be converted to a template literal
         * @param {string} textBeforeNode Text that should appear before the node
         * @param {string} textAfterNode Text that should appear after the node
         * @returns {string} A string form of this node, represented as a template literal
         */
        function getTemplateLiteral(currentNode, textBeforeNode, textAfterNode) {
            if (currentNode.type === "Literal" && typeof currentNode.value === "string") {

                /*
                 * If the current node is a string literal, escape any instances of ${ or ` to prevent them from being interpreted
                 * as a template placeholder. However, if the code already contains a backslash before the ${ or `
                 * for some reason, don't add another backslash, because that would change the meaning of the code (it would cause
                 * an actual backslash character to appear before the dollar sign).
                 */
                return `\`${currentNode.raw.slice(1, -1).replace(/\\*(\$\{|`)/gu, matched => {
                    if (matched.lastIndexOf("\\") % 2) {
                        return `\\${matched}`;
                    }
                    return matched;

                // Unescape any quotes that appear in the original Literal that no longer need to be escaped.
                }).replace(new RegExp(`\\\\${currentNode.raw[0]}`, "gu"), currentNode.raw[0])}\``;
            }

            if (currentNode.type === "TemplateLiteral") {
                return sourceCode.getText(currentNode);
            }

            if (isConcatenation(currentNode) && hasStringLiteral(currentNode)) {
                const plusSign = sourceCode.getFirstTokenBetween(currentNode.left, currentNode.right, token => token.value === "+");
                const textBeforePlus = getTextBetween(currentNode.left, plusSign);
                const textAfterPlus = getTextBetween(plusSign, currentNode.right);
                const leftEndsWithCurly = endsWithTemplateCurly(currentNode.left);
                const rightStartsWithCurly = startsWithTemplateCurly(currentNode.right);

                if (leftEndsWithCurly) {

                    // If the left side of the expression ends with a template curly, add the extra text to the end of the curly bracket.
                    // `foo${bar}` /* comment */ + 'baz' --> `foo${bar /* comment */  }${baz}`
                    return getTemplateLiteral(currentNode.left, textBeforeNode, textBeforePlus + textAfterPlus).slice(0, -1) +
                        getTemplateLiteral(currentNode.right, null, textAfterNode).slice(1);
                }
                if (rightStartsWithCurly) {

                    // Otherwise, if the right side of the expression starts with a template curly, add the text there.
                    // 'foo' /* comment */ + `${bar}baz` --> `foo${ /* comment */  bar}baz`
                    return getTemplateLiteral(currentNode.left, textBeforeNode, null).slice(0, -1) +
                        getTemplateLiteral(currentNode.right, textBeforePlus + textAfterPlus, textAfterNode).slice(1);
                }

                /*
                 * Otherwise, these nodes should not be combined into a template curly, since there is nowhere to put
                 * the text between them.
                 */
                return `${getTemplateLiteral(currentNode.left, textBeforeNode, null)}${textBeforePlus}+${textAfterPlus}${getTemplateLiteral(currentNode.right, textAfterNode, null)}`;
            }

            return `\`\${${textBeforeNode || ""}${sourceCode.getText(currentNode)}${textAfterNode || ""}}\``;
        }

        /**
         * Returns a fixer object that converts a non-string binary expression to a template literal
         * @param {SourceCodeFixer} fixer The fixer object
         * @param {ASTNode} node A node that should be converted to a template literal
         * @returns {Object} A fix for this binary expression
         */
        function fixNonStringBinaryExpression(fixer, node) {
            const topBinaryExpr = getTopConcatBinaryExpression(node.parent);

            if (hasOctalOrNonOctalDecimalEscapeSequence(topBinaryExpr)) {
                return null;
            }

            return fixer.replaceText(topBinaryExpr, getTemplateLiteral(topBinaryExpr, null, null));
        }

        /**
         * Reports if a given node is string concatenation with non string literals.
         * @param {ASTNode} node A node to check.
         * @returns {void}
         */
        function checkForStringConcat(node) {
            if (!astUtils.isStringLiteral(node) || !isConcatenation(node.parent)) {
                return;
            }

            const topBinaryExpr = getTopConcatBinaryExpression(node.parent);

            // Checks whether or not this node had been checked already.
            if (done[topBinaryExpr.range[0]]) {
                return;
            }
            done[topBinaryExpr.range[0]] = true;

            if (hasNonStringLiteral(topBinaryExpr)) {
                context.report({
                    node: topBinaryExpr,
                    messageId: "unexpectedStringConcatenation",
                    fix: fixer => fixNonStringBinaryExpression(fixer, node)
                });
            }
        }

        return {
            Program() {
                done = Object.create(null);
            },

            Literal: checkForStringConcat,
            TemplateLiteral: checkForStringConcat
        };
    }
};