286 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * @fileoverview Disallows or enforces spaces inside of parentheses.
 | |
|  * @author Jonathan Rajavuori
 | |
|  * @deprecated in ESLint v8.53.0
 | |
|  */
 | |
| "use strict";
 | |
| 
 | |
| const astUtils = require("./utils/ast-utils");
 | |
| 
 | |
| //------------------------------------------------------------------------------
 | |
| // Rule Definition
 | |
| //------------------------------------------------------------------------------
 | |
| 
 | |
| /** @type {import('../shared/types').Rule} */
 | |
| module.exports = {
 | |
|     meta: {
 | |
|         deprecated: true,
 | |
|         replacedBy: [],
 | |
|         type: "layout",
 | |
| 
 | |
|         docs: {
 | |
|             description: "Enforce consistent spacing inside parentheses",
 | |
|             recommended: false,
 | |
|             url: "https://eslint.org/docs/latest/rules/space-in-parens"
 | |
|         },
 | |
| 
 | |
|         fixable: "whitespace",
 | |
| 
 | |
|         schema: [
 | |
|             {
 | |
|                 enum: ["always", "never"]
 | |
|             },
 | |
|             {
 | |
|                 type: "object",
 | |
|                 properties: {
 | |
|                     exceptions: {
 | |
|                         type: "array",
 | |
|                         items: {
 | |
|                             enum: ["{}", "[]", "()", "empty"]
 | |
|                         },
 | |
|                         uniqueItems: true
 | |
|                     }
 | |
|                 },
 | |
|                 additionalProperties: false
 | |
|             }
 | |
|         ],
 | |
| 
 | |
|         messages: {
 | |
|             missingOpeningSpace: "There must be a space after this paren.",
 | |
|             missingClosingSpace: "There must be a space before this paren.",
 | |
|             rejectedOpeningSpace: "There should be no space after this paren.",
 | |
|             rejectedClosingSpace: "There should be no space before this paren."
 | |
|         }
 | |
|     },
 | |
| 
 | |
|     create(context) {
 | |
|         const ALWAYS = context.options[0] === "always",
 | |
|             exceptionsArrayOptions = (context.options[1] && context.options[1].exceptions) || [],
 | |
|             options = {};
 | |
| 
 | |
|         let exceptions;
 | |
| 
 | |
|         if (exceptionsArrayOptions.length) {
 | |
|             options.braceException = exceptionsArrayOptions.includes("{}");
 | |
|             options.bracketException = exceptionsArrayOptions.includes("[]");
 | |
|             options.parenException = exceptionsArrayOptions.includes("()");
 | |
|             options.empty = exceptionsArrayOptions.includes("empty");
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Produces an object with the opener and closer exception values
 | |
|          * @returns {Object} `openers` and `closers` exception values
 | |
|          * @private
 | |
|          */
 | |
|         function getExceptions() {
 | |
|             const openers = [],
 | |
|                 closers = [];
 | |
| 
 | |
|             if (options.braceException) {
 | |
|                 openers.push("{");
 | |
|                 closers.push("}");
 | |
|             }
 | |
| 
 | |
|             if (options.bracketException) {
 | |
|                 openers.push("[");
 | |
|                 closers.push("]");
 | |
|             }
 | |
| 
 | |
|             if (options.parenException) {
 | |
|                 openers.push("(");
 | |
|                 closers.push(")");
 | |
|             }
 | |
| 
 | |
|             if (options.empty) {
 | |
|                 openers.push(")");
 | |
|                 closers.push("(");
 | |
|             }
 | |
| 
 | |
|             return {
 | |
|                 openers,
 | |
|                 closers
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         //--------------------------------------------------------------------------
 | |
|         // Helpers
 | |
|         //--------------------------------------------------------------------------
 | |
|         const sourceCode = context.sourceCode;
 | |
| 
 | |
|         /**
 | |
|          * Determines if a token is one of the exceptions for the opener paren
 | |
|          * @param {Object} token The token to check
 | |
|          * @returns {boolean} True if the token is one of the exceptions for the opener paren
 | |
|          */
 | |
|         function isOpenerException(token) {
 | |
|             return exceptions.openers.includes(token.value);
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Determines if a token is one of the exceptions for the closer paren
 | |
|          * @param {Object} token The token to check
 | |
|          * @returns {boolean} True if the token is one of the exceptions for the closer paren
 | |
|          */
 | |
|         function isCloserException(token) {
 | |
|             return exceptions.closers.includes(token.value);
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Determines if an opening paren is immediately followed by a required space
 | |
|          * @param {Object} openingParenToken The paren token
 | |
|          * @param {Object} tokenAfterOpeningParen The token after it
 | |
|          * @returns {boolean} True if the opening paren is missing a required space
 | |
|          */
 | |
|         function openerMissingSpace(openingParenToken, tokenAfterOpeningParen) {
 | |
|             if (sourceCode.isSpaceBetweenTokens(openingParenToken, tokenAfterOpeningParen)) {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (!options.empty && astUtils.isClosingParenToken(tokenAfterOpeningParen)) {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (ALWAYS) {
 | |
|                 return !isOpenerException(tokenAfterOpeningParen);
 | |
|             }
 | |
|             return isOpenerException(tokenAfterOpeningParen);
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Determines if an opening paren is immediately followed by a disallowed space
 | |
|          * @param {Object} openingParenToken The paren token
 | |
|          * @param {Object} tokenAfterOpeningParen The token after it
 | |
|          * @returns {boolean} True if the opening paren has a disallowed space
 | |
|          */
 | |
|         function openerRejectsSpace(openingParenToken, tokenAfterOpeningParen) {
 | |
|             if (!astUtils.isTokenOnSameLine(openingParenToken, tokenAfterOpeningParen)) {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (tokenAfterOpeningParen.type === "Line") {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (!sourceCode.isSpaceBetweenTokens(openingParenToken, tokenAfterOpeningParen)) {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (ALWAYS) {
 | |
|                 return isOpenerException(tokenAfterOpeningParen);
 | |
|             }
 | |
|             return !isOpenerException(tokenAfterOpeningParen);
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Determines if a closing paren is immediately preceded by a required space
 | |
|          * @param {Object} tokenBeforeClosingParen The token before the paren
 | |
|          * @param {Object} closingParenToken The paren token
 | |
|          * @returns {boolean} True if the closing paren is missing a required space
 | |
|          */
 | |
|         function closerMissingSpace(tokenBeforeClosingParen, closingParenToken) {
 | |
|             if (sourceCode.isSpaceBetweenTokens(tokenBeforeClosingParen, closingParenToken)) {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (!options.empty && astUtils.isOpeningParenToken(tokenBeforeClosingParen)) {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (ALWAYS) {
 | |
|                 return !isCloserException(tokenBeforeClosingParen);
 | |
|             }
 | |
|             return isCloserException(tokenBeforeClosingParen);
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Determines if a closer paren is immediately preceded by a disallowed space
 | |
|          * @param {Object} tokenBeforeClosingParen The token before the paren
 | |
|          * @param {Object} closingParenToken The paren token
 | |
|          * @returns {boolean} True if the closing paren has a disallowed space
 | |
|          */
 | |
|         function closerRejectsSpace(tokenBeforeClosingParen, closingParenToken) {
 | |
|             if (!astUtils.isTokenOnSameLine(tokenBeforeClosingParen, closingParenToken)) {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (!sourceCode.isSpaceBetweenTokens(tokenBeforeClosingParen, closingParenToken)) {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (ALWAYS) {
 | |
|                 return isCloserException(tokenBeforeClosingParen);
 | |
|             }
 | |
|             return !isCloserException(tokenBeforeClosingParen);
 | |
|         }
 | |
| 
 | |
|         //--------------------------------------------------------------------------
 | |
|         // Public
 | |
|         //--------------------------------------------------------------------------
 | |
| 
 | |
|         return {
 | |
|             Program: function checkParenSpaces(node) {
 | |
|                 exceptions = getExceptions();
 | |
|                 const tokens = sourceCode.tokensAndComments;
 | |
| 
 | |
|                 tokens.forEach((token, i) => {
 | |
|                     const prevToken = tokens[i - 1];
 | |
|                     const nextToken = tokens[i + 1];
 | |
| 
 | |
|                     // if token is not an opening or closing paren token, do nothing
 | |
|                     if (!astUtils.isOpeningParenToken(token) && !astUtils.isClosingParenToken(token)) {
 | |
|                         return;
 | |
|                     }
 | |
| 
 | |
|                     // if token is an opening paren and is not followed by a required space
 | |
|                     if (token.value === "(" && openerMissingSpace(token, nextToken)) {
 | |
|                         context.report({
 | |
|                             node,
 | |
|                             loc: token.loc,
 | |
|                             messageId: "missingOpeningSpace",
 | |
|                             fix(fixer) {
 | |
|                                 return fixer.insertTextAfter(token, " ");
 | |
|                             }
 | |
|                         });
 | |
|                     }
 | |
| 
 | |
|                     // if token is an opening paren and is followed by a disallowed space
 | |
|                     if (token.value === "(" && openerRejectsSpace(token, nextToken)) {
 | |
|                         context.report({
 | |
|                             node,
 | |
|                             loc: { start: token.loc.end, end: nextToken.loc.start },
 | |
|                             messageId: "rejectedOpeningSpace",
 | |
|                             fix(fixer) {
 | |
|                                 return fixer.removeRange([token.range[1], nextToken.range[0]]);
 | |
|                             }
 | |
|                         });
 | |
|                     }
 | |
| 
 | |
|                     // if token is a closing paren and is not preceded by a required space
 | |
|                     if (token.value === ")" && closerMissingSpace(prevToken, token)) {
 | |
|                         context.report({
 | |
|                             node,
 | |
|                             loc: token.loc,
 | |
|                             messageId: "missingClosingSpace",
 | |
|                             fix(fixer) {
 | |
|                                 return fixer.insertTextBefore(token, " ");
 | |
|                             }
 | |
|                         });
 | |
|                     }
 | |
| 
 | |
|                     // if token is a closing paren and is preceded by a disallowed space
 | |
|                     if (token.value === ")" && closerRejectsSpace(prevToken, token)) {
 | |
|                         context.report({
 | |
|                             node,
 | |
|                             loc: { start: prevToken.loc.end, end: token.loc.start },
 | |
|                             messageId: "rejectedClosingSpace",
 | |
|                             fix(fixer) {
 | |
|                                 return fixer.removeRange([prevToken.range[1], token.range[0]]);
 | |
|                             }
 | |
|                         });
 | |
|                     }
 | |
|                 });
 | |
|             }
 | |
|         };
 | |
|     }
 | |
| };
 |