| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 | /** * @fileoverview This rule shoud require or disallow spaces before or after unary operations. * @author Marcin Kumorek */"use strict";//------------------------------------------------------------------------------// Requirements//------------------------------------------------------------------------------const astUtils = require("../ast-utils");//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------module.exports = {    meta: {        docs: {            description: "enforce consistent spacing before or after unary operators",            category: "Stylistic Issues",            recommended: false,            url: "https://eslint.org/docs/rules/space-unary-ops"        },        fixable: "whitespace",        schema: [            {                type: "object",                properties: {                    words: {                        type: "boolean"                    },                    nonwords: {                        type: "boolean"                    },                    overrides: {                        type: "object",                        additionalProperties: {                            type: "boolean"                        }                    }                },                additionalProperties: false            }        ]    },    create(context) {        const options = context.options && Array.isArray(context.options) && context.options[0] || { words: true, nonwords: false };        const sourceCode = context.getSourceCode();        //--------------------------------------------------------------------------        // Helpers        //--------------------------------------------------------------------------        /**         * Check if the node is the first "!" in a "!!" convert to Boolean expression         * @param {ASTnode} node AST node         * @returns {boolean} Whether or not the node is first "!" in "!!"         */        function isFirstBangInBangBangExpression(node) {            return node && node.type === "UnaryExpression" && node.argument.operator === "!" &&                node.argument && node.argument.type === "UnaryExpression" && node.argument.operator === "!";        }        /**         * Checks if an override exists for a given operator.         * @param {string} operator Operator         * @returns {boolean} Whether or not an override has been provided for the operator         */        function overrideExistsForOperator(operator) {            return options.overrides && options.overrides.hasOwnProperty(operator);        }        /**         * Gets the value that the override was set to for this operator         * @param {string} operator Operator         * @returns {boolean} Whether or not an override enforces a space with this operator         */        function overrideEnforcesSpaces(operator) {            return options.overrides[operator];        }        /**         * Verify Unary Word Operator has spaces after the word operator         * @param {ASTnode} node AST node         * @param {Object} firstToken first token from the AST node         * @param {Object} secondToken second token from the AST node         * @param {string} word The word to be used for reporting         * @returns {void}         */        function verifyWordHasSpaces(node, firstToken, secondToken, word) {            if (secondToken.range[0] === firstToken.range[1]) {                context.report({                    node,                    message: "Unary word operator '{{word}}' must be followed by whitespace.",                    data: {                        word                    },                    fix(fixer) {                        return fixer.insertTextAfter(firstToken, " ");                    }                });            }        }        /**         * Verify Unary Word Operator doesn't have spaces after the word operator         * @param {ASTnode} node AST node         * @param {Object} firstToken first token from the AST node         * @param {Object} secondToken second token from the AST node         * @param {string} word The word to be used for reporting         * @returns {void}         */        function verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word) {            if (astUtils.canTokensBeAdjacent(firstToken, secondToken)) {                if (secondToken.range[0] > firstToken.range[1]) {                    context.report({                        node,                        message: "Unexpected space after unary word operator '{{word}}'.",                        data: {                            word                        },                        fix(fixer) {                            return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);                        }                    });                }            }        }        /**         * Check Unary Word Operators for spaces after the word operator         * @param {ASTnode} node AST node         * @param {Object} firstToken first token from the AST node         * @param {Object} secondToken second token from the AST node         * @param {string} word The word to be used for reporting         * @returns {void}         */        function checkUnaryWordOperatorForSpaces(node, firstToken, secondToken, word) {            if (overrideExistsForOperator(word)) {                if (overrideEnforcesSpaces(word)) {                    verifyWordHasSpaces(node, firstToken, secondToken, word);                } else {                    verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word);                }            } else if (options.words) {                verifyWordHasSpaces(node, firstToken, secondToken, word);            } else {                verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word);            }        }        /**         * Verifies YieldExpressions satisfy spacing requirements         * @param {ASTnode} node AST node         * @returns {void}         */        function checkForSpacesAfterYield(node) {            const tokens = sourceCode.getFirstTokens(node, 3),                word = "yield";            if (!node.argument || node.delegate) {                return;            }            checkUnaryWordOperatorForSpaces(node, tokens[0], tokens[1], word);        }        /**         * Verifies AwaitExpressions satisfy spacing requirements         * @param {ASTNode} node AwaitExpression AST node         * @returns {void}         */        function checkForSpacesAfterAwait(node) {            const tokens = sourceCode.getFirstTokens(node, 3);            checkUnaryWordOperatorForSpaces(node, tokens[0], tokens[1], "await");        }        /**         * Verifies UnaryExpression, UpdateExpression and NewExpression have spaces before or after the operator         * @param {ASTnode} node AST node         * @param {Object} firstToken First token in the expression         * @param {Object} secondToken Second token in the expression         * @returns {void}         */        function verifyNonWordsHaveSpaces(node, firstToken, secondToken) {            if (node.prefix) {                if (isFirstBangInBangBangExpression(node)) {                    return;                }                if (firstToken.range[1] === secondToken.range[0]) {                    context.report({                        node,                        message: "Unary operator '{{operator}}' must be followed by whitespace.",                        data: {                            operator: firstToken.value                        },                        fix(fixer) {                            return fixer.insertTextAfter(firstToken, " ");                        }                    });                }            } else {                if (firstToken.range[1] === secondToken.range[0]) {                    context.report({                        node,                        message: "Space is required before unary expressions '{{token}}'.",                        data: {                            token: secondToken.value                        },                        fix(fixer) {                            return fixer.insertTextBefore(secondToken, " ");                        }                    });                }            }        }        /**         * Verifies UnaryExpression, UpdateExpression and NewExpression don't have spaces before or after the operator         * @param {ASTnode} node AST node         * @param {Object} firstToken First token in the expression         * @param {Object} secondToken Second token in the expression         * @returns {void}         */        function verifyNonWordsDontHaveSpaces(node, firstToken, secondToken) {            if (node.prefix) {                if (secondToken.range[0] > firstToken.range[1]) {                    context.report({                        node,                        message: "Unexpected space after unary operator '{{operator}}'.",                        data: {                            operator: firstToken.value                        },                        fix(fixer) {                            if (astUtils.canTokensBeAdjacent(firstToken, secondToken)) {                                return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);                            }                            return null;                        }                    });                }            } else {                if (secondToken.range[0] > firstToken.range[1]) {                    context.report({                        node,                        message: "Unexpected space before unary operator '{{operator}}'.",                        data: {                            operator: secondToken.value                        },                        fix(fixer) {                            return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);                        }                    });                }            }        }        /**         * Verifies UnaryExpression, UpdateExpression and NewExpression satisfy spacing requirements         * @param {ASTnode} node AST node         * @returns {void}         */        function checkForSpaces(node) {            const tokens = node.type === "UpdateExpression" && !node.prefix                ? sourceCode.getLastTokens(node, 2)                : sourceCode.getFirstTokens(node, 2);            const firstToken = tokens[0];            const secondToken = tokens[1];            if ((node.type === "NewExpression" || node.prefix) && firstToken.type === "Keyword") {                checkUnaryWordOperatorForSpaces(node, firstToken, secondToken, firstToken.value);                return;            }            const operator = node.prefix ? tokens[0].value : tokens[1].value;            if (overrideExistsForOperator(operator)) {                if (overrideEnforcesSpaces(operator)) {                    verifyNonWordsHaveSpaces(node, firstToken, secondToken);                } else {                    verifyNonWordsDontHaveSpaces(node, firstToken, secondToken);                }            } else if (options.nonwords) {                verifyNonWordsHaveSpaces(node, firstToken, secondToken);            } else {                verifyNonWordsDontHaveSpaces(node, firstToken, secondToken);            }        }        //--------------------------------------------------------------------------        // Public        //--------------------------------------------------------------------------        return {            UnaryExpression: checkForSpaces,            UpdateExpression: checkForSpaces,            NewExpression: checkForSpaces,            YieldExpression: checkForSpacesAfterYield,            AwaitExpression: checkForSpacesAfterAwait        };    }};
 |