UNPKG

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