.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / array-bracket-newline.js
1 /**
2  * @fileoverview Rule to enforce linebreaks after open and before close array brackets
3  * @author Jan Peer Stöcklmair <https://github.com/JPeer264>
4  */
5
6 "use strict";
7
8 const astUtils = require("./utils/ast-utils");
9
10 //------------------------------------------------------------------------------
11 // Rule Definition
12 //------------------------------------------------------------------------------
13
14 module.exports = {
15     meta: {
16         type: "layout",
17
18         docs: {
19             description: "enforce linebreaks after opening and before closing array brackets",
20             category: "Stylistic Issues",
21             recommended: false,
22             url: "https://eslint.org/docs/rules/array-bracket-newline"
23         },
24
25         fixable: "whitespace",
26
27         schema: [
28             {
29                 oneOf: [
30                     {
31                         enum: ["always", "never", "consistent"]
32                     },
33                     {
34                         type: "object",
35                         properties: {
36                             multiline: {
37                                 type: "boolean"
38                             },
39                             minItems: {
40                                 type: ["integer", "null"],
41                                 minimum: 0
42                             }
43                         },
44                         additionalProperties: false
45                     }
46                 ]
47             }
48         ],
49
50         messages: {
51             unexpectedOpeningLinebreak: "There should be no linebreak after '['.",
52             unexpectedClosingLinebreak: "There should be no linebreak before ']'.",
53             missingOpeningLinebreak: "A linebreak is required after '['.",
54             missingClosingLinebreak: "A linebreak is required before ']'."
55         }
56     },
57
58     create(context) {
59         const sourceCode = context.getSourceCode();
60
61
62         //----------------------------------------------------------------------
63         // Helpers
64         //----------------------------------------------------------------------
65
66         /**
67          * Normalizes a given option value.
68          * @param {string|Object|undefined} option An option value to parse.
69          * @returns {{multiline: boolean, minItems: number}} Normalized option object.
70          */
71         function normalizeOptionValue(option) {
72             let consistent = false;
73             let multiline = false;
74             let minItems = 0;
75
76             if (option) {
77                 if (option === "consistent") {
78                     consistent = true;
79                     minItems = Number.POSITIVE_INFINITY;
80                 } else if (option === "always" || option.minItems === 0) {
81                     minItems = 0;
82                 } else if (option === "never") {
83                     minItems = Number.POSITIVE_INFINITY;
84                 } else {
85                     multiline = Boolean(option.multiline);
86                     minItems = option.minItems || Number.POSITIVE_INFINITY;
87                 }
88             } else {
89                 consistent = false;
90                 multiline = true;
91                 minItems = Number.POSITIVE_INFINITY;
92             }
93
94             return { consistent, multiline, minItems };
95         }
96
97         /**
98          * Normalizes a given option value.
99          * @param {string|Object|undefined} options An option value to parse.
100          * @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object.
101          */
102         function normalizeOptions(options) {
103             const value = normalizeOptionValue(options);
104
105             return { ArrayExpression: value, ArrayPattern: value };
106         }
107
108         /**
109          * Reports that there shouldn't be a linebreak after the first token
110          * @param {ASTNode} node The node to report in the event of an error.
111          * @param {Token} token The token to use for the report.
112          * @returns {void}
113          */
114         function reportNoBeginningLinebreak(node, token) {
115             context.report({
116                 node,
117                 loc: token.loc,
118                 messageId: "unexpectedOpeningLinebreak",
119                 fix(fixer) {
120                     const nextToken = sourceCode.getTokenAfter(token, { includeComments: true });
121
122                     if (astUtils.isCommentToken(nextToken)) {
123                         return null;
124                     }
125
126                     return fixer.removeRange([token.range[1], nextToken.range[0]]);
127                 }
128             });
129         }
130
131         /**
132          * Reports that there shouldn't be a linebreak before the last token
133          * @param {ASTNode} node The node to report in the event of an error.
134          * @param {Token} token The token to use for the report.
135          * @returns {void}
136          */
137         function reportNoEndingLinebreak(node, token) {
138             context.report({
139                 node,
140                 loc: token.loc,
141                 messageId: "unexpectedClosingLinebreak",
142                 fix(fixer) {
143                     const previousToken = sourceCode.getTokenBefore(token, { includeComments: true });
144
145                     if (astUtils.isCommentToken(previousToken)) {
146                         return null;
147                     }
148
149                     return fixer.removeRange([previousToken.range[1], token.range[0]]);
150                 }
151             });
152         }
153
154         /**
155          * Reports that there should be a linebreak after the first token
156          * @param {ASTNode} node The node to report in the event of an error.
157          * @param {Token} token The token to use for the report.
158          * @returns {void}
159          */
160         function reportRequiredBeginningLinebreak(node, token) {
161             context.report({
162                 node,
163                 loc: token.loc,
164                 messageId: "missingOpeningLinebreak",
165                 fix(fixer) {
166                     return fixer.insertTextAfter(token, "\n");
167                 }
168             });
169         }
170
171         /**
172          * Reports that there should be a linebreak before the last token
173          * @param {ASTNode} node The node to report in the event of an error.
174          * @param {Token} token The token to use for the report.
175          * @returns {void}
176          */
177         function reportRequiredEndingLinebreak(node, token) {
178             context.report({
179                 node,
180                 loc: token.loc,
181                 messageId: "missingClosingLinebreak",
182                 fix(fixer) {
183                     return fixer.insertTextBefore(token, "\n");
184                 }
185             });
186         }
187
188         /**
189          * Reports a given node if it violated this rule.
190          * @param {ASTNode} node A node to check. This is an ArrayExpression node or an ArrayPattern node.
191          * @returns {void}
192          */
193         function check(node) {
194             const elements = node.elements;
195             const normalizedOptions = normalizeOptions(context.options[0]);
196             const options = normalizedOptions[node.type];
197             const openBracket = sourceCode.getFirstToken(node);
198             const closeBracket = sourceCode.getLastToken(node);
199             const firstIncComment = sourceCode.getTokenAfter(openBracket, { includeComments: true });
200             const lastIncComment = sourceCode.getTokenBefore(closeBracket, { includeComments: true });
201             const first = sourceCode.getTokenAfter(openBracket);
202             const last = sourceCode.getTokenBefore(closeBracket);
203
204             const needsLinebreaks = (
205                 elements.length >= options.minItems ||
206                 (
207                     options.multiline &&
208                     elements.length > 0 &&
209                     firstIncComment.loc.start.line !== lastIncComment.loc.end.line
210                 ) ||
211                 (
212                     elements.length === 0 &&
213                     firstIncComment.type === "Block" &&
214                     firstIncComment.loc.start.line !== lastIncComment.loc.end.line &&
215                     firstIncComment === lastIncComment
216                 ) ||
217                 (
218                     options.consistent &&
219                     openBracket.loc.end.line !== first.loc.start.line
220                 )
221             );
222
223             /*
224              * Use tokens or comments to check multiline or not.
225              * But use only tokens to check whether linebreaks are needed.
226              * This allows:
227              *     var arr = [ // eslint-disable-line foo
228              *         'a'
229              *     ]
230              */
231
232             if (needsLinebreaks) {
233                 if (astUtils.isTokenOnSameLine(openBracket, first)) {
234                     reportRequiredBeginningLinebreak(node, openBracket);
235                 }
236                 if (astUtils.isTokenOnSameLine(last, closeBracket)) {
237                     reportRequiredEndingLinebreak(node, closeBracket);
238                 }
239             } else {
240                 if (!astUtils.isTokenOnSameLine(openBracket, first)) {
241                     reportNoBeginningLinebreak(node, openBracket);
242                 }
243                 if (!astUtils.isTokenOnSameLine(last, closeBracket)) {
244                     reportNoEndingLinebreak(node, closeBracket);
245                 }
246             }
247         }
248
249         //----------------------------------------------------------------------
250         // Public
251         //----------------------------------------------------------------------
252
253         return {
254             ArrayPattern: check,
255             ArrayExpression: check
256         };
257     }
258 };