1/** 2 * @fileoverview Rule to disallow empty functions. 3 * @author Toru Nagashima 4 */ 5 6"use strict"; 7 8//------------------------------------------------------------------------------ 9// Requirements 10//------------------------------------------------------------------------------ 11 12const astUtils = require("./utils/ast-utils"); 13 14//------------------------------------------------------------------------------ 15// Helpers 16//------------------------------------------------------------------------------ 17 18const ALLOW_OPTIONS = Object.freeze([ 19 "functions", 20 "arrowFunctions", 21 "generatorFunctions", 22 "methods", 23 "generatorMethods", 24 "getters", 25 "setters", 26 "constructors", 27 "asyncFunctions", 28 "asyncMethods" 29]); 30 31/** 32 * Gets the kind of a given function node. 33 * @param {ASTNode} node A function node to get. This is one of 34 * an ArrowFunctionExpression, a FunctionDeclaration, or a 35 * FunctionExpression. 36 * @returns {string} The kind of the function. This is one of "functions", 37 * "arrowFunctions", "generatorFunctions", "asyncFunctions", "methods", 38 * "generatorMethods", "asyncMethods", "getters", "setters", and 39 * "constructors". 40 */ 41function getKind(node) { 42 const parent = node.parent; 43 let kind = ""; 44 45 if (node.type === "ArrowFunctionExpression") { 46 return "arrowFunctions"; 47 } 48 49 // Detects main kind. 50 if (parent.type === "Property") { 51 if (parent.kind === "get") { 52 return "getters"; 53 } 54 if (parent.kind === "set") { 55 return "setters"; 56 } 57 kind = parent.method ? "methods" : "functions"; 58 59 } else if (parent.type === "MethodDefinition") { 60 if (parent.kind === "get") { 61 return "getters"; 62 } 63 if (parent.kind === "set") { 64 return "setters"; 65 } 66 if (parent.kind === "constructor") { 67 return "constructors"; 68 } 69 kind = "methods"; 70 71 } else { 72 kind = "functions"; 73 } 74 75 // Detects prefix. 76 let prefix = ""; 77 78 if (node.generator) { 79 prefix = "generator"; 80 } else if (node.async) { 81 prefix = "async"; 82 } else { 83 return kind; 84 } 85 return prefix + kind[0].toUpperCase() + kind.slice(1); 86} 87 88//------------------------------------------------------------------------------ 89// Rule Definition 90//------------------------------------------------------------------------------ 91 92module.exports = { 93 meta: { 94 type: "suggestion", 95 96 docs: { 97 description: "disallow empty functions", 98 category: "Best Practices", 99 recommended: false, 100 url: "https://eslint.org/docs/rules/no-empty-function" 101 }, 102 103 schema: [ 104 { 105 type: "object", 106 properties: { 107 allow: { 108 type: "array", 109 items: { enum: ALLOW_OPTIONS }, 110 uniqueItems: true 111 } 112 }, 113 additionalProperties: false 114 } 115 ], 116 117 messages: { 118 unexpected: "Unexpected empty {{name}}." 119 } 120 }, 121 122 create(context) { 123 const options = context.options[0] || {}; 124 const allowed = options.allow || []; 125 126 const sourceCode = context.getSourceCode(); 127 128 /** 129 * Reports a given function node if the node matches the following patterns. 130 * 131 * - Not allowed by options. 132 * - The body is empty. 133 * - The body doesn't have any comments. 134 * @param {ASTNode} node A function node to report. This is one of 135 * an ArrowFunctionExpression, a FunctionDeclaration, or a 136 * FunctionExpression. 137 * @returns {void} 138 */ 139 function reportIfEmpty(node) { 140 const kind = getKind(node); 141 const name = astUtils.getFunctionNameWithKind(node); 142 const innerComments = sourceCode.getTokens(node.body, { 143 includeComments: true, 144 filter: astUtils.isCommentToken 145 }); 146 147 if (allowed.indexOf(kind) === -1 && 148 node.body.type === "BlockStatement" && 149 node.body.body.length === 0 && 150 innerComments.length === 0 151 ) { 152 context.report({ 153 node, 154 loc: node.body.loc, 155 messageId: "unexpected", 156 data: { name } 157 }); 158 } 159 } 160 161 return { 162 ArrowFunctionExpression: reportIfEmpty, 163 FunctionDeclaration: reportIfEmpty, 164 FunctionExpression: reportIfEmpty 165 }; 166 } 167}; 168