.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / object-curly-spacing.js
1 /**
2  * @fileoverview Disallows or enforces spaces inside of object literals.
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 braces",
19             category: "Stylistic Issues",
20             recommended: false,
21             url: "https://eslint.org/docs/rules/object-curly-spacing"
22         },
23
24         fixable: "whitespace",
25
26         schema: [
27             {
28                 enum: ["always", "never"]
29             },
30             {
31                 type: "object",
32                 properties: {
33                     arraysInObjects: {
34                         type: "boolean"
35                     },
36                     objectsInObjects: {
37                         type: "boolean"
38                     }
39                 },
40                 additionalProperties: false
41             }
42         ],
43
44         messages: {
45             requireSpaceBefore: "A space is required before '{{token}}'.",
46             requireSpaceAfter: "A space is required after '{{token}}'.",
47             unexpectedSpaceBefore: "There should be no space before '{{token}}'.",
48             unexpectedSpaceAfter: "There should be no space after '{{token}}'."
49         }
50     },
51
52     create(context) {
53         const spaced = context.options[0] === "always",
54             sourceCode = context.getSourceCode();
55
56         /**
57          * Determines whether an option is set, relative to the spacing option.
58          * If spaced is "always", then check whether option is set to false.
59          * If spaced is "never", then check whether option is set to true.
60          * @param {Object} option The option to exclude.
61          * @returns {boolean} Whether or not the property is excluded.
62          */
63         function isOptionSet(option) {
64             return context.options[1] ? context.options[1][option] === !spaced : false;
65         }
66
67         const options = {
68             spaced,
69             arraysInObjectsException: isOptionSet("arraysInObjects"),
70             objectsInObjectsException: isOptionSet("objectsInObjects")
71         };
72
73         //--------------------------------------------------------------------------
74         // Helpers
75         //--------------------------------------------------------------------------
76
77         /**
78          * Reports that there shouldn't be a space after the first token
79          * @param {ASTNode} node The node to report in the event of an error.
80          * @param {Token} token The token to use for the report.
81          * @returns {void}
82          */
83         function reportNoBeginningSpace(node, token) {
84             const nextToken = context.getSourceCode().getTokenAfter(token, { includeComments: true });
85
86             context.report({
87                 node,
88                 loc: { start: token.loc.end, end: nextToken.loc.start },
89                 messageId: "unexpectedSpaceAfter",
90                 data: {
91                     token: token.value
92                 },
93                 fix(fixer) {
94                     return fixer.removeRange([token.range[1], nextToken.range[0]]);
95                 }
96             });
97         }
98
99         /**
100          * Reports that there shouldn't be a space before the last token
101          * @param {ASTNode} node The node to report in the event of an error.
102          * @param {Token} token The token to use for the report.
103          * @returns {void}
104          */
105         function reportNoEndingSpace(node, token) {
106             const previousToken = context.getSourceCode().getTokenBefore(token, { includeComments: true });
107
108             context.report({
109                 node,
110                 loc: { start: previousToken.loc.end, end: token.loc.start },
111                 messageId: "unexpectedSpaceBefore",
112                 data: {
113                     token: token.value
114                 },
115                 fix(fixer) {
116                     return fixer.removeRange([previousToken.range[1], token.range[0]]);
117                 }
118             });
119         }
120
121         /**
122          * Reports that there should be a space after the first 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 reportRequiredBeginningSpace(node, token) {
128             context.report({
129                 node,
130                 loc: token.loc,
131                 messageId: "requireSpaceAfter",
132                 data: {
133                     token: token.value
134                 },
135                 fix(fixer) {
136                     return fixer.insertTextAfter(token, " ");
137                 }
138             });
139         }
140
141         /**
142          * Reports that there should be a space before the last token
143          * @param {ASTNode} node The node to report in the event of an error.
144          * @param {Token} token The token to use for the report.
145          * @returns {void}
146          */
147         function reportRequiredEndingSpace(node, token) {
148             context.report({
149                 node,
150                 loc: token.loc,
151                 messageId: "requireSpaceBefore",
152                 data: {
153                     token: token.value
154                 },
155                 fix(fixer) {
156                     return fixer.insertTextBefore(token, " ");
157                 }
158             });
159         }
160
161         /**
162          * Determines if spacing in curly braces is valid.
163          * @param {ASTNode} node The AST node to check.
164          * @param {Token} first The first token to check (should be the opening brace)
165          * @param {Token} second The second token to check (should be first after the opening brace)
166          * @param {Token} penultimate The penultimate token to check (should be last before closing brace)
167          * @param {Token} last The last token to check (should be closing brace)
168          * @returns {void}
169          */
170         function validateBraceSpacing(node, first, second, penultimate, last) {
171             if (astUtils.isTokenOnSameLine(first, second)) {
172                 const firstSpaced = sourceCode.isSpaceBetweenTokens(first, second);
173
174                 if (options.spaced && !firstSpaced) {
175                     reportRequiredBeginningSpace(node, first);
176                 }
177                 if (!options.spaced && firstSpaced && second.type !== "Line") {
178                     reportNoBeginningSpace(node, first);
179                 }
180             }
181
182             if (astUtils.isTokenOnSameLine(penultimate, last)) {
183                 const shouldCheckPenultimate = (
184                     options.arraysInObjectsException && astUtils.isClosingBracketToken(penultimate) ||
185                     options.objectsInObjectsException && astUtils.isClosingBraceToken(penultimate)
186                 );
187                 const penultimateType = shouldCheckPenultimate && sourceCode.getNodeByRangeIndex(penultimate.range[0]).type;
188
189                 const closingCurlyBraceMustBeSpaced = (
190                     options.arraysInObjectsException && penultimateType === "ArrayExpression" ||
191                     options.objectsInObjectsException && (penultimateType === "ObjectExpression" || penultimateType === "ObjectPattern")
192                 ) ? !options.spaced : options.spaced;
193
194                 const lastSpaced = sourceCode.isSpaceBetweenTokens(penultimate, last);
195
196                 if (closingCurlyBraceMustBeSpaced && !lastSpaced) {
197                     reportRequiredEndingSpace(node, last);
198                 }
199                 if (!closingCurlyBraceMustBeSpaced && lastSpaced) {
200                     reportNoEndingSpace(node, last);
201                 }
202             }
203         }
204
205         /**
206          * Gets '}' token of an object node.
207          *
208          * Because the last token of object patterns might be a type annotation,
209          * this traverses tokens preceded by the last property, then returns the
210          * first '}' token.
211          * @param {ASTNode} node The node to get. This node is an
212          *      ObjectExpression or an ObjectPattern. And this node has one or
213          *      more properties.
214          * @returns {Token} '}' token.
215          */
216         function getClosingBraceOfObject(node) {
217             const lastProperty = node.properties[node.properties.length - 1];
218
219             return sourceCode.getTokenAfter(lastProperty, astUtils.isClosingBraceToken);
220         }
221
222         /**
223          * Reports a given object node if spacing in curly braces is invalid.
224          * @param {ASTNode} node An ObjectExpression or ObjectPattern node to check.
225          * @returns {void}
226          */
227         function checkForObject(node) {
228             if (node.properties.length === 0) {
229                 return;
230             }
231
232             const first = sourceCode.getFirstToken(node),
233                 last = getClosingBraceOfObject(node),
234                 second = sourceCode.getTokenAfter(first, { includeComments: true }),
235                 penultimate = sourceCode.getTokenBefore(last, { includeComments: true });
236
237             validateBraceSpacing(node, first, second, penultimate, last);
238         }
239
240         /**
241          * Reports a given import node if spacing in curly braces is invalid.
242          * @param {ASTNode} node An ImportDeclaration node to check.
243          * @returns {void}
244          */
245         function checkForImport(node) {
246             if (node.specifiers.length === 0) {
247                 return;
248             }
249
250             let firstSpecifier = node.specifiers[0];
251             const lastSpecifier = node.specifiers[node.specifiers.length - 1];
252
253             if (lastSpecifier.type !== "ImportSpecifier") {
254                 return;
255             }
256             if (firstSpecifier.type !== "ImportSpecifier") {
257                 firstSpecifier = node.specifiers[1];
258             }
259
260             const first = sourceCode.getTokenBefore(firstSpecifier),
261                 last = sourceCode.getTokenAfter(lastSpecifier, astUtils.isNotCommaToken),
262                 second = sourceCode.getTokenAfter(first, { includeComments: true }),
263                 penultimate = sourceCode.getTokenBefore(last, { includeComments: true });
264
265             validateBraceSpacing(node, first, second, penultimate, last);
266         }
267
268         /**
269          * Reports a given export node if spacing in curly braces is invalid.
270          * @param {ASTNode} node An ExportNamedDeclaration node to check.
271          * @returns {void}
272          */
273         function checkForExport(node) {
274             if (node.specifiers.length === 0) {
275                 return;
276             }
277
278             const firstSpecifier = node.specifiers[0],
279                 lastSpecifier = node.specifiers[node.specifiers.length - 1],
280                 first = sourceCode.getTokenBefore(firstSpecifier),
281                 last = sourceCode.getTokenAfter(lastSpecifier, astUtils.isNotCommaToken),
282                 second = sourceCode.getTokenAfter(first, { includeComments: true }),
283                 penultimate = sourceCode.getTokenBefore(last, { includeComments: true });
284
285             validateBraceSpacing(node, first, second, penultimate, last);
286         }
287
288         //--------------------------------------------------------------------------
289         // Public
290         //--------------------------------------------------------------------------
291
292         return {
293
294             // var {x} = y;
295             ObjectPattern: checkForObject,
296
297             // var y = {x: 'y'}
298             ObjectExpression: checkForObject,
299
300             // import {y} from 'x';
301             ImportDeclaration: checkForImport,
302
303             // export {name} from 'yo';
304             ExportNamedDeclaration: checkForExport
305         };
306
307     }
308 };