Permalink
Cannot retrieve contributors at this time
282 lines (242 sloc)
10.5 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
codeql-action/node_modules/eslint/lib/rules/space-in-parens.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @fileoverview Disallows or enforces spaces inside of parentheses. | |
* @author Jonathan Rajavuori | |
*/ | |
"use strict"; | |
const astUtils = require("./utils/ast-utils"); | |
//------------------------------------------------------------------------------ | |
// Rule Definition | |
//------------------------------------------------------------------------------ | |
/** @type {import('../shared/types').Rule} */ | |
module.exports = { | |
meta: { | |
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]]); | |
} | |
}); | |
} | |
}); | |
} | |
}; | |
} | |
}; |