123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- /**
- * @fileoverview Rule to flag block statements that do not use the one true brace style
- * @author Ian Christian Myers
- */
-
- "use strict";
-
- const astUtils = require("./utils/ast-utils");
-
- //------------------------------------------------------------------------------
- // Rule Definition
- //------------------------------------------------------------------------------
-
- module.exports = {
- meta: {
- type: "layout",
-
- docs: {
- description: "enforce consistent brace style for blocks",
- category: "Stylistic Issues",
- recommended: false,
- url: "https://eslint.org/docs/rules/brace-style"
- },
-
- schema: [
- {
- enum: ["1tbs", "stroustrup", "allman"]
- },
- {
- type: "object",
- properties: {
- allowSingleLine: {
- type: "boolean",
- default: false
- }
- },
- additionalProperties: false
- }
- ],
-
- fixable: "whitespace",
-
- messages: {
- nextLineOpen: "Opening curly brace does not appear on the same line as controlling statement.",
- sameLineOpen: "Opening curly brace appears on the same line as controlling statement.",
- blockSameLine: "Statement inside of curly braces should be on next line.",
- nextLineClose: "Closing curly brace does not appear on the same line as the subsequent block.",
- singleLineClose: "Closing curly brace should be on the same line as opening curly brace or on the line after the previous block.",
- sameLineClose: "Closing curly brace appears on the same line as the subsequent block."
- }
- },
-
- create(context) {
- const style = context.options[0] || "1tbs",
- params = context.options[1] || {},
- sourceCode = context.getSourceCode();
-
- //--------------------------------------------------------------------------
- // Helpers
- //--------------------------------------------------------------------------
-
- /**
- * Fixes a place where a newline unexpectedly appears
- * @param {Token} firstToken The token before the unexpected newline
- * @param {Token} secondToken The token after the unexpected newline
- * @returns {Function} A fixer function to remove the newlines between the tokens
- */
- function removeNewlineBetween(firstToken, secondToken) {
- const textRange = [firstToken.range[1], secondToken.range[0]];
- const textBetween = sourceCode.text.slice(textRange[0], textRange[1]);
-
- // Don't do a fix if there is a comment between the tokens
- if (textBetween.trim()) {
- return null;
- }
- return fixer => fixer.replaceTextRange(textRange, " ");
- }
-
- /**
- * Validates a pair of curly brackets based on the user's config
- * @param {Token} openingCurly The opening curly bracket
- * @param {Token} closingCurly The closing curly bracket
- * @returns {void}
- */
- function validateCurlyPair(openingCurly, closingCurly) {
- const tokenBeforeOpeningCurly = sourceCode.getTokenBefore(openingCurly);
- const tokenAfterOpeningCurly = sourceCode.getTokenAfter(openingCurly);
- const tokenBeforeClosingCurly = sourceCode.getTokenBefore(closingCurly);
- const singleLineException = params.allowSingleLine && astUtils.isTokenOnSameLine(openingCurly, closingCurly);
-
- if (style !== "allman" && !astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly)) {
- context.report({
- node: openingCurly,
- messageId: "nextLineOpen",
- fix: removeNewlineBetween(tokenBeforeOpeningCurly, openingCurly)
- });
- }
-
- if (style === "allman" && astUtils.isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurly) && !singleLineException) {
- context.report({
- node: openingCurly,
- messageId: "sameLineOpen",
- fix: fixer => fixer.insertTextBefore(openingCurly, "\n")
- });
- }
-
- if (astUtils.isTokenOnSameLine(openingCurly, tokenAfterOpeningCurly) && tokenAfterOpeningCurly !== closingCurly && !singleLineException) {
- context.report({
- node: openingCurly,
- messageId: "blockSameLine",
- fix: fixer => fixer.insertTextAfter(openingCurly, "\n")
- });
- }
-
- if (tokenBeforeClosingCurly !== openingCurly && !singleLineException && astUtils.isTokenOnSameLine(tokenBeforeClosingCurly, closingCurly)) {
- context.report({
- node: closingCurly,
- messageId: "singleLineClose",
- fix: fixer => fixer.insertTextBefore(closingCurly, "\n")
- });
- }
- }
-
- /**
- * Validates the location of a token that appears before a keyword (e.g. a newline before `else`)
- * @param {Token} curlyToken The closing curly token. This is assumed to precede a keyword token (such as `else` or `finally`).
- * @returns {void}
- */
- function validateCurlyBeforeKeyword(curlyToken) {
- const keywordToken = sourceCode.getTokenAfter(curlyToken);
-
- if (style === "1tbs" && !astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {
- context.report({
- node: curlyToken,
- messageId: "nextLineClose",
- fix: removeNewlineBetween(curlyToken, keywordToken)
- });
- }
-
- if (style !== "1tbs" && astUtils.isTokenOnSameLine(curlyToken, keywordToken)) {
- context.report({
- node: curlyToken,
- messageId: "sameLineClose",
- fix: fixer => fixer.insertTextAfter(curlyToken, "\n")
- });
- }
- }
-
- //--------------------------------------------------------------------------
- // Public API
- //--------------------------------------------------------------------------
-
- return {
- BlockStatement(node) {
- if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
- validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
- }
- },
- ClassBody(node) {
- validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
- },
- SwitchStatement(node) {
- const closingCurly = sourceCode.getLastToken(node);
- const openingCurly = sourceCode.getTokenBefore(node.cases.length ? node.cases[0] : closingCurly);
-
- validateCurlyPair(openingCurly, closingCurly);
- },
- IfStatement(node) {
- if (node.consequent.type === "BlockStatement" && node.alternate) {
-
- // Handle the keyword after the `if` block (before `else`)
- validateCurlyBeforeKeyword(sourceCode.getLastToken(node.consequent));
- }
- },
- TryStatement(node) {
-
- // Handle the keyword after the `try` block (before `catch` or `finally`)
- validateCurlyBeforeKeyword(sourceCode.getLastToken(node.block));
-
- if (node.handler && node.finalizer) {
-
- // Handle the keyword after the `catch` block (before `finally`)
- validateCurlyBeforeKeyword(sourceCode.getLastToken(node.handler.body));
- }
- }
- };
- }
- };
|