]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/rules/func-names.js
import 8.4.0 source
[pve-eslint.git] / eslint / lib / rules / func-names.js
CommitLineData
eb39fafa
DC
1/**
2 * @fileoverview Rule to warn when a function expression does not have a name.
3 * @author Kyle T. Nunery
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14/**
15 * Checks whether or not a given variable is a function name.
16 * @param {eslint-scope.Variable} variable A variable to check.
17 * @returns {boolean} `true` if the variable is a function name.
18 */
19function isFunctionName(variable) {
20 return variable && variable.defs[0].type === "FunctionName";
21}
22
23//------------------------------------------------------------------------------
24// Rule Definition
25//------------------------------------------------------------------------------
26
34eeec05 27/** @type {import('../shared/types').Rule} */
eb39fafa
DC
28module.exports = {
29 meta: {
30 type: "suggestion",
31
32 docs: {
33 description: "require or disallow named `function` expressions",
eb39fafa
DC
34 recommended: false,
35 url: "https://eslint.org/docs/rules/func-names"
36 },
37
38 schema: {
39 definitions: {
40 value: {
41 enum: [
42 "always",
43 "as-needed",
44 "never"
45 ]
46 }
47 },
48 items: [
49 {
50 $ref: "#/definitions/value"
51 },
52 {
53 type: "object",
54 properties: {
55 generators: {
56 $ref: "#/definitions/value"
57 }
58 },
59 additionalProperties: false
60 }
61 ]
62 },
63
64 messages: {
65 unnamed: "Unexpected unnamed {{name}}.",
66 named: "Unexpected named {{name}}."
67 }
68 },
69
70 create(context) {
71
72 const sourceCode = context.getSourceCode();
73
74 /**
75 * Returns the config option for the given node.
76 * @param {ASTNode} node A node to get the config for.
77 * @returns {string} The config option.
78 */
79 function getConfigForNode(node) {
80 if (
81 node.generator &&
82 context.options.length > 1 &&
83 context.options[1].generators
84 ) {
85 return context.options[1].generators;
86 }
87
88 return context.options[0] || "always";
89 }
90
91 /**
92 * Determines whether the current FunctionExpression node is a get, set, or
93 * shorthand method in an object literal or a class.
94 * @param {ASTNode} node A node to check.
95 * @returns {boolean} True if the node is a get, set, or shorthand method.
96 */
97 function isObjectOrClassMethod(node) {
98 const parent = node.parent;
99
100 return (parent.type === "MethodDefinition" || (
101 parent.type === "Property" && (
102 parent.method ||
103 parent.kind === "get" ||
104 parent.kind === "set"
105 )
106 ));
107 }
108
109 /**
110 * Determines whether the current FunctionExpression node has a name that would be
111 * inferred from context in a conforming ES6 environment.
112 * @param {ASTNode} node A node to check.
113 * @returns {boolean} True if the node would have a name assigned automatically.
114 */
115 function hasInferredName(node) {
116 const parent = node.parent;
117
118 return isObjectOrClassMethod(node) ||
119 (parent.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.init === node) ||
120 (parent.type === "Property" && parent.value === node) ||
609c276f 121 (parent.type === "PropertyDefinition" && parent.value === node) ||
eb39fafa
DC
122 (parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) ||
123 (parent.type === "AssignmentPattern" && parent.left.type === "Identifier" && parent.right === node);
124 }
125
126 /**
127 * Reports that an unnamed function should be named
128 * @param {ASTNode} node The node to report in the event of an error.
129 * @returns {void}
130 */
131 function reportUnexpectedUnnamedFunction(node) {
132 context.report({
133 node,
134 messageId: "unnamed",
135 loc: astUtils.getFunctionHeadLoc(node, sourceCode),
136 data: { name: astUtils.getFunctionNameWithKind(node) }
137 });
138 }
139
140 /**
141 * Reports that a named function should be unnamed
142 * @param {ASTNode} node The node to report in the event of an error.
143 * @returns {void}
144 */
145 function reportUnexpectedNamedFunction(node) {
146 context.report({
147 node,
148 messageId: "named",
149 loc: astUtils.getFunctionHeadLoc(node, sourceCode),
150 data: { name: astUtils.getFunctionNameWithKind(node) }
151 });
152 }
153
154 /**
155 * The listener for function nodes.
156 * @param {ASTNode} node function node
157 * @returns {void}
158 */
159 function handleFunction(node) {
160
161 // Skip recursive functions.
162 const nameVar = context.getDeclaredVariables(node)[0];
163
164 if (isFunctionName(nameVar) && nameVar.references.length > 0) {
165 return;
166 }
167
168 const hasName = Boolean(node.id && node.id.name);
169 const config = getConfigForNode(node);
170
171 if (config === "never") {
172 if (hasName && node.type !== "FunctionDeclaration") {
173 reportUnexpectedNamedFunction(node);
174 }
175 } else if (config === "as-needed") {
176 if (!hasName && !hasInferredName(node)) {
177 reportUnexpectedUnnamedFunction(node);
178 }
179 } else {
180 if (!hasName && !isObjectOrClassMethod(node)) {
181 reportUnexpectedUnnamedFunction(node);
182 }
183 }
184 }
185
186 return {
187 "FunctionExpression:exit": handleFunction,
188 "ExportDefaultDeclaration > FunctionDeclaration": handleFunction
189 };
190 }
191};