eslint 6.5.0 → 6.7.1

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 (196) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +10 -9
  3. package/conf/config-schema.js +1 -0
  4. package/conf/default-cli-options.js +1 -1
  5. package/lib/cli-engine/cascading-config-array-factory.js +40 -14
  6. package/lib/cli-engine/cli-engine.js +49 -21
  7. package/lib/cli-engine/config-array/config-array.js +13 -4
  8. package/lib/cli-engine/config-array/config-dependency.js +2 -0
  9. package/lib/cli-engine/config-array/extracted-config.js +27 -0
  10. package/lib/cli-engine/config-array/ignore-pattern.js +231 -0
  11. package/lib/cli-engine/config-array/index.js +2 -0
  12. package/lib/cli-engine/config-array/override-tester.js +2 -0
  13. package/lib/cli-engine/config-array-factory.js +120 -2
  14. package/lib/cli-engine/file-enumerator.js +51 -30
  15. package/lib/cli-engine/formatters/html.js +1 -0
  16. package/lib/init/autoconfig.js +1 -11
  17. package/lib/init/config-file.js +0 -1
  18. package/lib/init/config-initializer.js +4 -4
  19. package/lib/init/config-rule.js +1 -5
  20. package/lib/init/npm-utils.js +0 -5
  21. package/lib/linter/code-path-analysis/code-path-analyzer.js +24 -38
  22. package/lib/linter/code-path-analysis/code-path-segment.js +17 -25
  23. package/lib/linter/code-path-analysis/code-path-state.js +40 -81
  24. package/lib/linter/code-path-analysis/code-path.js +10 -11
  25. package/lib/linter/code-path-analysis/debug-helpers.js +8 -12
  26. package/lib/linter/code-path-analysis/fork-context.js +23 -34
  27. package/lib/linter/code-path-analysis/id-generator.js +2 -2
  28. package/lib/linter/linter.js +121 -95
  29. package/lib/linter/node-event-generator.js +3 -2
  30. package/lib/linter/report-translator.js +73 -7
  31. package/lib/rule-tester/rule-tester.js +46 -14
  32. package/lib/rules/accessor-pairs.js +8 -8
  33. package/lib/rules/array-bracket-newline.js +12 -15
  34. package/lib/rules/array-bracket-spacing.js +12 -12
  35. package/lib/rules/array-callback-return.js +6 -11
  36. package/lib/rules/array-element-newline.js +5 -8
  37. package/lib/rules/arrow-parens.js +0 -1
  38. package/lib/rules/block-scoped-var.js +3 -3
  39. package/lib/rules/block-spacing.js +4 -4
  40. package/lib/rules/camelcase.js +19 -6
  41. package/lib/rules/capitalized-comments.js +0 -7
  42. package/lib/rules/class-methods-use-this.js +3 -3
  43. package/lib/rules/comma-dangle.js +20 -25
  44. package/lib/rules/comma-spacing.js +1 -1
  45. package/lib/rules/computed-property-spacing.js +14 -14
  46. package/lib/rules/consistent-return.js +4 -5
  47. package/lib/rules/consistent-this.js +5 -5
  48. package/lib/rules/constructor-super.js +14 -16
  49. package/lib/rules/curly.js +12 -9
  50. package/lib/rules/default-param-last.js +1 -0
  51. package/lib/rules/dot-location.js +11 -12
  52. package/lib/rules/func-names.js +6 -6
  53. package/lib/rules/function-call-argument-newline.js +8 -6
  54. package/lib/rules/generator-star-spacing.js +4 -9
  55. package/lib/rules/getter-return.js +4 -7
  56. package/lib/rules/grouped-accessor-pairs.js +224 -0
  57. package/lib/rules/indent.js +13 -2
  58. package/lib/rules/index.js +5 -0
  59. package/lib/rules/init-declarations.js +2 -2
  60. package/lib/rules/jsx-quotes.js +1 -1
  61. package/lib/rules/keyword-spacing.js +32 -56
  62. package/lib/rules/lines-around-directive.js +1 -1
  63. package/lib/rules/max-len.js +0 -5
  64. package/lib/rules/max-statements-per-line.js +3 -7
  65. package/lib/rules/multiline-comment-style.js +237 -106
  66. package/lib/rules/multiline-ternary.js +3 -3
  67. package/lib/rules/newline-after-var.js +6 -7
  68. package/lib/rules/newline-before-return.js +8 -9
  69. package/lib/rules/newline-per-chained-call.js +2 -4
  70. package/lib/rules/no-class-assign.js +2 -2
  71. package/lib/rules/no-compare-neg-zero.js +1 -2
  72. package/lib/rules/no-cond-assign.js +14 -4
  73. package/lib/rules/no-confusing-arrow.js +2 -2
  74. package/lib/rules/no-console.js +4 -8
  75. package/lib/rules/no-const-assign.js +1 -1
  76. package/lib/rules/no-constructor-return.js +62 -0
  77. package/lib/rules/no-dupe-args.js +1 -1
  78. package/lib/rules/no-dupe-class-members.js +3 -4
  79. package/lib/rules/no-dupe-else-if.js +122 -0
  80. package/lib/rules/no-dupe-keys.js +6 -5
  81. package/lib/rules/no-duplicate-imports.js +14 -18
  82. package/lib/rules/no-else-return.js +0 -8
  83. package/lib/rules/no-empty-function.js +2 -4
  84. package/lib/rules/no-eval.js +10 -18
  85. package/lib/rules/no-ex-assign.js +1 -1
  86. package/lib/rules/no-extra-bind.js +5 -12
  87. package/lib/rules/no-extra-boolean-cast.js +0 -2
  88. package/lib/rules/no-extra-label.js +4 -9
  89. package/lib/rules/no-extra-parens.js +17 -15
  90. package/lib/rules/no-extra-semi.js +5 -6
  91. package/lib/rules/no-fallthrough.js +6 -6
  92. package/lib/rules/no-func-assign.js +3 -3
  93. package/lib/rules/no-global-assign.js +4 -4
  94. package/lib/rules/no-implicit-coercion.js +10 -10
  95. package/lib/rules/no-implicit-globals.js +90 -8
  96. package/lib/rules/no-implied-eval.js +0 -1
  97. package/lib/rules/no-inline-comments.js +25 -11
  98. package/lib/rules/no-invalid-this.js +17 -5
  99. package/lib/rules/no-labels.js +3 -6
  100. package/lib/rules/no-lone-blocks.js +1 -1
  101. package/lib/rules/no-loop-func.js +6 -11
  102. package/lib/rules/no-magic-numbers.js +6 -6
  103. package/lib/rules/no-misleading-character-class.js +14 -7
  104. package/lib/rules/no-mixed-operators.js +13 -22
  105. package/lib/rules/no-mixed-requires.js +0 -1
  106. package/lib/rules/no-multi-spaces.js +1 -1
  107. package/lib/rules/no-native-reassign.js +4 -4
  108. package/lib/rules/no-octal-escape.js +1 -1
  109. package/lib/rules/no-param-reassign.js +28 -7
  110. package/lib/rules/no-redeclare.js +1 -1
  111. package/lib/rules/no-regex-spaces.js +0 -1
  112. package/lib/rules/no-restricted-imports.js +11 -11
  113. package/lib/rules/no-self-assign.js +12 -13
  114. package/lib/rules/no-sequences.js +3 -3
  115. package/lib/rules/no-setter-return.js +227 -0
  116. package/lib/rules/no-shadow.js +1 -4
  117. package/lib/rules/no-tabs.js +8 -2
  118. package/lib/rules/no-this-before-super.js +12 -13
  119. package/lib/rules/no-trailing-spaces.js +19 -7
  120. package/lib/rules/no-underscore-dangle.js +23 -4
  121. package/lib/rules/no-unmodified-loop-condition.js +16 -29
  122. package/lib/rules/no-unneeded-ternary.js +3 -3
  123. package/lib/rules/no-unreachable.js +7 -7
  124. package/lib/rules/no-unsafe-finally.js +4 -7
  125. package/lib/rules/no-unsafe-negation.js +32 -9
  126. package/lib/rules/no-unused-expressions.js +11 -7
  127. package/lib/rules/no-unused-labels.js +3 -6
  128. package/lib/rules/no-unused-vars.js +22 -29
  129. package/lib/rules/no-use-before-define.js +10 -15
  130. package/lib/rules/no-useless-call.js +4 -4
  131. package/lib/rules/no-useless-computed-key.js +60 -33
  132. package/lib/rules/no-useless-concat.js +4 -4
  133. package/lib/rules/no-useless-constructor.js +14 -22
  134. package/lib/rules/no-useless-escape.js +29 -8
  135. package/lib/rules/no-useless-rename.js +15 -7
  136. package/lib/rules/no-useless-return.js +8 -15
  137. package/lib/rules/no-var.js +12 -25
  138. package/lib/rules/no-warning-comments.js +0 -1
  139. package/lib/rules/no-whitespace-before-property.js +3 -3
  140. package/lib/rules/object-curly-newline.js +7 -10
  141. package/lib/rules/object-curly-spacing.js +21 -22
  142. package/lib/rules/object-shorthand.js +1 -1
  143. package/lib/rules/one-var-declaration-per-line.js +2 -2
  144. package/lib/rules/operator-assignment.js +33 -3
  145. package/lib/rules/padded-blocks.js +1 -1
  146. package/lib/rules/padding-line-between-statements.js +0 -16
  147. package/lib/rules/prefer-arrow-callback.js +6 -6
  148. package/lib/rules/prefer-const.js +27 -28
  149. package/lib/rules/prefer-destructuring.js +1 -7
  150. package/lib/rules/prefer-exponentiation-operator.js +189 -0
  151. package/lib/rules/prefer-named-capture-group.js +0 -1
  152. package/lib/rules/prefer-numeric-literals.js +32 -4
  153. package/lib/rules/prefer-object-spread.js +7 -7
  154. package/lib/rules/prefer-rest-params.js +3 -6
  155. package/lib/rules/prefer-spread.js +4 -4
  156. package/lib/rules/prefer-template.js +5 -6
  157. package/lib/rules/quote-props.js +1 -1
  158. package/lib/rules/quotes.js +5 -6
  159. package/lib/rules/radix.js +5 -10
  160. package/lib/rules/require-await.js +10 -5
  161. package/lib/rules/require-yield.js +2 -2
  162. package/lib/rules/rest-spread-spacing.js +1 -1
  163. package/lib/rules/semi.js +6 -3
  164. package/lib/rules/sort-imports.js +3 -4
  165. package/lib/rules/sort-keys.js +1 -3
  166. package/lib/rules/space-before-blocks.js +1 -2
  167. package/lib/rules/space-in-parens.js +4 -4
  168. package/lib/rules/space-infix-ops.js +6 -6
  169. package/lib/rules/spaced-comment.js +20 -22
  170. package/lib/rules/strict.js +2 -4
  171. package/lib/rules/symbol-description.js +1 -2
  172. package/lib/rules/template-curly-spacing.js +2 -2
  173. package/lib/rules/use-isnan.js +40 -3
  174. package/lib/rules/utils/ast-utils.js +84 -85
  175. package/lib/rules/utils/fix-tracker.js +0 -6
  176. package/lib/rules/utils/lazy-loading-rule-map.js +0 -1
  177. package/lib/rules/vars-on-top.js +11 -11
  178. package/lib/shared/config-ops.js +2 -2
  179. package/lib/shared/runtime-info.js +8 -8
  180. package/lib/shared/traverser.js +2 -0
  181. package/lib/shared/types.js +9 -0
  182. package/lib/source-code/source-code.js +62 -17
  183. package/lib/source-code/token-store/backward-token-comment-cursor.js +5 -5
  184. package/lib/source-code/token-store/backward-token-cursor.js +5 -5
  185. package/lib/source-code/token-store/cursors.js +17 -19
  186. package/lib/source-code/token-store/decorative-cursor.js +1 -1
  187. package/lib/source-code/token-store/filter-cursor.js +2 -2
  188. package/lib/source-code/token-store/forward-token-comment-cursor.js +5 -5
  189. package/lib/source-code/token-store/forward-token-cursor.js +5 -5
  190. package/lib/source-code/token-store/index.js +86 -92
  191. package/lib/source-code/token-store/limit-cursor.js +2 -2
  192. package/lib/source-code/token-store/padded-token-cursor.js +7 -7
  193. package/lib/source-code/token-store/skip-cursor.js +2 -2
  194. package/lib/source-code/token-store/utils.js +9 -13
  195. package/package.json +9 -7
  196. package/lib/cli-engine/ignored-paths.js +0 -362
@@ -61,7 +61,6 @@ module.exports = {
61
61
 
62
62
  /**
63
63
  * Reports with the first extra statement, and clears it.
64
- *
65
64
  * @returns {void}
66
65
  */
67
66
  function reportFirstExtraStatementAndClear() {
@@ -81,8 +80,7 @@ module.exports = {
81
80
 
82
81
  /**
83
82
  * Gets the actual last token of a given node.
84
- *
85
- * @param {ASTNode} node - A node to get. This is a node except EmptyStatement.
83
+ * @param {ASTNode} node A node to get. This is a node except EmptyStatement.
86
84
  * @returns {Token} The actual last token.
87
85
  */
88
86
  function getActualLastToken(node) {
@@ -92,8 +90,7 @@ module.exports = {
92
90
  /**
93
91
  * Addresses a given node.
94
92
  * It updates the state of this rule, then reports the node if the node violated this rule.
95
- *
96
- * @param {ASTNode} node - A node to check.
93
+ * @param {ASTNode} node A node to check.
97
94
  * @returns {void}
98
95
  */
99
96
  function enterStatement(node) {
@@ -127,8 +124,7 @@ module.exports = {
127
124
 
128
125
  /**
129
126
  * Updates the state of this rule with the end line of leaving node to check with the next statement.
130
- *
131
- * @param {ASTNode} node - A node to check.
127
+ * @param {ASTNode} node A node to check.
132
128
  * @returns {void}
133
129
  */
134
130
  function leaveStatement(node) {
@@ -43,17 +43,150 @@ module.exports = {
43
43
  //----------------------------------------------------------------------
44
44
 
45
45
  /**
46
- * Gets a list of comment lines in a group
47
- * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment
48
- * @returns {string[]} A list of comment lines
46
+ * Checks if a comment line is starred.
47
+ * @param {string} line A string representing a comment line.
48
+ * @returns {boolean} Whether or not the comment line is starred.
49
+ */
50
+ function isStarredCommentLine(line) {
51
+ return /^\s*\*/u.test(line);
52
+ }
53
+
54
+ /**
55
+ * Checks if a comment group is in starred-block form.
56
+ * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment.
57
+ * @returns {boolean} Whether or not the comment group is in starred block form.
58
+ */
59
+ function isStarredBlockComment([firstComment]) {
60
+ if (firstComment.type !== "Block") {
61
+ return false;
62
+ }
63
+
64
+ const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER);
65
+
66
+ // The first and last lines can only contain whitespace.
67
+ return lines.length > 0 && lines.every((line, i) => (i === 0 || i === lines.length - 1 ? /^\s*$/u : /^\s*\*/u).test(line));
68
+ }
69
+
70
+ /**
71
+ * Checks if a comment group is in JSDoc form.
72
+ * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment.
73
+ * @returns {boolean} Whether or not the comment group is in JSDoc form.
74
+ */
75
+ function isJSDocComment([firstComment]) {
76
+ if (firstComment.type !== "Block") {
77
+ return false;
78
+ }
79
+
80
+ const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER);
81
+
82
+ return /^\*\s*$/u.test(lines[0]) &&
83
+ lines.slice(1, -1).every(line => /^\s* /u.test(line)) &&
84
+ /^\s*$/u.test(lines[lines.length - 1]);
85
+ }
86
+
87
+ /**
88
+ * Processes a comment group that is currently in separate-line form, calculating the offset for each line.
89
+ * @param {Token[]} commentGroup A group of comments containing multiple line comments.
90
+ * @returns {string[]} An array of the processed lines.
91
+ */
92
+ function processSeparateLineComments(commentGroup) {
93
+ const allLinesHaveLeadingSpace = commentGroup
94
+ .map(({ value }) => value)
95
+ .filter(line => line.trim().length)
96
+ .every(line => line.startsWith(" "));
97
+
98
+ return commentGroup.map(({ value }) => (allLinesHaveLeadingSpace ? value.replace(/^ /u, "") : value));
99
+ }
100
+
101
+ /**
102
+ * Processes a comment group that is currently in starred-block form, calculating the offset for each line.
103
+ * @param {Token} comment A single block comment token in starred-block form.
104
+ * @returns {string[]} An array of the processed lines.
105
+ */
106
+ function processStarredBlockComment(comment) {
107
+ const lines = comment.value.split(astUtils.LINEBREAK_MATCHER)
108
+ .filter((line, i, linesArr) => !(i === 0 || i === linesArr.length - 1))
109
+ .map(line => line.replace(/^\s*$/u, ""));
110
+ const allLinesHaveLeadingSpace = lines
111
+ .map(line => line.replace(/\s*\*/u, ""))
112
+ .filter(line => line.trim().length)
113
+ .every(line => line.startsWith(" "));
114
+
115
+ return lines.map(line => line.replace(allLinesHaveLeadingSpace ? /\s*\* ?/u : /\s*\*/u, ""));
116
+ }
117
+
118
+ /**
119
+ * Processes a comment group that is currently in bare-block form, calculating the offset for each line.
120
+ * @param {Token} comment A single block comment token in bare-block form.
121
+ * @returns {string[]} An array of the processed lines.
122
+ */
123
+ function processBareBlockComment(comment) {
124
+ const lines = comment.value.split(astUtils.LINEBREAK_MATCHER).map(line => line.replace(/^\s*$/u, ""));
125
+ const leadingWhitespace = `${sourceCode.text.slice(comment.range[0] - comment.loc.start.column, comment.range[0])} `;
126
+ let offset = "";
127
+
128
+ /*
129
+ * Calculate the offset of the least indented line and use that as the basis for offsetting all the lines.
130
+ * The first line should not be checked because it is inline with the opening block comment delimiter.
131
+ */
132
+ for (const [i, line] of lines.entries()) {
133
+ if (!line.trim().length || i === 0) {
134
+ continue;
135
+ }
136
+
137
+ const [, lineOffset] = line.match(/^(\s*\*?\s*)/u);
138
+
139
+ if (lineOffset.length < leadingWhitespace.length) {
140
+ const newOffset = leadingWhitespace.slice(lineOffset.length - leadingWhitespace.length);
141
+
142
+ if (newOffset.length > offset.length) {
143
+ offset = newOffset;
144
+ }
145
+ }
146
+ }
147
+
148
+ return lines.map(line => {
149
+ const match = line.match(/^(\s*\*?\s*)(.*)/u);
150
+ const [, lineOffset, lineContents] = match;
151
+
152
+ if (lineOffset.length > leadingWhitespace.length) {
153
+ return `${lineOffset.slice(leadingWhitespace.length - (offset.length + lineOffset.length))}${lineContents}`;
154
+ }
155
+
156
+ if (lineOffset.length < leadingWhitespace.length) {
157
+ return `${lineOffset.slice(leadingWhitespace.length)}${lineContents}`;
158
+ }
159
+
160
+ return lineContents;
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Gets a list of comment lines in a group, formatting leading whitespace as necessary.
166
+ * @param {Token[]} commentGroup A group of comments containing either multiple line comments or a single block comment.
167
+ * @returns {string[]} A list of comment lines.
49
168
  */
50
169
  function getCommentLines(commentGroup) {
51
- if (commentGroup[0].type === "Line") {
52
- return commentGroup.map(comment => comment.value);
170
+ const [firstComment] = commentGroup;
171
+
172
+ if (firstComment.type === "Line") {
173
+ return processSeparateLineComments(commentGroup);
53
174
  }
54
- return commentGroup[0].value
55
- .split(astUtils.LINEBREAK_MATCHER)
56
- .map(line => line.replace(/^\s*\*?/u, ""));
175
+
176
+ if (isStarredBlockComment(commentGroup)) {
177
+ return processStarredBlockComment(firstComment);
178
+ }
179
+
180
+ return processBareBlockComment(firstComment);
181
+ }
182
+
183
+ /**
184
+ * Gets the initial offset (whitespace) from the beginning of a line to a given comment token.
185
+ * @param {Token} comment The token to check.
186
+ * @returns {string} The offset from the beginning of a line to the token.
187
+ */
188
+ function getInitialOffset(comment) {
189
+ return sourceCode.text.slice(comment.range[0] - comment.loc.start.column, comment.range[0]);
57
190
  }
58
191
 
59
192
  /**
@@ -63,10 +196,9 @@ module.exports = {
63
196
  * @returns {string} A representation of the comment value in starred-block form, excluding start and end markers
64
197
  */
65
198
  function convertToStarredBlock(firstComment, commentLinesList) {
66
- const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
67
- const starredLines = commentLinesList.map(line => `${initialOffset} *${line}`);
199
+ const initialOffset = getInitialOffset(firstComment);
68
200
 
69
- return `\n${starredLines.join("\n")}\n${initialOffset} `;
201
+ return `/*\n${commentLinesList.map(line => `${initialOffset} * ${line}`).join("\n")}\n${initialOffset} */`;
70
202
  }
71
203
 
72
204
  /**
@@ -76,10 +208,7 @@ module.exports = {
76
208
  * @returns {string} A representation of the comment value in separate-line form
77
209
  */
78
210
  function convertToSeparateLines(firstComment, commentLinesList) {
79
- const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
80
- const separateLines = commentLinesList.map(line => `// ${line.trim()}`);
81
-
82
- return separateLines.join(`\n${initialOffset}`);
211
+ return commentLinesList.map(line => `// ${line}`).join(`\n${getInitialOffset(firstComment)}`);
83
212
  }
84
213
 
85
214
  /**
@@ -89,24 +218,7 @@ module.exports = {
89
218
  * @returns {string} A representation of the comment value in bare-block form
90
219
  */
91
220
  function convertToBlock(firstComment, commentLinesList) {
92
- const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]);
93
- const blockLines = commentLinesList.map(line => line.trim());
94
-
95
- return `/* ${blockLines.join(`\n${initialOffset} `)} */`;
96
- }
97
-
98
- /**
99
- * Check a comment is JSDoc form
100
- * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment
101
- * @returns {boolean} if commentGroup is JSDoc form, return true
102
- */
103
- function isJSDoc(commentGroup) {
104
- const lines = commentGroup[0].value.split(astUtils.LINEBREAK_MATCHER);
105
-
106
- return commentGroup[0].type === "Block" &&
107
- /^\*\s*$/u.test(lines[0]) &&
108
- lines.slice(1, -1).every(line => /^\s* /u.test(line)) &&
109
- /^\s*$/u.test(lines[lines.length - 1]);
221
+ return `/* ${commentLinesList.join(`\n${getInitialOffset(firstComment)} `)} */`;
110
222
  }
111
223
 
112
224
  /**
@@ -117,6 +229,7 @@ module.exports = {
117
229
  */
118
230
  const commentGroupCheckers = {
119
231
  "starred-block"(commentGroup) {
232
+ const [firstComment] = commentGroup;
120
233
  const commentLines = getCommentLines(commentGroup);
121
234
 
122
235
  if (commentLines.some(value => value.includes("*/"))) {
@@ -126,31 +239,30 @@ module.exports = {
126
239
  if (commentGroup.length > 1) {
127
240
  context.report({
128
241
  loc: {
129
- start: commentGroup[0].loc.start,
242
+ start: firstComment.loc.start,
130
243
  end: commentGroup[commentGroup.length - 1].loc.end
131
244
  },
132
245
  messageId: "expectedBlock",
133
246
  fix(fixer) {
134
- const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
135
- const starredBlock = `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`;
247
+ const range = [firstComment.range[0], commentGroup[commentGroup.length - 1].range[1]];
136
248
 
137
249
  return commentLines.some(value => value.startsWith("/"))
138
250
  ? null
139
- : fixer.replaceTextRange(range, starredBlock);
251
+ : fixer.replaceTextRange(range, convertToStarredBlock(firstComment, commentLines));
140
252
  }
141
253
  });
142
254
  } else {
143
- const block = commentGroup[0];
144
- const lines = block.value.split(astUtils.LINEBREAK_MATCHER);
145
- const expectedLinePrefix = `${sourceCode.text.slice(block.range[0] - block.loc.start.column, block.range[0])} *`;
255
+ const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER);
256
+ const expectedLeadingWhitespace = getInitialOffset(firstComment);
257
+ const expectedLinePrefix = `${expectedLeadingWhitespace} *`;
146
258
 
147
259
  if (!/^\*?\s*$/u.test(lines[0])) {
148
- const start = block.value.startsWith("*") ? block.range[0] + 1 : block.range[0];
260
+ const start = firstComment.value.startsWith("*") ? firstComment.range[0] + 1 : firstComment.range[0];
149
261
 
150
262
  context.report({
151
263
  loc: {
152
- start: block.loc.start,
153
- end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
264
+ start: firstComment.loc.start,
265
+ end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 }
154
266
  },
155
267
  messageId: "startNewline",
156
268
  fix: fixer => fixer.insertTextAfterRange([start, start + 2], `\n${expectedLinePrefix}`)
@@ -160,36 +272,54 @@ module.exports = {
160
272
  if (!/^\s*$/u.test(lines[lines.length - 1])) {
161
273
  context.report({
162
274
  loc: {
163
- start: { line: block.loc.end.line, column: block.loc.end.column - 2 },
164
- end: block.loc.end
275
+ start: { line: firstComment.loc.end.line, column: firstComment.loc.end.column - 2 },
276
+ end: firstComment.loc.end
165
277
  },
166
278
  messageId: "endNewline",
167
- fix: fixer => fixer.replaceTextRange([block.range[1] - 2, block.range[1]], `\n${expectedLinePrefix}/`)
279
+ fix: fixer => fixer.replaceTextRange([firstComment.range[1] - 2, firstComment.range[1]], `\n${expectedLinePrefix}/`)
168
280
  });
169
281
  }
170
282
 
171
- for (let lineNumber = block.loc.start.line + 1; lineNumber <= block.loc.end.line; lineNumber++) {
283
+ for (let lineNumber = firstComment.loc.start.line + 1; lineNumber <= firstComment.loc.end.line; lineNumber++) {
172
284
  const lineText = sourceCode.lines[lineNumber - 1];
285
+ const errorType = isStarredCommentLine(lineText)
286
+ ? "alignment"
287
+ : "missingStar";
173
288
 
174
289
  if (!lineText.startsWith(expectedLinePrefix)) {
175
290
  context.report({
176
291
  loc: {
177
292
  start: { line: lineNumber, column: 0 },
178
- end: { line: lineNumber, column: sourceCode.lines[lineNumber - 1].length }
293
+ end: { line: lineNumber, column: lineText.length }
179
294
  },
180
- messageId: /^\s*\*/u.test(lineText)
181
- ? "alignment"
182
- : "missingStar",
295
+ messageId: errorType,
183
296
  fix(fixer) {
184
297
  const lineStartIndex = sourceCode.getIndexFromLoc({ line: lineNumber, column: 0 });
185
- const linePrefixLength = lineText.match(/^\s*\*? ?/u)[0].length;
186
- const commentStartIndex = lineStartIndex + linePrefixLength;
187
298
 
188
- const replacementText = lineNumber === block.loc.end.line || lineText.length === linePrefixLength
189
- ? expectedLinePrefix
190
- : `${expectedLinePrefix} `;
299
+ if (errorType === "alignment") {
300
+ const [, commentTextPrefix = ""] = lineText.match(/^(\s*\*)/u) || [];
301
+ const commentTextStartIndex = lineStartIndex + commentTextPrefix.length;
302
+
303
+ return fixer.replaceTextRange([lineStartIndex, commentTextStartIndex], expectedLinePrefix);
304
+ }
305
+
306
+ const [, commentTextPrefix = ""] = lineText.match(/^(\s*)/u) || [];
307
+ const commentTextStartIndex = lineStartIndex + commentTextPrefix.length;
308
+ let offset;
309
+
310
+ for (const [idx, line] of lines.entries()) {
311
+ if (!/\S+/u.test(line)) {
312
+ continue;
313
+ }
314
+
315
+ const lineTextToAlignWith = sourceCode.lines[firstComment.loc.start.line - 1 + idx];
316
+ const [, prefix = "", initialOffset = ""] = lineTextToAlignWith.match(/^(\s*(?:\/?\*)?(\s*))/u) || [];
191
317
 
192
- return fixer.replaceTextRange([lineStartIndex, commentStartIndex], replacementText);
318
+ offset = `${commentTextPrefix.slice(prefix.length)}${initialOffset}`;
319
+ break;
320
+ }
321
+
322
+ return fixer.replaceTextRange([lineStartIndex, commentTextStartIndex], `${expectedLinePrefix}${offset}`);
193
323
  }
194
324
  });
195
325
  }
@@ -197,67 +327,68 @@ module.exports = {
197
327
  }
198
328
  },
199
329
  "separate-lines"(commentGroup) {
200
- if (!isJSDoc(commentGroup) && commentGroup[0].type === "Block") {
201
- const commentLines = getCommentLines(commentGroup);
202
- const block = commentGroup[0];
203
- const tokenAfter = sourceCode.getTokenAfter(block, { includeComments: true });
330
+ const [firstComment] = commentGroup;
331
+
332
+ if (firstComment.type !== "Block" || isJSDocComment(commentGroup)) {
333
+ return;
334
+ }
204
335
 
205
- if (tokenAfter && block.loc.end.line === tokenAfter.loc.start.line) {
206
- return;
336
+ const commentLines = getCommentLines(commentGroup);
337
+ const tokenAfter = sourceCode.getTokenAfter(firstComment, { includeComments: true });
338
+
339
+ if (tokenAfter && firstComment.loc.end.line === tokenAfter.loc.start.line) {
340
+ return;
341
+ }
342
+
343
+ context.report({
344
+ loc: {
345
+ start: firstComment.loc.start,
346
+ end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 }
347
+ },
348
+ messageId: "expectedLines",
349
+ fix(fixer) {
350
+ return fixer.replaceText(firstComment, convertToSeparateLines(firstComment, commentLines));
207
351
  }
352
+ });
353
+ },
354
+ "bare-block"(commentGroup) {
355
+ if (isJSDocComment(commentGroup)) {
356
+ return;
357
+ }
208
358
 
359
+ const [firstComment] = commentGroup;
360
+ const commentLines = getCommentLines(commentGroup);
361
+
362
+ // Disallows consecutive line comments in favor of using a block comment.
363
+ if (firstComment.type === "Line" && commentLines.length > 1 &&
364
+ !commentLines.some(value => value.includes("*/"))) {
209
365
  context.report({
210
366
  loc: {
211
- start: block.loc.start,
212
- end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
367
+ start: firstComment.loc.start,
368
+ end: commentGroup[commentGroup.length - 1].loc.end
213
369
  },
214
- messageId: "expectedLines",
370
+ messageId: "expectedBlock",
215
371
  fix(fixer) {
216
- return fixer.replaceText(block, convertToSeparateLines(block, commentLines.filter(line => line)));
372
+ return fixer.replaceTextRange(
373
+ [firstComment.range[0], commentGroup[commentGroup.length - 1].range[1]],
374
+ convertToBlock(firstComment, commentLines)
375
+ );
217
376
  }
218
377
  });
219
378
  }
220
- },
221
- "bare-block"(commentGroup) {
222
- if (!isJSDoc(commentGroup)) {
223
- const commentLines = getCommentLines(commentGroup);
224
-
225
- // disallows consecutive line comments in favor of using a block comment.
226
- if (commentGroup[0].type === "Line" && commentLines.length > 1 &&
227
- !commentLines.some(value => value.includes("*/"))) {
228
- context.report({
229
- loc: {
230
- start: commentGroup[0].loc.start,
231
- end: commentGroup[commentGroup.length - 1].loc.end
232
- },
233
- messageId: "expectedBlock",
234
- fix(fixer) {
235
- const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]];
236
- const block = convertToBlock(commentGroup[0], commentLines.filter(line => line));
237
-
238
- return fixer.replaceTextRange(range, block);
239
- }
240
- });
241
- }
242
379
 
243
- // prohibits block comments from having a * at the beginning of each line.
244
- if (commentGroup[0].type === "Block") {
245
- const block = commentGroup[0];
246
- const lines = block.value.split(astUtils.LINEBREAK_MATCHER).filter(line => line.trim());
247
-
248
- if (lines.length > 0 && lines.every(line => /^\s*\*/u.test(line))) {
249
- context.report({
250
- loc: {
251
- start: block.loc.start,
252
- end: { line: block.loc.start.line, column: block.loc.start.column + 2 }
253
- },
254
- messageId: "expectedBareBlock",
255
- fix(fixer) {
256
- return fixer.replaceText(block, convertToBlock(block, commentLines.filter(line => line)));
257
- }
258
- });
380
+ // Prohibits block comments from having a * at the beginning of each line.
381
+ if (isStarredBlockComment(commentGroup)) {
382
+ context.report({
383
+ loc: {
384
+ start: firstComment.loc.start,
385
+ end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 }
386
+ },
387
+ messageId: "expectedBareBlock",
388
+ fix(fixer) {
389
+ return fixer.replaceText(firstComment, convertToBlock(firstComment, commentLines));
259
390
  }
260
- }
391
+ });
261
392
  }
262
393
  }
263
394
  };
@@ -46,9 +46,9 @@ module.exports = {
46
46
 
47
47
  /**
48
48
  * Tests whether node is preceded by supplied tokens
49
- * @param {ASTNode} node - node to check
50
- * @param {ASTNode} parentNode - parent of node to report
51
- * @param {boolean} expected - whether newline was expected or not
49
+ * @param {ASTNode} node node to check
50
+ * @param {ASTNode} parentNode parent of node to report
51
+ * @param {boolean} expected whether newline was expected or not
52
52
  * @returns {void}
53
53
  * @private
54
54
  */
@@ -72,8 +72,7 @@ module.exports = {
72
72
  * var foo = 1
73
73
  *
74
74
  * ;(a || b).doSomething()
75
- *
76
- * @param {ASTNode} node - The node to get.
75
+ * @param {ASTNode} node The node to get.
77
76
  * @returns {Token} The token to compare line to the next statement.
78
77
  */
79
78
  function getLastToken(node) {
@@ -93,7 +92,7 @@ module.exports = {
93
92
  /**
94
93
  * Determine if provided keyword is a variable declaration
95
94
  * @private
96
- * @param {string} keyword - keyword to test
95
+ * @param {string} keyword keyword to test
97
96
  * @returns {boolean} True if `keyword` is a type of var
98
97
  */
99
98
  function isVar(keyword) {
@@ -103,7 +102,7 @@ module.exports = {
103
102
  /**
104
103
  * Determine if provided keyword is a variant of for specifiers
105
104
  * @private
106
- * @param {string} keyword - keyword to test
105
+ * @param {string} keyword keyword to test
107
106
  * @returns {boolean} True if `keyword` is a variant of for specifier
108
107
  */
109
108
  function isForTypeSpecifier(keyword) {
@@ -113,7 +112,7 @@ module.exports = {
113
112
  /**
114
113
  * Determine if provided keyword is an export specifiers
115
114
  * @private
116
- * @param {string} nodeType - nodeType to test
115
+ * @param {string} nodeType nodeType to test
117
116
  * @returns {boolean} True if `nodeType` is an export specifier
118
117
  */
119
118
  function isExportSpecifier(nodeType) {
@@ -124,7 +123,7 @@ module.exports = {
124
123
  /**
125
124
  * Determine if provided node is the last of their parent block.
126
125
  * @private
127
- * @param {ASTNode} node - node to test
126
+ * @param {ASTNode} node node to test
128
127
  * @returns {boolean} True if `node` is last of their parent block.
129
128
  */
130
129
  function isLastNode(node) {
@@ -159,7 +158,7 @@ module.exports = {
159
158
  * set to "always", or checks that there is no blank line when mode is set
160
159
  * to "never"
161
160
  * @private
162
- * @param {ASTNode} node - `VariableDeclaration` node to test
161
+ * @param {ASTNode} node `VariableDeclaration` node to test
163
162
  * @returns {void}
164
163
  */
165
164
  function checkForBlankLine(node) {
@@ -39,8 +39,8 @@ module.exports = {
39
39
 
40
40
  /**
41
41
  * Tests whether node is preceded by supplied tokens
42
- * @param {ASTNode} node - node to check
43
- * @param {Array} testTokens - array of tokens to test against
42
+ * @param {ASTNode} node node to check
43
+ * @param {Array} testTokens array of tokens to test against
44
44
  * @returns {boolean} Whether or not the node is preceded by one of the supplied tokens
45
45
  * @private
46
46
  */
@@ -52,7 +52,7 @@ module.exports = {
52
52
 
53
53
  /**
54
54
  * Checks whether node is the first node after statement or in block
55
- * @param {ASTNode} node - node to check
55
+ * @param {ASTNode} node node to check
56
56
  * @returns {boolean} Whether or not the node is the first node after statement or in block
57
57
  * @private
58
58
  */
@@ -80,8 +80,8 @@ module.exports = {
80
80
 
81
81
  /**
82
82
  * Returns the number of lines of comments that precede the node
83
- * @param {ASTNode} node - node to check for overlapping comments
84
- * @param {number} lineNumTokenBefore - line number of previous token, to check for overlapping comments
83
+ * @param {ASTNode} node node to check for overlapping comments
84
+ * @param {number} lineNumTokenBefore line number of previous token, to check for overlapping comments
85
85
  * @returns {number} Number of lines of comments that precede the node
86
86
  * @private
87
87
  */
@@ -115,7 +115,7 @@ module.exports = {
115
115
 
116
116
  /**
117
117
  * Returns the line number of the token before the node that is passed in as an argument
118
- * @param {ASTNode} node - The node to use as the start of the calculation
118
+ * @param {ASTNode} node The node to use as the start of the calculation
119
119
  * @returns {number} Line number of the token before `node`
120
120
  * @private
121
121
  */
@@ -142,7 +142,7 @@ module.exports = {
142
142
 
143
143
  /**
144
144
  * Checks whether node is preceded by a newline
145
- * @param {ASTNode} node - node to check
145
+ * @param {ASTNode} node node to check
146
146
  * @returns {boolean} Whether or not the node is preceded by a newline
147
147
  * @private
148
148
  */
@@ -160,8 +160,7 @@ module.exports = {
160
160
  * The fix is not considered safe if the given return statement has leading comments,
161
161
  * as we cannot safely determine if the newline should be added before or after the comments.
162
162
  * For more information, see: https://github.com/eslint/eslint/issues/5958#issuecomment-222767211
163
- *
164
- * @param {ASTNode} node - The return statement node to check.
163
+ * @param {ASTNode} node The return statement node to check.
165
164
  * @returns {boolean} `true` if it can fix the node.
166
165
  * @private
167
166
  */
@@ -53,8 +53,7 @@ module.exports = {
53
53
  * Get the prefix of a given MemberExpression node.
54
54
  * If the MemberExpression node is a computed value it returns a
55
55
  * left bracket. If not it returns a period.
56
- *
57
- * @param {ASTNode} node - A MemberExpression node to get
56
+ * @param {ASTNode} node A MemberExpression node to get
58
57
  * @returns {string} The prefix of the node.
59
58
  */
60
59
  function getPrefix(node) {
@@ -64,8 +63,7 @@ module.exports = {
64
63
  /**
65
64
  * Gets the property text of a given MemberExpression node.
66
65
  * If the text is multiline, this returns only the first line.
67
- *
68
- * @param {ASTNode} node - A MemberExpression node to get.
66
+ * @param {ASTNode} node A MemberExpression node to get.
69
67
  * @returns {string} The property text of the node.
70
68
  */
71
69
  function getPropertyText(node) {
@@ -33,7 +33,7 @@ module.exports = {
33
33
 
34
34
  /**
35
35
  * Finds and reports references that are non initializer and writable.
36
- * @param {Variable} variable - A variable to check.
36
+ * @param {Variable} variable A variable to check.
37
37
  * @returns {void}
38
38
  */
39
39
  function checkVariable(variable) {
@@ -45,7 +45,7 @@ module.exports = {
45
45
 
46
46
  /**
47
47
  * Finds and reports references that are non initializer and writable.
48
- * @param {ASTNode} node - A ClassDeclaration/ClassExpression node to check.
48
+ * @param {ASTNode} node A ClassDeclaration/ClassExpression node to check.
49
49
  * @returns {void}
50
50
  */
51
51
  function checkForClass(node) {
@@ -35,8 +35,7 @@ module.exports = {
35
35
 
36
36
  /**
37
37
  * Checks a given node is -0
38
- *
39
- * @param {ASTNode} node - A node to check.
38
+ * @param {ASTNode} node A node to check.
40
39
  * @returns {boolean} `true` if the node is -0.
41
40
  */
42
41
  function isNegZero(node) {