eslint 4.7.2 → 4.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/README.md +34 -19
  3. package/conf/default-cli-options.js +7 -4
  4. package/conf/eslint-recommended.js +2 -0
  5. package/lib/ast-utils.js +83 -42
  6. package/lib/cli-engine.js +53 -17
  7. package/lib/cli.js +17 -9
  8. package/lib/code-path-analysis/code-path-analyzer.js +8 -4
  9. package/lib/code-path-analysis/code-path-segment.js +43 -41
  10. package/lib/code-path-analysis/code-path-state.js +7 -2
  11. package/lib/config/autoconfig.js +14 -12
  12. package/lib/config/config-file.js +8 -51
  13. package/lib/config/config-initializer.js +10 -6
  14. package/lib/config/config-ops.js +21 -21
  15. package/lib/config/config-rule.js +24 -24
  16. package/lib/config/config-validator.js +38 -36
  17. package/lib/config/plugins.js +8 -35
  18. package/lib/config.js +12 -8
  19. package/lib/formatters/html-template-message.html +1 -1
  20. package/lib/formatters/html-template-page.html +3 -1
  21. package/lib/formatters/html.js +2 -1
  22. package/lib/formatters/junit.js +21 -15
  23. package/lib/formatters/tap.js +5 -3
  24. package/lib/ignored-paths.js +5 -3
  25. package/lib/linter.js +42 -42
  26. package/lib/logging.js +2 -2
  27. package/lib/options.js +12 -0
  28. package/lib/rules/.eslintrc.yml +2 -2
  29. package/lib/rules/array-bracket-newline.js +39 -25
  30. package/lib/rules/array-bracket-spacing.js +28 -28
  31. package/lib/rules/array-callback-return.js +13 -9
  32. package/lib/rules/array-element-newline.js +8 -8
  33. package/lib/rules/arrow-body-style.js +12 -6
  34. package/lib/rules/arrow-parens.js +4 -2
  35. package/lib/rules/block-spacing.js +1 -1
  36. package/lib/rules/brace-style.js +14 -14
  37. package/lib/rules/callback-return.js +2 -1
  38. package/lib/rules/capitalized-comments.js +2 -1
  39. package/lib/rules/comma-style.js +3 -1
  40. package/lib/rules/computed-property-spacing.js +22 -22
  41. package/lib/rules/consistent-return.js +4 -4
  42. package/lib/rules/consistent-this.js +4 -2
  43. package/lib/rules/curly.js +13 -9
  44. package/lib/rules/dot-notation.js +56 -35
  45. package/lib/rules/func-call-spacing.js +4 -2
  46. package/lib/rules/generator-star-spacing.js +3 -3
  47. package/lib/rules/getter-return.js +2 -1
  48. package/lib/rules/indent-legacy.js +25 -14
  49. package/lib/rules/indent.js +101 -91
  50. package/lib/rules/key-spacing.js +5 -3
  51. package/lib/rules/lines-around-comment.js +33 -4
  52. package/lib/rules/lines-around-directive.js +16 -12
  53. package/lib/rules/lines-between-class-members.js +91 -0
  54. package/lib/rules/max-len.js +2 -3
  55. package/lib/rules/max-statements-per-line.js +5 -3
  56. package/lib/rules/multiline-comment-style.js +294 -0
  57. package/lib/rules/new-cap.js +2 -1
  58. package/lib/rules/newline-after-var.js +8 -6
  59. package/lib/rules/newline-before-return.js +13 -9
  60. package/lib/rules/no-alert.js +7 -15
  61. package/lib/rules/no-await-in-loop.js +17 -9
  62. package/lib/rules/no-bitwise.js +5 -3
  63. package/lib/rules/no-catch-shadow.js +4 -2
  64. package/lib/rules/no-console.js +2 -1
  65. package/lib/rules/no-constant-condition.js +2 -2
  66. package/lib/rules/no-control-regex.js +2 -1
  67. package/lib/rules/no-else-return.js +60 -19
  68. package/lib/rules/no-empty-character-class.js +11 -11
  69. package/lib/rules/no-extra-parens.js +22 -11
  70. package/lib/rules/no-extra-semi.js +5 -3
  71. package/lib/rules/no-global-assign.js +4 -2
  72. package/lib/rules/no-implicit-coercion.js +6 -6
  73. package/lib/rules/no-implied-eval.js +2 -1
  74. package/lib/rules/no-label-var.js +4 -2
  75. package/lib/rules/no-lone-blocks.js +3 -3
  76. package/lib/rules/no-lonely-if.js +2 -1
  77. package/lib/rules/no-loop-func.js +10 -7
  78. package/lib/rules/no-mixed-requires.js +8 -4
  79. package/lib/rules/no-native-reassign.js +4 -2
  80. package/lib/rules/no-param-reassign.js +4 -2
  81. package/lib/rules/no-regex-spaces.js +1 -1
  82. package/lib/rules/no-restricted-imports.js +86 -17
  83. package/lib/rules/no-restricted-modules.js +84 -15
  84. package/lib/rules/no-restricted-properties.js +10 -10
  85. package/lib/rules/no-return-await.js +6 -6
  86. package/lib/rules/no-self-assign.js +4 -2
  87. package/lib/rules/no-sequences.js +6 -4
  88. package/lib/rules/no-trailing-spaces.js +14 -8
  89. package/lib/rules/no-unneeded-ternary.js +3 -1
  90. package/lib/rules/no-unreachable.js +4 -2
  91. package/lib/rules/no-unused-labels.js +2 -1
  92. package/lib/rules/no-use-before-define.js +13 -11
  93. package/lib/rules/no-useless-call.js +1 -25
  94. package/lib/rules/no-useless-computed-key.js +2 -1
  95. package/lib/rules/no-useless-escape.js +31 -23
  96. package/lib/rules/no-useless-return.js +14 -8
  97. package/lib/rules/no-var.js +11 -0
  98. package/lib/rules/no-whitespace-before-property.js +4 -2
  99. package/lib/rules/object-curly-newline.js +9 -2
  100. package/lib/rules/object-curly-spacing.js +20 -20
  101. package/lib/rules/object-shorthand.js +47 -35
  102. package/lib/rules/operator-assignment.js +9 -9
  103. package/lib/rules/operator-linebreak.js +15 -11
  104. package/lib/rules/padding-line-between-statements.js +6 -4
  105. package/lib/rules/prefer-arrow-callback.js +12 -10
  106. package/lib/rules/prefer-const.js +18 -10
  107. package/lib/rules/prefer-destructuring.js +4 -2
  108. package/lib/rules/prefer-numeric-literals.js +4 -2
  109. package/lib/rules/prefer-promise-reject-errors.js +16 -16
  110. package/lib/rules/prefer-rest-params.js +4 -2
  111. package/lib/rules/prefer-spread.js +1 -25
  112. package/lib/rules/prefer-template.js +33 -29
  113. package/lib/rules/quote-props.js +8 -8
  114. package/lib/rules/require-jsdoc.js +11 -18
  115. package/lib/rules/semi-style.js +44 -19
  116. package/lib/rules/semi.js +5 -3
  117. package/lib/rules/sort-imports.js +11 -6
  118. package/lib/rules/space-unary-ops.js +67 -69
  119. package/lib/rules/strict.js +8 -8
  120. package/lib/rules/valid-jsdoc.js +39 -33
  121. package/lib/rules/valid-typeof.js +4 -4
  122. package/lib/rules/wrap-iife.js +4 -4
  123. package/lib/rules/yoda.js +9 -7
  124. package/lib/testers/rule-tester.js +63 -40
  125. package/lib/token-store/backward-token-cursor.js +5 -3
  126. package/lib/token-store/forward-token-cursor.js +5 -3
  127. package/lib/token-store/utils.js +8 -4
  128. package/lib/util/apply-disable-directives.js +56 -27
  129. package/lib/util/glob.js +1 -1
  130. package/lib/util/naming.js +112 -0
  131. package/lib/util/node-event-generator.js +13 -27
  132. package/lib/util/safe-emitter.js +54 -0
  133. package/lib/util/source-code-fixer.js +4 -2
  134. package/lib/util/source-code.js +70 -65
  135. package/messages/no-config-found.txt +1 -1
  136. package/package.json +8 -8
  137. package/lib/internal-rules/.eslintrc.yml +0 -3
  138. package/lib/internal-rules/internal-consistent-docs-description.js +0 -130
  139. package/lib/internal-rules/internal-no-invalid-meta.js +0 -188
@@ -82,6 +82,12 @@ module.exports = {
82
82
  allowBlockEnd: {
83
83
  type: "boolean"
84
84
  },
85
+ allowClassStart: {
86
+ type: "boolean"
87
+ },
88
+ allowClassEnd: {
89
+ type: "boolean"
90
+ },
85
91
  allowObjectStart: {
86
92
  type: "boolean"
87
93
  },
@@ -224,6 +230,24 @@ module.exports = {
224
230
  return isCommentAtParentEnd(token, "ClassBody") || isCommentAtParentEnd(token, "BlockStatement") || isCommentAtParentEnd(token, "SwitchCase") || isCommentAtParentEnd(token, "SwitchStatement");
225
231
  }
226
232
 
233
+ /**
234
+ * Returns whether or not comments are at the class start or not.
235
+ * @param {token} token The Comment token.
236
+ * @returns {boolean} True if the comment is at class start.
237
+ */
238
+ function isCommentAtClassStart(token) {
239
+ return isCommentAtParentStart(token, "ClassBody");
240
+ }
241
+
242
+ /**
243
+ * Returns whether or not comments are at the class end or not.
244
+ * @param {token} token The Comment token.
245
+ * @returns {boolean} True if the comment is at class end.
246
+ */
247
+ function isCommentAtClassEnd(token) {
248
+ return isCommentAtParentEnd(token, "ClassBody");
249
+ }
250
+
227
251
  /**
228
252
  * Returns whether or not comments are at the object start or not.
229
253
  * @param {token} token The Comment token.
@@ -284,15 +308,20 @@ module.exports = {
284
308
  nextLineNum = token.loc.end.line + 1,
285
309
  commentIsNotAlone = codeAroundComment(token);
286
310
 
287
- const blockStartAllowed = options.allowBlockStart && isCommentAtBlockStart(token),
288
- blockEndAllowed = options.allowBlockEnd && isCommentAtBlockEnd(token),
311
+ const blockStartAllowed = options.allowBlockStart &&
312
+ isCommentAtBlockStart(token) &&
313
+ !(options.allowClassStart === false &&
314
+ isCommentAtClassStart(token)),
315
+ blockEndAllowed = options.allowBlockEnd && isCommentAtBlockEnd(token) && !(options.allowClassEnd === false && isCommentAtClassEnd(token)),
316
+ classStartAllowed = options.allowClassStart && isCommentAtClassStart(token),
317
+ classEndAllowed = options.allowClassEnd && isCommentAtClassEnd(token),
289
318
  objectStartAllowed = options.allowObjectStart && isCommentAtObjectStart(token),
290
319
  objectEndAllowed = options.allowObjectEnd && isCommentAtObjectEnd(token),
291
320
  arrayStartAllowed = options.allowArrayStart && isCommentAtArrayStart(token),
292
321
  arrayEndAllowed = options.allowArrayEnd && isCommentAtArrayEnd(token);
293
322
 
294
- const exceptionStartAllowed = blockStartAllowed || objectStartAllowed || arrayStartAllowed;
295
- const exceptionEndAllowed = blockEndAllowed || objectEndAllowed || arrayEndAllowed;
323
+ const exceptionStartAllowed = blockStartAllowed || classStartAllowed || objectStartAllowed || arrayStartAllowed;
324
+ const exceptionEndAllowed = blockEndAllowed || classEndAllowed || objectEndAllowed || arrayEndAllowed;
296
325
 
297
326
  // ignore top of the file and bottom of the file
298
327
  if (prevLineNum < 1) {
@@ -67,12 +67,12 @@ module.exports = {
67
67
  }
68
68
 
69
69
  /**
70
- * Gets the last token of a node that is on the same line as the rest of the node.
71
- * This will usually be the last token of the node, but it will be the second-to-last token if the node has a trailing
72
- * semicolon on a different line.
73
- * @param {ASTNode} node A directive node
74
- * @returns {Token} The last token of the node on the line
75
- */
70
+ * Gets the last token of a node that is on the same line as the rest of the node.
71
+ * This will usually be the last token of the node, but it will be the second-to-last token if the node has a trailing
72
+ * semicolon on a different line.
73
+ * @param {ASTNode} node A directive node
74
+ * @returns {Token} The last token of the node on the line
75
+ */
76
76
  function getLastTokenOnLine(node) {
77
77
  const lastToken = sourceCode.getLastToken(node);
78
78
  const secondToLastToken = sourceCode.getTokenBefore(lastToken);
@@ -136,9 +136,11 @@ module.exports = {
136
136
  const firstDirective = directives[0];
137
137
  const leadingComments = sourceCode.getCommentsBefore(firstDirective);
138
138
 
139
- // Only check before the first directive if it is preceded by a comment or if it is at the top of
140
- // the file and expectLineBefore is set to "never". This is to not force a newline at the top of
141
- // the file if there are no comments as well as for compatibility with padded-blocks.
139
+ /*
140
+ * Only check before the first directive if it is preceded by a comment or if it is at the top of
141
+ * the file and expectLineBefore is set to "never". This is to not force a newline at the top of
142
+ * the file if there are no comments as well as for compatibility with padded-blocks.
143
+ */
142
144
  if (leadingComments.length) {
143
145
  if (expectLineBefore === "always" && !hasNewlineBefore(firstDirective)) {
144
146
  reportError(firstDirective, "before", true);
@@ -159,9 +161,11 @@ module.exports = {
159
161
  const lastDirective = directives[directives.length - 1];
160
162
  const statements = node.type === "Program" ? node.body : node.body.body;
161
163
 
162
- // Do not check after the last directive if the body only
163
- // contains a directive prologue and isn't followed by a comment to ensure
164
- // this rule behaves well with padded-blocks.
164
+ /*
165
+ * Do not check after the last directive if the body only
166
+ * contains a directive prologue and isn't followed by a comment to ensure
167
+ * this rule behaves well with padded-blocks.
168
+ */
165
169
  if (lastDirective === statements[statements.length - 1] && !lastDirective.trailingComments) {
166
170
  return;
167
171
  }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * @fileoverview Rule to check empty newline between class members
3
+ * @author 薛定谔的猫<hh_2013@foxmail.com>
4
+ */
5
+ "use strict";
6
+
7
+ const astUtils = require("../ast-utils");
8
+
9
+ //------------------------------------------------------------------------------
10
+ // Rule Definition
11
+ //------------------------------------------------------------------------------
12
+
13
+ module.exports = {
14
+ meta: {
15
+ docs: {
16
+ description: "require or disallow an empty line between class members",
17
+ category: "Stylistic Issues",
18
+ recommended: false
19
+ },
20
+
21
+ fixable: "whitespace",
22
+
23
+ schema: [
24
+ {
25
+ enum: ["always", "never"]
26
+ },
27
+ {
28
+ type: "object",
29
+ properties: {
30
+ exceptAfterSingleLine: {
31
+ type: "boolean"
32
+ }
33
+ },
34
+ additionalProperties: false
35
+ }
36
+ ]
37
+ },
38
+
39
+ create(context) {
40
+
41
+ const options = [];
42
+
43
+ options[0] = context.options[0] || "always";
44
+ options[1] = context.options[1] || { exceptAfterSingleLine: false };
45
+
46
+ const ALWAYS_MESSAGE = "Expected blank line between class members.";
47
+ const NEVER_MESSAGE = "Unexpected blank line between class members.";
48
+
49
+ const sourceCode = context.getSourceCode();
50
+
51
+ /**
52
+ * Checks if there is padding between two tokens
53
+ * @param {Token} first The first token
54
+ * @param {Token} second The second token
55
+ * @returns {boolean} True if there is at least a line between the tokens
56
+ */
57
+ function isPaddingBetweenTokens(first, second) {
58
+ return second.loc.start.line - first.loc.end.line >= 2;
59
+ }
60
+
61
+ return {
62
+ ClassBody(node) {
63
+ const body = node.body;
64
+
65
+ for (let i = 0; i < body.length - 1; i++) {
66
+ const curFirst = sourceCode.getFirstToken(body[i]);
67
+ const curLast = sourceCode.getLastToken(body[i]);
68
+ const comments = sourceCode.getCommentsBefore(body[i + 1]);
69
+ const nextFirst = comments.length ? comments[0] : sourceCode.getFirstToken(body[i + 1]);
70
+ const isPadded = isPaddingBetweenTokens(curLast, nextFirst);
71
+ const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
72
+ const skip = !isMulti && options[1].exceptAfterSingleLine;
73
+
74
+
75
+ if ((options[0] === "always" && !skip && !isPadded) ||
76
+ (options[0] === "never" && isPadded)) {
77
+ context.report({
78
+ node: body[i + 1],
79
+ message: isPadded ? NEVER_MESSAGE : ALWAYS_MESSAGE,
80
+ fix(fixer) {
81
+ return isPadded
82
+ ? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n")
83
+ : fixer.insertTextAfter(curLast, "\n");
84
+ }
85
+ });
86
+ }
87
+ }
88
+ }
89
+ };
90
+ }
91
+ };
@@ -181,11 +181,10 @@ module.exports = {
181
181
  * Gets the line after the comment and any remaining trailing whitespace is
182
182
  * stripped.
183
183
  * @param {string} line The source line with a trailing comment
184
- * @param {number} lineNumber The one-indexed line number this is on
185
184
  * @param {ASTNode} comment The comment to remove
186
185
  * @returns {string} Line without comment and trailing whitepace
187
186
  */
188
- function stripTrailingComment(line, lineNumber, comment) {
187
+ function stripTrailingComment(line, comment) {
189
188
 
190
189
  // loc.column is zero-indexed
191
190
  return line.slice(0, comment.loc.start.column).replace(/\s+$/, "");
@@ -306,7 +305,7 @@ module.exports = {
306
305
  if (isFullLineComment(line, lineNumber, comment)) {
307
306
  lineIsComment = true;
308
307
  } else if (ignoreTrailingComments && isTrailingComment(line, lineNumber, comment)) {
309
- line = stripTrailingComment(line, lineNumber, comment);
308
+ line = stripTrailingComment(line, comment);
310
309
  }
311
310
  }
312
311
  if (ignorePattern && ignorePattern.test(line) ||
@@ -93,9 +93,11 @@ module.exports = {
93
93
  function enterStatement(node) {
94
94
  const line = node.loc.start.line;
95
95
 
96
- // Skip to allow non-block statements if this is direct child of control statements.
97
- // `if (a) foo();` is counted as 1.
98
- // But `if (a) foo(); else foo();` should be counted as 2.
96
+ /*
97
+ * Skip to allow non-block statements if this is direct child of control statements.
98
+ * `if (a) foo();` is counted as 1.
99
+ * But `if (a) foo(); else foo();` should be counted as 2.
100
+ */
99
101
  if (SINGLE_CHILD_ALLOWED.test(node.parent.type) &&
100
102
  node.parent.alternate !== node
101
103
  ) {
@@ -0,0 +1,294 @@
1
+ /**
2
+ * @fileoverview enforce a particular style for multiline comments
3
+ * @author Teddy Katz
4
+ */
5
+ "use strict";
6
+
7
+ const astUtils = require("../ast-utils");
8
+
9
+ //------------------------------------------------------------------------------
10
+ // Rule Definition
11
+ //------------------------------------------------------------------------------
12
+
13
+ module.exports = {
14
+ meta: {
15
+ docs: {
16
+ description: "enforce a particular style for multiline comments",
17
+ category: "Stylistic Issues",
18
+ recommended: false
19
+ },
20
+ fixable: "whitespace",
21
+ schema: [{ enum: ["starred-block", "separate-lines", "bare-block"] }]
22
+ },
23
+
24
+ create(context) {
25
+ const sourceCode = context.getSourceCode();
26
+ const option = context.options[0] || "starred-block";
27
+
28
+ const EXPECTED_BLOCK_ERROR = "Expected a block comment instead of consecutive line comments.";
29
+ const START_NEWLINE_ERROR = "Expected a linebreak after '/*'.";
30
+ const END_NEWLINE_ERROR = "Expected a linebreak before '*/'.";
31
+ const MISSING_STAR_ERROR = "Expected a '*' at the start of this line.";
32
+ const ALIGNMENT_ERROR = "Expected this line to be aligned with the start of the comment.";
33
+ const EXPECTED_LINES_ERROR = "Expected multiple line comments instead of a block comment.";
34
+
35
+ //----------------------------------------------------------------------
36
+ // Helpers
37
+ //----------------------------------------------------------------------
38
+
39
+ /**
40
+ * Gets a list of comment lines in a group
41
+ * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment
42
+ * @returns {string[]} A list of comment lines
43
+ */
44
+ function getCommentLines(commentGroup) {
45
+ if (commentGroup[0].type === "Line") {
46
+ return commentGroup.map(comment => comment.value);
47
+ }
48
+ return commentGroup[0].value
49
+ .split(astUtils.LINEBREAK_MATCHER)
50
+ .map(line => line.replace(/^\s*\*?/, ""));
51
+ }
52
+
53
+ /**
54
+ * Converts a comment into starred-block form
55
+ * @param {Token} firstComment The first comment of the group being converted
56
+ * @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
57
+ * @returns {string} A representation of the comment value in starred-block form, excluding start and end markers
58
+ */
59
+ function convertToStarredBlock(firstComment, commentLinesList) {
60
+ const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
61
+ const starredLines = commentLinesList.map(line => `${initialOffset} *${line}`);
62
+
63
+ return `\n${starredLines.join("\n")}\n${initialOffset} `;
64
+ }
65
+
66
+ /**
67
+ * Converts a comment into separate-line form
68
+ * @param {Token} firstComment The first comment of the group being converted
69
+ * @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
70
+ * @returns {string} A representation of the comment value in separate-line form
71
+ */
72
+ function convertToSeparateLines(firstComment, commentLinesList) {
73
+ const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
74
+ const separateLines = commentLinesList.map(line => `// ${line.trim()}`);
75
+
76
+ return separateLines.join(`\n${initialOffset}`);
77
+ }
78
+
79
+ /**
80
+ * Converts a comment into bare-block form
81
+ * @param {Token} firstComment The first comment of the group being converted
82
+ * @param {string[]} commentLinesList A list of lines to appear in the new starred-block comment
83
+ * @returns {string} A representation of the comment value in bare-block form
84
+ */
85
+ function convertToBlock(firstComment, commentLinesList) {
86
+ const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
87
+ const blockLines = commentLinesList.map(line => line.trim());
88
+
89
+ return `/* ${blockLines.join(`\n${initialOffset} `)} */`;
90
+ }
91
+
92
+ /**
93
+ * Check a comment is JSDoc form
94
+ * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment
95
+ * @returns {boolean} if commentGroup is JSDoc form, return true
96
+ */
97
+ function isJSDoc(commentGroup) {
98
+ const lines = commentGroup[0].value.split(astUtils.LINEBREAK_MATCHER);
99
+
100
+ return commentGroup[0].type === "Block" &&
101
+ /^\*\s*$/.test(lines[0]) &&
102
+ lines.slice(1, -1).every(line => /^\s* /.test(line)) &&
103
+ /^\s*$/.test(lines[lines.length - 1]);
104
+ }
105
+
106
+ /**
107
+ * Each method checks a group of comments to see if it's valid according to the given option.
108
+ * @param {Token[]} commentGroup A list of comments that appear together. This will either contain a single
109
+ * block comment or multiple line comments.
110
+ * @returns {void}
111
+ */
112
+ const commentGroupCheckers = {
113
+ "starred-block"(commentGroup) {
114
+ const commentLines = getCommentLines(commentGroup);
115
+
116
+ if (commentLines.some(value => value.includes("*/"))) {
117
+ return;
118
+ }
119
+
120
+ if (commentGroup.length > 1) {
121
+ context.report({
122
+ loc: {
123
+ start: commentGroup[0].loc.start,
124
+ end: commentGroup[commentGroup.length - 1].loc.end
125
+ },
126
+ message: EXPECTED_BLOCK_ERROR,
127
+ fix(fixer) {
128
+ const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
129
+ const starredBlock = `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`;
130
+
131
+ return commentLines.some(value => value.startsWith("/"))
132
+ ? null
133
+ : fixer.replaceTextRange(range, starredBlock);
134
+ }
135
+ });
136
+ } else {
137
+ const block = commentGroup[0];
138
+ const lines = block.value.split(astUtils.LINEBREAK_MATCHER);
139
+ const expectedLinePrefix = `${sourceCode.text.slice(block.range[0] - block.loc.start.column, block.range[0])} *`;
140
+
141
+ if (!/^\*?\s*$/.test(lines[0])) {
142
+ const start = block.value.startsWith("*") ? block.range[0] + 1 : block.range[0];
143
+
144
+ context.report({
145
+ loc: {
146
+ start: block.loc.start,
147
+ end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
148
+ },
149
+ message: START_NEWLINE_ERROR,
150
+ fix: fixer => fixer.insertTextAfterRange([start, start + 2], `\n${expectedLinePrefix}`)
151
+ });
152
+ }
153
+
154
+ if (!/^\s*$/.test(lines[lines.length - 1])) {
155
+ context.report({
156
+ loc: {
157
+ start: { line: block.loc.end.line, column: block.loc.end.column - 2 },
158
+ end: block.loc.end
159
+ },
160
+ message: END_NEWLINE_ERROR,
161
+ fix: fixer => fixer.replaceTextRange([block.range[1] - 2, block.range[1]], `\n${expectedLinePrefix}/`)
162
+ });
163
+ }
164
+
165
+ for (let lineNumber = block.loc.start.line + 1; lineNumber <= block.loc.end.line; lineNumber++) {
166
+ const lineText = sourceCode.lines[lineNumber - 1];
167
+
168
+ if (!lineText.startsWith(expectedLinePrefix)) {
169
+ context.report({
170
+ loc: {
171
+ start: { line: lineNumber, column: 0 },
172
+ end: { line: lineNumber, column: sourceCode.lines[lineNumber - 1].length }
173
+ },
174
+ message: /^\s*\*/.test(lineText)
175
+ ? ALIGNMENT_ERROR
176
+ : MISSING_STAR_ERROR,
177
+ fix(fixer) {
178
+ const lineStartIndex = sourceCode.getIndexFromLoc({ line: lineNumber, column: 0 });
179
+ const linePrefixLength = lineText.match(/^\s*\*? ?/)[0].length;
180
+ const commentStartIndex = lineStartIndex + linePrefixLength;
181
+
182
+ const replacementText = lineNumber === block.loc.end.line || lineText.length === linePrefixLength
183
+ ? expectedLinePrefix
184
+ : `${expectedLinePrefix} `;
185
+
186
+ return fixer.replaceTextRange([lineStartIndex, commentStartIndex], replacementText);
187
+ }
188
+ });
189
+ }
190
+ }
191
+ }
192
+ },
193
+ "separate-lines"(commentGroup) {
194
+ if (!isJSDoc(commentGroup) && commentGroup[0].type === "Block") {
195
+ const commentLines = getCommentLines(commentGroup);
196
+ const block = commentGroup[0];
197
+ const tokenAfter = sourceCode.getTokenAfter(block, { includeComments: true });
198
+
199
+ if (tokenAfter && block.loc.end.line === tokenAfter.loc.start.line) {
200
+ return;
201
+ }
202
+
203
+ context.report({
204
+ loc: {
205
+ start: block.loc.start,
206
+ end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
207
+ },
208
+ message: EXPECTED_LINES_ERROR,
209
+ fix(fixer) {
210
+ return fixer.replaceText(block, convertToSeparateLines(block, commentLines.filter(line => line)));
211
+ }
212
+ });
213
+ }
214
+ },
215
+ "bare-block"(commentGroup) {
216
+ if (!isJSDoc(commentGroup)) {
217
+ const commentLines = getCommentLines(commentGroup);
218
+
219
+ // disallows consecutive line comments in favor of using a block comment.
220
+ if (commentGroup[0].type === "Line" && commentLines.length > 1 &&
221
+ !commentLines.some(value => value.includes("*/"))) {
222
+ context.report({
223
+ loc: {
224
+ start: commentGroup[0].loc.start,
225
+ end: commentGroup[commentGroup.length - 1].loc.end
226
+ },
227
+ message: EXPECTED_BLOCK_ERROR,
228
+ fix(fixer) {
229
+ const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
230
+ const block = convertToBlock(commentGroup[0], commentLines.filter(line => line));
231
+
232
+ return fixer.replaceTextRange(range, block);
233
+ }
234
+ });
235
+ }
236
+
237
+ // prohibits block comments from having a * at the beginning of each line.
238
+ if (commentGroup[0].type === "Block") {
239
+ const block = commentGroup[0];
240
+ const lines = block.value.split(astUtils.LINEBREAK_MATCHER).filter(line => line.trim());
241
+
242
+ if (lines.length > 0 && lines.every(line => /^\s*\*/.test(line))) {
243
+ context.report({
244
+ loc: {
245
+ start: block.loc.start,
246
+ end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
247
+ },
248
+ message: EXPECTED_BLOCK_ERROR,
249
+ fix(fixer) {
250
+ return fixer.replaceText(block, convertToBlock(block, commentLines.filter(line => line)));
251
+ }
252
+ });
253
+ }
254
+ }
255
+ }
256
+ }
257
+ };
258
+
259
+ //----------------------------------------------------------------------
260
+ // Public
261
+ //----------------------------------------------------------------------
262
+
263
+ return {
264
+ Program() {
265
+ return sourceCode.getAllComments()
266
+ .filter(comment => comment.type !== "Shebang")
267
+ .filter(comment => !astUtils.COMMENTS_IGNORE_PATTERN.test(comment.value))
268
+ .filter(comment => {
269
+ const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
270
+
271
+ return !tokenBefore || tokenBefore.loc.end.line < comment.loc.start.line;
272
+ })
273
+ .reduce((commentGroups, comment, index, commentList) => {
274
+ const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
275
+
276
+ if (
277
+ comment.type === "Line" &&
278
+ index && commentList[index - 1].type === "Line" &&
279
+ tokenBefore && tokenBefore.loc.end.line === comment.loc.start.line - 1 &&
280
+ tokenBefore === commentList[index - 1]
281
+ ) {
282
+ commentGroups[commentGroups.length - 1].push(comment);
283
+ } else {
284
+ commentGroups.push([comment]);
285
+ }
286
+
287
+ return commentGroups;
288
+ }, [])
289
+ .filter(commentGroup => !(commentGroup.length === 1 && commentGroup[0].loc.start.line === commentGroup[0].loc.end.line))
290
+ .forEach(commentGroupCheckers[option]);
291
+ }
292
+ };
293
+ }
294
+ };
@@ -178,7 +178,8 @@ module.exports = {
178
178
 
179
179
  // char has no uppercase variant, so it's non-alphabetic
180
180
  return "non-alpha";
181
- } else if (firstChar === firstCharLower) {
181
+ }
182
+ if (firstChar === firstCharLower) {
182
183
  return "lower";
183
184
  }
184
185
  return "upper";
@@ -132,10 +132,10 @@ module.exports = {
132
132
  }
133
133
 
134
134
  /**
135
- * Gets the last line of a group of consecutive comments
136
- * @param {number} commentStartLine The starting line of the group
137
- * @returns {number} The number of the last comment line of the group
138
- */
135
+ * Gets the last line of a group of consecutive comments
136
+ * @param {number} commentStartLine The starting line of the group
137
+ * @returns {number} The number of the last comment line of the group
138
+ */
139
139
  function getLastCommentLineOfBlock(commentStartLine) {
140
140
  const currentCommentEnd = commentEndLine[commentStartLine];
141
141
 
@@ -190,8 +190,10 @@ module.exports = {
190
190
  return;
191
191
  }
192
192
 
193
- // Some coding styles use multiple `var` statements, so do nothing if
194
- // the next token is a `var` statement.
193
+ /*
194
+ * Some coding styles use multiple `var` statements, so do nothing if
195
+ * the next token is a `var` statement.
196
+ */
195
197
  if (nextToken.type === "Keyword" && isVar(nextToken.value)) {
196
198
  return;
197
199
  }
@@ -59,9 +59,11 @@ module.exports = {
59
59
 
60
60
  if (parentType === "IfStatement") {
61
61
  return isPrecededByTokens(node, ["else", ")"]);
62
- } else if (parentType === "DoWhileStatement") {
62
+ }
63
+ if (parentType === "DoWhileStatement") {
63
64
  return isPrecededByTokens(node, ["do"]);
64
- } else if (parentType === "SwitchCase") {
65
+ }
66
+ if (parentType === "SwitchCase") {
65
67
  return isPrecededByTokens(node, [":"]);
66
68
  }
67
69
  return isPrecededByTokens(node, [")"]);
@@ -164,13 +166,15 @@ module.exports = {
164
166
  return true;
165
167
  }
166
168
 
167
- // if the last leading comment ends in the same line as the previous token and
168
- // does not share a line with the `return` node, we can consider it safe to fix.
169
- // Example:
170
- // function a() {
171
- // var b; //comment
172
- // return;
173
- // }
169
+ /*
170
+ * if the last leading comment ends in the same line as the previous token and
171
+ * does not share a line with the `return` node, we can consider it safe to fix.
172
+ * Example:
173
+ * function a() {
174
+ * var b; //comment
175
+ * return;
176
+ * }
177
+ */
174
178
  if (lastLeadingComment.loc.end.line === tokenBefore.loc.end.line &&
175
179
  lastLeadingComment.loc.end.line !== node.loc.start.line) {
176
180
  return true;