code:
/**
 * @fileoverview A rule to control the use of single variable declarations.
 * @author Ian Christian Myers
 */

"use strict";

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

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

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

/**
 * Determines whether the given node is in a statement list.
 * @param {ASTNode} node node to check
 * @returns {boolean} `true` if the given node is in a statement list
 */
function isInStatementList(node) {
    return astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type);
}

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

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

        docs: {
            description: "Enforce variables to be declared either together or separately in functions",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/one-var"
        },

        fixable: "code",

        schema: [
            {
                oneOf: [
                    {
                        enum: ["always", "never", "consecutive"]
                    },
                    {
                        type: "object",
                        properties: {
                            separateRequires: {
                                type: "boolean"
                            },
                            var: {
                                enum: ["always", "never", "consecutive"]
                            },
                            let: {
                                enum: ["always", "never", "consecutive"]
                            },
                            const: {
                                enum: ["always", "never", "consecutive"]
                            }
                        },
                        additionalProperties: false
                    },
                    {
                        type: "object",
                        properties: {
                            initialized: {
                                enum: ["always", "never", "consecutive"]
                            },
                            uninitialized: {
                                enum: ["always", "never", "consecutive"]
                            }
                        },
                        additionalProperties: false
                    }
                ]
            }
        ],

        messages: {
            combineUninitialized: "Combine this with the previous '{{type}}' statement with uninitialized variables.",
            combineInitialized: "Combine this with the previous '{{type}}' statement with initialized variables.",
            splitUninitialized: "Split uninitialized '{{type}}' declarations into multiple statements.",
            splitInitialized: "Split initialized '{{type}}' declarations into multiple statements.",
            splitRequires: "Split requires to be separated into a single block.",
            combine: "Combine this with the previous '{{type}}' statement.",
            split: "Split '{{type}}' declarations into multiple statements."
        }
    },

    create(context) {
        const MODE_ALWAYS = "always";
        const MODE_NEVER = "never";
        const MODE_CONSECUTIVE = "consecutive";
        const mode = context.options[0] || MODE_ALWAYS;

        const options = {};

        if (typeof mode === "string") { // simple options configuration with just a string
            options.var = { uninitialized: mode, initialized: mode };
            options.let = { uninitialized: mode, initialized: mode };
            options.const = { uninitialized: mode, initialized: mode };
        } else if (typeof mode === "object") { // options configuration is an object
            options.separateRequires = !!mode.separateRequires;
            options.var = { uninitialized: mode.var, initialized: mode.var };
            options.let = { uninitialized: mode.let, initialized: mode.let };
            options.const = { uninitialized: mode.const, initialized: mode.const };
            if (Object.prototype.hasOwnProperty.call(mode, "uninitialized")) {
                options.var.uninitialized = mode.uninitialized;
                options.let.uninitialized = mode.uninitialized;
                options.const.uninitialized = mode.uninitialized;
            }
            if (Object.prototype.hasOwnProperty.call(mode, "initialized")) {
                options.var.initialized = mode.initialized;
                options.let.initialized = mode.initialized;
                options.const.initialized = mode.initialized;
            }
        }

        const sourceCode = context.sourceCode;

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

        const functionStack = [];
        const blockStack = [];

        /**
         * Increments the blockStack counter.
         * @returns {void}
         * @private
         */
        function startBlock() {
            blockStack.push({
                let: { initialized: false, uninitialized: false },
                const: { initialized: false, uninitialized: false }
            });
        }

        /**
         * Increments the functionStack counter.
         * @returns {void}
         * @private
         */
        function startFunction() {
            functionStack.push({ initialized: false, uninitialized: false });
            startBlock();
        }

        /**
         * Decrements the blockStack counter.
         * @returns {void}
         * @private
         */
        function endBlock() {
            blockStack.pop();
        }

        /**
         * Decrements the functionStack counter.
         * @returns {void}
         * @private
         */
        function endFunction() {
            functionStack.pop();
            endBlock();
        }

        /**
         * Check if a variable declaration is a require.
         * @param {ASTNode} decl variable declaration Node
         * @returns {bool} if decl is a require, return true; else return false.
         * @private
         */
        function isRequire(decl) {
            return decl.init && decl.init.type === "CallExpression" && decl.init.callee.name === "require";
        }

        /**
         * Records whether initialized/uninitialized/required variables are defined in current scope.
         * @param {string} statementType node.kind, one of: "var", "let", or "const"
         * @param {ASTNode[]} declarations List of declarations
         * @param {Object} currentScope The scope being investigated
         * @returns {void}
         * @private
         */
        function recordTypes(statementType, declarations, currentScope) {
            for (let i = 0; i < declarations.length; i++) {
                if (declarations[i].init === null) {
                    if (options[statementType] && options[statementType].uninitialized === MODE_ALWAYS) {
                        currentScope.uninitialized = true;
                    }
                } else {
                    if (options[statementType] && options[statementType].initialized === MODE_ALWAYS) {
                        if (options.separateRequires && isRequire(declarations[i])) {
                            currentScope.required = true;
                        } else {
                            currentScope.initialized = true;
                        }
                    }
                }
            }
        }

        /**
         * Determines the current scope (function or block)
         * @param {string} statementType node.kind, one of: "var", "let", or "const"
         * @returns {Object} The scope associated with statementType
         */
        function getCurrentScope(statementType) {
            let currentScope;

            if (statementType === "var") {
                currentScope = functionStack[functionStack.length - 1];
            } else if (statementType === "let") {
                currentScope = blockStack[blockStack.length - 1].let;
            } else if (statementType === "const") {
                currentScope = blockStack[blockStack.length - 1].const;
            }
            return currentScope;
        }

        /**
         * Counts the number of initialized and uninitialized declarations in a list of declarations
         * @param {ASTNode[]} declarations List of declarations
         * @returns {Object} Counts of 'uninitialized' and 'initialized' declarations
         * @private
         */
        function countDeclarations(declarations) {
            const counts = { uninitialized: 0, initialized: 0 };

            for (let i = 0; i < declarations.length; i++) {
                if (declarations[i].init === null) {
                    counts.uninitialized++;
                } else {
                    counts.initialized++;
                }
            }
            return counts;
        }

        /**
         * Determines if there is more than one var statement in the current scope.
         * @param {string} statementType node.kind, one of: "var", "let", or "const"
         * @param {ASTNode[]} declarations List of declarations
         * @returns {boolean} Returns true if it is the first var declaration, false if not.
         * @private
         */
        function hasOnlyOneStatement(statementType, declarations) {

            const declarationCounts = countDeclarations(declarations);
            const currentOptions = options[statementType] || {};
            const currentScope = getCurrentScope(statementType);
            const hasRequires = declarations.some(isRequire);

            if (currentOptions.uninitialized === MODE_ALWAYS && currentOptions.initialized === MODE_ALWAYS) {
                if (currentScope.uninitialized || currentScope.initialized) {
                    if (!hasRequires) {
                        return false;
                    }
                }
            }

            if (declarationCounts.uninitialized > 0) {
                if (currentOptions.uninitialized === MODE_ALWAYS && currentScope.uninitialized) {
                    return false;
                }
            }
            if (declarationCounts.initialized > 0) {
                if (currentOptions.initialized === MODE_ALWAYS && currentScope.initialized) {
                    if (!hasRequires) {
                        return false;
                    }
                }
            }
            if (currentScope.required && hasRequires) {
                return false;
            }
            recordTypes(statementType, declarations, currentScope);
            return true;
        }

        /**
         * Fixer to join VariableDeclaration's into a single declaration
         * @param {VariableDeclarator[]} declarations The `VariableDeclaration` to join
         * @returns {Function} The fixer function
         */
        function joinDeclarations(declarations) {
            const declaration = declarations[0];
            const body = Array.isArray(declaration.parent.parent.body) ? declaration.parent.parent.body : [];
            const currentIndex = body.findIndex(node => node.range[0] === declaration.parent.range[0]);
            const previousNode = body[currentIndex - 1];

            return fixer => {
                const type = sourceCode.getTokenBefore(declaration);
                const prevSemi = sourceCode.getTokenBefore(type);
                const res = [];

                if (previousNode && previousNode.kind === sourceCode.getText(type)) {
                    if (prevSemi.value === ";") {
                        res.push(fixer.replaceText(prevSemi, ","));
                    } else {
                        res.push(fixer.insertTextAfter(prevSemi, ","));
                    }
                    res.push(fixer.replaceText(type, ""));
                }

                return res;
            };
        }

        /**
         * Fixer to split a VariableDeclaration into individual declarations
         * @param {VariableDeclaration} declaration The `VariableDeclaration` to split
         * @returns {Function|null} The fixer function
         */
        function splitDeclarations(declaration) {
            const { parent } = declaration;

            // don't autofix code such as: if (foo) var x, y;
            if (!isInStatementList(parent.type === "ExportNamedDeclaration" ? parent : declaration)) {
                return null;
            }

            return fixer => declaration.declarations.map(declarator => {
                const tokenAfterDeclarator = sourceCode.getTokenAfter(declarator);

                if (tokenAfterDeclarator === null) {
                    return null;
                }

                const afterComma = sourceCode.getTokenAfter(tokenAfterDeclarator, { includeComments: true });

                if (tokenAfterDeclarator.value !== ",") {
                    return null;
                }

                const exportPlacement = declaration.parent.type === "ExportNamedDeclaration" ? "export " : "";

                /*
                 * `var x,y`
                 * tokenAfterDeclarator ^^ afterComma
                 */
                if (afterComma.range[0] === tokenAfterDeclarator.range[1]) {
                    return fixer.replaceText(tokenAfterDeclarator, `; ${exportPlacement}${declaration.kind} `);
                }

                /*
                 * `var x,
                 * tokenAfterDeclarator ^
                 *      y`
                 *      ^ afterComma
                 */
                if (
                    afterComma.loc.start.line > tokenAfterDeclarator.loc.end.line ||
                    afterComma.type === "Line" ||
                    afterComma.type === "Block"
                ) {
                    let lastComment = afterComma;

                    while (lastComment.type === "Line" || lastComment.type === "Block") {
                        lastComment = sourceCode.getTokenAfter(lastComment, { includeComments: true });
                    }

                    return fixer.replaceTextRange(
                        [tokenAfterDeclarator.range[0], lastComment.range[0]],
                        `;${sourceCode.text.slice(tokenAfterDeclarator.range[1], lastComment.range[0])}${exportPlacement}${declaration.kind} `
                    );
                }

                return fixer.replaceText(tokenAfterDeclarator, `; ${exportPlacement}${declaration.kind}`);
            }).filter(x => x);
        }

        /**
         * Checks a given VariableDeclaration node for errors.
         * @param {ASTNode} node The VariableDeclaration node to check
         * @returns {void}
         * @private
         */
        function checkVariableDeclaration(node) {
            const parent = node.parent;
            const type = node.kind;

            if (!options[type]) {
                return;
            }

            const declarations = node.declarations;
            const declarationCounts = countDeclarations(declarations);
            const mixedRequires = declarations.some(isRequire) && !declarations.every(isRequire);

            if (options[type].initialized === MODE_ALWAYS) {
                if (options.separateRequires && mixedRequires) {
                    context.report({
                        node,
                        messageId: "splitRequires"
                    });
                }
            }

            // consecutive
            const nodeIndex = (parent.body && parent.body.length > 0 && parent.body.indexOf(node)) || 0;

            if (nodeIndex > 0) {
                const previousNode = parent.body[nodeIndex - 1];
                const isPreviousNodeDeclaration = previousNode.type === "VariableDeclaration";
                const declarationsWithPrevious = declarations.concat(previousNode.declarations || []);

                if (
                    isPreviousNodeDeclaration &&
                    previousNode.kind === type &&
                    !(declarationsWithPrevious.some(isRequire) && !declarationsWithPrevious.every(isRequire))
                ) {
                    const previousDeclCounts = countDeclarations(previousNode.declarations);

                    if (options[type].initialized === MODE_CONSECUTIVE && options[type].uninitialized === MODE_CONSECUTIVE) {
                        context.report({
                            node,
                            messageId: "combine",
                            data: {
                                type
                            },
                            fix: joinDeclarations(declarations)
                        });
                    } else if (options[type].initialized === MODE_CONSECUTIVE && declarationCounts.initialized > 0 && previousDeclCounts.initialized > 0) {
                        context.report({
                            node,
                            messageId: "combineInitialized",
                            data: {
                                type
                            },
                            fix: joinDeclarations(declarations)
                        });
                    } else if (options[type].uninitialized === MODE_CONSECUTIVE &&
                            declarationCounts.uninitialized > 0 &&
                            previousDeclCounts.uninitialized > 0) {
                        context.report({
                            node,
                            messageId: "combineUninitialized",
                            data: {
                                type
                            },
                            fix: joinDeclarations(declarations)
                        });
                    }
                }
            }

            // always
            if (!hasOnlyOneStatement(type, declarations)) {
                if (options[type].initialized === MODE_ALWAYS && options[type].uninitialized === MODE_ALWAYS) {
                    context.report({
                        node,
                        messageId: "combine",
                        data: {
                            type
                        },
                        fix: joinDeclarations(declarations)
                    });
                } else {
                    if (options[type].initialized === MODE_ALWAYS && declarationCounts.initialized > 0) {
                        context.report({
                            node,
                            messageId: "combineInitialized",
                            data: {
                                type
                            },
                            fix: joinDeclarations(declarations)
                        });
                    }
                    if (options[type].uninitialized === MODE_ALWAYS && declarationCounts.uninitialized > 0) {
                        if (node.parent.left === node && (node.parent.type === "ForInStatement" || node.parent.type === "ForOfStatement")) {
                            return;
                        }
                        context.report({
                            node,
                            messageId: "combineUninitialized",
                            data: {
                                type
                            },
                            fix: joinDeclarations(declarations)
                        });
                    }
                }
            }

            // never
            if (parent.type !== "ForStatement" || parent.init !== node) {
                const totalDeclarations = declarationCounts.uninitialized + declarationCounts.initialized;

                if (totalDeclarations > 1) {
                    if (options[type].initialized === MODE_NEVER && options[type].uninitialized === MODE_NEVER) {

                        // both initialized and uninitialized
                        context.report({
                            node,
                            messageId: "split",
                            data: {
                                type
                            },
                            fix: splitDeclarations(node)
                        });
                    } else if (options[type].initialized === MODE_NEVER && declarationCounts.initialized > 0) {

                        // initialized
                        context.report({
                            node,
                            messageId: "splitInitialized",
                            data: {
                                type
                            },
                            fix: splitDeclarations(node)
                        });
                    } else if (options[type].uninitialized === MODE_NEVER && declarationCounts.uninitialized > 0) {

                        // uninitialized
                        context.report({
                            node,
                            messageId: "splitUninitialized",
                            data: {
                                type
                            },
                            fix: splitDeclarations(node)
                        });
                    }
                }
            }
        }

        //--------------------------------------------------------------------------
        // Public API
        //--------------------------------------------------------------------------

        return {
            Program: startFunction,
            FunctionDeclaration: startFunction,
            FunctionExpression: startFunction,
            ArrowFunctionExpression: startFunction,
            StaticBlock: startFunction, // StaticBlock creates a new scope for `var` variables

            BlockStatement: startBlock,
            ForStatement: startBlock,
            ForInStatement: startBlock,
            ForOfStatement: startBlock,
            SwitchStatement: startBlock,
            VariableDeclaration: checkVariableDeclaration,
            "ForStatement:exit": endBlock,
            "ForOfStatement:exit": endBlock,
            "ForInStatement:exit": endBlock,
            "SwitchStatement:exit": endBlock,
            "BlockStatement:exit": endBlock,

            "Program:exit": endFunction,
            "FunctionDeclaration:exit": endFunction,
            "FunctionExpression:exit": endFunction,
            "ArrowFunctionExpression:exit": endFunction,
            "StaticBlock:exit": endFunction
        };

    }
};


code:
/**
 * @fileoverview Rule to replace assignment expressions with operator assignment
 * @author Brandon Mills
 */
"use strict";

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

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

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

/**
 * Checks whether an operator is commutative and has an operator assignment
 * shorthand form.
 * @param {string} operator Operator to check.
 * @returns {boolean} True if the operator is commutative and has a
 *     shorthand form.
 */
function isCommutativeOperatorWithShorthand(operator) {
    return ["*", "&", "^", "|"].includes(operator);
}

/**
 * Checks whether an operator is not commutative and has an operator assignment
 * shorthand form.
 * @param {string} operator Operator to check.
 * @returns {boolean} True if the operator is not commutative and has
 *     a shorthand form.
 */
function isNonCommutativeOperatorWithShorthand(operator) {
    return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].includes(operator);
}

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

/**
 * Determines if the left side of a node can be safely fixed (i.e. if it activates the same getters/setters and)
 * toString calls regardless of whether assignment shorthand is used)
 * @param {ASTNode} node The node on the left side of the expression
 * @returns {boolean} `true` if the node can be fixed
 */
function canBeFixed(node) {
    return (
        node.type === "Identifier" ||
        (
            node.type === "MemberExpression" &&
            (node.object.type === "Identifier" || node.object.type === "ThisExpression") &&
            (!node.computed || node.property.type === "Literal")
        )
    );
}

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

        docs: {
            description: "Require or disallow assignment operator shorthand where possible",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/operator-assignment"
        },

        schema: [
            {
                enum: ["always", "never"]
            }
        ],

        fixable: "code",
        messages: {
            replaced: "Assignment (=) can be replaced with operator assignment ({{operator}}).",
            unexpected: "Unexpected operator assignment ({{operator}}) shorthand."
        }
    },

    create(context) {

        const sourceCode = context.sourceCode;

        /**
         * Returns the operator token of an AssignmentExpression or BinaryExpression
         * @param {ASTNode} node An AssignmentExpression or BinaryExpression node
         * @returns {Token} The operator token in the node
         */
        function getOperatorToken(node) {
            return sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
        }

        /**
         * Ensures that an assignment uses the shorthand form where possible.
         * @param {ASTNode} node An AssignmentExpression node.
         * @returns {void}
         */
        function verify(node) {
            if (node.operator !== "=" || node.right.type !== "BinaryExpression") {
                return;
            }

            const left = node.left;
            const expr = node.right;
            const operator = expr.operator;

            if (isCommutativeOperatorWithShorthand(operator) || isNonCommutativeOperatorWithShorthand(operator)) {
                const replacementOperator = `${operator}=`;

                if (astUtils.isSameReference(left, expr.left, true)) {
                    context.report({
                        node,
                        messageId: "replaced",
                        data: { operator: replacementOperator },
                        fix(fixer) {
                            if (canBeFixed(left) && canBeFixed(expr.left)) {
                                const equalsToken = getOperatorToken(node);
                                const operatorToken = getOperatorToken(expr);
                                const leftText = sourceCode.getText().slice(node.range[0], equalsToken.range[0]);
                                const rightText = sourceCode.getText().slice(operatorToken.range[1], node.right.range[1]);

                                // Check for comments that would be removed.
                                if (sourceCode.commentsExistBetween(equalsToken, operatorToken)) {
                                    return null;
                                }

                                return fixer.replaceText(node, `${leftText}${replacementOperator}${rightText}`);
                            }
                            return null;
                        }
                    });
                } else if (astUtils.isSameReference(left, expr.right, true) && isCommutativeOperatorWithShorthand(operator)) {

                    /*
                     * This case can't be fixed safely.
                     * If `a` and `b` both have custom valueOf() behavior, then fixing `a = b * a` to `a *= b` would
                     * change the execution order of the valueOf() functions.
                     */
                    context.report({
                        node,
                        messageId: "replaced",
                        data: { operator: replacementOperator }
                    });
                }
            }
        }

        /**
         * Warns if an assignment expression uses operator assignment shorthand.
         * @param {ASTNode} node An AssignmentExpression node.
         * @returns {void}
         */
        function prohibit(node) {
            if (node.operator !== "=" && !astUtils.isLogicalAssignmentOperator(node.operator)) {
                context.report({
                    node,
                    messageId: "unexpected",
                    data: { operator: node.operator },
                    fix(fixer) {
                        if (canBeFixed(node.left)) {
                            const firstToken = sourceCode.getFirstToken(node);
                            const operatorToken = getOperatorToken(node);
                            const leftText = sourceCode.getText().slice(node.range[0], operatorToken.range[0]);
                            const newOperator = node.operator.slice(0, -1);
                            let rightText;

                            // Check for comments that would be duplicated.
                            if (sourceCode.commentsExistBetween(firstToken, operatorToken)) {
                                return null;
                            }

                            // If this change would modify precedence (e.g. `foo *= bar + 1` => `foo = foo * (bar + 1)`), parenthesize the right side.
                            if (
                                astUtils.getPrecedence(node.right) <= astUtils.getPrecedence({ type: "BinaryExpression", operator: newOperator }) &&
                                !astUtils.isParenthesised(sourceCode, node.right)
                            ) {
                                rightText = `${sourceCode.text.slice(operatorToken.range[1], node.right.range[0])}(${sourceCode.getText(node.right)})`;
                            } else {
                                const tokenAfterOperator = sourceCode.getTokenAfter(operatorToken, { includeComments: true });
                                let rightTextPrefix = "";

                                if (
                                    operatorToken.range[1] === tokenAfterOperator.range[0] &&
                                    !astUtils.canTokensBeAdjacent({ type: "Punctuator", value: newOperator }, tokenAfterOperator)
                                ) {
                                    rightTextPrefix = " "; // foo+=+bar -> foo= foo+ +bar
                                }

                                rightText = `${rightTextPrefix}${sourceCode.text.slice(operatorToken.range[1], node.range[1])}`;
                            }

                            return fixer.replaceText(node, `${leftText}= ${leftText}${newOperator}${rightText}`);
                        }
                        return null;
                    }
                });
            }
        }

        return {
            AssignmentExpression: context.options[0] !== "never" ? verify : prohibit
        };

    }
};

license: mit


code:
/**
 * @fileoverview Operator linebreak - enforces operator linebreak style of two types: after and before
 * @author Benoît Zugmeyer
 * @deprecated in ESLint v8.53.0
 */

"use strict";

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

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

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

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        deprecated: true,
        replacedBy: [],
        type: "layout",

        docs: {
            description: "Enforce consistent linebreak style for operators",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/operator-linebreak"
        },

        schema: [
            {
                enum: ["after", "before", "none", null]
            },
            {
                type: "object",
                properties: {
                    overrides: {
                        type: "object",
                        additionalProperties: {
                            enum: ["after", "before", "none", "ignore"]
                        }
                    }
                },
                additionalProperties: false
            }
        ],

        fixable: "code",

        messages: {
            operatorAtBeginning: "'{{operator}}' should be placed at the beginning of the line.",
            operatorAtEnd: "'{{operator}}' should be placed at the end of the line.",
            badLinebreak: "Bad line breaking before and after '{{operator}}'.",
            noLinebreak: "There should be no line break before or after '{{operator}}'."
        }
    },

    create(context) {

        const usedDefaultGlobal = !context.options[0];
        const globalStyle = context.options[0] || "after";
        const options = context.options[1] || {};
        const styleOverrides = options.overrides ? Object.assign({}, options.overrides) : {};

        if (usedDefaultGlobal && !styleOverrides["?"]) {
            styleOverrides["?"] = "before";
        }

        if (usedDefaultGlobal && !styleOverrides[":"]) {
            styleOverrides[":"] = "before";
        }

        const sourceCode = context.sourceCode;

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

        /**
         * Gets a fixer function to fix rule issues
         * @param {Token} operatorToken The operator token of an expression
         * @param {string} desiredStyle The style for the rule. One of 'before', 'after', 'none'
         * @returns {Function} A fixer function
         */
        function getFixer(operatorToken, desiredStyle) {
            return fixer => {
                const tokenBefore = sourceCode.getTokenBefore(operatorToken);
                const tokenAfter = sourceCode.getTokenAfter(operatorToken);
                const textBefore = sourceCode.text.slice(tokenBefore.range[1], operatorToken.range[0]);
                const textAfter = sourceCode.text.slice(operatorToken.range[1], tokenAfter.range[0]);
                const hasLinebreakBefore = !astUtils.isTokenOnSameLine(tokenBefore, operatorToken);
                const hasLinebreakAfter = !astUtils.isTokenOnSameLine(operatorToken, tokenAfter);
                let newTextBefore, newTextAfter;

                if (hasLinebreakBefore !== hasLinebreakAfter && desiredStyle !== "none") {

                    // If there is a comment before and after the operator, don't do a fix.
                    if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore &&
                        sourceCode.getTokenAfter(operatorToken, { includeComments: true }) !== tokenAfter) {

                        return null;
                    }

                    /*
                     * If there is only one linebreak and it's on the wrong side of the operator, swap the text before and after the operator.
                     * foo &&
                     *           bar
                     * would get fixed to
                     * foo
                     *        && bar
                     */
                    newTextBefore = textAfter;
                    newTextAfter = textBefore;
                } else {
                    const LINEBREAK_REGEX = astUtils.createGlobalLinebreakMatcher();

                    // Otherwise, if no linebreak is desired and no comments interfere, replace the linebreaks with empty strings.
                    newTextBefore = desiredStyle === "before" || textBefore.trim() ? textBefore : textBefore.replace(LINEBREAK_REGEX, "");
                    newTextAfter = desiredStyle === "after" || textAfter.trim() ? textAfter : textAfter.replace(LINEBREAK_REGEX, "");

                    // If there was no change (due to interfering comments), don't output a fix.
                    if (newTextBefore === textBefore && newTextAfter === textAfter) {
                        return null;
                    }
                }

                if (newTextAfter === "" && tokenAfter.type === "Punctuator" && "+-".includes(operatorToken.value) && tokenAfter.value === operatorToken.value) {

                    // To avoid accidentally creating a ++ or -- operator, insert a space if the operator is a +/- and the following token is a unary +/-.
                    newTextAfter += " ";
                }

                return fixer.replaceTextRange([tokenBefore.range[1], tokenAfter.range[0]], newTextBefore + operatorToken.value + newTextAfter);
            };
        }

        /**
         * Checks the operator placement
         * @param {ASTNode} node The node to check
         * @param {ASTNode} rightSide The node that comes after the operator in `node`
         * @param {string} operator The operator
         * @private
         * @returns {void}
         */
        function validateNode(node, rightSide, operator) {

            /*
             * Find the operator token by searching from the right side, because between the left side and the operator
             * there could be additional tokens from type annotations. Search specifically for the token which
             * value equals the operator, in order to skip possible opening parentheses before the right side node.
             */
            const operatorToken = sourceCode.getTokenBefore(rightSide, token => token.value === operator);
            const leftToken = sourceCode.getTokenBefore(operatorToken);
            const rightToken = sourceCode.getTokenAfter(operatorToken);
            const operatorStyleOverride = styleOverrides[operator];
            const style = operatorStyleOverride || globalStyle;
            const fix = getFixer(operatorToken, style);

            // if single line
            if (astUtils.isTokenOnSameLine(leftToken, operatorToken) &&
                    astUtils.isTokenOnSameLine(operatorToken, rightToken)) {

                // do nothing.

            } else if (operatorStyleOverride !== "ignore" && !astUtils.isTokenOnSameLine(leftToken, operatorToken) &&
                    !astUtils.isTokenOnSameLine(operatorToken, rightToken)) {

                // lone operator
                context.report({
                    node,
                    loc: operatorToken.loc,
                    messageId: "badLinebreak",
                    data: {
                        operator
                    },
                    fix
                });

            } else if (style === "before" && astUtils.isTokenOnSameLine(leftToken, operatorToken)) {

                context.report({
                    node,
                    loc: operatorToken.loc,
                    messageId: "operatorAtBeginning",
                    data: {
                        operator
                    },
                    fix
                });

            } else if (style === "after" && astUtils.isTokenOnSameLine(operatorToken, rightToken)) {

                context.report({
                    node,
                    loc: operatorToken.loc,
                    messageId: "operatorAtEnd",
                    data: {
                        operator
                    },
                    fix
                });

            } else if (style === "none") {

                context.report({
                    node,
                    loc: operatorToken.loc,
                    messageId: "noLinebreak",
                    data: {
                        operator
                    },
                    fix
                });

            }
        }

        /**
         * Validates a binary expression using `validateNode`
         * @param {BinaryExpression|LogicalExpression|AssignmentExpression} node node to be validated
         * @returns {void}
         */
        function validateBinaryExpression(node) {
            validateNode(node, node.right, node.operator);
        }

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

        return {
            BinaryExpression: validateBinaryExpression,
            LogicalExpression: validateBinaryExpression,
            AssignmentExpression: validateBinaryExpression,
            VariableDeclarator(node) {
                if (node.init) {
                    validateNode(node, node.init, "=");
                }
            },
            PropertyDefinition(node) {
                if (node.value) {
                    validateNode(node, node.value, "=");
                }
            },
            ConditionalExpression(node) {
                validateNode(node, node.consequent, "?");
                validateNode(node, node.alternate, ":");
            }
        };
    }
};


code:
/**
 * @fileoverview A rule to ensure blank lines within blocks.
 * @author Mathias Schreck <https://github.com/lo1tuma>
 * @deprecated in ESLint v8.53.0
 */

"use strict";

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

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

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

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        deprecated: true,
        replacedBy: [],
        type: "layout",

        docs: {
            description: "Require or disallow padding within blocks",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/padded-blocks"
        },

        fixable: "whitespace",

        schema: [
            {
                oneOf: [
                    {
                        enum: ["always", "never"]
                    },
                    {
                        type: "object",
                        properties: {
                            blocks: {
                                enum: ["always", "never"]
                            },
                            switches: {
                                enum: ["always", "never"]
                            },
                            classes: {
                                enum: ["always", "never"]
                            }
                        },
                        additionalProperties: false,
                        minProperties: 1
                    }
                ]
            },
            {
                type: "object",
                properties: {
                    allowSingleLineBlocks: {
                        type: "boolean"
                    }
                },
                additionalProperties: false
            }
        ],

        messages: {
            alwaysPadBlock: "Block must be padded by blank lines.",
            neverPadBlock: "Block must not be padded by blank lines."
        }
    },

    create(context) {
        const options = {};
        const typeOptions = context.options[0] || "always";
        const exceptOptions = context.options[1] || {};

        if (typeof typeOptions === "string") {
            const shouldHavePadding = typeOptions === "always";

            options.blocks = shouldHavePadding;
            options.switches = shouldHavePadding;
            options.classes = shouldHavePadding;
        } else {
            if (Object.prototype.hasOwnProperty.call(typeOptions, "blocks")) {
                options.blocks = typeOptions.blocks === "always";
            }
            if (Object.prototype.hasOwnProperty.call(typeOptions, "switches")) {
                options.switches = typeOptions.switches === "always";
            }
            if (Object.prototype.hasOwnProperty.call(typeOptions, "classes")) {
                options.classes = typeOptions.classes === "always";
            }
        }

        if (Object.prototype.hasOwnProperty.call(exceptOptions, "allowSingleLineBlocks")) {
            options.allowSingleLineBlocks = exceptOptions.allowSingleLineBlocks === true;
        }

        const sourceCode = context.sourceCode;

        /**
         * Gets the open brace token from a given node.
         * @param {ASTNode} node A BlockStatement or SwitchStatement node from which to get the open brace.
         * @returns {Token} The token of the open brace.
         */
        function getOpenBrace(node) {
            if (node.type === "SwitchStatement") {
                return sourceCode.getTokenBefore(node.cases[0]);
            }

            if (node.type === "StaticBlock") {
                return sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
            }

            // `BlockStatement` or `ClassBody`
            return sourceCode.getFirstToken(node);
        }

        /**
         * Checks if the given parameter is a comment node
         * @param {ASTNode|Token} node An AST node or token
         * @returns {boolean} True if node is a comment
         */
        function isComment(node) {
            return node.type === "Line" || node.type === "Block";
        }

        /**
         * Checks if there is padding between two tokens
         * @param {Token} first The first token
         * @param {Token} second The second token
         * @returns {boolean} True if there is at least a line between the tokens
         */
        function isPaddingBetweenTokens(first, second) {
            return second.loc.start.line - first.loc.end.line >= 2;
        }

        /**
         * Checks if the given token has a blank line after it.
         * @param {Token} token The token to check.
         * @returns {boolean} Whether or not the token is followed by a blank line.
         */
        function getFirstBlockToken(token) {
            let prev,
                first = token;

            do {
                prev = first;
                first = sourceCode.getTokenAfter(first, { includeComments: true });
            } while (isComment(first) && first.loc.start.line === prev.loc.end.line);

            return first;
        }

        /**
         * Checks if the given token is preceded by a blank line.
         * @param {Token} token The token to check
         * @returns {boolean} Whether or not the token is preceded by a blank line
         */
        function getLastBlockToken(token) {
            let last = token,
                next;

            do {
                next = last;
                last = sourceCode.getTokenBefore(last, { includeComments: true });
            } while (isComment(last) && last.loc.end.line === next.loc.start.line);

            return last;
        }

        /**
         * Checks if a node should be padded, according to the rule config.
         * @param {ASTNode} node The AST node to check.
         * @throws {Error} (Unreachable)
         * @returns {boolean} True if the node should be padded, false otherwise.
         */
        function requirePaddingFor(node) {
            switch (node.type) {
                case "BlockStatement":
                case "StaticBlock":
                    return options.blocks;
                case "SwitchStatement":
                    return options.switches;
                case "ClassBody":
                    return options.classes;

                /* c8 ignore next */
                default:
                    throw new Error("unreachable");
            }
        }

        /**
         * Checks the given BlockStatement node to be padded if the block is not empty.
         * @param {ASTNode} node The AST node of a BlockStatement.
         * @returns {void} undefined.
         */
        function checkPadding(node) {
            const openBrace = getOpenBrace(node),
                firstBlockToken = getFirstBlockToken(openBrace),
                tokenBeforeFirst = sourceCode.getTokenBefore(firstBlockToken, { includeComments: true }),
                closeBrace = sourceCode.getLastToken(node),
                lastBlockToken = getLastBlockToken(closeBrace),
                tokenAfterLast = sourceCode.getTokenAfter(lastBlockToken, { includeComments: true }),
                blockHasTopPadding = isPaddingBetweenTokens(tokenBeforeFirst, firstBlockToken),
                blockHasBottomPadding = isPaddingBetweenTokens(lastBlockToken, tokenAfterLast);

            if (options.allowSingleLineBlocks && astUtils.isTokenOnSameLine(tokenBeforeFirst, tokenAfterLast)) {
                return;
            }

            if (requirePaddingFor(node)) {

                if (!blockHasTopPadding) {
                    context.report({
                        node,
                        loc: {
                            start: tokenBeforeFirst.loc.start,
                            end: firstBlockToken.loc.start
                        },
                        fix(fixer) {
                            return fixer.insertTextAfter(tokenBeforeFirst, "\n");
                        },
                        messageId: "alwaysPadBlock"
                    });
                }
                if (!blockHasBottomPadding) {
                    context.report({
                        node,
                        loc: {
                            end: tokenAfterLast.loc.start,
                            start: lastBlockToken.loc.end
                        },
                        fix(fixer) {
                            return fixer.insertTextBefore(tokenAfterLast, "\n");
                        },
                        messageId: "alwaysPadBlock"
                    });
                }
            } else {
                if (blockHasTopPadding) {

                    context.report({
                        node,
                        loc: {
                            start: tokenBeforeFirst.loc.start,
                            end: firstBlockToken.loc.start
                        },
                        fix(fixer) {
                            return fixer.replaceTextRange([tokenBeforeFirst.range[1], firstBlockToken.range[0] - firstBlockToken.loc.start.column], "\n");
                        },
                        messageId: "neverPadBlock"
                    });
                }

                if (blockHasBottomPadding) {

                    context.report({
                        node,
                        loc: {
                            end: tokenAfterLast.loc.start,
                            start: lastBlockToken.loc.end
                        },
                        messageId: "neverPadBlock",
                        fix(fixer) {
                            return fixer.replaceTextRange([lastBlockToken.range[1], tokenAfterLast.range[0] - tokenAfterLast.loc.start.column], "\n");
                        }
                    });
                }
            }
        }

        const rule = {};

        if (Object.prototype.hasOwnProperty.call(options, "switches")) {
            rule.SwitchStatement = function(node) {
                if (node.cases.length === 0) {
                    return;
                }
                checkPadding(node);
            };
        }

        if (Object.prototype.hasOwnProperty.call(options, "blocks")) {
            rule.BlockStatement = function(node) {
                if (node.body.length === 0) {
                    return;
                }
                checkPadding(node);
            };
            rule.StaticBlock = rule.BlockStatement;
        }

        if (Object.prototype.hasOwnProperty.call(options, "classes")) {
            rule.ClassBody = function(node) {
                if (node.body.length === 0) {
                    return;
                }
                checkPadding(node);
            };
        }

        return rule;
    }
};


code:
/**
 * @fileoverview Rule to require or disallow newlines between statements
 * @author Toru Nagashima
 * @deprecated in ESLint v8.53.0
 */

"use strict";

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

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

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

const LT = `[${Array.from(astUtils.LINEBREAKS).join("")}]`;
const PADDING_LINE_SEQUENCE = new RegExp(
    String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`,
    "u"
);
const CJS_EXPORT = /^(?:module\s*\.\s*)?exports(?:\s*\.|\s*\[|$)/u;
const CJS_IMPORT = /^require\(/u;

/**
 * Creates tester which check if a node starts with specific keyword.
 * @param {string} keyword The keyword to test.
 * @returns {Object} the created tester.
 * @private
 */
function newKeywordTester(keyword) {
    return {
        test: (node, sourceCode) =>
            sourceCode.getFirstToken(node).value === keyword
    };
}

/**
 * Creates tester which check if a node starts with specific keyword and spans a single line.
 * @param {string} keyword The keyword to test.
 * @returns {Object} the created tester.
 * @private
 */
function newSinglelineKeywordTester(keyword) {
    return {
        test: (node, sourceCode) =>
            node.loc.start.line === node.loc.end.line &&
            sourceCode.getFirstToken(node).value === keyword
    };
}

/**
 * Creates tester which check if a node starts with specific keyword and spans multiple lines.
 * @param {string} keyword The keyword to test.
 * @returns {Object} the created tester.
 * @private
 */
function newMultilineKeywordTester(keyword) {
    return {
        test: (node, sourceCode) =>
            node.loc.start.line !== node.loc.end.line &&
            sourceCode.getFirstToken(node).value === keyword
    };
}

/**
 * Creates tester which check if a node is specific type.
 * @param {string} type The node type to test.
 * @returns {Object} the created tester.
 * @private
 */
function newNodeTypeTester(type) {
    return {
        test: node =>
            node.type === type
    };
}

/**
 * Checks the given node is an expression statement of IIFE.
 * @param {ASTNode} node The node to check.
 * @returns {boolean} `true` if the node is an expression statement of IIFE.
 * @private
 */
function isIIFEStatement(node) {
    if (node.type === "ExpressionStatement") {
        let call = astUtils.skipChainExpression(node.expression);

        if (call.type === "UnaryExpression") {
            call = astUtils.skipChainExpression(call.argument);
        }
        return call.type === "CallExpression" && astUtils.isFunction(call.callee);
    }
    return false;
}

/**
 * Checks whether the given node is a block-like statement.
 * This checks the last token of the node is the closing brace of a block.
 * @param {SourceCode} sourceCode The source code to get tokens.
 * @param {ASTNode} node The node to check.
 * @returns {boolean} `true` if the node is a block-like statement.
 * @private
 */
function isBlockLikeStatement(sourceCode, node) {

    // do-while with a block is a block-like statement.
    if (node.type === "DoWhileStatement" && node.body.type === "BlockStatement") {
        return true;
    }

    /*
     * IIFE is a block-like statement specially from
     * JSCS#disallowPaddingNewLinesAfterBlocks.
     */
    if (isIIFEStatement(node)) {
        return true;
    }

    // Checks the last token is a closing brace of blocks.
    const lastToken = sourceCode.getLastToken(node, astUtils.isNotSemicolonToken);
    const belongingNode = lastToken && astUtils.isClosingBraceToken(lastToken)
        ? sourceCode.getNodeByRangeIndex(lastToken.range[0])
        : null;

    return Boolean(belongingNode) && (
        belongingNode.type === "BlockStatement" ||
        belongingNode.type === "SwitchStatement"
    );
}

/**
 * Gets the actual last token.
 *
 * If a semicolon is semicolon-less style's semicolon, this ignores it.
 * For example:
 *
 *     foo()
 *     ;[1, 2, 3].forEach(bar)
 * @param {SourceCode} sourceCode The source code to get tokens.
 * @param {ASTNode} node The node to get.
 * @returns {Token} The actual last token.
 * @private
 */
function getActualLastToken(sourceCode, node) {
    const semiToken = sourceCode.getLastToken(node);
    const prevToken = sourceCode.getTokenBefore(semiToken);
    const nextToken = sourceCode.getTokenAfter(semiToken);
    const isSemicolonLessStyle = Boolean(
        prevToken &&
        nextToken &&
        prevToken.range[0] >= node.range[0] &&
        astUtils.isSemicolonToken(semiToken) &&
        semiToken.loc.start.line !== prevToken.loc.end.line &&
        semiToken.loc.end.line === nextToken.loc.start.line
    );

    return isSemicolonLessStyle ? prevToken : semiToken;
}

/**
 * This returns the concatenation of the first 2 captured strings.
 * @param {string} _ Unused. Whole matched string.
 * @param {string} trailingSpaces The trailing spaces of the first line.
 * @param {string} indentSpaces The indentation spaces of the last line.
 * @returns {string} The concatenation of trailingSpaces and indentSpaces.
 * @private
 */
function replacerToRemovePaddingLines(_, trailingSpaces, indentSpaces) {
    return trailingSpaces + indentSpaces;
}

/**
 * Check and report statements for `any` configuration.
 * It does nothing.
 * @returns {void}
 * @private
 */
function verifyForAny() {
}

/**
 * Check and report statements for `never` configuration.
 * This autofix removes blank lines between the given 2 statements.
 * However, if comments exist between 2 blank lines, it does not remove those
 * blank lines automatically.
 * @param {RuleContext} context The rule context to report.
 * @param {ASTNode} _ Unused. The previous node to check.
 * @param {ASTNode} nextNode The next node to check.
 * @param {Array<Token[]>} paddingLines The array of token pairs that blank
 * lines exist between the pair.
 * @returns {void}
 * @private
 */
function verifyForNever(context, _, nextNode, paddingLines) {
    if (paddingLines.length === 0) {
        return;
    }

    context.report({
        node: nextNode,
        messageId: "unexpectedBlankLine",
        fix(fixer) {
            if (paddingLines.length >= 2) {
                return null;
            }

            const prevToken = paddingLines[0][0];
            const nextToken = paddingLines[0][1];
            const start = prevToken.range[1];
            const end = nextToken.range[0];
            const text = context.sourceCode.text
                .slice(start, end)
                .replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines);

            return fixer.replaceTextRange([start, end], text);
        }
    });
}

/**
 * Check and report statements for `always` configuration.
 * This autofix inserts a blank line between the given 2 statements.
 * If the `prevNode` has trailing comments, it inserts a blank line after the
 * trailing comments.
 * @param {RuleContext} context The rule context to report.
 * @param {ASTNode} prevNode The previous node to check.
 * @param {ASTNode} nextNode The next node to check.
 * @param {Array<Token[]>} paddingLines The array of token pairs that blank
 * lines exist between the pair.
 * @returns {void}
 * @private
 */
function verifyForAlways(context, prevNode, nextNode, paddingLines) {
    if (paddingLines.length > 0) {
        return;
    }

    context.report({
        node: nextNode,
        messageId: "expectedBlankLine",
        fix(fixer) {
            const sourceCode = context.sourceCode;
            let prevToken = getActualLastToken(sourceCode, prevNode);
            const nextToken = sourceCode.getFirstTokenBetween(
                prevToken,
                nextNode,
                {
                    includeComments: true,

                    /**
                     * Skip the trailing comments of the previous node.
                     * This inserts a blank line after the last trailing comment.
                     *
                     * For example:
                     *
                     *     foo(); // trailing comment.
                     *     // comment.
                     *     bar();
                     *
                     * Get fixed to:
                     *
                     *     foo(); // trailing comment.
                     *
                     *     // comment.
                     *     bar();
                     * @param {Token} token The token to check.
                     * @returns {boolean} `true` if the token is not a trailing comment.
                     * @private
                     */
                    filter(token) {
                        if (astUtils.isTokenOnSameLine(prevToken, token)) {
                            prevToken = token;
                            return false;
                        }
                        return true;
                    }
                }
            ) || nextNode;
            const insertText = astUtils.isTokenOnSameLine(prevToken, nextToken)
                ? "\n\n"
                : "\n";

            return fixer.insertTextAfter(prevToken, insertText);
        }
    });
}

/**
 * Types of blank lines.
 * `any`, `never`, and `always` are defined.
 * Those have `verify` method to check and report statements.
 * @private
 */
const PaddingTypes = {
    any: { verify: verifyForAny },
    never: { verify: verifyForNever },
    always: { verify: verifyForAlways }
};

/**
 * Types of statements.
 * Those have `test` method to check it matches to the given statement.
 * @private
 */
const StatementTypes = {
    "*": { test: () => true },
    "block-like": {
        test: (node, sourceCode) => isBlockLikeStatement(sourceCode, node)
    },
    "cjs-export": {
        test: (node, sourceCode) =>
            node.type === "ExpressionStatement" &&
            node.expression.type === "AssignmentExpression" &&
            CJS_EXPORT.test(sourceCode.getText(node.expression.left))
    },
    "cjs-import": {
        test: (node, sourceCode) =>
            node.type === "VariableDeclaration" &&
            node.declarations.length > 0 &&
            Boolean(node.declarations[0].init) &&
            CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init))
    },
    directive: {
        test: astUtils.isDirective
    },
    expression: {
        test: node => node.type === "ExpressionStatement" && !astUtils.isDirective(node)
    },
    iife: {
        test: isIIFEStatement
    },
    "multiline-block-like": {
        test: (node, sourceCode) =>
            node.loc.start.line !== node.loc.end.line &&
            isBlockLikeStatement(sourceCode, node)
    },
    "multiline-expression": {
        test: node =>
            node.loc.start.line !== node.loc.end.line &&
            node.type === "ExpressionStatement" &&
            !astUtils.isDirective(node)
    },

    "multiline-const": newMultilineKeywordTester("const"),
    "multiline-let": newMultilineKeywordTester("let"),
    "multiline-var": newMultilineKeywordTester("var"),
    "singleline-const": newSinglelineKeywordTester("const"),
    "singleline-let": newSinglelineKeywordTester("let"),
    "singleline-var": newSinglelineKeywordTester("var"),

    block: newNodeTypeTester("BlockStatement"),
    empty: newNodeTypeTester("EmptyStatement"),
    function: newNodeTypeTester("FunctionDeclaration"),

    break: newKeywordTester("break"),
    case: newKeywordTester("case"),
    class: newKeywordTester("class"),
    const: newKeywordTester("const"),
    continue: newKeywordTester("continue"),
    debugger: newKeywordTester("debugger"),
    default: newKeywordTester("default"),
    do: newKeywordTester("do"),
    export: newKeywordTester("export"),
    for: newKeywordTester("for"),
    if: newKeywordTester("if"),
    import: newKeywordTester("import"),
    let: newKeywordTester("let"),
    return: newKeywordTester("return"),
    switch: newKeywordTester("switch"),
    throw: newKeywordTester("throw"),
    try: newKeywordTester("try"),
    var: newKeywordTester("var"),
    while: newKeywordTester("while"),
    with: newKeywordTester("with")
};

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

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        deprecated: true,
        replacedBy: [],
        type: "layout",

        docs: {
            description: "Require or disallow padding lines between statements",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/padding-line-between-statements"
        },

        fixable: "whitespace",

        schema: {
            definitions: {
                paddingType: {
                    enum: Object.keys(PaddingTypes)
                },
                statementType: {
                    anyOf: [
                        { enum: Object.keys(StatementTypes) },
                        {
                            type: "array",
                            items: { enum: Object.keys(StatementTypes) },
                            minItems: 1,
                            uniqueItems: true
                        }
                    ]
                }
            },
            type: "array",
            items: {
                type: "object",
                properties: {
                    blankLine: { $ref: "#/definitions/paddingType" },
                    prev: { $ref: "#/definitions/statementType" },
                    next: { $ref: "#/definitions/statementType" }
                },
                additionalProperties: false,
                required: ["blankLine", "prev", "next"]
            }
        },

        messages: {
            unexpectedBlankLine: "Unexpected blank line before this statement.",
            expectedBlankLine: "Expected blank line before this statement."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;
        const configureList = context.options || [];
        let scopeInfo = null;

        /**
         * Processes to enter to new scope.
         * This manages the current previous statement.
         * @returns {void}
         * @private
         */
        function enterScope() {
            scopeInfo = {
                upper: scopeInfo,
                prevNode: null
            };
        }

        /**
         * Processes to exit from the current scope.
         * @returns {void}
         * @private
         */
        function exitScope() {
            scopeInfo = scopeInfo.upper;
        }

        /**
         * Checks whether the given node matches the given type.
         * @param {ASTNode} node The statement node to check.
         * @param {string|string[]} type The statement type to check.
         * @returns {boolean} `true` if the statement node matched the type.
         * @private
         */
        function match(node, type) {
            let innerStatementNode = node;

            while (innerStatementNode.type === "LabeledStatement") {
                innerStatementNode = innerStatementNode.body;
            }
            if (Array.isArray(type)) {
                return type.some(match.bind(null, innerStatementNode));
            }
            return StatementTypes[type].test(innerStatementNode, sourceCode);
        }

        /**
         * Finds the last matched configure from configureList.
         * @param {ASTNode} prevNode The previous statement to match.
         * @param {ASTNode} nextNode The current statement to match.
         * @returns {Object} The tester of the last matched configure.
         * @private
         */
        function getPaddingType(prevNode, nextNode) {
            for (let i = configureList.length - 1; i >= 0; --i) {
                const configure = configureList[i];
                const matched =
                    match(prevNode, configure.prev) &&
                    match(nextNode, configure.next);

                if (matched) {
                    return PaddingTypes[configure.blankLine];
                }
            }
            return PaddingTypes.any;
        }

        /**
         * Gets padding line sequences between the given 2 statements.
         * Comments are separators of the padding line sequences.
         * @param {ASTNode} prevNode The previous statement to count.
         * @param {ASTNode} nextNode The current statement to count.
         * @returns {Array<Token[]>} The array of token pairs.
         * @private
         */
        function getPaddingLineSequences(prevNode, nextNode) {
            const pairs = [];
            let prevToken = getActualLastToken(sourceCode, prevNode);

            if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) {
                do {
                    const token = sourceCode.getTokenAfter(
                        prevToken,
                        { includeComments: true }
                    );

                    if (token.loc.start.line - prevToken.loc.end.line >= 2) {
                        pairs.push([prevToken, token]);
                    }
                    prevToken = token;

                } while (prevToken.range[0] < nextNode.range[0]);
            }

            return pairs;
        }

        /**
         * Verify padding lines between the given node and the previous node.
         * @param {ASTNode} node The node to verify.
         * @returns {void}
         * @private
         */
        function verify(node) {
            const parentType = node.parent.type;
            const validParent =
                astUtils.STATEMENT_LIST_PARENTS.has(parentType) ||
                parentType === "SwitchStatement";

            if (!validParent) {
                return;
            }

            // Save this node as the current previous statement.
            const prevNode = scopeInfo.prevNode;

            // Verify.
            if (prevNode) {
                const type = getPaddingType(prevNode, node);
                const paddingLines = getPaddingLineSequences(prevNode, node);

                type.verify(context, prevNode, node, paddingLines);
            }

            scopeInfo.prevNode = node;
        }

        /**
         * Verify padding lines between the given node and the previous node.
         * Then process to enter to new scope.
         * @param {ASTNode} node The node to verify.
         * @returns {void}
         * @private
         */
        function verifyThenEnterScope(node) {
            verify(node);
            enterScope();
        }

        return {
            Program: enterScope,
            BlockStatement: enterScope,
            SwitchStatement: enterScope,
            StaticBlock: enterScope,
            "Program:exit": exitScope,
            "BlockStatement:exit": exitScope,
            "SwitchStatement:exit": exitScope,
            "StaticBlock:exit": exitScope,

            ":statement": verify,

            SwitchCase: verifyThenEnterScope,
            "SwitchCase:exit": exitScope
        };
    }
};


code:
/**
 * @fileoverview A rule to suggest using arrow functions as callbacks.
 * @author Toru Nagashima
 */

"use strict";

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

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

/**
 * Checks whether or not a given variable is a function name.
 * @param {eslint-scope.Variable} variable A variable to check.
 * @returns {boolean} `true` if the variable is a function name.
 */
function isFunctionName(variable) {
    return variable && variable.defs[0].type === "FunctionName";
}

/**
 * Checks whether or not a given MetaProperty node equals to a given value.
 * @param {ASTNode} node A MetaProperty node to check.
 * @param {string} metaName The name of `MetaProperty.meta`.
 * @param {string} propertyName The name of `MetaProperty.property`.
 * @returns {boolean} `true` if the node is the specific value.
 */
function checkMetaProperty(node, metaName, propertyName) {
    return node.meta.name === metaName && node.property.name === propertyName;
}

/**
 * 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 whether or not a given node is a callback.
 * @param {ASTNode} node A node to check.
 * @throws {Error} (Unreachable.)
 * @returns {Object}
 *   {boolean} retv.isCallback - `true` if the node is a callback.
 *   {boolean} retv.isLexicalThis - `true` if the node is with `.bind(this)`.
 */
function getCallbackInfo(node) {
    const retv = { isCallback: false, isLexicalThis: false };
    let currentNode = node;
    let parent = node.parent;
    let bound = false;

    while (currentNode) {
        switch (parent.type) {

            // Checks parents recursively.

            case "LogicalExpression":
            case "ChainExpression":
            case "ConditionalExpression":
                break;

            // Checks whether the parent node is `.bind(this)` call.
            case "MemberExpression":
                if (
                    parent.object === currentNode &&
                    !parent.property.computed &&
                    parent.property.type === "Identifier" &&
                    parent.property.name === "bind"
                ) {
                    const maybeCallee = parent.parent.type === "ChainExpression"
                        ? parent.parent
                        : parent;

                    if (astUtils.isCallee(maybeCallee)) {
                        if (!bound) {
                            bound = true; // Use only the first `.bind()` to make `isLexicalThis` value.
                            retv.isLexicalThis = (
                                maybeCallee.parent.arguments.length === 1 &&
                                maybeCallee.parent.arguments[0].type === "ThisExpression"
                            );
                        }
                        parent = maybeCallee.parent;
                    } else {
                        return retv;
                    }
                } else {
                    return retv;
                }
                break;

            // Checks whether the node is a callback.
            case "CallExpression":
            case "NewExpression":
                if (parent.callee !== currentNode) {
                    retv.isCallback = true;
                }
                return retv;

            default:
                return retv;
        }

        currentNode = parent;
        parent = parent.parent;
    }

    /* c8 ignore next */
    throw new Error("unreachable");
}

/**
 * Checks whether a simple list of parameters contains any duplicates. This does not handle complex
 * parameter lists (e.g. with destructuring), since complex parameter lists are a SyntaxError with duplicate
 * parameter names anyway. Instead, it always returns `false` for complex parameter lists.
 * @param {ASTNode[]} paramsList The list of parameters for a function
 * @returns {boolean} `true` if the list of parameters contains any duplicates
 */
function hasDuplicateParams(paramsList) {
    return paramsList.every(param => param.type === "Identifier") && paramsList.length !== new Set(paramsList.map(param => param.name)).size;
}

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

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

        docs: {
            description: "Require using arrow functions for callbacks",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/prefer-arrow-callback"
        },

        schema: [
            {
                type: "object",
                properties: {
                    allowNamedFunctions: {
                        type: "boolean",
                        default: false
                    },
                    allowUnboundThis: {
                        type: "boolean",
                        default: true
                    }
                },
                additionalProperties: false
            }
        ],

        fixable: "code",

        messages: {
            preferArrowCallback: "Unexpected function expression."
        }
    },

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

        const allowUnboundThis = options.allowUnboundThis !== false; // default to true
        const allowNamedFunctions = options.allowNamedFunctions;
        const sourceCode = context.sourceCode;

        /*
         * {Array<{this: boolean, super: boolean, meta: boolean}>}
         * - this - A flag which shows there are one or more ThisExpression.
         * - super - A flag which shows there are one or more Super.
         * - meta - A flag which shows there are one or more MethProperty.
         */
        let stack = [];

        /**
         * Pushes new function scope with all `false` flags.
         * @returns {void}
         */
        function enterScope() {
            stack.push({ this: false, super: false, meta: false });
        }

        /**
         * Pops a function scope from the stack.
         * @returns {{this: boolean, super: boolean, meta: boolean}} The information of the last scope.
         */
        function exitScope() {
            return stack.pop();
        }

        return {

            // Reset internal state.
            Program() {
                stack = [];
            },

            // If there are below, it cannot replace with arrow functions merely.
            ThisExpression() {
                const info = stack[stack.length - 1];

                if (info) {
                    info.this = true;
                }
            },

            Super() {
                const info = stack[stack.length - 1];

                if (info) {
                    info.super = true;
                }
            },

            MetaProperty(node) {
                const info = stack[stack.length - 1];

                if (info && checkMetaProperty(node, "new", "target")) {
                    info.meta = true;
                }
            },

            // To skip nested scopes.
            FunctionDeclaration: enterScope,
            "FunctionDeclaration:exit": exitScope,

            // Main.
            FunctionExpression: enterScope,
            "FunctionExpression:exit"(node) {
                const scopeInfo = exitScope();

                // Skip named function expressions
                if (allowNamedFunctions && node.id && node.id.name) {
                    return;
                }

                // Skip generators.
                if (node.generator) {
                    return;
                }

                // Skip recursive functions.
                const nameVar = sourceCode.getDeclaredVariables(node)[0];

                if (isFunctionName(nameVar) && nameVar.references.length > 0) {
                    return;
                }

                // Skip if it's using arguments.
                const variable = getVariableOfArguments(sourceCode.getScope(node));

                if (variable && variable.references.length > 0) {
                    return;
                }

                // Reports if it's a callback which can replace with arrows.
                const callbackInfo = getCallbackInfo(node);

                if (callbackInfo.isCallback &&
                    (!allowUnboundThis || !scopeInfo.this || callbackInfo.isLexicalThis) &&
                    !scopeInfo.super &&
                    !scopeInfo.meta
                ) {
                    context.report({
                        node,
                        messageId: "preferArrowCallback",
                        *fix(fixer) {
                            if ((!callbackInfo.isLexicalThis && scopeInfo.this) || hasDuplicateParams(node.params)) {

                                /*
                                 * If the callback function does not have .bind(this) and contains a reference to `this`, there
                                 * is no way to determine what `this` should be, so don't perform any fixes.
                                 * If the callback function has duplicates in its list of parameters (possible in sloppy mode),
                                 * don't replace it with an arrow function, because this is a SyntaxError with arrow functions.
                                 */
                                return;
                            }

                            // Remove `.bind(this)` if exists.
                            if (callbackInfo.isLexicalThis) {
                                const memberNode = node.parent;

                                /*
                                 * If `.bind(this)` exists but the parent is not `.bind(this)`, don't remove it automatically.
                                 * E.g. `(foo || function(){}).bind(this)`
                                 */
                                if (memberNode.type !== "MemberExpression") {
                                    return;
                                }

                                const callNode = memberNode.parent;
                                const firstTokenToRemove = sourceCode.getTokenAfter(memberNode.object, astUtils.isNotClosingParenToken);
                                const lastTokenToRemove = sourceCode.getLastToken(callNode);

                                /*
                                 * If the member expression is parenthesized, don't remove the right paren.
                                 * E.g. `(function(){}.bind)(this)`
                                 *                    ^^^^^^^^^^^^
                                 */
                                if (astUtils.isParenthesised(sourceCode, memberNode)) {
                                    return;
                                }

                                // If comments exist in the `.bind(this)`, don't remove those.
                                if (sourceCode.commentsExistBetween(firstTokenToRemove, lastTokenToRemove)) {
                                    return;
                                }

                                yield fixer.removeRange([firstTokenToRemove.range[0], lastTokenToRemove.range[1]]);
                            }

                            // Convert the function expression to an arrow function.
                            const functionToken = sourceCode.getFirstToken(node, node.async ? 1 : 0);
                            const leftParenToken = sourceCode.getTokenAfter(functionToken, astUtils.isOpeningParenToken);
                            const tokenBeforeBody = sourceCode.getTokenBefore(node.body);

                            if (sourceCode.commentsExistBetween(functionToken, leftParenToken)) {

                                // Remove only extra tokens to keep comments.
                                yield fixer.remove(functionToken);
                                if (node.id) {
                                    yield fixer.remove(node.id);
                                }
                            } else {

                                // Remove extra tokens and spaces.
                                yield fixer.removeRange([functionToken.range[0], leftParenToken.range[0]]);
                            }
                            yield fixer.insertTextAfter(tokenBeforeBody, " =>");

                            // Get the node that will become the new arrow function.
                            let replacedNode = callbackInfo.isLexicalThis ? node.parent.parent : node;

                            if (replacedNode.type === "ChainExpression") {
                                replacedNode = replacedNode.parent;
                            }

                            /*
                             * If the replaced node is part of a BinaryExpression, LogicalExpression, or MemberExpression, then
                             * the arrow function needs to be parenthesized, because `foo || () => {}` is invalid syntax even
                             * though `foo || function() {}` is valid.
                             */
                            if (
                                replacedNode.parent.type !== "CallExpression" &&
                                replacedNode.parent.type !== "ConditionalExpression" &&
                                !astUtils.isParenthesised(sourceCode, replacedNode) &&
                                !astUtils.isParenthesised(sourceCode, node)
                            ) {
                                yield fixer.insertTextBefore(replacedNode, "(");
                                yield fixer.insertTextAfter(replacedNode, ")");
                            }
                        }
                    });
                }
            }
        };
    }
};