]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Disallows or enforces spaces inside computed properties. | |
3 | * @author Jamund Ferguson | |
4 | */ | |
5 | "use strict"; | |
6 | ||
7 | const astUtils = require("./utils/ast-utils"); | |
8 | ||
9 | //------------------------------------------------------------------------------ | |
10 | // Rule Definition | |
11 | //------------------------------------------------------------------------------ | |
12 | ||
13 | module.exports = { | |
14 | meta: { | |
15 | type: "layout", | |
16 | ||
17 | docs: { | |
18 | description: "enforce consistent spacing inside computed property brackets", | |
eb39fafa DC |
19 | recommended: false, |
20 | url: "https://eslint.org/docs/rules/computed-property-spacing" | |
21 | }, | |
22 | ||
23 | fixable: "whitespace", | |
24 | ||
25 | schema: [ | |
26 | { | |
27 | enum: ["always", "never"] | |
28 | }, | |
29 | { | |
30 | type: "object", | |
31 | properties: { | |
32 | enforceForClassMembers: { | |
33 | type: "boolean", | |
34 | default: true | |
35 | } | |
36 | }, | |
37 | additionalProperties: false | |
38 | } | |
39 | ], | |
40 | ||
41 | messages: { | |
42 | unexpectedSpaceBefore: "There should be no space before '{{tokenValue}}'.", | |
43 | unexpectedSpaceAfter: "There should be no space after '{{tokenValue}}'.", | |
44 | ||
45 | missingSpaceBefore: "A space is required before '{{tokenValue}}'.", | |
46 | missingSpaceAfter: "A space is required after '{{tokenValue}}'." | |
47 | } | |
48 | }, | |
49 | ||
50 | create(context) { | |
51 | const sourceCode = context.getSourceCode(); | |
52 | const propertyNameMustBeSpaced = context.options[0] === "always"; // default is "never" | |
53 | const enforceForClassMembers = !context.options[1] || context.options[1].enforceForClassMembers; | |
54 | ||
55 | //-------------------------------------------------------------------------- | |
56 | // Helpers | |
57 | //-------------------------------------------------------------------------- | |
58 | ||
59 | /** | |
60 | * Reports that there shouldn't be a space after the first token | |
61 | * @param {ASTNode} node The node to report in the event of an error. | |
62 | * @param {Token} token The token to use for the report. | |
63 | * @param {Token} tokenAfter The token after `token`. | |
64 | * @returns {void} | |
65 | */ | |
66 | function reportNoBeginningSpace(node, token, tokenAfter) { | |
67 | context.report({ | |
68 | node, | |
69 | loc: { start: token.loc.end, end: tokenAfter.loc.start }, | |
70 | messageId: "unexpectedSpaceAfter", | |
71 | data: { | |
72 | tokenValue: token.value | |
73 | }, | |
74 | fix(fixer) { | |
75 | return fixer.removeRange([token.range[1], tokenAfter.range[0]]); | |
76 | } | |
77 | }); | |
78 | } | |
79 | ||
80 | /** | |
81 | * Reports that there shouldn't be a space before the last token | |
82 | * @param {ASTNode} node The node to report in the event of an error. | |
83 | * @param {Token} token The token to use for the report. | |
84 | * @param {Token} tokenBefore The token before `token`. | |
85 | * @returns {void} | |
86 | */ | |
87 | function reportNoEndingSpace(node, token, tokenBefore) { | |
88 | context.report({ | |
89 | node, | |
90 | loc: { start: tokenBefore.loc.end, end: token.loc.start }, | |
91 | messageId: "unexpectedSpaceBefore", | |
92 | data: { | |
93 | tokenValue: token.value | |
94 | }, | |
95 | fix(fixer) { | |
96 | return fixer.removeRange([tokenBefore.range[1], token.range[0]]); | |
97 | } | |
98 | }); | |
99 | } | |
100 | ||
101 | /** | |
102 | * Reports that there should be a space after the first token | |
103 | * @param {ASTNode} node The node to report in the event of an error. | |
104 | * @param {Token} token The token to use for the report. | |
105 | * @returns {void} | |
106 | */ | |
107 | function reportRequiredBeginningSpace(node, token) { | |
108 | context.report({ | |
109 | node, | |
110 | loc: token.loc, | |
111 | messageId: "missingSpaceAfter", | |
112 | data: { | |
113 | tokenValue: token.value | |
114 | }, | |
115 | fix(fixer) { | |
116 | return fixer.insertTextAfter(token, " "); | |
117 | } | |
118 | }); | |
119 | } | |
120 | ||
121 | /** | |
122 | * Reports that there should be a space before the last token | |
123 | * @param {ASTNode} node The node to report in the event of an error. | |
124 | * @param {Token} token The token to use for the report. | |
125 | * @returns {void} | |
126 | */ | |
127 | function reportRequiredEndingSpace(node, token) { | |
128 | context.report({ | |
129 | node, | |
130 | loc: token.loc, | |
131 | messageId: "missingSpaceBefore", | |
132 | data: { | |
133 | tokenValue: token.value | |
134 | }, | |
135 | fix(fixer) { | |
136 | return fixer.insertTextBefore(token, " "); | |
137 | } | |
138 | }); | |
139 | } | |
140 | ||
141 | /** | |
142 | * Returns a function that checks the spacing of a node on the property name | |
143 | * that was passed in. | |
144 | * @param {string} propertyName The property on the node to check for spacing | |
145 | * @returns {Function} A function that will check spacing on a node | |
146 | */ | |
147 | function checkSpacing(propertyName) { | |
148 | return function(node) { | |
149 | if (!node.computed) { | |
150 | return; | |
151 | } | |
152 | ||
153 | const property = node[propertyName]; | |
154 | ||
155 | const before = sourceCode.getTokenBefore(property, astUtils.isOpeningBracketToken), | |
156 | first = sourceCode.getTokenAfter(before, { includeComments: true }), | |
157 | after = sourceCode.getTokenAfter(property, astUtils.isClosingBracketToken), | |
158 | last = sourceCode.getTokenBefore(after, { includeComments: true }); | |
159 | ||
160 | if (astUtils.isTokenOnSameLine(before, first)) { | |
161 | if (propertyNameMustBeSpaced) { | |
162 | if (!sourceCode.isSpaceBetweenTokens(before, first) && astUtils.isTokenOnSameLine(before, first)) { | |
163 | reportRequiredBeginningSpace(node, before); | |
164 | } | |
165 | } else { | |
166 | if (sourceCode.isSpaceBetweenTokens(before, first)) { | |
167 | reportNoBeginningSpace(node, before, first); | |
168 | } | |
169 | } | |
170 | } | |
171 | ||
172 | if (astUtils.isTokenOnSameLine(last, after)) { | |
173 | if (propertyNameMustBeSpaced) { | |
174 | if (!sourceCode.isSpaceBetweenTokens(last, after) && astUtils.isTokenOnSameLine(last, after)) { | |
175 | reportRequiredEndingSpace(node, after); | |
176 | } | |
177 | } else { | |
178 | if (sourceCode.isSpaceBetweenTokens(last, after)) { | |
179 | reportNoEndingSpace(node, after, last); | |
180 | } | |
181 | } | |
182 | } | |
183 | }; | |
184 | } | |
185 | ||
186 | ||
187 | //-------------------------------------------------------------------------- | |
188 | // Public | |
189 | //-------------------------------------------------------------------------- | |
190 | ||
191 | const listeners = { | |
192 | Property: checkSpacing("key"), | |
193 | MemberExpression: checkSpacing("property") | |
194 | }; | |
195 | ||
196 | if (enforceForClassMembers) { | |
609c276f TL |
197 | listeners.MethodDefinition = |
198 | listeners.PropertyDefinition = listeners.Property; | |
eb39fafa DC |
199 | } |
200 | ||
201 | return listeners; | |
202 | ||
203 | } | |
204 | }; |