| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- /**
- * @fileoverview This rule should require or disallow spaces before or after unary operations.
- * @author Marcin Kumorek
- * @deprecated in ESLint v8.53.0
- */
- "use strict";
- //------------------------------------------------------------------------------
- // Requirements
- //------------------------------------------------------------------------------
- const astUtils = require("./utils/ast-utils");
- //------------------------------------------------------------------------------
- // Rule Definition
- //------------------------------------------------------------------------------
- /** @type {import('../types').Rule.RuleModule} */
- module.exports = {
- meta: {
- deprecated: {
- message: "Formatting rules are being moved out of ESLint core.",
- url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/",
- deprecatedSince: "8.53.0",
- availableUntil: "11.0.0",
- replacedBy: [
- {
- message:
- "ESLint Stylistic now maintains deprecated stylistic core rules.",
- url: "https://eslint.style/guide/migration",
- plugin: {
- name: "@stylistic/eslint-plugin",
- url: "https://eslint.style",
- },
- rule: {
- name: "space-unary-ops",
- url: "https://eslint.style/rules/space-unary-ops",
- },
- },
- ],
- },
- type: "layout",
- docs: {
- description:
- "Enforce consistent spacing before or after unary operators",
- recommended: false,
- url: "https://eslint.org/docs/latest/rules/space-unary-ops",
- },
- fixable: "whitespace",
- schema: [
- {
- type: "object",
- properties: {
- words: {
- type: "boolean",
- default: true,
- },
- nonwords: {
- type: "boolean",
- default: false,
- },
- overrides: {
- type: "object",
- additionalProperties: {
- type: "boolean",
- },
- },
- },
- additionalProperties: false,
- },
- ],
- messages: {
- unexpectedBefore:
- "Unexpected space before unary operator '{{operator}}'.",
- unexpectedAfter:
- "Unexpected space after unary operator '{{operator}}'.",
- unexpectedAfterWord:
- "Unexpected space after unary word operator '{{word}}'.",
- wordOperator:
- "Unary word operator '{{word}}' must be followed by whitespace.",
- operator:
- "Unary operator '{{operator}}' must be followed by whitespace.",
- beforeUnaryExpressions:
- "Space is required before unary expressions '{{token}}'.",
- },
- },
- create(context) {
- const options = context.options[0] || { words: true, nonwords: false };
- const sourceCode = context.sourceCode;
- //--------------------------------------------------------------------------
- // 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 && Object.hasOwn(options.overrides, 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,
- messageId: "wordOperator",
- 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,
- messageId: "unexpectedAfterWord",
- 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,
- messageId: "operator",
- data: {
- operator: firstToken.value,
- },
- fix(fixer) {
- return fixer.insertTextAfter(firstToken, " ");
- },
- });
- }
- } else {
- if (firstToken.range[1] === secondToken.range[0]) {
- context.report({
- node,
- messageId: "beforeUnaryExpressions",
- 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,
- messageId: "unexpectedAfter",
- 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,
- messageId: "unexpectedBefore",
- 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,
- };
- },
- };
|