UNPKG

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