.
This commit is contained in:
		
							
								
								
									
										353
									
								
								qwen/nodejs/node_modules/eslint/lib/rules/yoda.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								qwen/nodejs/node_modules/eslint/lib/rules/yoda.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,353 @@
 | 
			
		||||
/**
 | 
			
		||||
 * @fileoverview Rule to require or disallow yoda comparisons
 | 
			
		||||
 * @author Nicholas C. Zakas
 | 
			
		||||
 */
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
//--------------------------------------------------------------------------
 | 
			
		||||
// Requirements
 | 
			
		||||
//--------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
const astUtils = require("./utils/ast-utils");
 | 
			
		||||
 | 
			
		||||
//--------------------------------------------------------------------------
 | 
			
		||||
// Helpers
 | 
			
		||||
//--------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determines whether an operator is a comparison operator.
 | 
			
		||||
 * @param {string} operator The operator to check.
 | 
			
		||||
 * @returns {boolean} Whether or not it is a comparison operator.
 | 
			
		||||
 */
 | 
			
		||||
function isComparisonOperator(operator) {
 | 
			
		||||
    return /^(==|===|!=|!==|<|>|<=|>=)$/u.test(operator);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determines whether an operator is an equality operator.
 | 
			
		||||
 * @param {string} operator The operator to check.
 | 
			
		||||
 * @returns {boolean} Whether or not it is an equality operator.
 | 
			
		||||
 */
 | 
			
		||||
function isEqualityOperator(operator) {
 | 
			
		||||
    return /^(==|===)$/u.test(operator);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determines whether an operator is one used in a range test.
 | 
			
		||||
 * Allowed operators are `<` and `<=`.
 | 
			
		||||
 * @param {string} operator The operator to check.
 | 
			
		||||
 * @returns {boolean} Whether the operator is used in range tests.
 | 
			
		||||
 */
 | 
			
		||||
function isRangeTestOperator(operator) {
 | 
			
		||||
    return ["<", "<="].includes(operator);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determines whether a non-Literal node is a negative number that should be
 | 
			
		||||
 * treated as if it were a single Literal node.
 | 
			
		||||
 * @param {ASTNode} node Node to test.
 | 
			
		||||
 * @returns {boolean} True if the node is a negative number that looks like a
 | 
			
		||||
 *                    real literal and should be treated as such.
 | 
			
		||||
 */
 | 
			
		||||
function isNegativeNumericLiteral(node) {
 | 
			
		||||
    return (
 | 
			
		||||
        node.type === "UnaryExpression" &&
 | 
			
		||||
        node.operator === "-" &&
 | 
			
		||||
        node.prefix &&
 | 
			
		||||
        astUtils.isNumericLiteral(node.argument)
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determines whether a non-Literal node should be treated as a single Literal node.
 | 
			
		||||
 * @param {ASTNode} node Node to test
 | 
			
		||||
 * @returns {boolean} True if the node should be treated as a single Literal node.
 | 
			
		||||
 */
 | 
			
		||||
function looksLikeLiteral(node) {
 | 
			
		||||
    return isNegativeNumericLiteral(node) || astUtils.isStaticTemplateLiteral(node);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Attempts to derive a Literal node from nodes that are treated like literals.
 | 
			
		||||
 * @param {ASTNode} node Node to normalize.
 | 
			
		||||
 * @returns {ASTNode} One of the following options.
 | 
			
		||||
 *  1. The original node if the node is already a Literal
 | 
			
		||||
 *  2. A normalized Literal node with the negative number as the value if the
 | 
			
		||||
 *     node represents a negative number literal.
 | 
			
		||||
 *  3. A normalized Literal node with the string as the value if the node is
 | 
			
		||||
 *     a Template Literal without expression.
 | 
			
		||||
 *  4. Otherwise `null`.
 | 
			
		||||
 */
 | 
			
		||||
function getNormalizedLiteral(node) {
 | 
			
		||||
    if (node.type === "Literal") {
 | 
			
		||||
        return node;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (isNegativeNumericLiteral(node)) {
 | 
			
		||||
        return {
 | 
			
		||||
            type: "Literal",
 | 
			
		||||
            value: -node.argument.value,
 | 
			
		||||
            raw: `-${node.argument.value}`
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (astUtils.isStaticTemplateLiteral(node)) {
 | 
			
		||||
        return {
 | 
			
		||||
            type: "Literal",
 | 
			
		||||
            value: node.quasis[0].value.cooked,
 | 
			
		||||
            raw: node.quasis[0].value.raw
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Rule Definition
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/** @type {import('../shared/types').Rule} */
 | 
			
		||||
module.exports = {
 | 
			
		||||
    meta: {
 | 
			
		||||
        type: "suggestion",
 | 
			
		||||
 | 
			
		||||
        docs: {
 | 
			
		||||
            description: 'Require or disallow "Yoda" conditions',
 | 
			
		||||
            recommended: false,
 | 
			
		||||
            url: "https://eslint.org/docs/latest/rules/yoda"
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        schema: [
 | 
			
		||||
            {
 | 
			
		||||
                enum: ["always", "never"]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                type: "object",
 | 
			
		||||
                properties: {
 | 
			
		||||
                    exceptRange: {
 | 
			
		||||
                        type: "boolean",
 | 
			
		||||
                        default: false
 | 
			
		||||
                    },
 | 
			
		||||
                    onlyEquality: {
 | 
			
		||||
                        type: "boolean",
 | 
			
		||||
                        default: false
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                additionalProperties: false
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
 | 
			
		||||
        fixable: "code",
 | 
			
		||||
        messages: {
 | 
			
		||||
            expected:
 | 
			
		||||
                "Expected literal to be on the {{expectedSide}} side of {{operator}}."
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    create(context) {
 | 
			
		||||
 | 
			
		||||
        // Default to "never" (!always) if no option
 | 
			
		||||
        const always = context.options[0] === "always";
 | 
			
		||||
        const exceptRange =
 | 
			
		||||
            context.options[1] && context.options[1].exceptRange;
 | 
			
		||||
        const onlyEquality =
 | 
			
		||||
            context.options[1] && context.options[1].onlyEquality;
 | 
			
		||||
 | 
			
		||||
        const sourceCode = context.sourceCode;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Determines whether node represents a range test.
 | 
			
		||||
         * A range test is a "between" test like `(0 <= x && x < 1)` or an "outside"
 | 
			
		||||
         * test like `(x < 0 || 1 <= x)`. It must be wrapped in parentheses, and
 | 
			
		||||
         * both operators must be `<` or `<=`. Finally, the literal on the left side
 | 
			
		||||
         * must be less than or equal to the literal on the right side so that the
 | 
			
		||||
         * test makes any sense.
 | 
			
		||||
         * @param {ASTNode} node LogicalExpression node to test.
 | 
			
		||||
         * @returns {boolean} Whether node is a range test.
 | 
			
		||||
         */
 | 
			
		||||
        function isRangeTest(node) {
 | 
			
		||||
            const left = node.left,
 | 
			
		||||
                right = node.right;
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Determines whether node is of the form `0 <= x && x < 1`.
 | 
			
		||||
             * @returns {boolean} Whether node is a "between" range test.
 | 
			
		||||
             */
 | 
			
		||||
            function isBetweenTest() {
 | 
			
		||||
                if (node.operator === "&&" && astUtils.isSameReference(left.right, right.left)) {
 | 
			
		||||
                    const leftLiteral = getNormalizedLiteral(left.left);
 | 
			
		||||
                    const rightLiteral = getNormalizedLiteral(right.right);
 | 
			
		||||
 | 
			
		||||
                    if (leftLiteral === null && rightLiteral === null) {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (rightLiteral === null || leftLiteral === null) {
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (leftLiteral.value <= rightLiteral.value) {
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Determines whether node is of the form `x < 0 || 1 <= x`.
 | 
			
		||||
             * @returns {boolean} Whether node is an "outside" range test.
 | 
			
		||||
             */
 | 
			
		||||
            function isOutsideTest() {
 | 
			
		||||
                if (node.operator === "||" && astUtils.isSameReference(left.left, right.right)) {
 | 
			
		||||
                    const leftLiteral = getNormalizedLiteral(left.right);
 | 
			
		||||
                    const rightLiteral = getNormalizedLiteral(right.left);
 | 
			
		||||
 | 
			
		||||
                    if (leftLiteral === null && rightLiteral === null) {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (rightLiteral === null || leftLiteral === null) {
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (leftLiteral.value <= rightLiteral.value) {
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Determines whether node is wrapped in parentheses.
 | 
			
		||||
             * @returns {boolean} Whether node is preceded immediately by an open
 | 
			
		||||
             *                    paren token and followed immediately by a close
 | 
			
		||||
             *                    paren token.
 | 
			
		||||
             */
 | 
			
		||||
            function isParenWrapped() {
 | 
			
		||||
                return astUtils.isParenthesised(sourceCode, node);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return (
 | 
			
		||||
                node.type === "LogicalExpression" &&
 | 
			
		||||
                left.type === "BinaryExpression" &&
 | 
			
		||||
                right.type === "BinaryExpression" &&
 | 
			
		||||
                isRangeTestOperator(left.operator) &&
 | 
			
		||||
                isRangeTestOperator(right.operator) &&
 | 
			
		||||
                (isBetweenTest() || isOutsideTest()) &&
 | 
			
		||||
                isParenWrapped()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const OPERATOR_FLIP_MAP = {
 | 
			
		||||
            "===": "===",
 | 
			
		||||
            "!==": "!==",
 | 
			
		||||
            "==": "==",
 | 
			
		||||
            "!=": "!=",
 | 
			
		||||
            "<": ">",
 | 
			
		||||
            ">": "<",
 | 
			
		||||
            "<=": ">=",
 | 
			
		||||
            ">=": "<="
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Returns a string representation of a BinaryExpression node with its sides/operator flipped around.
 | 
			
		||||
         * @param {ASTNode} node The BinaryExpression node
 | 
			
		||||
         * @returns {string} A string representation of the node with the sides and operator flipped
 | 
			
		||||
         */
 | 
			
		||||
        function getFlippedString(node) {
 | 
			
		||||
            const operatorToken = sourceCode.getFirstTokenBetween(
 | 
			
		||||
                node.left,
 | 
			
		||||
                node.right,
 | 
			
		||||
                token => token.value === node.operator
 | 
			
		||||
            );
 | 
			
		||||
            const lastLeftToken = sourceCode.getTokenBefore(operatorToken);
 | 
			
		||||
            const firstRightToken = sourceCode.getTokenAfter(operatorToken);
 | 
			
		||||
 | 
			
		||||
            const source = sourceCode.getText();
 | 
			
		||||
 | 
			
		||||
            const leftText = source.slice(
 | 
			
		||||
                node.range[0],
 | 
			
		||||
                lastLeftToken.range[1]
 | 
			
		||||
            );
 | 
			
		||||
            const textBeforeOperator = source.slice(
 | 
			
		||||
                lastLeftToken.range[1],
 | 
			
		||||
                operatorToken.range[0]
 | 
			
		||||
            );
 | 
			
		||||
            const textAfterOperator = source.slice(
 | 
			
		||||
                operatorToken.range[1],
 | 
			
		||||
                firstRightToken.range[0]
 | 
			
		||||
            );
 | 
			
		||||
            const rightText = source.slice(
 | 
			
		||||
                firstRightToken.range[0],
 | 
			
		||||
                node.range[1]
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            const tokenBefore = sourceCode.getTokenBefore(node);
 | 
			
		||||
            const tokenAfter = sourceCode.getTokenAfter(node);
 | 
			
		||||
            let prefix = "";
 | 
			
		||||
            let suffix = "";
 | 
			
		||||
 | 
			
		||||
            if (
 | 
			
		||||
                tokenBefore &&
 | 
			
		||||
                tokenBefore.range[1] === node.range[0] &&
 | 
			
		||||
                !astUtils.canTokensBeAdjacent(tokenBefore, firstRightToken)
 | 
			
		||||
            ) {
 | 
			
		||||
                prefix = " ";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (
 | 
			
		||||
                tokenAfter &&
 | 
			
		||||
                node.range[1] === tokenAfter.range[0] &&
 | 
			
		||||
                !astUtils.canTokensBeAdjacent(lastLeftToken, tokenAfter)
 | 
			
		||||
            ) {
 | 
			
		||||
                suffix = " ";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return (
 | 
			
		||||
                prefix +
 | 
			
		||||
                rightText +
 | 
			
		||||
                textBeforeOperator +
 | 
			
		||||
                OPERATOR_FLIP_MAP[operatorToken.value] +
 | 
			
		||||
                textAfterOperator +
 | 
			
		||||
                leftText +
 | 
			
		||||
                suffix
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //--------------------------------------------------------------------------
 | 
			
		||||
        // Public
 | 
			
		||||
        //--------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            BinaryExpression(node) {
 | 
			
		||||
                const expectedLiteral = always ? node.left : node.right;
 | 
			
		||||
                const expectedNonLiteral = always ? node.right : node.left;
 | 
			
		||||
 | 
			
		||||
                // If `expectedLiteral` is not a literal, and `expectedNonLiteral` is a literal, raise an error.
 | 
			
		||||
                if (
 | 
			
		||||
                    (expectedNonLiteral.type === "Literal" ||
 | 
			
		||||
                        looksLikeLiteral(expectedNonLiteral)) &&
 | 
			
		||||
                    !(
 | 
			
		||||
                        expectedLiteral.type === "Literal" ||
 | 
			
		||||
                        looksLikeLiteral(expectedLiteral)
 | 
			
		||||
                    ) &&
 | 
			
		||||
                    !(!isEqualityOperator(node.operator) && onlyEquality) &&
 | 
			
		||||
                    isComparisonOperator(node.operator) &&
 | 
			
		||||
                    !(exceptRange && isRangeTest(node.parent))
 | 
			
		||||
                ) {
 | 
			
		||||
                    context.report({
 | 
			
		||||
                        node,
 | 
			
		||||
                        messageId: "expected",
 | 
			
		||||
                        data: {
 | 
			
		||||
                            operator: node.operator,
 | 
			
		||||
                            expectedSide: always ? "left" : "right"
 | 
			
		||||
                        },
 | 
			
		||||
                        fix: fixer =>
 | 
			
		||||
                            fixer.replaceText(node, getFlippedString(node))
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user