]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/rules/generator-star-spacing.js
import 8.3.0 source
[pve-eslint.git] / eslint / lib / rules / generator-star-spacing.js
CommitLineData
eb39fafa
DC
1/**
2 * @fileoverview Rule to check the spacing around the * in generator functions.
3 * @author Jamund Ferguson
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
12const OVERRIDE_SCHEMA = {
13 oneOf: [
14 {
15 enum: ["before", "after", "both", "neither"]
16 },
17 {
18 type: "object",
19 properties: {
20 before: { type: "boolean" },
21 after: { type: "boolean" }
22 },
23 additionalProperties: false
24 }
25 ]
26};
27
28module.exports = {
29 meta: {
30 type: "layout",
31
32 docs: {
33 description: "enforce consistent spacing around `*` operators in generator functions",
eb39fafa
DC
34 recommended: false,
35 url: "https://eslint.org/docs/rules/generator-star-spacing"
36 },
37
38 fixable: "whitespace",
39
40 schema: [
41 {
42 oneOf: [
43 {
44 enum: ["before", "after", "both", "neither"]
45 },
46 {
47 type: "object",
48 properties: {
49 before: { type: "boolean" },
50 after: { type: "boolean" },
51 named: OVERRIDE_SCHEMA,
52 anonymous: OVERRIDE_SCHEMA,
53 method: OVERRIDE_SCHEMA
54 },
55 additionalProperties: false
56 }
57 ]
58 }
59 ],
60
61 messages: {
62 missingBefore: "Missing space before *.",
63 missingAfter: "Missing space after *.",
64 unexpectedBefore: "Unexpected space before *.",
65 unexpectedAfter: "Unexpected space after *."
66 }
67 },
68
69 create(context) {
70
71 const optionDefinitions = {
72 before: { before: true, after: false },
73 after: { before: false, after: true },
74 both: { before: true, after: true },
75 neither: { before: false, after: false }
76 };
77
78 /**
79 * Returns resolved option definitions based on an option and defaults
80 * @param {any} option The option object or string value
81 * @param {Object} defaults The defaults to use if options are not present
82 * @returns {Object} the resolved object definition
83 */
84 function optionToDefinition(option, defaults) {
85 if (!option) {
86 return defaults;
87 }
88
89 return typeof option === "string"
90 ? optionDefinitions[option]
91 : Object.assign({}, defaults, option);
92 }
93
94 const modes = (function(option) {
95 const defaults = optionToDefinition(option, optionDefinitions.before);
96
97 return {
98 named: optionToDefinition(option.named, defaults),
99 anonymous: optionToDefinition(option.anonymous, defaults),
100 method: optionToDefinition(option.method, defaults)
101 };
102 }(context.options[0] || {}));
103
104 const sourceCode = context.getSourceCode();
105
106 /**
107 * Checks if the given token is a star token or not.
108 * @param {Token} token The token to check.
109 * @returns {boolean} `true` if the token is a star token.
110 */
111 function isStarToken(token) {
112 return token.value === "*" && token.type === "Punctuator";
113 }
114
115 /**
116 * Gets the generator star token of the given function node.
117 * @param {ASTNode} node The function node to get.
118 * @returns {Token} Found star token.
119 */
120 function getStarToken(node) {
121 return sourceCode.getFirstToken(
122 (node.parent.method || node.parent.type === "MethodDefinition") ? node.parent : node,
123 isStarToken
124 );
125 }
126
127 /**
128 * capitalize a given string.
129 * @param {string} str the given string.
130 * @returns {string} the capitalized string.
131 */
132 function capitalize(str) {
133 return str[0].toUpperCase() + str.slice(1);
134 }
135
136 /**
137 * Checks the spacing between two tokens before or after the star token.
138 * @param {string} kind Either "named", "anonymous", or "method"
139 * @param {string} side Either "before" or "after".
140 * @param {Token} leftToken `function` keyword token if side is "before", or
141 * star token if side is "after".
142 * @param {Token} rightToken Star token if side is "before", or identifier
143 * token if side is "after".
144 * @returns {void}
145 */
146 function checkSpacing(kind, side, leftToken, rightToken) {
147 if (!!(rightToken.range[0] - leftToken.range[1]) !== modes[kind][side]) {
148 const after = leftToken.value === "*";
149 const spaceRequired = modes[kind][side];
150 const node = after ? leftToken : rightToken;
151 const messageId = `${spaceRequired ? "missing" : "unexpected"}${capitalize(side)}`;
152
153 context.report({
154 node,
155 messageId,
156 fix(fixer) {
157 if (spaceRequired) {
158 if (after) {
159 return fixer.insertTextAfter(node, " ");
160 }
161 return fixer.insertTextBefore(node, " ");
162 }
163 return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
164 }
165 });
166 }
167 }
168
169 /**
170 * Enforces the spacing around the star if node is a generator function.
171 * @param {ASTNode} node A function expression or declaration node.
172 * @returns {void}
173 */
174 function checkFunction(node) {
175 if (!node.generator) {
176 return;
177 }
178
179 const starToken = getStarToken(node);
180 const prevToken = sourceCode.getTokenBefore(starToken);
181 const nextToken = sourceCode.getTokenAfter(starToken);
182
183 let kind = "named";
184
185 if (node.parent.type === "MethodDefinition" || (node.parent.type === "Property" && node.parent.method)) {
186 kind = "method";
187 } else if (!node.id) {
188 kind = "anonymous";
189 }
190
191 // Only check before when preceded by `function`|`static` keyword
192 if (!(kind === "method" && starToken === sourceCode.getFirstToken(node.parent))) {
193 checkSpacing(kind, "before", prevToken, starToken);
194 }
195
196 checkSpacing(kind, "after", starToken, nextToken);
197 }
198
199 return {
200 FunctionDeclaration: checkFunction,
201 FunctionExpression: checkFunction
202 };
203
204 }
205};