eslint 6.6.0 → 6.8.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 (62) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +7 -8
  3. package/conf/config-schema.js +2 -0
  4. package/conf/default-cli-options.js +1 -1
  5. package/conf/eslint-recommended.js +0 -1
  6. package/lib/cli-engine/cascading-config-array-factory.js +38 -13
  7. package/lib/cli-engine/cli-engine.js +41 -14
  8. package/lib/cli-engine/config-array/config-array.js +13 -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-factory.js +115 -1
  13. package/lib/cli-engine/file-enumerator.js +73 -40
  14. package/lib/cli-engine/lint-result-cache.js +2 -1
  15. package/lib/cli.js +2 -1
  16. package/lib/init/config-initializer.js +4 -3
  17. package/lib/linter/config-comment-parser.js +1 -1
  18. package/lib/linter/report-translator.js +73 -7
  19. package/lib/options.js +6 -0
  20. package/lib/rule-tester/rule-tester.js +42 -6
  21. package/lib/rules/array-bracket-spacing.js +8 -8
  22. package/lib/rules/camelcase.js +19 -6
  23. package/lib/rules/comma-dangle.js +5 -2
  24. package/lib/rules/computed-property-spacing.js +4 -4
  25. package/lib/rules/curly.js +9 -4
  26. package/lib/rules/function-call-argument-newline.js +3 -1
  27. package/lib/rules/grouped-accessor-pairs.js +224 -0
  28. package/lib/rules/indent.js +11 -0
  29. package/lib/rules/index.js +5 -0
  30. package/lib/rules/key-spacing.js +34 -15
  31. package/lib/rules/lines-between-class-members.js +42 -53
  32. package/lib/rules/multiline-comment-style.js +237 -106
  33. package/lib/rules/no-cond-assign.js +14 -4
  34. package/lib/rules/no-constructor-return.js +62 -0
  35. package/lib/rules/no-dupe-else-if.js +122 -0
  36. package/lib/rules/no-implicit-globals.js +90 -8
  37. package/lib/rules/no-inline-comments.js +25 -11
  38. package/lib/rules/no-invalid-this.js +16 -2
  39. package/lib/rules/no-multiple-empty-lines.js +1 -1
  40. package/lib/rules/no-octal-escape.js +1 -1
  41. package/lib/rules/no-restricted-imports.js +2 -2
  42. package/lib/rules/no-setter-return.js +227 -0
  43. package/lib/rules/no-underscore-dangle.js +23 -4
  44. package/lib/rules/no-unexpected-multiline.js +8 -0
  45. package/lib/rules/no-unsafe-negation.js +30 -5
  46. package/lib/rules/no-useless-computed-key.js +60 -33
  47. package/lib/rules/no-useless-escape.js +26 -3
  48. package/lib/rules/object-curly-spacing.js +8 -8
  49. package/lib/rules/operator-assignment.js +11 -2
  50. package/lib/rules/prefer-const.js +14 -7
  51. package/lib/rules/prefer-exponentiation-operator.js +189 -0
  52. package/lib/rules/prefer-numeric-literals.js +29 -28
  53. package/lib/rules/require-atomic-updates.js +1 -1
  54. package/lib/rules/require-await.js +8 -0
  55. package/lib/rules/semi.js +6 -3
  56. package/lib/rules/space-infix-ops.js +1 -1
  57. package/lib/rules/spaced-comment.js +5 -4
  58. package/lib/rules/utils/ast-utils.js +31 -4
  59. package/lib/shared/types.js +9 -0
  60. package/lib/source-code/source-code.js +87 -10
  61. package/package.json +4 -3
  62. package/lib/cli-engine/ignored-paths.js +0 -363
@@ -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
  };
@@ -2,10 +2,21 @@
2
2
  * @fileoverview Rule to flag assignment in a conditional statement's test expression
3
3
  * @author Stephen Murray <spmurrayzzz>
4
4
  */
5
+
5
6
  "use strict";
6
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
7
12
  const astUtils = require("./utils/ast-utils");
8
13
 
14
+ //------------------------------------------------------------------------------
15
+ // Helpers
16
+ //------------------------------------------------------------------------------
17
+
18
+ const TEST_CONDITION_PARENT_TYPES = new Set(["IfStatement", "WhileStatement", "DoWhileStatement", "ForStatement", "ConditionalExpression"]);
19
+
9
20
  const NODE_DESCRIPTIONS = {
10
21
  DoWhileStatement: "a 'do...while' statement",
11
22
  ForStatement: "a 'for' statement",
@@ -55,7 +66,7 @@ module.exports = {
55
66
  */
56
67
  function isConditionalTestExpression(node) {
57
68
  return node.parent &&
58
- node.parent.test &&
69
+ TEST_CONDITION_PARENT_TYPES.has(node.parent.type) &&
59
70
  node === node.parent.test;
60
71
  }
61
72
 
@@ -105,8 +116,7 @@ module.exports = {
105
116
  ) {
106
117
 
107
118
  context.report({
108
- node,
109
- loc: node.test.loc.start,
119
+ node: node.test,
110
120
  messageId: "missing"
111
121
  });
112
122
  }
@@ -122,7 +132,7 @@ module.exports = {
122
132
 
123
133
  if (ancestor) {
124
134
  context.report({
125
- node: ancestor,
135
+ node,
126
136
  messageId: "unexpected",
127
137
  data: {
128
138
  type: NODE_DESCRIPTIONS[ancestor.type] || ancestor.type
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @fileoverview Rule to disallow returning value from constructor.
3
+ * @author Pig Fang <https://github.com/g-plane>
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Rule Definition
10
+ //------------------------------------------------------------------------------
11
+
12
+ module.exports = {
13
+ meta: {
14
+ type: "problem",
15
+
16
+ docs: {
17
+ description: "disallow returning value from constructor",
18
+ category: "Best Practices",
19
+ recommended: false,
20
+ url: "https://eslint.org/docs/rules/no-constructor-return"
21
+ },
22
+
23
+ schema: {},
24
+
25
+ fixable: null,
26
+
27
+ messages: {
28
+ unexpected: "Unexpected return statement in constructor."
29
+ }
30
+ },
31
+
32
+ create(context) {
33
+ const stack = [];
34
+
35
+ return {
36
+ onCodePathStart(_, node) {
37
+ stack.push(node);
38
+ },
39
+ onCodePathEnd() {
40
+ stack.pop();
41
+ },
42
+ ReturnStatement(node) {
43
+ const last = stack[stack.length - 1];
44
+
45
+ if (!last.parent) {
46
+ return;
47
+ }
48
+
49
+ if (
50
+ last.parent.type === "MethodDefinition" &&
51
+ last.parent.kind === "constructor" &&
52
+ (node.parent.parent === last || node.argument)
53
+ ) {
54
+ context.report({
55
+ node,
56
+ messageId: "unexpected"
57
+ });
58
+ }
59
+ }
60
+ };
61
+ }
62
+ };