| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 | /** * @fileoverview Rule to flag statements that use != and == instead of !== and === * @author Nicholas C. Zakas */"use strict";//------------------------------------------------------------------------------// Requirements//------------------------------------------------------------------------------const astUtils = require("./utils/ast-utils");//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        type: "suggestion",        docs: {            description: "Require the use of `===` and `!==`",            recommended: false,            url: "https://eslint.org/docs/rules/eqeqeq"        },        schema: {            anyOf: [                {                    type: "array",                    items: [                        {                            enum: ["always"]                        },                        {                            type: "object",                            properties: {                                null: {                                    enum: ["always", "never", "ignore"]                                }                            },                            additionalProperties: false                        }                    ],                    additionalItems: false                },                {                    type: "array",                    items: [                        {                            enum: ["smart", "allow-null"]                        }                    ],                    additionalItems: false                }            ]        },        fixable: "code",        messages: {            unexpected: "Expected '{{expectedOperator}}' and instead saw '{{actualOperator}}'."        }    },    create(context) {        const config = context.options[0] || "always";        const options = context.options[1] || {};        const sourceCode = context.getSourceCode();        const nullOption = (config === "always")            ? options.null || "always"            : "ignore";        const enforceRuleForNull = (nullOption === "always");        const enforceInverseRuleForNull = (nullOption === "never");        /**         * Checks if an expression is a typeof expression         * @param {ASTNode} node The node to check         * @returns {boolean} if the node is a typeof expression         */        function isTypeOf(node) {            return node.type === "UnaryExpression" && node.operator === "typeof";        }        /**         * Checks if either operand of a binary expression is a typeof operation         * @param {ASTNode} node The node to check         * @returns {boolean} if one of the operands is typeof         * @private         */        function isTypeOfBinary(node) {            return isTypeOf(node.left) || isTypeOf(node.right);        }        /**         * Checks if operands are literals of the same type (via typeof)         * @param {ASTNode} node The node to check         * @returns {boolean} if operands are of same type         * @private         */        function areLiteralsAndSameType(node) {            return node.left.type === "Literal" && node.right.type === "Literal" &&                    typeof node.left.value === typeof node.right.value;        }        /**         * Checks if one of the operands is a literal null         * @param {ASTNode} node The node to check         * @returns {boolean} if operands are null         * @private         */        function isNullCheck(node) {            return astUtils.isNullLiteral(node.right) || astUtils.isNullLiteral(node.left);        }        /**         * Reports a message for this rule.         * @param {ASTNode} node The binary expression node that was checked         * @param {string} expectedOperator The operator that was expected (either '==', '!=', '===', or '!==')         * @returns {void}         * @private         */        function report(node, expectedOperator) {            const operatorToken = sourceCode.getFirstTokenBetween(                node.left,                node.right,                token => token.value === node.operator            );            context.report({                node,                loc: operatorToken.loc,                messageId: "unexpected",                data: { expectedOperator, actualOperator: node.operator },                fix(fixer) {                    // If the comparison is a `typeof` comparison or both sides are literals with the same type, then it's safe to fix.                    if (isTypeOfBinary(node) || areLiteralsAndSameType(node)) {                        return fixer.replaceText(operatorToken, expectedOperator);                    }                    return null;                }            });        }        return {            BinaryExpression(node) {                const isNull = isNullCheck(node);                if (node.operator !== "==" && node.operator !== "!=") {                    if (enforceInverseRuleForNull && isNull) {                        report(node, node.operator.slice(0, -1));                    }                    return;                }                if (config === "smart" && (isTypeOfBinary(node) ||                        areLiteralsAndSameType(node) || isNull)) {                    return;                }                if (!enforceRuleForNull && isNull) {                    return;                }                report(node, `${node.operator}=`);            }        };    }};
 |