| /** | |
|  * @fileoverview A rule to ensure blank lines within blocks. | |
|  * @author Mathias Schreck <https://github.com/lo1tuma> | |
|  * @deprecated in ESLint v8.53.0 | |
|  */ | |
| 
 | |
| "use strict"; | |
| 
 | |
| //------------------------------------------------------------------------------ | |
| // Requirements | |
| //------------------------------------------------------------------------------ | |
|  | |
| const astUtils = require("./utils/ast-utils"); | |
| 
 | |
| //------------------------------------------------------------------------------ | |
| // Rule Definition | |
| //------------------------------------------------------------------------------ | |
|  | |
| /** @type {import('../shared/types').Rule} */ | |
| module.exports = { | |
|     meta: { | |
|         deprecated: true, | |
|         replacedBy: [], | |
|         type: "layout", | |
| 
 | |
|         docs: { | |
|             description: "Require or disallow padding within blocks", | |
|             recommended: false, | |
|             url: "https://eslint.org/docs/latest/rules/padded-blocks" | |
|         }, | |
| 
 | |
|         fixable: "whitespace", | |
| 
 | |
|         schema: [ | |
|             { | |
|                 oneOf: [ | |
|                     { | |
|                         enum: ["always", "never"] | |
|                     }, | |
|                     { | |
|                         type: "object", | |
|                         properties: { | |
|                             blocks: { | |
|                                 enum: ["always", "never"] | |
|                             }, | |
|                             switches: { | |
|                                 enum: ["always", "never"] | |
|                             }, | |
|                             classes: { | |
|                                 enum: ["always", "never"] | |
|                             } | |
|                         }, | |
|                         additionalProperties: false, | |
|                         minProperties: 1 | |
|                     } | |
|                 ] | |
|             }, | |
|             { | |
|                 type: "object", | |
|                 properties: { | |
|                     allowSingleLineBlocks: { | |
|                         type: "boolean" | |
|                     } | |
|                 }, | |
|                 additionalProperties: false | |
|             } | |
|         ], | |
| 
 | |
|         messages: { | |
|             alwaysPadBlock: "Block must be padded by blank lines.", | |
|             neverPadBlock: "Block must not be padded by blank lines." | |
|         } | |
|     }, | |
| 
 | |
|     create(context) { | |
|         const options = {}; | |
|         const typeOptions = context.options[0] || "always"; | |
|         const exceptOptions = context.options[1] || {}; | |
| 
 | |
|         if (typeof typeOptions === "string") { | |
|             const shouldHavePadding = typeOptions === "always"; | |
| 
 | |
|             options.blocks = shouldHavePadding; | |
|             options.switches = shouldHavePadding; | |
|             options.classes = shouldHavePadding; | |
|         } else { | |
|             if (Object.prototype.hasOwnProperty.call(typeOptions, "blocks")) { | |
|                 options.blocks = typeOptions.blocks === "always"; | |
|             } | |
|             if (Object.prototype.hasOwnProperty.call(typeOptions, "switches")) { | |
|                 options.switches = typeOptions.switches === "always"; | |
|             } | |
|             if (Object.prototype.hasOwnProperty.call(typeOptions, "classes")) { | |
|                 options.classes = typeOptions.classes === "always"; | |
|             } | |
|         } | |
| 
 | |
|         if (Object.prototype.hasOwnProperty.call(exceptOptions, "allowSingleLineBlocks")) { | |
|             options.allowSingleLineBlocks = exceptOptions.allowSingleLineBlocks === true; | |
|         } | |
| 
 | |
|         const sourceCode = context.sourceCode; | |
| 
 | |
|         /** | |
|          * Gets the open brace token from a given node. | |
|          * @param {ASTNode} node A BlockStatement or SwitchStatement node from which to get the open brace. | |
|          * @returns {Token} The token of the open brace. | |
|          */ | |
|         function getOpenBrace(node) { | |
|             if (node.type === "SwitchStatement") { | |
|                 return sourceCode.getTokenBefore(node.cases[0]); | |
|             } | |
| 
 | |
|             if (node.type === "StaticBlock") { | |
|                 return sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token | |
|             } | |
| 
 | |
|             // `BlockStatement` or `ClassBody` | |
|             return sourceCode.getFirstToken(node); | |
|         } | |
| 
 | |
|         /** | |
|          * Checks if the given parameter is a comment node | |
|          * @param {ASTNode|Token} node An AST node or token | |
|          * @returns {boolean} True if node is a comment | |
|          */ | |
|         function isComment(node) { | |
|             return node.type === "Line" || node.type === "Block"; | |
|         } | |
| 
 | |
|         /** | |
|          * Checks if there is padding between two tokens | |
|          * @param {Token} first The first token | |
|          * @param {Token} second The second token | |
|          * @returns {boolean} True if there is at least a line between the tokens | |
|          */ | |
|         function isPaddingBetweenTokens(first, second) { | |
|             return second.loc.start.line - first.loc.end.line >= 2; | |
|         } | |
| 
 | |
| 
 | |
|         /** | |
|          * Checks if the given token has a blank line after it. | |
|          * @param {Token} token The token to check. | |
|          * @returns {boolean} Whether or not the token is followed by a blank line. | |
|          */ | |
|         function getFirstBlockToken(token) { | |
|             let prev, | |
|                 first = token; | |
| 
 | |
|             do { | |
|                 prev = first; | |
|                 first = sourceCode.getTokenAfter(first, { includeComments: true }); | |
|             } while (isComment(first) && first.loc.start.line === prev.loc.end.line); | |
| 
 | |
|             return first; | |
|         } | |
| 
 | |
|         /** | |
|          * Checks if the given token is preceded by a blank line. | |
|          * @param {Token} token The token to check | |
|          * @returns {boolean} Whether or not the token is preceded by a blank line | |
|          */ | |
|         function getLastBlockToken(token) { | |
|             let last = token, | |
|                 next; | |
| 
 | |
|             do { | |
|                 next = last; | |
|                 last = sourceCode.getTokenBefore(last, { includeComments: true }); | |
|             } while (isComment(last) && last.loc.end.line === next.loc.start.line); | |
| 
 | |
|             return last; | |
|         } | |
| 
 | |
|         /** | |
|          * Checks if a node should be padded, according to the rule config. | |
|          * @param {ASTNode} node The AST node to check. | |
|          * @throws {Error} (Unreachable) | |
|          * @returns {boolean} True if the node should be padded, false otherwise. | |
|          */ | |
|         function requirePaddingFor(node) { | |
|             switch (node.type) { | |
|                 case "BlockStatement": | |
|                 case "StaticBlock": | |
|                     return options.blocks; | |
|                 case "SwitchStatement": | |
|                     return options.switches; | |
|                 case "ClassBody": | |
|                     return options.classes; | |
| 
 | |
|                 /* c8 ignore next */ | |
|                 default: | |
|                     throw new Error("unreachable"); | |
|             } | |
|         } | |
| 
 | |
|         /** | |
|          * Checks the given BlockStatement node to be padded if the block is not empty. | |
|          * @param {ASTNode} node The AST node of a BlockStatement. | |
|          * @returns {void} undefined. | |
|          */ | |
|         function checkPadding(node) { | |
|             const openBrace = getOpenBrace(node), | |
|                 firstBlockToken = getFirstBlockToken(openBrace), | |
|                 tokenBeforeFirst = sourceCode.getTokenBefore(firstBlockToken, { includeComments: true }), | |
|                 closeBrace = sourceCode.getLastToken(node), | |
|                 lastBlockToken = getLastBlockToken(closeBrace), | |
|                 tokenAfterLast = sourceCode.getTokenAfter(lastBlockToken, { includeComments: true }), | |
|                 blockHasTopPadding = isPaddingBetweenTokens(tokenBeforeFirst, firstBlockToken), | |
|                 blockHasBottomPadding = isPaddingBetweenTokens(lastBlockToken, tokenAfterLast); | |
| 
 | |
|             if (options.allowSingleLineBlocks && astUtils.isTokenOnSameLine(tokenBeforeFirst, tokenAfterLast)) { | |
|                 return; | |
|             } | |
| 
 | |
|             if (requirePaddingFor(node)) { | |
| 
 | |
|                 if (!blockHasTopPadding) { | |
|                     context.report({ | |
|                         node, | |
|                         loc: { | |
|                             start: tokenBeforeFirst.loc.start, | |
|                             end: firstBlockToken.loc.start | |
|                         }, | |
|                         fix(fixer) { | |
|                             return fixer.insertTextAfter(tokenBeforeFirst, "\n"); | |
|                         }, | |
|                         messageId: "alwaysPadBlock" | |
|                     }); | |
|                 } | |
|                 if (!blockHasBottomPadding) { | |
|                     context.report({ | |
|                         node, | |
|                         loc: { | |
|                             end: tokenAfterLast.loc.start, | |
|                             start: lastBlockToken.loc.end | |
|                         }, | |
|                         fix(fixer) { | |
|                             return fixer.insertTextBefore(tokenAfterLast, "\n"); | |
|                         }, | |
|                         messageId: "alwaysPadBlock" | |
|                     }); | |
|                 } | |
|             } else { | |
|                 if (blockHasTopPadding) { | |
| 
 | |
|                     context.report({ | |
|                         node, | |
|                         loc: { | |
|                             start: tokenBeforeFirst.loc.start, | |
|                             end: firstBlockToken.loc.start | |
|                         }, | |
|                         fix(fixer) { | |
|                             return fixer.replaceTextRange([tokenBeforeFirst.range[1], firstBlockToken.range[0] - firstBlockToken.loc.start.column], "\n"); | |
|                         }, | |
|                         messageId: "neverPadBlock" | |
|                     }); | |
|                 } | |
| 
 | |
|                 if (blockHasBottomPadding) { | |
| 
 | |
|                     context.report({ | |
|                         node, | |
|                         loc: { | |
|                             end: tokenAfterLast.loc.start, | |
|                             start: lastBlockToken.loc.end | |
|                         }, | |
|                         messageId: "neverPadBlock", | |
|                         fix(fixer) { | |
|                             return fixer.replaceTextRange([lastBlockToken.range[1], tokenAfterLast.range[0] - tokenAfterLast.loc.start.column], "\n"); | |
|                         } | |
|                     }); | |
|                 } | |
|             } | |
|         } | |
| 
 | |
|         const rule = {}; | |
| 
 | |
|         if (Object.prototype.hasOwnProperty.call(options, "switches")) { | |
|             rule.SwitchStatement = function(node) { | |
|                 if (node.cases.length === 0) { | |
|                     return; | |
|                 } | |
|                 checkPadding(node); | |
|             }; | |
|         } | |
| 
 | |
|         if (Object.prototype.hasOwnProperty.call(options, "blocks")) { | |
|             rule.BlockStatement = function(node) { | |
|                 if (node.body.length === 0) { | |
|                     return; | |
|                 } | |
|                 checkPadding(node); | |
|             }; | |
|             rule.StaticBlock = rule.BlockStatement; | |
|         } | |
| 
 | |
|         if (Object.prototype.hasOwnProperty.call(options, "classes")) { | |
|             rule.ClassBody = function(node) { | |
|                 if (node.body.length === 0) { | |
|                     return; | |
|                 } | |
|                 checkPadding(node); | |
|             }; | |
|         } | |
| 
 | |
|         return rule; | |
|     } | |
| };
 |