eslint 7.0.0 → 7.3.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 (70) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +14 -10
  3. package/lib/cli-engine/cli-engine.js +2 -2
  4. package/lib/cli-engine/config-array-factory.js +34 -2
  5. package/lib/init/config-initializer.js +92 -74
  6. package/lib/linter/code-path-analysis/code-path-analyzer.js +2 -2
  7. package/lib/linter/code-path-analysis/code-path-state.js +34 -12
  8. package/lib/linter/config-comment-parser.js +1 -1
  9. package/lib/linter/linter.js +2 -1
  10. package/lib/rule-tester/rule-tester.js +9 -0
  11. package/lib/rules/accessor-pairs.js +1 -1
  12. package/lib/rules/arrow-parens.js +19 -3
  13. package/lib/rules/block-spacing.js +19 -2
  14. package/lib/rules/callback-return.js +1 -1
  15. package/lib/rules/comma-dangle.js +2 -1
  16. package/lib/rules/curly.js +8 -1
  17. package/lib/rules/func-call-spacing.js +18 -3
  18. package/lib/rules/global-require.js +1 -1
  19. package/lib/rules/handle-callback-err.js +1 -1
  20. package/lib/rules/id-match.js +2 -1
  21. package/lib/rules/index.js +3 -0
  22. package/lib/rules/key-spacing.js +6 -2
  23. package/lib/rules/keyword-spacing.js +9 -2
  24. package/lib/rules/linebreak-style.js +8 -2
  25. package/lib/rules/max-lines-per-function.js +1 -1
  26. package/lib/rules/max-lines.js +34 -8
  27. package/lib/rules/multiline-ternary.js +44 -25
  28. package/lib/rules/no-buffer-constructor.js +1 -1
  29. package/lib/rules/no-control-regex.js +1 -1
  30. package/lib/rules/no-extra-boolean-cast.js +3 -0
  31. package/lib/rules/no-extra-parens.js +30 -2
  32. package/lib/rules/no-invalid-regexp.js +1 -1
  33. package/lib/rules/no-loss-of-precision.js +198 -0
  34. package/lib/rules/no-misleading-character-class.js +1 -1
  35. package/lib/rules/no-mixed-operators.js +3 -2
  36. package/lib/rules/no-mixed-requires.js +1 -1
  37. package/lib/rules/no-mixed-spaces-and-tabs.js +14 -6
  38. package/lib/rules/no-new-func.js +22 -19
  39. package/lib/rules/no-new-require.js +1 -1
  40. package/lib/rules/no-new-symbol.js +2 -1
  41. package/lib/rules/no-path-concat.js +1 -1
  42. package/lib/rules/no-process-env.js +1 -1
  43. package/lib/rules/no-process-exit.js +1 -1
  44. package/lib/rules/no-promise-executor-return.js +121 -0
  45. package/lib/rules/no-regex-spaces.js +1 -1
  46. package/lib/rules/no-restricted-exports.js +6 -0
  47. package/lib/rules/no-restricted-modules.js +1 -1
  48. package/lib/rules/no-sync.js +1 -1
  49. package/lib/rules/no-unneeded-ternary.js +6 -4
  50. package/lib/rules/no-unreachable-loop.js +150 -0
  51. package/lib/rules/no-unused-expressions.js +1 -1
  52. package/lib/rules/no-unused-vars.js +5 -2
  53. package/lib/rules/no-useless-backreference.js +1 -1
  54. package/lib/rules/object-property-newline.js +1 -1
  55. package/lib/rules/one-var-declaration-per-line.js +1 -1
  56. package/lib/rules/operator-linebreak.js +2 -5
  57. package/lib/rules/padded-blocks.js +19 -5
  58. package/lib/rules/prefer-named-capture-group.js +1 -1
  59. package/lib/rules/quote-props.js +2 -2
  60. package/lib/rules/rest-spread-spacing.js +3 -6
  61. package/lib/rules/semi-spacing.js +33 -8
  62. package/lib/rules/template-tag-spacing.js +8 -2
  63. package/lib/rules/utils/ast-utils.js +106 -9
  64. package/lib/source-code/source-code.js +1 -0
  65. package/messages/extend-config-missing.txt +1 -1
  66. package/messages/no-config-found.txt +1 -1
  67. package/messages/plugin-conflict.txt +1 -1
  68. package/messages/plugin-missing.txt +1 -1
  69. package/messages/whitespace-found.txt +1 -1
  70. package/package.json +6 -6
@@ -147,10 +147,12 @@ module.exports = {
147
147
  loc: node.consequent.loc.start,
148
148
  messageId: "unnecessaryConditionalAssignment",
149
149
  fix: fixer => {
150
- const shouldParenthesizeAlternate = (
151
- astUtils.getPrecedence(node.alternate) < OR_PRECEDENCE &&
152
- !astUtils.isParenthesised(sourceCode, node.alternate)
153
- );
150
+ const shouldParenthesizeAlternate =
151
+ (
152
+ astUtils.getPrecedence(node.alternate) < OR_PRECEDENCE ||
153
+ astUtils.isCoalesceExpression(node.alternate)
154
+ ) &&
155
+ !astUtils.isParenthesised(sourceCode, node.alternate);
154
156
  const alternateText = shouldParenthesizeAlternate
155
157
  ? `(${sourceCode.getText(node.alternate)})`
156
158
  : astUtils.getParenthesisedText(sourceCode, node.alternate);
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @fileoverview Rule to disallow loops with a body that allows only one iteration
3
+ * @author Milos Djermanovic
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Helpers
10
+ //------------------------------------------------------------------------------
11
+
12
+ const allLoopTypes = ["WhileStatement", "DoWhileStatement", "ForStatement", "ForInStatement", "ForOfStatement"];
13
+
14
+ /**
15
+ * Determines whether the given node is the first node in the code path to which a loop statement
16
+ * 'loops' for the next iteration.
17
+ * @param {ASTNode} node The node to check.
18
+ * @returns {boolean} `true` if the node is a looping target.
19
+ */
20
+ function isLoopingTarget(node) {
21
+ const parent = node.parent;
22
+
23
+ if (parent) {
24
+ switch (parent.type) {
25
+ case "WhileStatement":
26
+ return node === parent.test;
27
+ case "DoWhileStatement":
28
+ return node === parent.body;
29
+ case "ForStatement":
30
+ return node === (parent.update || parent.test || parent.body);
31
+ case "ForInStatement":
32
+ case "ForOfStatement":
33
+ return node === parent.left;
34
+
35
+ // no default
36
+ }
37
+ }
38
+
39
+ return false;
40
+ }
41
+
42
+ /**
43
+ * Creates an array with elements from the first given array that are not included in the second given array.
44
+ * @param {Array} arrA The array to compare from.
45
+ * @param {Array} arrB The array to compare against.
46
+ * @returns {Array} a new array that represents `arrA \ arrB`.
47
+ */
48
+ function getDifference(arrA, arrB) {
49
+ return arrA.filter(a => !arrB.includes(a));
50
+ }
51
+
52
+ //------------------------------------------------------------------------------
53
+ // Rule Definition
54
+ //------------------------------------------------------------------------------
55
+
56
+ module.exports = {
57
+ meta: {
58
+ type: "problem",
59
+
60
+ docs: {
61
+ description: "disallow loops with a body that allows only one iteration",
62
+ category: "Possible Errors",
63
+ recommended: false,
64
+ url: "https://eslint.org/docs/rules/no-unreachable-loop"
65
+ },
66
+
67
+ schema: [{
68
+ type: "object",
69
+ properties: {
70
+ ignore: {
71
+ type: "array",
72
+ items: {
73
+ enum: allLoopTypes
74
+ },
75
+ uniqueItems: true
76
+ }
77
+ },
78
+ additionalProperties: false
79
+ }],
80
+
81
+ messages: {
82
+ invalid: "Invalid loop. Its body allows only one iteration."
83
+ }
84
+ },
85
+
86
+ create(context) {
87
+ const ignoredLoopTypes = context.options[0] && context.options[0].ignore || [],
88
+ loopTypesToCheck = getDifference(allLoopTypes, ignoredLoopTypes),
89
+ loopSelector = loopTypesToCheck.join(","),
90
+ loopsByTargetSegments = new Map(),
91
+ loopsToReport = new Set();
92
+
93
+ let currentCodePath = null;
94
+
95
+ return {
96
+ onCodePathStart(codePath) {
97
+ currentCodePath = codePath;
98
+ },
99
+
100
+ onCodePathEnd() {
101
+ currentCodePath = currentCodePath.upper;
102
+ },
103
+
104
+ [loopSelector](node) {
105
+
106
+ /**
107
+ * Ignore unreachable loop statements to avoid unnecessary complexity in the implementation, or false positives otherwise.
108
+ * For unreachable segments, the code path analysis does not raise events required for this implementation.
109
+ */
110
+ if (currentCodePath.currentSegments.some(segment => segment.reachable)) {
111
+ loopsToReport.add(node);
112
+ }
113
+ },
114
+
115
+ onCodePathSegmentStart(segment, node) {
116
+ if (isLoopingTarget(node)) {
117
+ const loop = node.parent;
118
+
119
+ loopsByTargetSegments.set(segment, loop);
120
+ }
121
+ },
122
+
123
+ onCodePathSegmentLoop(_, toSegment, node) {
124
+ const loop = loopsByTargetSegments.get(toSegment);
125
+
126
+ /**
127
+ * The second iteration is reachable, meaning that the loop is valid by the logic of this rule,
128
+ * only if there is at least one loop event with the appropriate target (which has been already
129
+ * determined in the `loopsByTargetSegments` map), raised from either:
130
+ *
131
+ * - the end of the loop's body (in which case `node === loop`)
132
+ * - a `continue` statement
133
+ *
134
+ * This condition skips loop events raised from `ForInStatement > .right` and `ForOfStatement > .right` nodes.
135
+ */
136
+ if (node === loop || node.type === "ContinueStatement") {
137
+
138
+ // Removes loop if it exists in the set. Otherwise, `Set#delete` has no effect and doesn't throw.
139
+ loopsToReport.delete(loop);
140
+ }
141
+ },
142
+
143
+ "Program:exit"() {
144
+ loopsToReport.forEach(
145
+ node => context.report({ node, messageId: "invalid" })
146
+ );
147
+ }
148
+ };
149
+ }
150
+ };
@@ -124,7 +124,7 @@ module.exports = {
124
124
  return true;
125
125
  }
126
126
 
127
- return /^(?:Assignment|Call|New|Update|Yield|Await)Expression$/u.test(node.type) ||
127
+ return /^(?:Assignment|Call|New|Update|Yield|Await|Import)Expression$/u.test(node.type) ||
128
128
  (node.type === "UnaryExpression" && ["delete", "void"].indexOf(node.operator) >= 0);
129
129
  }
130
130
 
@@ -68,7 +68,8 @@ module.exports = {
68
68
  caughtErrorsIgnorePattern: {
69
69
  type: "string"
70
70
  }
71
- }
71
+ },
72
+ additionalProperties: false
72
73
  }
73
74
  ]
74
75
  }
@@ -619,7 +620,9 @@ module.exports = {
619
620
  // Report the first declaration.
620
621
  if (unusedVar.defs.length > 0) {
621
622
  context.report({
622
- node: unusedVar.identifiers[0],
623
+ node: unusedVar.references.length ? unusedVar.references[
624
+ unusedVar.references.length - 1
625
+ ].identifier : unusedVar.identifiers[0],
623
626
  messageId: "unusedVar",
624
627
  data: unusedVar.references.some(ref => ref.isWrite())
625
628
  ? getAssignedMessageData(unusedVar)
@@ -95,7 +95,7 @@ module.exports = {
95
95
 
96
96
  try {
97
97
  regExpAST = parser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
98
- } catch (e) {
98
+ } catch {
99
99
 
100
100
  // Ignore regular expressions with syntax errors
101
101
  return;
@@ -77,7 +77,7 @@ module.exports = {
77
77
  if (lastTokenOfPreviousProperty.loc.end.line === firstTokenOfCurrentProperty.loc.start.line) {
78
78
  context.report({
79
79
  node,
80
- loc: firstTokenOfCurrentProperty.loc.start,
80
+ loc: firstTokenOfCurrentProperty.loc,
81
81
  messageId,
82
82
  fix(fixer) {
83
83
  const comma = sourceCode.getTokenBefore(firstTokenOfCurrentProperty);
@@ -71,7 +71,7 @@ module.exports = {
71
71
  context.report({
72
72
  node,
73
73
  messageId: "expectVarOnNewline",
74
- loc: current.loc.start,
74
+ loc: current.loc,
75
75
  fix: fixer => fixer.insertTextBefore(current, "\n")
76
76
  });
77
77
  }
@@ -35,11 +35,8 @@ module.exports = {
35
35
  properties: {
36
36
  overrides: {
37
37
  type: "object",
38
- properties: {
39
- anyOf: {
40
- type: "string",
41
- enum: ["after", "before", "none", "ignore"]
42
- }
38
+ additionalProperties: {
39
+ enum: ["after", "before", "none", "ignore"]
43
40
  }
44
41
  }
45
42
  },
@@ -58,7 +58,8 @@ module.exports = {
58
58
  allowSingleLineBlocks: {
59
59
  type: "boolean"
60
60
  }
61
- }
61
+ },
62
+ additionalProperties: false
62
63
  }
63
64
  ],
64
65
 
@@ -203,10 +204,14 @@ module.exports = {
203
204
  }
204
205
 
205
206
  if (requirePaddingFor(node)) {
207
+
206
208
  if (!blockHasTopPadding) {
207
209
  context.report({
208
210
  node,
209
- loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },
211
+ loc: {
212
+ start: tokenBeforeFirst.loc.start,
213
+ end: firstBlockToken.loc.start
214
+ },
210
215
  fix(fixer) {
211
216
  return fixer.insertTextAfter(tokenBeforeFirst, "\n");
212
217
  },
@@ -216,7 +221,10 @@ module.exports = {
216
221
  if (!blockHasBottomPadding) {
217
222
  context.report({
218
223
  node,
219
- loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },
224
+ loc: {
225
+ end: tokenAfterLast.loc.start,
226
+ start: lastBlockToken.loc.end
227
+ },
220
228
  fix(fixer) {
221
229
  return fixer.insertTextBefore(tokenAfterLast, "\n");
222
230
  },
@@ -228,7 +236,10 @@ module.exports = {
228
236
 
229
237
  context.report({
230
238
  node,
231
- loc: { line: tokenBeforeFirst.loc.start.line, column: tokenBeforeFirst.loc.start.column },
239
+ loc: {
240
+ start: tokenBeforeFirst.loc.start,
241
+ end: firstBlockToken.loc.start
242
+ },
232
243
  fix(fixer) {
233
244
  return fixer.replaceTextRange([tokenBeforeFirst.range[1], firstBlockToken.range[0] - firstBlockToken.loc.start.column], "\n");
234
245
  },
@@ -240,7 +251,10 @@ module.exports = {
240
251
 
241
252
  context.report({
242
253
  node,
243
- loc: { line: tokenAfterLast.loc.end.line, column: tokenAfterLast.loc.end.column - 1 },
254
+ loc: {
255
+ end: tokenAfterLast.loc.start,
256
+ start: lastBlockToken.loc.end
257
+ },
244
258
  messageId: "neverPadBlock",
245
259
  fix(fixer) {
246
260
  return fixer.replaceTextRange([lastBlockToken.range[1], tokenAfterLast.range[0] - tokenAfterLast.loc.start.column], "\n");
@@ -59,7 +59,7 @@ module.exports = {
59
59
 
60
60
  try {
61
61
  ast = parser.parsePattern(pattern, 0, pattern.length, uFlag);
62
- } catch (_) {
62
+ } catch {
63
63
 
64
64
  // ignore regex syntax errors
65
65
  return;
@@ -154,7 +154,7 @@ module.exports = {
154
154
 
155
155
  try {
156
156
  tokens = espree.tokenize(key.value);
157
- } catch (e) {
157
+ } catch {
158
158
  return;
159
159
  }
160
160
 
@@ -239,7 +239,7 @@ module.exports = {
239
239
 
240
240
  try {
241
241
  tokens = espree.tokenize(key.value);
242
- } catch (e) {
242
+ } catch {
243
243
  necessaryQuotes = true;
244
244
  return;
245
245
  }
@@ -79,10 +79,7 @@ module.exports = {
79
79
  if (alwaysSpace && !hasWhitespace) {
80
80
  context.report({
81
81
  node,
82
- loc: {
83
- line: operator.loc.end.line,
84
- column: operator.loc.end.column
85
- },
82
+ loc: operator.loc,
86
83
  messageId: "expectedWhitespace",
87
84
  data: {
88
85
  type
@@ -95,8 +92,8 @@ module.exports = {
95
92
  context.report({
96
93
  node,
97
94
  loc: {
98
- line: operator.loc.end.line,
99
- column: operator.loc.end.column
95
+ start: operator.loc.end,
96
+ end: nextToken.loc.start
100
97
  },
101
98
  messageId: "unexpectedWhitespace",
102
99
  data: {
@@ -117,6 +117,18 @@ module.exports = {
117
117
  }
118
118
 
119
119
  /**
120
+ * Report location example :
121
+ *
122
+ * for unexpected space `before`
123
+ *
124
+ * var a = 'b' ;
125
+ * ^^^
126
+ *
127
+ * for unexpected space `after`
128
+ *
129
+ * var a = 'b'; c = 10;
130
+ * ^^
131
+ *
120
132
  * Reports if the given token has invalid spacing.
121
133
  * @param {Token} token The semicolon token to check.
122
134
  * @param {ASTNode} node The corresponding node of the token.
@@ -124,16 +136,19 @@ module.exports = {
124
136
  */
125
137
  function checkSemicolonSpacing(token, node) {
126
138
  if (astUtils.isSemicolonToken(token)) {
127
- const location = token.loc.start;
128
-
129
139
  if (hasLeadingSpace(token)) {
130
140
  if (!requireSpaceBefore) {
141
+ const tokenBefore = sourceCode.getTokenBefore(token);
142
+ const loc = {
143
+ start: tokenBefore.loc.end,
144
+ end: token.loc.start
145
+ };
146
+
131
147
  context.report({
132
148
  node,
133
- loc: location,
149
+ loc,
134
150
  messageId: "unexpectedWhitespaceBefore",
135
151
  fix(fixer) {
136
- const tokenBefore = sourceCode.getTokenBefore(token);
137
152
 
138
153
  return fixer.removeRange([tokenBefore.range[1], token.range[0]]);
139
154
  }
@@ -141,9 +156,11 @@ module.exports = {
141
156
  }
142
157
  } else {
143
158
  if (requireSpaceBefore) {
159
+ const loc = token.loc;
160
+
144
161
  context.report({
145
162
  node,
146
- loc: location,
163
+ loc,
147
164
  messageId: "missingWhitespaceBefore",
148
165
  fix(fixer) {
149
166
  return fixer.insertTextBefore(token, " ");
@@ -155,12 +172,17 @@ module.exports = {
155
172
  if (!isFirstTokenInCurrentLine(token) && !isLastTokenInCurrentLine(token) && !isBeforeClosingParen(token)) {
156
173
  if (hasTrailingSpace(token)) {
157
174
  if (!requireSpaceAfter) {
175
+ const tokenAfter = sourceCode.getTokenAfter(token);
176
+ const loc = {
177
+ start: token.loc.end,
178
+ end: tokenAfter.loc.start
179
+ };
180
+
158
181
  context.report({
159
182
  node,
160
- loc: location,
183
+ loc,
161
184
  messageId: "unexpectedWhitespaceAfter",
162
185
  fix(fixer) {
163
- const tokenAfter = sourceCode.getTokenAfter(token);
164
186
 
165
187
  return fixer.removeRange([token.range[1], tokenAfter.range[0]]);
166
188
  }
@@ -168,9 +190,11 @@ module.exports = {
168
190
  }
169
191
  } else {
170
192
  if (requireSpaceAfter) {
193
+ const loc = token.loc;
194
+
171
195
  context.report({
172
196
  node,
173
- loc: location,
197
+ loc,
174
198
  messageId: "missingWhitespaceAfter",
175
199
  fix(fixer) {
176
200
  return fixer.insertTextAfter(token, " ");
@@ -199,6 +223,7 @@ module.exports = {
199
223
  BreakStatement: checkNode,
200
224
  ContinueStatement: checkNode,
201
225
  DebuggerStatement: checkNode,
226
+ DoWhileStatement: checkNode,
202
227
  ReturnStatement: checkNode,
203
228
  ThrowStatement: checkNode,
204
229
  ImportDeclaration: checkNode,
@@ -49,7 +49,10 @@ module.exports = {
49
49
  if (never && hasWhitespace) {
50
50
  context.report({
51
51
  node,
52
- loc: tagToken.loc.start,
52
+ loc: {
53
+ start: tagToken.loc.end,
54
+ end: literalToken.loc.start
55
+ },
53
56
  messageId: "unexpected",
54
57
  fix(fixer) {
55
58
  const comments = sourceCode.getCommentsBefore(node.quasi);
@@ -68,7 +71,10 @@ module.exports = {
68
71
  } else if (!never && !hasWhitespace) {
69
72
  context.report({
70
73
  node,
71
- loc: tagToken.loc.start,
74
+ loc: {
75
+ start: node.loc.start,
76
+ end: literalToken.loc.start
77
+ },
72
78
  messageId: "missing",
73
79
  fix(fixer) {
74
80
  return fixer.insertTextAfter(tagToken, " ");
@@ -416,6 +416,53 @@ function equalTokens(left, right, sourceCode) {
416
416
  return true;
417
417
  }
418
418
 
419
+ /**
420
+ * Check if the given node is a true logical expression or not.
421
+ *
422
+ * The three binary expressions logical-or (`||`), logical-and (`&&`), and
423
+ * coalesce (`??`) are known as `ShortCircuitExpression`.
424
+ * But ESTree represents those by `LogicalExpression` node.
425
+ *
426
+ * This function rejects coalesce expressions of `LogicalExpression` node.
427
+ * @param {ASTNode} node The node to check.
428
+ * @returns {boolean} `true` if the node is `&&` or `||`.
429
+ * @see https://tc39.es/ecma262/#prod-ShortCircuitExpression
430
+ */
431
+ function isLogicalExpression(node) {
432
+ return (
433
+ node.type === "LogicalExpression" &&
434
+ (node.operator === "&&" || node.operator === "||")
435
+ );
436
+ }
437
+
438
+ /**
439
+ * Check if the given node is a nullish coalescing expression or not.
440
+ *
441
+ * The three binary expressions logical-or (`||`), logical-and (`&&`), and
442
+ * coalesce (`??`) are known as `ShortCircuitExpression`.
443
+ * But ESTree represents those by `LogicalExpression` node.
444
+ *
445
+ * This function finds only coalesce expressions of `LogicalExpression` node.
446
+ * @param {ASTNode} node The node to check.
447
+ * @returns {boolean} `true` if the node is `??`.
448
+ */
449
+ function isCoalesceExpression(node) {
450
+ return node.type === "LogicalExpression" && node.operator === "??";
451
+ }
452
+
453
+ /**
454
+ * Check if given two nodes are the pair of a logical expression and a coalesce expression.
455
+ * @param {ASTNode} left A node to check.
456
+ * @param {ASTNode} right Another node to check.
457
+ * @returns {boolean} `true` if the two nodes are the pair of a logical expression and a coalesce expression.
458
+ */
459
+ function isMixedLogicalAndCoalesceExpressions(left, right) {
460
+ return (
461
+ (isLogicalExpression(left) && isCoalesceExpression(right)) ||
462
+ (isCoalesceExpression(left) && isLogicalExpression(right))
463
+ );
464
+ }
465
+
419
466
  //------------------------------------------------------------------------------
420
467
  // Public Interface
421
468
  //------------------------------------------------------------------------------
@@ -779,6 +826,7 @@ module.exports = {
779
826
  case "LogicalExpression":
780
827
  switch (node.operator) {
781
828
  case "||":
829
+ case "??":
782
830
  return 4;
783
831
  case "&&":
784
832
  return 5;
@@ -1243,19 +1291,64 @@ module.exports = {
1243
1291
 
1244
1292
  /**
1245
1293
  * Gets next location when the result is not out of bound, otherwise returns null.
1294
+ *
1295
+ * Assumptions:
1296
+ *
1297
+ * - The given location represents a valid location in the given source code.
1298
+ * - Columns are 0-based.
1299
+ * - Lines are 1-based.
1300
+ * - Column immediately after the last character in a line (not incl. linebreaks) is considered to be a valid location.
1301
+ * - If the source code ends with a linebreak, `sourceCode.lines` array will have an extra element (empty string) at the end.
1302
+ * The start (column 0) of that extra line is considered to be a valid location.
1303
+ *
1304
+ * Examples of successive locations (line, column):
1305
+ *
1306
+ * code: foo
1307
+ * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> null
1308
+ *
1309
+ * code: foo<LF>
1310
+ * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1311
+ *
1312
+ * code: foo<CR><LF>
1313
+ * locations: (1, 0) -> (1, 1) -> (1, 2) -> (1, 3) -> (2, 0) -> null
1314
+ *
1315
+ * code: a<LF>b
1316
+ * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> null
1317
+ *
1318
+ * code: a<LF>b<LF>
1319
+ * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1320
+ *
1321
+ * code: a<CR><LF>b<CR><LF>
1322
+ * locations: (1, 0) -> (1, 1) -> (2, 0) -> (2, 1) -> (3, 0) -> null
1323
+ *
1324
+ * code: a<LF><LF>
1325
+ * locations: (1, 0) -> (1, 1) -> (2, 0) -> (3, 0) -> null
1326
+ *
1327
+ * code: <LF>
1328
+ * locations: (1, 0) -> (2, 0) -> null
1329
+ *
1330
+ * code:
1331
+ * locations: (1, 0) -> null
1246
1332
  * @param {SourceCode} sourceCode The sourceCode
1247
1333
  * @param {{line: number, column: number}} location The location
1248
1334
  * @returns {{line: number, column: number} | null} Next location
1249
1335
  */
1250
- getNextLocation(sourceCode, location) {
1251
- const index = sourceCode.getIndexFromLoc(location);
1336
+ getNextLocation(sourceCode, { line, column }) {
1337
+ if (column < sourceCode.lines[line - 1].length) {
1338
+ return {
1339
+ line,
1340
+ column: column + 1
1341
+ };
1342
+ }
1252
1343
 
1253
- // Avoid out of bound location
1254
- if (index + 1 > sourceCode.text.length) {
1255
- return null;
1344
+ if (line < sourceCode.lines.length) {
1345
+ return {
1346
+ line: line + 1,
1347
+ column: 0
1348
+ };
1256
1349
  }
1257
1350
 
1258
- return sourceCode.getLocFromIndex(index + 1);
1351
+ return null;
1259
1352
  },
1260
1353
 
1261
1354
  /**
@@ -1370,7 +1463,7 @@ module.exports = {
1370
1463
 
1371
1464
  try {
1372
1465
  tokens = espree.tokenize(leftValue, espreeOptions);
1373
- } catch (e) {
1466
+ } catch {
1374
1467
  return false;
1375
1468
  }
1376
1469
 
@@ -1399,7 +1492,7 @@ module.exports = {
1399
1492
 
1400
1493
  try {
1401
1494
  tokens = espree.tokenize(rightValue, espreeOptions);
1402
- } catch (e) {
1495
+ } catch {
1403
1496
  return false;
1404
1497
  }
1405
1498
 
@@ -1493,5 +1586,9 @@ module.exports = {
1493
1586
  */
1494
1587
  hasOctalEscapeSequence(rawString) {
1495
1588
  return OCTAL_ESCAPE_PATTERN.test(rawString);
1496
- }
1589
+ },
1590
+
1591
+ isLogicalExpression,
1592
+ isCoalesceExpression,
1593
+ isMixedLogicalAndCoalesceExpressions
1497
1594
  };
@@ -305,6 +305,7 @@ class SourceCode extends TokenStore {
305
305
  * @returns {Object} An object containing a leading and trailing array
306
306
  * of comments indexed by their position.
307
307
  * @public
308
+ * @deprecated replaced by getCommentsBefore(), getCommentsAfter(), and getCommentsInside().
308
309
  */
309
310
  getComments(node) {
310
311
  if (this._commentCache.has(node)) {
@@ -2,4 +2,4 @@ ESLint couldn't find the config "<%- configName %>" to extend from. Please check
2
2
 
3
3
  The config "<%- configName %>" was referenced from the config file in "<%- importerName %>".
4
4
 
5
- If you still have problems, please stop by https://gitter.im/eslint/eslint to chat with the team.
5
+ If you still have problems, please stop by https://eslint.org/chat to chat with the team.
@@ -4,4 +4,4 @@ ESLint couldn't find a configuration file. To set up a configuration file for th
4
4
 
5
5
  ESLint looked for configuration files in <%= directoryPath %> and its ancestors. If it found none, it then looked in your home directory.
6
6
 
7
- If you think you already have a configuration file or if you need more help, please stop by the ESLint chat room: https://gitter.im/eslint/eslint
7
+ If you think you already have a configuration file or if you need more help, please stop by the ESLint chat room: https://eslint.org/chat
@@ -4,4 +4,4 @@ ESLint couldn't determine the plugin "<%- pluginId %>" uniquely.
4
4
 
5
5
  Please remove the "plugins" setting from either config or remove either plugin installation.
6
6
 
7
- If you still can't figure out the problem, please stop by https://gitter.im/eslint/eslint to chat with the team.
7
+ If you still can't figure out the problem, please stop by https://eslint.org/chat to chat with the team.