UNPKG

4.92 kBJavaScriptView Raw
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
12module.exports = {
13 meta: {
14 docs: {
15 description: "enforce consistent spacing around `*` operators in generator functions",
16 category: "ECMAScript 6",
17 recommended: false
18 },
19
20 fixable: "whitespace",
21
22 schema: [
23 {
24 oneOf: [
25 {
26 enum: ["before", "after", "both", "neither"]
27 },
28 {
29 type: "object",
30 properties: {
31 before: {type: "boolean"},
32 after: {type: "boolean"}
33 },
34 additionalProperties: false
35 }
36 ]
37 }
38 ]
39 },
40
41 create(context) {
42
43 const mode = (function(option) {
44 if (!option || typeof option === "string") {
45 return {
46 before: { before: true, after: false },
47 after: { before: false, after: true },
48 both: { before: true, after: true },
49 neither: { before: false, after: false }
50 }[option || "before"];
51 }
52 return option;
53 }(context.options[0]));
54
55 const sourceCode = context.getSourceCode();
56
57 /**
58 * Gets `*` token from a given node.
59 *
60 * @param {ASTNode} node - A node to get `*` token. This is one of
61 * FunctionDeclaration, FunctionExpression, Property, and
62 * MethodDefinition.
63 * @returns {Token} `*` token.
64 */
65 function getStarToken(node) {
66 let token = sourceCode.getFirstToken(node);
67
68 while (token.value !== "*") {
69 token = sourceCode.getTokenAfter(token);
70 }
71
72 return token;
73 }
74
75 /**
76 * Checks the spacing between two tokens before or after the star token.
77 * @param {string} side Either "before" or "after".
78 * @param {Token} leftToken `function` keyword token if side is "before", or
79 * star token if side is "after".
80 * @param {Token} rightToken Star token if side is "before", or identifier
81 * token if side is "after".
82 * @returns {void}
83 */
84 function checkSpacing(side, leftToken, rightToken) {
85 if (!!(rightToken.range[0] - leftToken.range[1]) !== mode[side]) {
86 const after = leftToken.value === "*";
87 const spaceRequired = mode[side];
88 const node = after ? leftToken : rightToken;
89 const type = spaceRequired ? "Missing" : "Unexpected";
90 const message = "{{type}} space {{side}} *.";
91 const data = {
92 type,
93 side
94 };
95
96 context.report({
97 node,
98 message,
99 data,
100 fix(fixer) {
101 if (spaceRequired) {
102 if (after) {
103 return fixer.insertTextAfter(node, " ");
104 }
105 return fixer.insertTextBefore(node, " ");
106 }
107 return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
108 }
109 });
110 }
111 }
112
113 /**
114 * Enforces the spacing around the star if node is a generator function.
115 * @param {ASTNode} node A function expression or declaration node.
116 * @returns {void}
117 */
118 function checkFunction(node) {
119 let starToken;
120
121 if (!node.generator) {
122 return;
123 }
124
125 if (node.parent.method || node.parent.type === "MethodDefinition") {
126 starToken = getStarToken(node.parent);
127 } else {
128 starToken = getStarToken(node);
129 }
130
131 // Only check before when preceded by `function`|`static` keyword
132 const prevToken = sourceCode.getTokenBefore(starToken);
133
134 if (prevToken.value === "function" || prevToken.value === "static") {
135 checkSpacing("before", prevToken, starToken);
136 }
137
138 const nextToken = sourceCode.getTokenAfter(starToken);
139
140 checkSpacing("after", starToken, nextToken);
141 }
142
143 return {
144 FunctionDeclaration: checkFunction,
145 FunctionExpression: checkFunction
146 };
147
148 }
149};