code:
/**
 * @fileoverview Rule to check empty newline between class members
 * @author 薛定谔的猫<hh_2013@foxmail.com>
 * @deprecated in ESLint v8.53.0
 */
"use strict";

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

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

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

/**
 * Types of class members.
 * Those have `test` method to check it matches to the given class member.
 * @private
 */
const ClassMemberTypes = {
    "*": { test: () => true },
    field: { test: node => node.type === "PropertyDefinition" },
    method: { test: node => node.type === "MethodDefinition" }
};

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

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

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

        fixable: "whitespace",

        schema: [
            {
                anyOf: [
                    {
                        type: "object",
                        properties: {
                            enforce: {
                                type: "array",
                                items: {
                                    type: "object",
                                    properties: {
                                        blankLine: { enum: ["always", "never"] },
                                        prev: { enum: ["method", "field", "*"] },
                                        next: { enum: ["method", "field", "*"] }
                                    },
                                    additionalProperties: false,
                                    required: ["blankLine", "prev", "next"]
                                },
                                minItems: 1
                            }
                        },
                        additionalProperties: false,
                        required: ["enforce"]
                    },
                    {
                        enum: ["always", "never"]
                    }
                ]
            },
            {
                type: "object",
                properties: {
                    exceptAfterSingleLine: {
                        type: "boolean",
                        default: false
                    }
                },
                additionalProperties: false
            }
        ],
        messages: {
            never: "Unexpected blank line between class members.",
            always: "Expected blank line between class members."
        }
    },

    create(context) {

        const options = [];

        options[0] = context.options[0] || "always";
        options[1] = context.options[1] || { exceptAfterSingleLine: false };

        const configureList = typeof options[0] === "object" ? options[0].enforce : [{ blankLine: options[0], prev: "*", next: "*" }];
        const sourceCode = context.sourceCode;

        /**
         * Gets a pair of tokens that should be used to check lines between two class member nodes.
         *
         * In most cases, this returns the very last token of the current node and
         * the very first token of the next node.
         * For example:
         *
         *     class C {
         *         x = 1;   // curLast: `;` nextFirst: `in`
         *         in = 2
         *     }
         *
         * There is only one exception. If the given node ends with a semicolon, and it looks like
         * a semicolon-less style's semicolon - one that is not on the same line as the preceding
         * token, but is on the line where the next class member starts - this returns the preceding
         * token and the semicolon as boundary tokens.
         * For example:
         *
         *     class C {
         *         x = 1    // curLast: `1` nextFirst: `;`
         *         ;in = 2
         *     }
         * When determining the desired layout of the code, we should treat this semicolon as
         * a part of the next class member node instead of the one it technically belongs to.
         * @param {ASTNode} curNode Current class member node.
         * @param {ASTNode} nextNode Next class member node.
         * @returns {Token} The actual last token of `node`.
         * @private
         */
        function getBoundaryTokens(curNode, nextNode) {
            const lastToken = sourceCode.getLastToken(curNode);
            const prevToken = sourceCode.getTokenBefore(lastToken);
            const nextToken = sourceCode.getFirstToken(nextNode); // skip possible lone `;` between nodes

            const isSemicolonLessStyle = (
                astUtils.isSemicolonToken(lastToken) &&
                !astUtils.isTokenOnSameLine(prevToken, lastToken) &&
                astUtils.isTokenOnSameLine(lastToken, nextToken)
            );

            return isSemicolonLessStyle
                ? { curLast: prevToken, nextFirst: lastToken }
                : { curLast: lastToken, nextFirst: nextToken };
        }

        /**
         * Return the last token among the consecutive tokens that have no exceed max line difference in between, before the first token in the next member.
         * @param {Token} prevLastToken The last token in the previous member node.
         * @param {Token} nextFirstToken The first token in the next member node.
         * @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
         * @returns {Token} The last token among the consecutive tokens.
         */
        function findLastConsecutiveTokenAfter(prevLastToken, nextFirstToken, maxLine) {
            const after = sourceCode.getTokenAfter(prevLastToken, { includeComments: true });

            if (after !== nextFirstToken && after.loc.start.line - prevLastToken.loc.end.line <= maxLine) {
                return findLastConsecutiveTokenAfter(after, nextFirstToken, maxLine);
            }
            return prevLastToken;
        }

        /**
         * Return the first token among the consecutive tokens that have no exceed max line difference in between, after the last token in the previous member.
         * @param {Token} nextFirstToken The first token in the next member node.
         * @param {Token} prevLastToken The last token in the previous member node.
         * @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
         * @returns {Token} The first token among the consecutive tokens.
         */
        function findFirstConsecutiveTokenBefore(nextFirstToken, prevLastToken, maxLine) {
            const before = sourceCode.getTokenBefore(nextFirstToken, { includeComments: true });

            if (before !== prevLastToken && nextFirstToken.loc.start.line - before.loc.end.line <= maxLine) {
                return findFirstConsecutiveTokenBefore(before, prevLastToken, maxLine);
            }
            return nextFirstToken;
        }

        /**
         * Checks if there is a token or comment between two tokens.
         * @param {Token} before The token before.
         * @param {Token} after The token after.
         * @returns {boolean} True if there is a token or comment between two tokens.
         */
        function hasTokenOrCommentBetween(before, after) {
            return sourceCode.getTokensBetween(before, after, { includeComments: true }).length !== 0;
        }

        /**
         * Checks whether the given node matches the given type.
         * @param {ASTNode} node The class member node to check.
         * @param {string} type The class member type to check.
         * @returns {boolean} `true` if the class member node matched the type.
         * @private
         */
        function match(node, type) {
            return ClassMemberTypes[type].test(node);
        }

        /**
         * Finds the last matched configuration from the configureList.
         * @param {ASTNode} prevNode The previous node to match.
         * @param {ASTNode} nextNode The current node to match.
         * @returns {string|null} Padding type or `null` if no matches were found.
         * @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 configure.blankLine;
                }
            }
            return null;
        }

        return {
            ClassBody(node) {
                const body = node.body;

                for (let i = 0; i < body.length - 1; i++) {
                    const curFirst = sourceCode.getFirstToken(body[i]);
                    const { curLast, nextFirst } = getBoundaryTokens(body[i], body[i + 1]);
                    const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
                    const skip = !isMulti && options[1].exceptAfterSingleLine;
                    const beforePadding = findLastConsecutiveTokenAfter(curLast, nextFirst, 1);
                    const afterPadding = findFirstConsecutiveTokenBefore(nextFirst, curLast, 1);
                    const isPadded = afterPadding.loc.start.line - beforePadding.loc.end.line > 1;
                    const hasTokenInPadding = hasTokenOrCommentBetween(beforePadding, afterPadding);
                    const curLineLastToken = findLastConsecutiveTokenAfter(curLast, nextFirst, 0);
                    const paddingType = getPaddingType(body[i], body[i + 1]);

                    if (paddingType === "never" && isPadded) {
                        context.report({
                            node: body[i + 1],
                            messageId: "never",

                            fix(fixer) {
                                if (hasTokenInPadding) {
                                    return null;
                                }
                                return fixer.replaceTextRange([beforePadding.range[1], afterPadding.range[0]], "\n");
                            }
                        });
                    } else if (paddingType === "always" && !skip && !isPadded) {
                        context.report({
                            node: body[i + 1],
                            messageId: "always",

                            fix(fixer) {
                                if (hasTokenInPadding) {
                                    return null;
                                }
                                return fixer.insertTextAfter(curLineLastToken, "\n");
                            }
                        });
                    }

                }
            }
        };
    }
};


code:
/**
 * @fileoverview Rule to replace assignment expressions with logical operator assignment
 * @author Daniel Martens
 */
"use strict";

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

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

const baseTypes = new Set(["Identifier", "Super", "ThisExpression"]);

/**
 * Returns true iff either "undefined" or a void expression (eg. "void 0")
 * @param {ASTNode} expression Expression to check
 * @param {import('eslint-scope').Scope} scope Scope of the expression
 * @returns {boolean} True iff "undefined" or "void ..."
 */
function isUndefined(expression, scope) {
    if (expression.type === "Identifier" && expression.name === "undefined") {
        return astUtils.isReferenceToGlobalVariable(scope, expression);
    }

    return expression.type === "UnaryExpression" &&
           expression.operator === "void" &&
           expression.argument.type === "Literal" &&
           expression.argument.value === 0;
}

/**
 * Returns true iff the reference is either an identifier or member expression
 * @param {ASTNode} expression Expression to check
 * @returns {boolean} True for identifiers and member expressions
 */
function isReference(expression) {
    return (expression.type === "Identifier" && expression.name !== "undefined") ||
           expression.type === "MemberExpression";
}

/**
 * Returns true iff the expression checks for nullish with loose equals.
 * Examples: value == null, value == void 0
 * @param {ASTNode} expression Test condition
 * @param {import('eslint-scope').Scope} scope Scope of the expression
 * @returns {boolean} True iff implicit nullish comparison
 */
function isImplicitNullishComparison(expression, scope) {
    if (expression.type !== "BinaryExpression" || expression.operator !== "==") {
        return false;
    }

    const reference = isReference(expression.left) ? "left" : "right";
    const nullish = reference === "left" ? "right" : "left";

    return isReference(expression[reference]) &&
           (astUtils.isNullLiteral(expression[nullish]) || isUndefined(expression[nullish], scope));
}

/**
 * Condition with two equal comparisons.
 * @param {ASTNode} expression Condition
 * @returns {boolean} True iff matches ? === ? || ? === ?
 */
function isDoubleComparison(expression) {
    return expression.type === "LogicalExpression" &&
           expression.operator === "||" &&
           expression.left.type === "BinaryExpression" &&
           expression.left.operator === "===" &&
           expression.right.type === "BinaryExpression" &&
           expression.right.operator === "===";
}

/**
 * Returns true iff the expression checks for undefined and null.
 * Example: value === null || value === undefined
 * @param {ASTNode} expression Test condition
 * @param {import('eslint-scope').Scope} scope Scope of the expression
 * @returns {boolean} True iff explicit nullish comparison
 */
function isExplicitNullishComparison(expression, scope) {
    if (!isDoubleComparison(expression)) {
        return false;
    }
    const leftReference = isReference(expression.left.left) ? "left" : "right";
    const leftNullish = leftReference === "left" ? "right" : "left";
    const rightReference = isReference(expression.right.left) ? "left" : "right";
    const rightNullish = rightReference === "left" ? "right" : "left";

    return astUtils.isSameReference(expression.left[leftReference], expression.right[rightReference]) &&
           ((astUtils.isNullLiteral(expression.left[leftNullish]) && isUndefined(expression.right[rightNullish], scope)) ||
           (isUndefined(expression.left[leftNullish], scope) && astUtils.isNullLiteral(expression.right[rightNullish])));
}

/**
 * Returns true for Boolean(arg) calls
 * @param {ASTNode} expression Test condition
 * @param {import('eslint-scope').Scope} scope Scope of the expression
 * @returns {boolean} Whether the expression is a boolean cast
 */
function isBooleanCast(expression, scope) {
    return expression.type === "CallExpression" &&
           expression.callee.name === "Boolean" &&
           expression.arguments.length === 1 &&
           astUtils.isReferenceToGlobalVariable(scope, expression.callee);
}

/**
 * Returns true for:
 * truthiness checks:  value, Boolean(value), !!value
 * falsiness checks:   !value, !Boolean(value)
 * nullish checks:     value == null, value === undefined || value === null
 * @param {ASTNode} expression Test condition
 * @param {import('eslint-scope').Scope} scope Scope of the expression
 * @returns {?{ reference: ASTNode, operator: '??'|'||'|'&&'}} Null if not a known existence
 */
function getExistence(expression, scope) {
    const isNegated = expression.type === "UnaryExpression" && expression.operator === "!";
    const base = isNegated ? expression.argument : expression;

    switch (true) {
        case isReference(base):
            return { reference: base, operator: isNegated ? "||" : "&&" };
        case base.type === "UnaryExpression" && base.operator === "!" && isReference(base.argument):
            return { reference: base.argument, operator: "&&" };
        case isBooleanCast(base, scope) && isReference(base.arguments[0]):
            return { reference: base.arguments[0], operator: isNegated ? "||" : "&&" };
        case isImplicitNullishComparison(expression, scope):
            return { reference: isReference(expression.left) ? expression.left : expression.right, operator: "??" };
        case isExplicitNullishComparison(expression, scope):
            return { reference: isReference(expression.left.left) ? expression.left.left : expression.left.right, operator: "??" };
        default: return null;
    }
}

/**
 * Returns true iff the node is inside a with block
 * @param {ASTNode} node Node to check
 * @returns {boolean} True iff passed node is inside a with block
 */
function isInsideWithBlock(node) {
    if (node.type === "Program") {
        return false;
    }

    return node.parent.type === "WithStatement" && node.parent.body === node ? true : isInsideWithBlock(node.parent);
}

/**
 * Gets the leftmost operand of a consecutive logical expression.
 * @param {SourceCode} sourceCode The ESLint source code object
 * @param {LogicalExpression} node LogicalExpression
 * @returns {Expression} Leftmost operand
 */
function getLeftmostOperand(sourceCode, node) {
    let left = node.left;

    while (left.type === "LogicalExpression" && left.operator === node.operator) {

        if (astUtils.isParenthesised(sourceCode, left)) {

            /*
             * It should have associativity,
             * but ignore it if use parentheses to make the evaluation order clear.
             */
            return left;
        }
        left = left.left;
    }
    return left;

}

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

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

        schema: {
            type: "array",
            oneOf: [{
                items: [
                    { const: "always" },
                    {
                        type: "object",
                        properties: {
                            enforceForIfStatements: {
                                type: "boolean"
                            }
                        },
                        additionalProperties: false
                    }
                ],
                minItems: 0, // 0 for allowing passing no options
                maxItems: 2
            }, {
                items: [{ const: "never" }],
                minItems: 1,
                maxItems: 1
            }]
        },
        fixable: "code",
        hasSuggestions: true,
        messages: {
            assignment: "Assignment (=) can be replaced with operator assignment ({{operator}}).",
            useLogicalOperator: "Convert this assignment to use the operator {{ operator }}.",
            logical: "Logical expression can be replaced with an assignment ({{ operator }}).",
            convertLogical: "Replace this logical expression with an assignment with the operator {{ operator }}.",
            if: "'if' statement can be replaced with a logical operator assignment with operator {{ operator }}.",
            convertIf: "Replace this 'if' statement with a logical assignment with operator {{ operator }}.",
            unexpected: "Unexpected logical operator assignment ({{operator}}) shorthand.",
            separate: "Separate the logical assignment into an assignment with a logical operator."
        }
    },

    create(context) {
        const mode = context.options[0] === "never" ? "never" : "always";
        const checkIf = mode === "always" && context.options.length > 1 && context.options[1].enforceForIfStatements;
        const sourceCode = context.sourceCode;
        const isStrict = sourceCode.getScope(sourceCode.ast).isStrict;

        /**
         * Returns false if the access could be a getter
         * @param {ASTNode} node Assignment expression
         * @returns {boolean} True iff the fix is safe
         */
        function cannotBeGetter(node) {
            return node.type === "Identifier" &&
                   (isStrict || !isInsideWithBlock(node));
        }

        /**
         * Check whether only a single property is accessed
         * @param {ASTNode} node reference
         * @returns {boolean} True iff a single property is accessed
         */
        function accessesSingleProperty(node) {
            if (!isStrict && isInsideWithBlock(node)) {
                return node.type === "Identifier";
            }

            return node.type === "MemberExpression" &&
                   baseTypes.has(node.object.type) &&
                   (!node.computed || (node.property.type !== "MemberExpression" && node.property.type !== "ChainExpression"));
        }

        /**
         * Adds a fixer or suggestion whether on the fix is safe.
         * @param {{ messageId: string, node: ASTNode }} descriptor Report descriptor without fix or suggest
         * @param {{ messageId: string, fix: Function }} suggestion Adds the fix or the whole suggestion as only element in "suggest" to suggestion
         * @param {boolean} shouldBeFixed Fix iff the condition is true
         * @returns {Object} Descriptor with either an added fix or suggestion
         */
        function createConditionalFixer(descriptor, suggestion, shouldBeFixed) {
            if (shouldBeFixed) {
                return {
                    ...descriptor,
                    fix: suggestion.fix
                };
            }

            return {
                ...descriptor,
                suggest: [suggestion]
            };
        }

        /**
         * Returns the operator token for assignments and binary expressions
         * @param {ASTNode} node AssignmentExpression or BinaryExpression
         * @returns {import('eslint').AST.Token} Operator token between the left and right expression
         */
        function getOperatorToken(node) {
            return sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
        }

        if (mode === "never") {
            return {

                // foo ||= bar
                "AssignmentExpression"(assignment) {
                    if (!astUtils.isLogicalAssignmentOperator(assignment.operator)) {
                        return;
                    }

                    const descriptor = {
                        messageId: "unexpected",
                        node: assignment,
                        data: { operator: assignment.operator }
                    };
                    const suggestion = {
                        messageId: "separate",
                        *fix(ruleFixer) {
                            if (sourceCode.getCommentsInside(assignment).length > 0) {
                                return;
                            }

                            const operatorToken = getOperatorToken(assignment);

                            // -> foo = bar
                            yield ruleFixer.replaceText(operatorToken, "=");

                            const assignmentText = sourceCode.getText(assignment.left);
                            const operator = assignment.operator.slice(0, -1);

                            // -> foo = foo || bar
                            yield ruleFixer.insertTextAfter(operatorToken, ` ${assignmentText} ${operator}`);

                            const precedence = astUtils.getPrecedence(assignment.right) <= astUtils.getPrecedence({ type: "LogicalExpression", operator });

                            // ?? and || / && cannot be mixed but have same precedence
                            const mixed = assignment.operator === "??=" && astUtils.isLogicalExpression(assignment.right);

                            if (!astUtils.isParenthesised(sourceCode, assignment.right) && (precedence || mixed)) {

                                // -> foo = foo || (bar)
                                yield ruleFixer.insertTextBefore(assignment.right, "(");
                                yield ruleFixer.insertTextAfter(assignment.right, ")");
                            }
                        }
                    };

                    context.report(createConditionalFixer(descriptor, suggestion, cannotBeGetter(assignment.left)));
                }
            };
        }

        return {

            // foo = foo || bar
            "AssignmentExpression[operator='='][right.type='LogicalExpression']"(assignment) {
                const leftOperand = getLeftmostOperand(sourceCode, assignment.right);

                if (!astUtils.isSameReference(assignment.left, leftOperand)
                ) {
                    return;
                }

                const descriptor = {
                    messageId: "assignment",
                    node: assignment,
                    data: { operator: `${assignment.right.operator}=` }
                };
                const suggestion = {
                    messageId: "useLogicalOperator",
                    data: { operator: `${assignment.right.operator}=` },
                    *fix(ruleFixer) {
                        if (sourceCode.getCommentsInside(assignment).length > 0) {
                            return;
                        }

                        // No need for parenthesis around the assignment based on precedence as the precedence stays the same even with changed operator
                        const assignmentOperatorToken = getOperatorToken(assignment);

                        // -> foo ||= foo || bar
                        yield ruleFixer.insertTextBefore(assignmentOperatorToken, assignment.right.operator);

                        // -> foo ||= bar
                        const logicalOperatorToken = getOperatorToken(leftOperand.parent);
                        const firstRightOperandToken = sourceCode.getTokenAfter(logicalOperatorToken);

                        yield ruleFixer.removeRange([leftOperand.parent.range[0], firstRightOperandToken.range[0]]);
                    }
                };

                context.report(createConditionalFixer(descriptor, suggestion, cannotBeGetter(assignment.left)));
            },

            // foo || (foo = bar)
            'LogicalExpression[right.type="AssignmentExpression"][right.operator="="]'(logical) {

                // Right side has to be parenthesized, otherwise would be parsed as (foo || foo) = bar which is illegal
                if (isReference(logical.left) && astUtils.isSameReference(logical.left, logical.right.left)) {
                    const descriptor = {
                        messageId: "logical",
                        node: logical,
                        data: { operator: `${logical.operator}=` }
                    };
                    const suggestion = {
                        messageId: "convertLogical",
                        data: { operator: `${logical.operator}=` },
                        *fix(ruleFixer) {
                            if (sourceCode.getCommentsInside(logical).length > 0) {
                                return;
                            }

                            const parentPrecedence = astUtils.getPrecedence(logical.parent);
                            const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" && (
                                parentPrecedence === -1 ||
                                astUtils.getPrecedence({ type: "AssignmentExpression" }) < parentPrecedence
                            );

                            if (!astUtils.isParenthesised(sourceCode, logical) && requiresOuterParenthesis) {
                                yield ruleFixer.insertTextBefore(logical, "(");
                                yield ruleFixer.insertTextAfter(logical, ")");
                            }

                            // Also removes all opening parenthesis
                            yield ruleFixer.removeRange([logical.range[0], logical.right.range[0]]); // -> foo = bar)

                            // Also removes all ending parenthesis
                            yield ruleFixer.removeRange([logical.right.range[1], logical.range[1]]); // -> foo = bar

                            const operatorToken = getOperatorToken(logical.right);

                            yield ruleFixer.insertTextBefore(operatorToken, logical.operator); // -> foo ||= bar
                        }
                    };
                    const fix = cannotBeGetter(logical.left) || accessesSingleProperty(logical.left);

                    context.report(createConditionalFixer(descriptor, suggestion, fix));
                }
            },

            // if (foo) foo = bar
            "IfStatement[alternate=null]"(ifNode) {
                if (!checkIf) {
                    return;
                }

                const hasBody = ifNode.consequent.type === "BlockStatement";

                if (hasBody && ifNode.consequent.body.length !== 1) {
                    return;
                }

                const body = hasBody ? ifNode.consequent.body[0] : ifNode.consequent;
                const scope = sourceCode.getScope(ifNode);
                const existence = getExistence(ifNode.test, scope);

                if (
                    body.type === "ExpressionStatement" &&
                    body.expression.type === "AssignmentExpression" &&
                    body.expression.operator === "=" &&
                    existence !== null &&
                    astUtils.isSameReference(existence.reference, body.expression.left)
                ) {
                    const descriptor = {
                        messageId: "if",
                        node: ifNode,
                        data: { operator: `${existence.operator}=` }
                    };
                    const suggestion = {
                        messageId: "convertIf",
                        data: { operator: `${existence.operator}=` },
                        *fix(ruleFixer) {
                            if (sourceCode.getCommentsInside(ifNode).length > 0) {
                                return;
                            }

                            const firstBodyToken = sourceCode.getFirstToken(body);
                            const prevToken = sourceCode.getTokenBefore(ifNode);

                            if (
                                prevToken !== null &&
                                prevToken.value !== ";" &&
                                prevToken.value !== "{" &&
                                firstBodyToken.type !== "Identifier" &&
                                firstBodyToken.type !== "Keyword"
                            ) {

                                // Do not fix if the fixed statement could be part of the previous statement (eg. fn() if (a == null) (a) = b --> fn()(a) ??= b)
                                return;
                            }

                            const operatorToken = getOperatorToken(body.expression);

                            yield ruleFixer.insertTextBefore(operatorToken, existence.operator); // -> if (foo) foo ||= bar

                            yield ruleFixer.removeRange([ifNode.range[0], body.range[0]]); // -> foo ||= bar

                            yield ruleFixer.removeRange([body.range[1], ifNode.range[1]]); // -> foo ||= bar, only present if "if" had a body

                            const nextToken = sourceCode.getTokenAfter(body.expression);

                            if (hasBody && (nextToken !== null && nextToken.value !== ";")) {
                                yield ruleFixer.insertTextAfter(ifNode, ";");
                            }
                        }
                    };
                    const shouldBeFixed = cannotBeGetter(existence.reference) ||
                                          (ifNode.test.type !== "LogicalExpression" && accessesSingleProperty(existence.reference));

                    context.report(createConditionalFixer(descriptor, suggestion, shouldBeFixed));
                }
            }
        };
    }
};

license: mit


code:
/**
 * @fileoverview Enforce a maximum number of classes per file
 * @author James Garbutt <https://github.com/43081j>
 */
"use strict";

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

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

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

        docs: {
            description: "Enforce a maximum number of classes per file",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/max-classes-per-file"
        },

        schema: [
            {
                oneOf: [
                    {
                        type: "integer",
                        minimum: 1
                    },
                    {
                        type: "object",
                        properties: {
                            ignoreExpressions: {
                                type: "boolean"
                            },
                            max: {
                                type: "integer",
                                minimum: 1
                            }
                        },
                        additionalProperties: false
                    }
                ]
            }
        ],

        messages: {
            maximumExceeded: "File has too many classes ({{ classCount }}). Maximum allowed is {{ max }}."
        }
    },
    create(context) {
        const [option = {}] = context.options;
        const [ignoreExpressions, max] = typeof option === "number"
            ? [false, option || 1]
            : [option.ignoreExpressions, option.max || 1];

        let classCount = 0;

        return {
            Program() {
                classCount = 0;
            },
            "Program:exit"(node) {
                if (classCount > max) {
                    context.report({
                        node,
                        messageId: "maximumExceeded",
                        data: {
                            classCount,
                            max
                        }
                    });
                }
            },
            "ClassDeclaration"() {
                classCount++;
            },
            "ClassExpression"() {
                if (!ignoreExpressions) {
                    classCount++;
                }
            }
        };
    }
};


code:
/**
 * @fileoverview A rule to set the maximum depth block can be nested in a function.
 * @author Ian Christian Myers
 */

"use strict";

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

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

        docs: {
            description: "Enforce a maximum depth that blocks can be nested",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/max-depth"
        },

        schema: [
            {
                oneOf: [
                    {
                        type: "integer",
                        minimum: 0
                    },
                    {
                        type: "object",
                        properties: {
                            maximum: {
                                type: "integer",
                                minimum: 0
                            },
                            max: {
                                type: "integer",
                                minimum: 0
                            }
                        },
                        additionalProperties: false
                    }
                ]
            }
        ],
        messages: {
            tooDeeply: "Blocks are nested too deeply ({{depth}}). Maximum allowed is {{maxDepth}}."
        }
    },

    create(context) {

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

        const functionStack = [],
            option = context.options[0];
        let maxDepth = 4;

        if (
            typeof option === "object" &&
            (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max"))
        ) {
            maxDepth = option.maximum || option.max;
        }
        if (typeof option === "number") {
            maxDepth = option;
        }

        /**
         * When parsing a new function, store it in our function stack
         * @returns {void}
         * @private
         */
        function startFunction() {
            functionStack.push(0);
        }

        /**
         * When parsing is done then pop out the reference
         * @returns {void}
         * @private
         */
        function endFunction() {
            functionStack.pop();
        }

        /**
         * Save the block and Evaluate the node
         * @param {ASTNode} node node to evaluate
         * @returns {void}
         * @private
         */
        function pushBlock(node) {
            const len = ++functionStack[functionStack.length - 1];

            if (len > maxDepth) {
                context.report({ node, messageId: "tooDeeply", data: { depth: len, maxDepth } });
            }
        }

        /**
         * Pop the saved block
         * @returns {void}
         * @private
         */
        function popBlock() {
            functionStack[functionStack.length - 1]--;
        }

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

        return {
            Program: startFunction,
            FunctionDeclaration: startFunction,
            FunctionExpression: startFunction,
            ArrowFunctionExpression: startFunction,
            StaticBlock: startFunction,

            IfStatement(node) {
                if (node.parent.type !== "IfStatement") {
                    pushBlock(node);
                }
            },
            SwitchStatement: pushBlock,
            TryStatement: pushBlock,
            DoWhileStatement: pushBlock,
            WhileStatement: pushBlock,
            WithStatement: pushBlock,
            ForStatement: pushBlock,
            ForInStatement: pushBlock,
            ForOfStatement: pushBlock,

            "IfStatement:exit": popBlock,
            "SwitchStatement:exit": popBlock,
            "TryStatement:exit": popBlock,
            "DoWhileStatement:exit": popBlock,
            "WhileStatement:exit": popBlock,
            "WithStatement:exit": popBlock,
            "ForStatement:exit": popBlock,
            "ForInStatement:exit": popBlock,
            "ForOfStatement:exit": popBlock,

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

    }
};


code:
/**
 * @fileoverview Rule to check for max length on a line.
 * @author Matt DuVall <http://www.mattduvall.com>
 * @deprecated in ESLint v8.53.0
 */

"use strict";

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

const OPTIONS_SCHEMA = {
    type: "object",
    properties: {
        code: {
            type: "integer",
            minimum: 0
        },
        comments: {
            type: "integer",
            minimum: 0
        },
        tabWidth: {
            type: "integer",
            minimum: 0
        },
        ignorePattern: {
            type: "string"
        },
        ignoreComments: {
            type: "boolean"
        },
        ignoreStrings: {
            type: "boolean"
        },
        ignoreUrls: {
            type: "boolean"
        },
        ignoreTemplateLiterals: {
            type: "boolean"
        },
        ignoreRegExpLiterals: {
            type: "boolean"
        },
        ignoreTrailingComments: {
            type: "boolean"
        }
    },
    additionalProperties: false
};

const OPTIONS_OR_INTEGER_SCHEMA = {
    anyOf: [
        OPTIONS_SCHEMA,
        {
            type: "integer",
            minimum: 0
        }
    ]
};

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

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

        docs: {
            description: "Enforce a maximum line length",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/max-len"
        },

        schema: [
            OPTIONS_OR_INTEGER_SCHEMA,
            OPTIONS_OR_INTEGER_SCHEMA,
            OPTIONS_SCHEMA
        ],
        messages: {
            max: "This line has a length of {{lineLength}}. Maximum allowed is {{maxLength}}.",
            maxComment: "This line has a comment length of {{lineLength}}. Maximum allowed is {{maxCommentLength}}."
        }
    },

    create(context) {

        /*
         * Inspired by http://tools.ietf.org/html/rfc3986#appendix-B, however:
         * - They're matching an entire string that we know is a URI
         * - We're matching part of a string where we think there *might* be a URL
         * - We're only concerned about URLs, as picking out any URI would cause
         *   too many false positives
         * - We don't care about matching the entire URL, any small segment is fine
         */
        const URL_REGEXP = /[^:/?#]:\/\/[^?#]/u;

        const sourceCode = context.sourceCode;

        /**
         * Computes the length of a line that may contain tabs. The width of each
         * tab will be the number of spaces to the next tab stop.
         * @param {string} line The line.
         * @param {int} tabWidth The width of each tab stop in spaces.
         * @returns {int} The computed line length.
         * @private
         */
        function computeLineLength(line, tabWidth) {
            let extraCharacterCount = 0;

            line.replace(/\t/gu, (match, offset) => {
                const totalOffset = offset + extraCharacterCount,
                    previousTabStopOffset = tabWidth ? totalOffset % tabWidth : 0,
                    spaceCount = tabWidth - previousTabStopOffset;

                extraCharacterCount += spaceCount - 1; // -1 for the replaced tab
            });
            return Array.from(line).length + extraCharacterCount;
        }

        // The options object must be the last option specified…
        const options = Object.assign({}, context.options[context.options.length - 1]);

        // …but max code length…
        if (typeof context.options[0] === "number") {
            options.code = context.options[0];
        }

        // …and tabWidth can be optionally specified directly as integers.
        if (typeof context.options[1] === "number") {
            options.tabWidth = context.options[1];
        }

        const maxLength = typeof options.code === "number" ? options.code : 80,
            tabWidth = typeof options.tabWidth === "number" ? options.tabWidth : 4,
            ignoreComments = !!options.ignoreComments,
            ignoreStrings = !!options.ignoreStrings,
            ignoreTemplateLiterals = !!options.ignoreTemplateLiterals,
            ignoreRegExpLiterals = !!options.ignoreRegExpLiterals,
            ignoreTrailingComments = !!options.ignoreTrailingComments || !!options.ignoreComments,
            ignoreUrls = !!options.ignoreUrls,
            maxCommentLength = options.comments;
        let ignorePattern = options.ignorePattern || null;

        if (ignorePattern) {
            ignorePattern = new RegExp(ignorePattern, "u");
        }

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

        /**
         * Tells if a given comment is trailing: it starts on the current line and
         * extends to or past the end of the current line.
         * @param {string} line The source line we want to check for a trailing comment on
         * @param {number} lineNumber The one-indexed line number for line
         * @param {ASTNode} comment The comment to inspect
         * @returns {boolean} If the comment is trailing on the given line
         */
        function isTrailingComment(line, lineNumber, comment) {
            return comment &&
                (comment.loc.start.line === lineNumber && lineNumber <= comment.loc.end.line) &&
                (comment.loc.end.line > lineNumber || comment.loc.end.column === line.length);
        }

        /**
         * Tells if a comment encompasses the entire line.
         * @param {string} line The source line with a trailing comment
         * @param {number} lineNumber The one-indexed line number this is on
         * @param {ASTNode} comment The comment to remove
         * @returns {boolean} If the comment covers the entire line
         */
        function isFullLineComment(line, lineNumber, comment) {
            const start = comment.loc.start,
                end = comment.loc.end,
                isFirstTokenOnLine = !line.slice(0, comment.loc.start.column).trim();

            return comment &&
                (start.line < lineNumber || (start.line === lineNumber && isFirstTokenOnLine)) &&
                (end.line > lineNumber || (end.line === lineNumber && end.column === line.length));
        }

        /**
         * Check if a node is a JSXEmptyExpression contained in a single line JSXExpressionContainer.
         * @param {ASTNode} node A node to check.
         * @returns {boolean} True if the node is a JSXEmptyExpression contained in a single line JSXExpressionContainer.
         */
        function isJSXEmptyExpressionInSingleLineContainer(node) {
            if (!node || !node.parent || node.type !== "JSXEmptyExpression" || node.parent.type !== "JSXExpressionContainer") {
                return false;
            }

            const parent = node.parent;

            return parent.loc.start.line === parent.loc.end.line;
        }

        /**
         * Gets the line after the comment and any remaining trailing whitespace is
         * stripped.
         * @param {string} line The source line with a trailing comment
         * @param {ASTNode} comment The comment to remove
         * @returns {string} Line without comment and trailing whitespace
         */
        function stripTrailingComment(line, comment) {

            // loc.column is zero-indexed
            return line.slice(0, comment.loc.start.column).replace(/\s+$/u, "");
        }

        /**
         * Ensure that an array exists at [key] on `object`, and add `value` to it.
         * @param {Object} object the object to mutate
         * @param {string} key the object's key
         * @param {any} value the value to add
         * @returns {void}
         * @private
         */
        function ensureArrayAndPush(object, key, value) {
            if (!Array.isArray(object[key])) {
                object[key] = [];
            }
            object[key].push(value);
        }

        /**
         * Retrieves an array containing all strings (" or ') in the source code.
         * @returns {ASTNode[]} An array of string nodes.
         */
        function getAllStrings() {
            return sourceCode.ast.tokens.filter(token => (token.type === "String" ||
                (token.type === "JSXText" && sourceCode.getNodeByRangeIndex(token.range[0] - 1).type === "JSXAttribute")));
        }

        /**
         * Retrieves an array containing all template literals in the source code.
         * @returns {ASTNode[]} An array of template literal nodes.
         */
        function getAllTemplateLiterals() {
            return sourceCode.ast.tokens.filter(token => token.type === "Template");
        }

        /**
         * Retrieves an array containing all RegExp literals in the source code.
         * @returns {ASTNode[]} An array of RegExp literal nodes.
         */
        function getAllRegExpLiterals() {
            return sourceCode.ast.tokens.filter(token => token.type === "RegularExpression");
        }

        /**
         *
         * reduce an array of AST nodes by line number, both start and end.
         * @param {ASTNode[]} arr array of AST nodes
         * @returns {Object} accululated AST nodes
         */
        function groupArrayByLineNumber(arr) {
            const obj = {};

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

                for (let j = node.loc.start.line; j <= node.loc.end.line; ++j) {
                    ensureArrayAndPush(obj, j, node);
                }
            }
            return obj;
        }

        /**
         * Returns an array of all comments in the source code.
         * If the element in the array is a JSXEmptyExpression contained with a single line JSXExpressionContainer,
         * the element is changed with JSXExpressionContainer node.
         * @returns {ASTNode[]} An array of comment nodes
         */
        function getAllComments() {
            const comments = [];

            sourceCode.getAllComments()
                .forEach(commentNode => {
                    const containingNode = sourceCode.getNodeByRangeIndex(commentNode.range[0]);

                    if (isJSXEmptyExpressionInSingleLineContainer(containingNode)) {

                        // push a unique node only
                        if (comments[comments.length - 1] !== containingNode.parent) {
                            comments.push(containingNode.parent);
                        }
                    } else {
                        comments.push(commentNode);
                    }
                });

            return comments;
        }

        /**
         * Check the program for max length
         * @param {ASTNode} node Node to examine
         * @returns {void}
         * @private
         */
        function checkProgramForMaxLength(node) {

            // split (honors line-ending)
            const lines = sourceCode.lines,

                // list of comments to ignore
                comments = ignoreComments || maxCommentLength || ignoreTrailingComments ? getAllComments() : [];

            // we iterate over comments in parallel with the lines
            let commentsIndex = 0;

            const strings = getAllStrings();
            const stringsByLine = groupArrayByLineNumber(strings);

            const templateLiterals = getAllTemplateLiterals();
            const templateLiteralsByLine = groupArrayByLineNumber(templateLiterals);

            const regExpLiterals = getAllRegExpLiterals();
            const regExpLiteralsByLine = groupArrayByLineNumber(regExpLiterals);

            lines.forEach((line, i) => {

                // i is zero-indexed, line numbers are one-indexed
                const lineNumber = i + 1;

                /*
                 * if we're checking comment length; we need to know whether this
                 * line is a comment
                 */
                let lineIsComment = false;
                let textToMeasure;

                /*
                 * We can short-circuit the comment checks if we're already out of
                 * comments to check.
                 */
                if (commentsIndex < comments.length) {
                    let comment = null;

                    // iterate over comments until we find one past the current line
                    do {
                        comment = comments[++commentsIndex];
                    } while (comment && comment.loc.start.line <= lineNumber);

                    // and step back by one
                    comment = comments[--commentsIndex];

                    if (isFullLineComment(line, lineNumber, comment)) {
                        lineIsComment = true;
                        textToMeasure = line;
                    } else if (ignoreTrailingComments && isTrailingComment(line, lineNumber, comment)) {
                        textToMeasure = stripTrailingComment(line, comment);

                        // ignore multiple trailing comments in the same line
                        let lastIndex = commentsIndex;

                        while (isTrailingComment(textToMeasure, lineNumber, comments[--lastIndex])) {
                            textToMeasure = stripTrailingComment(textToMeasure, comments[lastIndex]);
                        }
                    } else {
                        textToMeasure = line;
                    }
                } else {
                    textToMeasure = line;
                }
                if (ignorePattern && ignorePattern.test(textToMeasure) ||
                    ignoreUrls && URL_REGEXP.test(textToMeasure) ||
                    ignoreStrings && stringsByLine[lineNumber] ||
                    ignoreTemplateLiterals && templateLiteralsByLine[lineNumber] ||
                    ignoreRegExpLiterals && regExpLiteralsByLine[lineNumber]
                ) {

                    // ignore this line
                    return;
                }

                const lineLength = computeLineLength(textToMeasure, tabWidth);
                const commentLengthApplies = lineIsComment && maxCommentLength;

                if (lineIsComment && ignoreComments) {
                    return;
                }

                const loc = {
                    start: {
                        line: lineNumber,
                        column: 0
                    },
                    end: {
                        line: lineNumber,
                        column: textToMeasure.length
                    }
                };

                if (commentLengthApplies) {
                    if (lineLength > maxCommentLength) {
                        context.report({
                            node,
                            loc,
                            messageId: "maxComment",
                            data: {
                                lineLength,
                                maxCommentLength
                            }
                        });
                    }
                } else if (lineLength > maxLength) {
                    context.report({
                        node,
                        loc,
                        messageId: "max",
                        data: {
                            lineLength,
                            maxLength
                        }
                    });
                }
            });
        }

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

        return {
            Program: checkProgramForMaxLength
        };

    }
};


code:
/**
 * @fileoverview A rule to set the maximum number of line of code in a function.
 * @author Pete Ward <peteward44@gmail.com>
 */
"use strict";

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

const astUtils = require("./utils/ast-utils");
const { upperCaseFirst } = require("../shared/string-utils");

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

const OPTIONS_SCHEMA = {
    type: "object",
    properties: {
        max: {
            type: "integer",
            minimum: 0
        },
        skipComments: {
            type: "boolean"
        },
        skipBlankLines: {
            type: "boolean"
        },
        IIFEs: {
            type: "boolean"
        }
    },
    additionalProperties: false
};

const OPTIONS_OR_INTEGER_SCHEMA = {
    oneOf: [
        OPTIONS_SCHEMA,
        {
            type: "integer",
            minimum: 1
        }
    ]
};

/**
 * Given a list of comment nodes, return a map with numeric keys (source code line numbers) and comment token values.
 * @param {Array} comments An array of comment nodes.
 * @returns {Map<string, Node>} A map with numeric keys (source code line numbers) and comment token values.
 */
function getCommentLineNumbers(comments) {
    const map = new Map();

    comments.forEach(comment => {
        for (let i = comment.loc.start.line; i <= comment.loc.end.line; i++) {
            map.set(i, comment);
        }
    });
    return map;
}

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

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

        docs: {
            description: "Enforce a maximum number of lines of code in a function",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/max-lines-per-function"
        },

        schema: [
            OPTIONS_OR_INTEGER_SCHEMA
        ],
        messages: {
            exceed: "{{name}} has too many lines ({{lineCount}}). Maximum allowed is {{maxLines}}."
        }
    },

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

        const option = context.options[0];
        let maxLines = 50;
        let skipComments = false;
        let skipBlankLines = false;
        let IIFEs = false;

        if (typeof option === "object") {
            maxLines = typeof option.max === "number" ? option.max : 50;
            skipComments = !!option.skipComments;
            skipBlankLines = !!option.skipBlankLines;
            IIFEs = !!option.IIFEs;
        } else if (typeof option === "number") {
            maxLines = option;
        }

        const commentLineNumbers = getCommentLineNumbers(sourceCode.getAllComments());

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

        /**
         * Tells if a comment encompasses the entire line.
         * @param {string} line The source line with a trailing comment
         * @param {number} lineNumber The one-indexed line number this is on
         * @param {ASTNode} comment The comment to remove
         * @returns {boolean} If the comment covers the entire line
         */
        function isFullLineComment(line, lineNumber, comment) {
            const start = comment.loc.start,
                end = comment.loc.end,
                isFirstTokenOnLine = start.line === lineNumber && !line.slice(0, start.column).trim(),
                isLastTokenOnLine = end.line === lineNumber && !line.slice(end.column).trim();

            return comment &&
                (start.line < lineNumber || isFirstTokenOnLine) &&
                (end.line > lineNumber || isLastTokenOnLine);
        }

        /**
         * Identifies is a node is a FunctionExpression which is part of an IIFE
         * @param {ASTNode} node Node to test
         * @returns {boolean} True if it's an IIFE
         */
        function isIIFE(node) {
            return (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
        }

        /**
         * Identifies is a node is a FunctionExpression which is embedded within a MethodDefinition or Property
         * @param {ASTNode} node Node to test
         * @returns {boolean} True if it's a FunctionExpression embedded within a MethodDefinition or Property
         */
        function isEmbedded(node) {
            if (!node.parent) {
                return false;
            }
            if (node !== node.parent.value) {
                return false;
            }
            if (node.parent.type === "MethodDefinition") {
                return true;
            }
            if (node.parent.type === "Property") {
                return node.parent.method === true || node.parent.kind === "get" || node.parent.kind === "set";
            }
            return false;
        }

        /**
         * Count the lines in the function
         * @param {ASTNode} funcNode Function AST node
         * @returns {void}
         * @private
         */
        function processFunction(funcNode) {
            const node = isEmbedded(funcNode) ? funcNode.parent : funcNode;

            if (!IIFEs && isIIFE(node)) {
                return;
            }
            let lineCount = 0;

            for (let i = node.loc.start.line - 1; i < node.loc.end.line; ++i) {
                const line = lines[i];

                if (skipComments) {
                    if (commentLineNumbers.has(i + 1) && isFullLineComment(line, i + 1, commentLineNumbers.get(i + 1))) {
                        continue;
                    }
                }

                if (skipBlankLines) {
                    if (line.match(/^\s*$/u)) {
                        continue;
                    }
                }

                lineCount++;
            }

            if (lineCount > maxLines) {
                const name = upperCaseFirst(astUtils.getFunctionNameWithKind(funcNode));

                context.report({
                    node,
                    messageId: "exceed",
                    data: { name, lineCount, maxLines }
                });
            }
        }

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

        return {
            FunctionDeclaration: processFunction,
            FunctionExpression: processFunction,
            ArrowFunctionExpression: processFunction
        };
    }
};


code:
/**
 * @fileoverview Rule to enforce a maximum number of nested callbacks.
 * @author Ian Christian Myers
 */

"use strict";

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

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

        docs: {
            description: "Enforce a maximum depth that callbacks can be nested",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/max-nested-callbacks"
        },

        schema: [
            {
                oneOf: [
                    {
                        type: "integer",
                        minimum: 0
                    },
                    {
                        type: "object",
                        properties: {
                            maximum: {
                                type: "integer",
                                minimum: 0
                            },
                            max: {
                                type: "integer",
                                minimum: 0
                            }
                        },
                        additionalProperties: false
                    }
                ]
            }
        ],
        messages: {
            exceed: "Too many nested callbacks ({{num}}). Maximum allowed is {{max}}."
        }
    },

    create(context) {

        //--------------------------------------------------------------------------
        // Constants
        //--------------------------------------------------------------------------
        const option = context.options[0];
        let THRESHOLD = 10;

        if (
            typeof option === "object" &&
            (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max"))
        ) {
            THRESHOLD = option.maximum || option.max;
        } else if (typeof option === "number") {
            THRESHOLD = option;
        }

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

        const callbackStack = [];

        /**
         * Checks a given function node for too many callbacks.
         * @param {ASTNode} node The node to check.
         * @returns {void}
         * @private
         */
        function checkFunction(node) {
            const parent = node.parent;

            if (parent.type === "CallExpression") {
                callbackStack.push(node);
            }

            if (callbackStack.length > THRESHOLD) {
                const opts = { num: callbackStack.length, max: THRESHOLD };

                context.report({ node, messageId: "exceed", data: opts });
            }
        }

        /**
         * Pops the call stack.
         * @returns {void}
         * @private
         */
        function popStack() {
            callbackStack.pop();
        }

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

        return {
            ArrowFunctionExpression: checkFunction,
            "ArrowFunctionExpression:exit": popStack,

            FunctionExpression: checkFunction,
            "FunctionExpression:exit": popStack
        };

    }
};


code:
/**
 * @fileoverview Rule to flag when a function has too many parameters
 * @author Ilya Volodin
 */

"use strict";

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

const astUtils = require("./utils/ast-utils");
const { upperCaseFirst } = require("../shared/string-utils");

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

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

        docs: {
            description: "Enforce a maximum number of parameters in function definitions",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/max-params"
        },

        schema: [
            {
                oneOf: [
                    {
                        type: "integer",
                        minimum: 0
                    },
                    {
                        type: "object",
                        properties: {
                            maximum: {
                                type: "integer",
                                minimum: 0
                            },
                            max: {
                                type: "integer",
                                minimum: 0
                            }
                        },
                        additionalProperties: false
                    }
                ]
            }
        ],
        messages: {
            exceed: "{{name}} has too many parameters ({{count}}). Maximum allowed is {{max}}."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;
        const option = context.options[0];
        let numParams = 3;

        if (
            typeof option === "object" &&
            (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max"))
        ) {
            numParams = option.maximum || option.max;
        }
        if (typeof option === "number") {
            numParams = option;
        }

        /**
         * Checks a function to see if it has too many parameters.
         * @param {ASTNode} node The node to check.
         * @returns {void}
         * @private
         */
        function checkFunction(node) {
            if (node.params.length > numParams) {
                context.report({
                    loc: astUtils.getFunctionHeadLoc(node, sourceCode),
                    node,
                    messageId: "exceed",
                    data: {
                        name: upperCaseFirst(astUtils.getFunctionNameWithKind(node)),
                        count: node.params.length,
                        max: numParams
                    }
                });
            }
        }

        return {
            FunctionDeclaration: checkFunction,
            ArrowFunctionExpression: checkFunction,
            FunctionExpression: checkFunction
        };

    }
};


code:
/**
 * @fileoverview Specify the maximum number of statements allowed per line.
 * @author Kenneth Williams
 * @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 a maximum number of statements allowed per line",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/max-statements-per-line"
        },

        schema: [
            {
                type: "object",
                properties: {
                    max: {
                        type: "integer",
                        minimum: 1,
                        default: 1
                    }
                },
                additionalProperties: false
            }
        ],
        messages: {
            exceed: "This line has {{numberOfStatementsOnThisLine}} {{statements}}. Maximum allowed is {{maxStatementsPerLine}}."
        }
    },

    create(context) {

        const sourceCode = context.sourceCode,
            options = context.options[0] || {},
            maxStatementsPerLine = typeof options.max !== "undefined" ? options.max : 1;

        let lastStatementLine = 0,
            numberOfStatementsOnThisLine = 0,
            firstExtraStatement;

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

        const SINGLE_CHILD_ALLOWED = /^(?:(?:DoWhile|For|ForIn|ForOf|If|Labeled|While)Statement|Export(?:Default|Named)Declaration)$/u;

        /**
         * Reports with the first extra statement, and clears it.
         * @returns {void}
         */
        function reportFirstExtraStatementAndClear() {
            if (firstExtraStatement) {
                context.report({
                    node: firstExtraStatement,
                    messageId: "exceed",
                    data: {
                        numberOfStatementsOnThisLine,
                        maxStatementsPerLine,
                        statements: numberOfStatementsOnThisLine === 1 ? "statement" : "statements"
                    }
                });
            }
            firstExtraStatement = null;
        }

        /**
         * Gets the actual last token of a given node.
         * @param {ASTNode} node A node to get. This is a node except EmptyStatement.
         * @returns {Token} The actual last token.
         */
        function getActualLastToken(node) {
            return sourceCode.getLastToken(node, astUtils.isNotSemicolonToken);
        }

        /**
         * Addresses a given node.
         * It updates the state of this rule, then reports the node if the node violated this rule.
         * @param {ASTNode} node A node to check.
         * @returns {void}
         */
        function enterStatement(node) {
            const line = node.loc.start.line;

            /*
             * Skip to allow non-block statements if this is direct child of control statements.
             * `if (a) foo();` is counted as 1.
             * But `if (a) foo(); else foo();` should be counted as 2.
             */
            if (SINGLE_CHILD_ALLOWED.test(node.parent.type) &&
                node.parent.alternate !== node
            ) {
                return;
            }

            // Update state.
            if (line === lastStatementLine) {
                numberOfStatementsOnThisLine += 1;
            } else {
                reportFirstExtraStatementAndClear();
                numberOfStatementsOnThisLine = 1;
                lastStatementLine = line;
            }

            // Reports if the node violated this rule.
            if (numberOfStatementsOnThisLine === maxStatementsPerLine + 1) {
                firstExtraStatement = firstExtraStatement || node;
            }
        }

        /**
         * Updates the state of this rule with the end line of leaving node to check with the next statement.
         * @param {ASTNode} node A node to check.
         * @returns {void}
         */
        function leaveStatement(node) {
            const line = getActualLastToken(node).loc.end.line;

            // Update state.
            if (line !== lastStatementLine) {
                reportFirstExtraStatementAndClear();
                numberOfStatementsOnThisLine = 1;
                lastStatementLine = line;
            }
        }

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

        return {
            BreakStatement: enterStatement,
            ClassDeclaration: enterStatement,
            ContinueStatement: enterStatement,
            DebuggerStatement: enterStatement,
            DoWhileStatement: enterStatement,
            ExpressionStatement: enterStatement,
            ForInStatement: enterStatement,
            ForOfStatement: enterStatement,
            ForStatement: enterStatement,
            FunctionDeclaration: enterStatement,
            IfStatement: enterStatement,
            ImportDeclaration: enterStatement,
            LabeledStatement: enterStatement,
            ReturnStatement: enterStatement,
            SwitchStatement: enterStatement,
            ThrowStatement: enterStatement,
            TryStatement: enterStatement,
            VariableDeclaration: enterStatement,
            WhileStatement: enterStatement,
            WithStatement: enterStatement,
            ExportNamedDeclaration: enterStatement,
            ExportDefaultDeclaration: enterStatement,
            ExportAllDeclaration: enterStatement,

            "BreakStatement:exit": leaveStatement,
            "ClassDeclaration:exit": leaveStatement,
            "ContinueStatement:exit": leaveStatement,
            "DebuggerStatement:exit": leaveStatement,
            "DoWhileStatement:exit": leaveStatement,
            "ExpressionStatement:exit": leaveStatement,
            "ForInStatement:exit": leaveStatement,
            "ForOfStatement:exit": leaveStatement,
            "ForStatement:exit": leaveStatement,
            "FunctionDeclaration:exit": leaveStatement,
            "IfStatement:exit": leaveStatement,
            "ImportDeclaration:exit": leaveStatement,
            "LabeledStatement:exit": leaveStatement,
            "ReturnStatement:exit": leaveStatement,
            "SwitchStatement:exit": leaveStatement,
            "ThrowStatement:exit": leaveStatement,
            "TryStatement:exit": leaveStatement,
            "VariableDeclaration:exit": leaveStatement,
            "WhileStatement:exit": leaveStatement,
            "WithStatement:exit": leaveStatement,
            "ExportNamedDeclaration:exit": leaveStatement,
            "ExportDefaultDeclaration:exit": leaveStatement,
            "ExportAllDeclaration:exit": leaveStatement,
            "Program:exit": reportFirstExtraStatementAndClear
        };
    }
};


code:
/**
 * @fileoverview A rule to set the maximum number of statements in a function.
 * @author Ian Christian Myers
 */

"use strict";

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

const astUtils = require("./utils/ast-utils");
const { upperCaseFirst } = require("../shared/string-utils");

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

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

        docs: {
            description: "Enforce a maximum number of statements allowed in function blocks",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/max-statements"
        },

        schema: [
            {
                oneOf: [
                    {
                        type: "integer",
                        minimum: 0
                    },
                    {
                        type: "object",
                        properties: {
                            maximum: {
                                type: "integer",
                                minimum: 0
                            },
                            max: {
                                type: "integer",
                                minimum: 0
                            }
                        },
                        additionalProperties: false
                    }
                ]
            },
            {
                type: "object",
                properties: {
                    ignoreTopLevelFunctions: {
                        type: "boolean"
                    }
                },
                additionalProperties: false
            }
        ],
        messages: {
            exceed: "{{name}} has too many statements ({{count}}). Maximum allowed is {{max}}."
        }
    },

    create(context) {

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

        const functionStack = [],
            option = context.options[0],
            ignoreTopLevelFunctions = context.options[1] && context.options[1].ignoreTopLevelFunctions || false,
            topLevelFunctions = [];
        let maxStatements = 10;

        if (
            typeof option === "object" &&
            (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max"))
        ) {
            maxStatements = option.maximum || option.max;
        } else if (typeof option === "number") {
            maxStatements = option;
        }

        /**
         * Reports a node if it has too many statements
         * @param {ASTNode} node node to evaluate
         * @param {int} count Number of statements in node
         * @param {int} max Maximum number of statements allowed
         * @returns {void}
         * @private
         */
        function reportIfTooManyStatements(node, count, max) {
            if (count > max) {
                const name = upperCaseFirst(astUtils.getFunctionNameWithKind(node));

                context.report({
                    node,
                    messageId: "exceed",
                    data: { name, count, max }
                });
            }
        }

        /**
         * When parsing a new function, store it in our function stack
         * @returns {void}
         * @private
         */
        function startFunction() {
            functionStack.push(0);
        }

        /**
         * Evaluate the node at the end of function
         * @param {ASTNode} node node to evaluate
         * @returns {void}
         * @private
         */
        function endFunction(node) {
            const count = functionStack.pop();

            /*
             * This rule does not apply to class static blocks, but we have to track them so
             * that statements in them do not count as statements in the enclosing function.
             */
            if (node.type === "StaticBlock") {
                return;
            }

            if (ignoreTopLevelFunctions && functionStack.length === 0) {
                topLevelFunctions.push({ node, count });
            } else {
                reportIfTooManyStatements(node, count, maxStatements);
            }
        }

        /**
         * Increment the count of the functions
         * @param {ASTNode} node node to evaluate
         * @returns {void}
         * @private
         */
        function countStatements(node) {
            functionStack[functionStack.length - 1] += node.body.length;
        }

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

        return {
            FunctionDeclaration: startFunction,
            FunctionExpression: startFunction,
            ArrowFunctionExpression: startFunction,
            StaticBlock: startFunction,

            BlockStatement: countStatements,

            "FunctionDeclaration:exit": endFunction,
            "FunctionExpression:exit": endFunction,
            "ArrowFunctionExpression:exit": endFunction,
            "StaticBlock:exit": endFunction,

            "Program:exit"() {
                if (topLevelFunctions.length === 1) {
                    return;
                }

                topLevelFunctions.forEach(element => {
                    const count = element.count;
                    const node = element.node;

                    reportIfTooManyStatements(node, count, maxStatements);
                });
            }
        };

    }
};