Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / object-curly-newline.js
1 /**
2  * @fileoverview Rule to require or disallow line breaks inside braces.
3  * @author Toru Nagashima
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Requirements
10 //------------------------------------------------------------------------------
11
12 const astUtils = require("./utils/ast-utils");
13 const lodash = require("lodash");
14
15 //------------------------------------------------------------------------------
16 // Helpers
17 //------------------------------------------------------------------------------
18
19 // Schema objects.
20 const OPTION_VALUE = {
21     oneOf: [
22         {
23             enum: ["always", "never"]
24         },
25         {
26             type: "object",
27             properties: {
28                 multiline: {
29                     type: "boolean"
30                 },
31                 minProperties: {
32                     type: "integer",
33                     minimum: 0
34                 },
35                 consistent: {
36                     type: "boolean"
37                 }
38             },
39             additionalProperties: false,
40             minProperties: 1
41         }
42     ]
43 };
44
45 /**
46  * Normalizes a given option value.
47  * @param {string|Object|undefined} value An option value to parse.
48  * @returns {{multiline: boolean, minProperties: number, consistent: boolean}} Normalized option object.
49  */
50 function normalizeOptionValue(value) {
51     let multiline = false;
52     let minProperties = Number.POSITIVE_INFINITY;
53     let consistent = false;
54
55     if (value) {
56         if (value === "always") {
57             minProperties = 0;
58         } else if (value === "never") {
59             minProperties = Number.POSITIVE_INFINITY;
60         } else {
61             multiline = Boolean(value.multiline);
62             minProperties = value.minProperties || Number.POSITIVE_INFINITY;
63             consistent = Boolean(value.consistent);
64         }
65     } else {
66         consistent = true;
67     }
68
69     return { multiline, minProperties, consistent };
70 }
71
72 /**
73  * Normalizes a given option value.
74  * @param {string|Object|undefined} options An option value to parse.
75  * @returns {{
76  *   ObjectExpression: {multiline: boolean, minProperties: number, consistent: boolean},
77  *   ObjectPattern: {multiline: boolean, minProperties: number, consistent: boolean},
78  *   ImportDeclaration: {multiline: boolean, minProperties: number, consistent: boolean},
79  *   ExportNamedDeclaration : {multiline: boolean, minProperties: number, consistent: boolean}
80  * }} Normalized option object.
81  */
82 function normalizeOptions(options) {
83     const isNodeSpecificOption = lodash.overSome([lodash.isPlainObject, lodash.isString]);
84
85     if (lodash.isPlainObject(options) && lodash.some(options, isNodeSpecificOption)) {
86         return {
87             ObjectExpression: normalizeOptionValue(options.ObjectExpression),
88             ObjectPattern: normalizeOptionValue(options.ObjectPattern),
89             ImportDeclaration: normalizeOptionValue(options.ImportDeclaration),
90             ExportNamedDeclaration: normalizeOptionValue(options.ExportDeclaration)
91         };
92     }
93
94     const value = normalizeOptionValue(options);
95
96     return { ObjectExpression: value, ObjectPattern: value, ImportDeclaration: value, ExportNamedDeclaration: value };
97 }
98
99 /**
100  * Determines if ObjectExpression, ObjectPattern, ImportDeclaration or ExportNamedDeclaration
101  * node needs to be checked for missing line breaks
102  * @param {ASTNode} node Node under inspection
103  * @param {Object} options option specific to node type
104  * @param {Token} first First object property
105  * @param {Token} last Last object property
106  * @returns {boolean} `true` if node needs to be checked for missing line breaks
107  */
108 function areLineBreaksRequired(node, options, first, last) {
109     let objectProperties;
110
111     if (node.type === "ObjectExpression" || node.type === "ObjectPattern") {
112         objectProperties = node.properties;
113     } else {
114
115         // is ImportDeclaration or ExportNamedDeclaration
116         objectProperties = node.specifiers
117             .filter(s => s.type === "ImportSpecifier" || s.type === "ExportSpecifier");
118     }
119
120     return objectProperties.length >= options.minProperties ||
121         (
122             options.multiline &&
123             objectProperties.length > 0 &&
124             first.loc.start.line !== last.loc.end.line
125         );
126 }
127
128 //------------------------------------------------------------------------------
129 // Rule Definition
130 //------------------------------------------------------------------------------
131
132 module.exports = {
133     meta: {
134         type: "layout",
135
136         docs: {
137             description: "enforce consistent line breaks inside braces",
138             category: "Stylistic Issues",
139             recommended: false,
140             url: "https://eslint.org/docs/rules/object-curly-newline"
141         },
142
143         fixable: "whitespace",
144
145         schema: [
146             {
147                 oneOf: [
148                     OPTION_VALUE,
149                     {
150                         type: "object",
151                         properties: {
152                             ObjectExpression: OPTION_VALUE,
153                             ObjectPattern: OPTION_VALUE,
154                             ImportDeclaration: OPTION_VALUE,
155                             ExportDeclaration: OPTION_VALUE
156                         },
157                         additionalProperties: false,
158                         minProperties: 1
159                     }
160                 ]
161             }
162         ]
163     },
164
165     create(context) {
166         const sourceCode = context.getSourceCode();
167         const normalizedOptions = normalizeOptions(context.options[0]);
168
169         /**
170          * Reports a given node if it violated this rule.
171          * @param {ASTNode} node A node to check. This is an ObjectExpression, ObjectPattern, ImportDeclaration or ExportNamedDeclaration node.
172          * @returns {void}
173          */
174         function check(node) {
175             const options = normalizedOptions[node.type];
176
177             if (
178                 (node.type === "ImportDeclaration" &&
179                     !node.specifiers.some(specifier => specifier.type === "ImportSpecifier")) ||
180                 (node.type === "ExportNamedDeclaration" &&
181                     !node.specifiers.some(specifier => specifier.type === "ExportSpecifier"))
182             ) {
183                 return;
184             }
185
186             const openBrace = sourceCode.getFirstToken(node, token => token.value === "{");
187
188             let closeBrace;
189
190             if (node.typeAnnotation) {
191                 closeBrace = sourceCode.getTokenBefore(node.typeAnnotation);
192             } else {
193                 closeBrace = sourceCode.getLastToken(node, token => token.value === "}");
194             }
195
196             let first = sourceCode.getTokenAfter(openBrace, { includeComments: true });
197             let last = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
198
199             const needsLineBreaks = areLineBreaksRequired(node, options, first, last);
200
201             const hasCommentsFirstToken = astUtils.isCommentToken(first);
202             const hasCommentsLastToken = astUtils.isCommentToken(last);
203
204             /*
205              * Use tokens or comments to check multiline or not.
206              * But use only tokens to check whether line breaks are needed.
207              * This allows:
208              *     var obj = { // eslint-disable-line foo
209              *         a: 1
210              *     }
211              */
212             first = sourceCode.getTokenAfter(openBrace);
213             last = sourceCode.getTokenBefore(closeBrace);
214
215             if (needsLineBreaks) {
216                 if (astUtils.isTokenOnSameLine(openBrace, first)) {
217                     context.report({
218                         message: "Expected a line break after this opening brace.",
219                         node,
220                         loc: openBrace.loc.start,
221                         fix(fixer) {
222                             if (hasCommentsFirstToken) {
223                                 return null;
224                             }
225
226                             return fixer.insertTextAfter(openBrace, "\n");
227                         }
228                     });
229                 }
230                 if (astUtils.isTokenOnSameLine(last, closeBrace)) {
231                     context.report({
232                         message: "Expected a line break before this closing brace.",
233                         node,
234                         loc: closeBrace.loc.start,
235                         fix(fixer) {
236                             if (hasCommentsLastToken) {
237                                 return null;
238                             }
239
240                             return fixer.insertTextBefore(closeBrace, "\n");
241                         }
242                     });
243                 }
244             } else {
245                 const consistent = options.consistent;
246                 const hasLineBreakBetweenOpenBraceAndFirst = !astUtils.isTokenOnSameLine(openBrace, first);
247                 const hasLineBreakBetweenCloseBraceAndLast = !astUtils.isTokenOnSameLine(last, closeBrace);
248
249                 if (
250                     (!consistent && hasLineBreakBetweenOpenBraceAndFirst) ||
251                     (consistent && hasLineBreakBetweenOpenBraceAndFirst && !hasLineBreakBetweenCloseBraceAndLast)
252                 ) {
253                     context.report({
254                         message: "Unexpected line break after this opening brace.",
255                         node,
256                         loc: openBrace.loc.start,
257                         fix(fixer) {
258                             if (hasCommentsFirstToken) {
259                                 return null;
260                             }
261
262                             return fixer.removeRange([
263                                 openBrace.range[1],
264                                 first.range[0]
265                             ]);
266                         }
267                     });
268                 }
269                 if (
270                     (!consistent && hasLineBreakBetweenCloseBraceAndLast) ||
271                     (consistent && !hasLineBreakBetweenOpenBraceAndFirst && hasLineBreakBetweenCloseBraceAndLast)
272                 ) {
273                     context.report({
274                         message: "Unexpected line break before this closing brace.",
275                         node,
276                         loc: closeBrace.loc.start,
277                         fix(fixer) {
278                             if (hasCommentsLastToken) {
279                                 return null;
280                             }
281
282                             return fixer.removeRange([
283                                 last.range[1],
284                                 closeBrace.range[0]
285                             ]);
286                         }
287                     });
288                 }
289             }
290         }
291
292         return {
293             ObjectExpression: check,
294             ObjectPattern: check,
295             ImportDeclaration: check,
296             ExportNamedDeclaration: check
297         };
298     }
299 };