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