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