source: imaps-frontend/node_modules/eslint/lib/rules/lines-around-comment.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 17.9 KB
Line 
1/**
2 * @fileoverview Enforces empty lines around comments.
3 * @author Jamund Ferguson
4 * @deprecated in ESLint v8.53.0
5 */
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Helpers
16//------------------------------------------------------------------------------
17
18/**
19 * Return an array with any line numbers that are empty.
20 * @param {Array} lines An array of each line of the file.
21 * @returns {Array} An array of line numbers.
22 */
23function getEmptyLineNums(lines) {
24 const emptyLines = lines.map((line, i) => ({
25 code: line.trim(),
26 num: i + 1
27 })).filter(line => !line.code).map(line => line.num);
28
29 return emptyLines;
30}
31
32/**
33 * Return an array with any line numbers that contain comments.
34 * @param {Array} comments An array of comment tokens.
35 * @returns {Array} An array of line numbers.
36 */
37function getCommentLineNums(comments) {
38 const lines = [];
39
40 comments.forEach(token => {
41 const start = token.loc.start.line;
42 const end = token.loc.end.line;
43
44 lines.push(start, end);
45 });
46 return lines;
47}
48
49//------------------------------------------------------------------------------
50// Rule Definition
51//------------------------------------------------------------------------------
52
53/** @type {import('../shared/types').Rule} */
54module.exports = {
55 meta: {
56 deprecated: true,
57 replacedBy: [],
58 type: "layout",
59
60 docs: {
61 description: "Require empty lines around comments",
62 recommended: false,
63 url: "https://eslint.org/docs/latest/rules/lines-around-comment"
64 },
65
66 fixable: "whitespace",
67
68 schema: [
69 {
70 type: "object",
71 properties: {
72 beforeBlockComment: {
73 type: "boolean",
74 default: true
75 },
76 afterBlockComment: {
77 type: "boolean",
78 default: false
79 },
80 beforeLineComment: {
81 type: "boolean",
82 default: false
83 },
84 afterLineComment: {
85 type: "boolean",
86 default: false
87 },
88 allowBlockStart: {
89 type: "boolean",
90 default: false
91 },
92 allowBlockEnd: {
93 type: "boolean",
94 default: false
95 },
96 allowClassStart: {
97 type: "boolean"
98 },
99 allowClassEnd: {
100 type: "boolean"
101 },
102 allowObjectStart: {
103 type: "boolean"
104 },
105 allowObjectEnd: {
106 type: "boolean"
107 },
108 allowArrayStart: {
109 type: "boolean"
110 },
111 allowArrayEnd: {
112 type: "boolean"
113 },
114 ignorePattern: {
115 type: "string"
116 },
117 applyDefaultIgnorePatterns: {
118 type: "boolean"
119 },
120 afterHashbangComment: {
121 type: "boolean",
122 default: false
123 }
124 },
125 additionalProperties: false
126 }
127 ],
128 messages: {
129 after: "Expected line after comment.",
130 before: "Expected line before comment."
131 }
132 },
133
134 create(context) {
135
136 const options = Object.assign({}, context.options[0]);
137 const ignorePattern = options.ignorePattern;
138 const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
139 const customIgnoreRegExp = new RegExp(ignorePattern, "u");
140 const applyDefaultIgnorePatterns = options.applyDefaultIgnorePatterns !== false;
141
142 options.beforeBlockComment = typeof options.beforeBlockComment !== "undefined" ? options.beforeBlockComment : true;
143
144 const sourceCode = context.sourceCode;
145
146 const lines = sourceCode.lines,
147 numLines = lines.length + 1,
148 comments = sourceCode.getAllComments(),
149 commentLines = getCommentLineNums(comments),
150 emptyLines = getEmptyLineNums(lines),
151 commentAndEmptyLines = new Set(commentLines.concat(emptyLines));
152
153 /**
154 * Returns whether or not comments are on lines starting with or ending with code
155 * @param {token} token The comment token to check.
156 * @returns {boolean} True if the comment is not alone.
157 */
158 function codeAroundComment(token) {
159 let currentToken = token;
160
161 do {
162 currentToken = sourceCode.getTokenBefore(currentToken, { includeComments: true });
163 } while (currentToken && astUtils.isCommentToken(currentToken));
164
165 if (currentToken && astUtils.isTokenOnSameLine(currentToken, token)) {
166 return true;
167 }
168
169 currentToken = token;
170 do {
171 currentToken = sourceCode.getTokenAfter(currentToken, { includeComments: true });
172 } while (currentToken && astUtils.isCommentToken(currentToken));
173
174 if (currentToken && astUtils.isTokenOnSameLine(token, currentToken)) {
175 return true;
176 }
177
178 return false;
179 }
180
181 /**
182 * Returns whether or not comments are inside a node type or not.
183 * @param {ASTNode} parent The Comment parent node.
184 * @param {string} nodeType The parent type to check against.
185 * @returns {boolean} True if the comment is inside nodeType.
186 */
187 function isParentNodeType(parent, nodeType) {
188 return parent.type === nodeType ||
189 (parent.body && parent.body.type === nodeType) ||
190 (parent.consequent && parent.consequent.type === nodeType);
191 }
192
193 /**
194 * Returns the parent node that contains the given token.
195 * @param {token} token The token to check.
196 * @returns {ASTNode|null} The parent node that contains the given token.
197 */
198 function getParentNodeOfToken(token) {
199 const node = sourceCode.getNodeByRangeIndex(token.range[0]);
200
201 /*
202 * For the purpose of this rule, the comment token is in a `StaticBlock` node only
203 * if it's inside the braces of that `StaticBlock` node.
204 *
205 * Example where this function returns `null`:
206 *
207 * static
208 * // comment
209 * {
210 * }
211 *
212 * Example where this function returns `StaticBlock` node:
213 *
214 * static
215 * {
216 * // comment
217 * }
218 *
219 */
220 if (node && node.type === "StaticBlock") {
221 const openingBrace = sourceCode.getFirstToken(node, { skip: 1 }); // skip the `static` token
222
223 return token.range[0] >= openingBrace.range[0]
224 ? node
225 : null;
226 }
227
228 return node;
229 }
230
231 /**
232 * Returns whether or not comments are at the parent start or not.
233 * @param {token} token The Comment token.
234 * @param {string} nodeType The parent type to check against.
235 * @returns {boolean} True if the comment is at parent start.
236 */
237 function isCommentAtParentStart(token, nodeType) {
238 const parent = getParentNodeOfToken(token);
239
240 if (parent && isParentNodeType(parent, nodeType)) {
241 let parentStartNodeOrToken = parent;
242
243 if (parent.type === "StaticBlock") {
244 parentStartNodeOrToken = sourceCode.getFirstToken(parent, { skip: 1 }); // opening brace of the static block
245 } else if (parent.type === "SwitchStatement") {
246 parentStartNodeOrToken = sourceCode.getTokenAfter(parent.discriminant, {
247 filter: astUtils.isOpeningBraceToken
248 }); // opening brace of the switch statement
249 }
250
251 return token.loc.start.line - parentStartNodeOrToken.loc.start.line === 1;
252 }
253
254 return false;
255 }
256
257 /**
258 * Returns whether or not comments are at the parent end or not.
259 * @param {token} token The Comment token.
260 * @param {string} nodeType The parent type to check against.
261 * @returns {boolean} True if the comment is at parent end.
262 */
263 function isCommentAtParentEnd(token, nodeType) {
264 const parent = getParentNodeOfToken(token);
265
266 return !!parent && isParentNodeType(parent, nodeType) &&
267 parent.loc.end.line - token.loc.end.line === 1;
268 }
269
270 /**
271 * Returns whether or not comments are at the block start or not.
272 * @param {token} token The Comment token.
273 * @returns {boolean} True if the comment is at block start.
274 */
275 function isCommentAtBlockStart(token) {
276 return (
277 isCommentAtParentStart(token, "ClassBody") ||
278 isCommentAtParentStart(token, "BlockStatement") ||
279 isCommentAtParentStart(token, "StaticBlock") ||
280 isCommentAtParentStart(token, "SwitchCase") ||
281 isCommentAtParentStart(token, "SwitchStatement")
282 );
283 }
284
285 /**
286 * Returns whether or not comments are at the block end or not.
287 * @param {token} token The Comment token.
288 * @returns {boolean} True if the comment is at block end.
289 */
290 function isCommentAtBlockEnd(token) {
291 return (
292 isCommentAtParentEnd(token, "ClassBody") ||
293 isCommentAtParentEnd(token, "BlockStatement") ||
294 isCommentAtParentEnd(token, "StaticBlock") ||
295 isCommentAtParentEnd(token, "SwitchCase") ||
296 isCommentAtParentEnd(token, "SwitchStatement")
297 );
298 }
299
300 /**
301 * Returns whether or not comments are at the class start or not.
302 * @param {token} token The Comment token.
303 * @returns {boolean} True if the comment is at class start.
304 */
305 function isCommentAtClassStart(token) {
306 return isCommentAtParentStart(token, "ClassBody");
307 }
308
309 /**
310 * Returns whether or not comments are at the class end or not.
311 * @param {token} token The Comment token.
312 * @returns {boolean} True if the comment is at class end.
313 */
314 function isCommentAtClassEnd(token) {
315 return isCommentAtParentEnd(token, "ClassBody");
316 }
317
318 /**
319 * Returns whether or not comments are at the object start or not.
320 * @param {token} token The Comment token.
321 * @returns {boolean} True if the comment is at object start.
322 */
323 function isCommentAtObjectStart(token) {
324 return isCommentAtParentStart(token, "ObjectExpression") || isCommentAtParentStart(token, "ObjectPattern");
325 }
326
327 /**
328 * Returns whether or not comments are at the object end or not.
329 * @param {token} token The Comment token.
330 * @returns {boolean} True if the comment is at object end.
331 */
332 function isCommentAtObjectEnd(token) {
333 return isCommentAtParentEnd(token, "ObjectExpression") || isCommentAtParentEnd(token, "ObjectPattern");
334 }
335
336 /**
337 * Returns whether or not comments are at the array start or not.
338 * @param {token} token The Comment token.
339 * @returns {boolean} True if the comment is at array start.
340 */
341 function isCommentAtArrayStart(token) {
342 return isCommentAtParentStart(token, "ArrayExpression") || isCommentAtParentStart(token, "ArrayPattern");
343 }
344
345 /**
346 * Returns whether or not comments are at the array end or not.
347 * @param {token} token The Comment token.
348 * @returns {boolean} True if the comment is at array end.
349 */
350 function isCommentAtArrayEnd(token) {
351 return isCommentAtParentEnd(token, "ArrayExpression") || isCommentAtParentEnd(token, "ArrayPattern");
352 }
353
354 /**
355 * Checks if a comment token has lines around it (ignores inline comments)
356 * @param {token} token The Comment token.
357 * @param {Object} opts Options to determine the newline.
358 * @param {boolean} opts.after Should have a newline after this line.
359 * @param {boolean} opts.before Should have a newline before this line.
360 * @returns {void}
361 */
362 function checkForEmptyLine(token, opts) {
363 if (applyDefaultIgnorePatterns && defaultIgnoreRegExp.test(token.value)) {
364 return;
365 }
366
367 if (ignorePattern && customIgnoreRegExp.test(token.value)) {
368 return;
369 }
370
371 let after = opts.after,
372 before = opts.before;
373
374 const prevLineNum = token.loc.start.line - 1,
375 nextLineNum = token.loc.end.line + 1,
376 commentIsNotAlone = codeAroundComment(token);
377
378 const blockStartAllowed = options.allowBlockStart &&
379 isCommentAtBlockStart(token) &&
380 !(options.allowClassStart === false &&
381 isCommentAtClassStart(token)),
382 blockEndAllowed = options.allowBlockEnd && isCommentAtBlockEnd(token) && !(options.allowClassEnd === false && isCommentAtClassEnd(token)),
383 classStartAllowed = options.allowClassStart && isCommentAtClassStart(token),
384 classEndAllowed = options.allowClassEnd && isCommentAtClassEnd(token),
385 objectStartAllowed = options.allowObjectStart && isCommentAtObjectStart(token),
386 objectEndAllowed = options.allowObjectEnd && isCommentAtObjectEnd(token),
387 arrayStartAllowed = options.allowArrayStart && isCommentAtArrayStart(token),
388 arrayEndAllowed = options.allowArrayEnd && isCommentAtArrayEnd(token);
389
390 const exceptionStartAllowed = blockStartAllowed || classStartAllowed || objectStartAllowed || arrayStartAllowed;
391 const exceptionEndAllowed = blockEndAllowed || classEndAllowed || objectEndAllowed || arrayEndAllowed;
392
393 // ignore top of the file and bottom of the file
394 if (prevLineNum < 1) {
395 before = false;
396 }
397 if (nextLineNum >= numLines) {
398 after = false;
399 }
400
401 // we ignore all inline comments
402 if (commentIsNotAlone) {
403 return;
404 }
405
406 const previousTokenOrComment = sourceCode.getTokenBefore(token, { includeComments: true });
407 const nextTokenOrComment = sourceCode.getTokenAfter(token, { includeComments: true });
408
409 // check for newline before
410 if (!exceptionStartAllowed && before && !commentAndEmptyLines.has(prevLineNum) &&
411 !(astUtils.isCommentToken(previousTokenOrComment) && astUtils.isTokenOnSameLine(previousTokenOrComment, token))) {
412 const lineStart = token.range[0] - token.loc.start.column;
413 const range = [lineStart, lineStart];
414
415 context.report({
416 node: token,
417 messageId: "before",
418 fix(fixer) {
419 return fixer.insertTextBeforeRange(range, "\n");
420 }
421 });
422 }
423
424 // check for newline after
425 if (!exceptionEndAllowed && after && !commentAndEmptyLines.has(nextLineNum) &&
426 !(astUtils.isCommentToken(nextTokenOrComment) && astUtils.isTokenOnSameLine(token, nextTokenOrComment))) {
427 context.report({
428 node: token,
429 messageId: "after",
430 fix(fixer) {
431 return fixer.insertTextAfter(token, "\n");
432 }
433 });
434 }
435
436 }
437
438 //--------------------------------------------------------------------------
439 // Public
440 //--------------------------------------------------------------------------
441
442 return {
443 Program() {
444 comments.forEach(token => {
445 if (token.type === "Line") {
446 if (options.beforeLineComment || options.afterLineComment) {
447 checkForEmptyLine(token, {
448 after: options.afterLineComment,
449 before: options.beforeLineComment
450 });
451 }
452 } else if (token.type === "Block") {
453 if (options.beforeBlockComment || options.afterBlockComment) {
454 checkForEmptyLine(token, {
455 after: options.afterBlockComment,
456 before: options.beforeBlockComment
457 });
458 }
459 } else if (token.type === "Shebang") {
460 if (options.afterHashbangComment) {
461 checkForEmptyLine(token, {
462 after: options.afterHashbangComment,
463 before: false
464 });
465 }
466 }
467 });
468 }
469 };
470 }
471};
Note: See TracBrowser for help on using the repository browser.