eslint 7.1.0 → 7.4.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 (48) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +12 -7
  3. package/lib/cli-engine/cli-engine.js +2 -2
  4. package/lib/cli-engine/config-array-factory.js +1 -1
  5. package/lib/init/config-initializer.js +91 -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/rule-tester/rule-tester.js +9 -0
  10. package/lib/rules/array-callback-return.js +21 -10
  11. package/lib/rules/arrow-body-style.js +2 -2
  12. package/lib/rules/arrow-parens.js +91 -108
  13. package/lib/rules/camelcase.js +47 -0
  14. package/lib/rules/comma-dangle.js +2 -1
  15. package/lib/rules/curly.js +8 -1
  16. package/lib/rules/func-call-spacing.js +18 -3
  17. package/lib/rules/{id-blacklist.js → id-denylist.js} +12 -12
  18. package/lib/rules/id-match.js +2 -1
  19. package/lib/rules/index.js +6 -1
  20. package/lib/rules/key-spacing.js +6 -2
  21. package/lib/rules/keyword-spacing.js +9 -2
  22. package/lib/rules/max-lines.js +34 -8
  23. package/lib/rules/multiline-ternary.js +44 -25
  24. package/lib/rules/no-control-regex.js +1 -1
  25. package/lib/rules/no-extra-boolean-cast.js +3 -0
  26. package/lib/rules/no-extra-parens.js +50 -4
  27. package/lib/rules/no-invalid-regexp.js +1 -1
  28. package/lib/rules/no-misleading-character-class.js +1 -1
  29. package/lib/rules/no-mixed-operators.js +3 -2
  30. package/lib/rules/no-mixed-spaces-and-tabs.js +14 -6
  31. package/lib/rules/no-promise-executor-return.js +121 -0
  32. package/lib/rules/no-regex-spaces.js +1 -1
  33. package/lib/rules/no-restricted-exports.js +6 -0
  34. package/lib/rules/no-unneeded-ternary.js +6 -4
  35. package/lib/rules/no-unreachable-loop.js +150 -0
  36. package/lib/rules/no-unused-expressions.js +1 -1
  37. package/lib/rules/no-unused-vars.js +5 -2
  38. package/lib/rules/no-useless-backreference.js +1 -1
  39. package/lib/rules/object-property-newline.js +1 -1
  40. package/lib/rules/operator-linebreak.js +2 -5
  41. package/lib/rules/padded-blocks.js +2 -1
  42. package/lib/rules/prefer-named-capture-group.js +1 -1
  43. package/lib/rules/prefer-regex-literals.js +66 -8
  44. package/lib/rules/quote-props.js +2 -2
  45. package/lib/rules/semi-spacing.js +1 -0
  46. package/lib/rules/template-tag-spacing.js +8 -2
  47. package/lib/rules/utils/ast-utils.js +55 -3
  48. package/package.json +6 -6
@@ -152,7 +152,7 @@ module.exports = {
152
152
  sourceCode.isSpaceBetweenTokens(prevToken, token)
153
153
  ) {
154
154
  context.report({
155
- loc: token.loc.start,
155
+ loc: { start: prevToken.loc.end, end: token.loc.start },
156
156
  messageId: "unexpectedBefore",
157
157
  data: token,
158
158
  fix(fixer) {
@@ -203,8 +203,9 @@ module.exports = {
203
203
  astUtils.isTokenOnSameLine(token, nextToken) &&
204
204
  sourceCode.isSpaceBetweenTokens(token, nextToken)
205
205
  ) {
206
+
206
207
  context.report({
207
- loc: token.loc.start,
208
+ loc: { start: token.loc.end, end: nextToken.loc.start },
208
209
  messageId: "unexpectedAfter",
209
210
  data: token,
210
211
  fix(fixer) {
@@ -442,6 +443,12 @@ module.exports = {
442
443
  checkSpacingAround(sourceCode.getTokenAfter(firstToken));
443
444
  }
444
445
 
446
+ if (node.type === "ExportAllDeclaration" && node.exported) {
447
+ const asToken = sourceCode.getTokenBefore(node.exported);
448
+
449
+ checkSpacingBefore(asToken, PREV_TOKEN_M);
450
+ }
451
+
445
452
  if (node.source) {
446
453
  const fromToken = sourceCode.getTokenBefore(node.source);
447
454
 
@@ -53,7 +53,8 @@ module.exports = {
53
53
  }
54
54
  ],
55
55
  messages: {
56
- exceed: "File has too many lines ({{actual}}). Maximum allowed is {{max}}."
56
+ exceed:
57
+ "File has too many lines ({{actual}}). Maximum allowed is {{max}}."
57
58
  }
58
59
  },
59
60
 
@@ -61,7 +62,10 @@ module.exports = {
61
62
  const option = context.options[0];
62
63
  let max = 300;
63
64
 
64
- if (typeof option === "object" && Object.prototype.hasOwnProperty.call(option, "max")) {
65
+ if (
66
+ typeof option === "object" &&
67
+ Object.prototype.hasOwnProperty.call(option, "max")
68
+ ) {
65
69
  max = option.max;
66
70
  } else if (typeof option === "number") {
67
71
  max = option;
@@ -94,7 +98,9 @@ module.exports = {
94
98
 
95
99
  token = comment;
96
100
  do {
97
- token = sourceCode.getTokenBefore(token, { includeComments: true });
101
+ token = sourceCode.getTokenBefore(token, {
102
+ includeComments: true
103
+ });
98
104
  } while (isCommentNodeType(token));
99
105
 
100
106
  if (token && astUtils.isTokenOnSameLine(token, comment)) {
@@ -103,7 +109,9 @@ module.exports = {
103
109
 
104
110
  token = comment;
105
111
  do {
106
- token = sourceCode.getTokenAfter(token, { includeComments: true });
112
+ token = sourceCode.getTokenAfter(token, {
113
+ includeComments: true
114
+ });
107
115
  } while (isCommentNodeType(token));
108
116
 
109
117
  if (token && astUtils.isTokenOnSameLine(comment, token)) {
@@ -118,7 +126,10 @@ module.exports = {
118
126
 
119
127
  return {
120
128
  "Program:exit"() {
121
- let lines = sourceCode.lines.map((text, i) => ({ lineNumber: i + 1, text }));
129
+ let lines = sourceCode.lines.map((text, i) => ({
130
+ lineNumber: i + 1,
131
+ text
132
+ }));
122
133
 
123
134
  if (skipBlankLines) {
124
135
  lines = lines.filter(l => l.text.trim() !== "");
@@ -127,14 +138,29 @@ module.exports = {
127
138
  if (skipComments) {
128
139
  const comments = sourceCode.getAllComments();
129
140
 
130
- const commentLines = lodash.flatten(comments.map(comment => getLinesWithoutCode(comment)));
141
+ const commentLines = lodash.flatten(
142
+ comments.map(comment => getLinesWithoutCode(comment))
143
+ );
131
144
 
132
- lines = lines.filter(l => !lodash.includes(commentLines, l.lineNumber));
145
+ lines = lines.filter(
146
+ l => !lodash.includes(commentLines, l.lineNumber)
147
+ );
133
148
  }
134
149
 
135
150
  if (lines.length > max) {
151
+ const loc = {
152
+ start: {
153
+ line: lines[max].lineNumber,
154
+ column: 0
155
+ },
156
+ end: {
157
+ line: sourceCode.lines.length,
158
+ column: lodash.last(sourceCode.lines).length
159
+ }
160
+ };
161
+
136
162
  context.report({
137
- loc: { line: 1, column: 0 },
163
+ loc,
138
164
  messageId: "exceed",
139
165
  data: {
140
166
  max,
@@ -39,25 +39,7 @@ module.exports = {
39
39
  const option = context.options[0];
40
40
  const multiline = option !== "never";
41
41
  const allowSingleLine = option === "always-multiline";
42
-
43
- //--------------------------------------------------------------------------
44
- // Helpers
45
- //--------------------------------------------------------------------------
46
-
47
- /**
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
52
- * @returns {void}
53
- * @private
54
- */
55
- function reportError(node, parentNode, expected) {
56
- context.report({
57
- node,
58
- messageId: `${expected ? "expected" : "unexpected"}${node === parentNode.test ? "TestCons" : "ConsAlt"}`
59
- });
60
- }
42
+ const sourceCode = context.getSourceCode();
61
43
 
62
44
  //--------------------------------------------------------------------------
63
45
  // Public
@@ -65,16 +47,39 @@ module.exports = {
65
47
 
66
48
  return {
67
49
  ConditionalExpression(node) {
68
- const areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(node.test, node.consequent);
69
- const areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(node.consequent, node.alternate);
50
+ const questionToken = sourceCode.getTokenAfter(node.test, astUtils.isNotClosingParenToken);
51
+ const colonToken = sourceCode.getTokenAfter(node.consequent, astUtils.isNotClosingParenToken);
52
+
53
+ const firstTokenOfTest = sourceCode.getFirstToken(node);
54
+ const lastTokenOfTest = sourceCode.getTokenBefore(questionToken);
55
+ const firstTokenOfConsequent = sourceCode.getTokenAfter(questionToken);
56
+ const lastTokenOfConsequent = sourceCode.getTokenBefore(colonToken);
57
+ const firstTokenOfAlternate = sourceCode.getTokenAfter(colonToken);
58
+
59
+ const areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, firstTokenOfConsequent);
60
+ const areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, firstTokenOfAlternate);
70
61
 
71
62
  if (!multiline) {
72
63
  if (!areTestAndConsequentOnSameLine) {
73
- reportError(node.test, node, false);
64
+ context.report({
65
+ node: node.test,
66
+ loc: {
67
+ start: firstTokenOfTest.loc.start,
68
+ end: lastTokenOfTest.loc.end
69
+ },
70
+ messageId: "unexpectedTestCons"
71
+ });
74
72
  }
75
73
 
76
74
  if (!areConsequentAndAlternateOnSameLine) {
77
- reportError(node.consequent, node, false);
75
+ context.report({
76
+ node: node.consequent,
77
+ loc: {
78
+ start: firstTokenOfConsequent.loc.start,
79
+ end: lastTokenOfConsequent.loc.end
80
+ },
81
+ messageId: "unexpectedConsAlt"
82
+ });
78
83
  }
79
84
  } else {
80
85
  if (allowSingleLine && node.loc.start.line === node.loc.end.line) {
@@ -82,11 +87,25 @@ module.exports = {
82
87
  }
83
88
 
84
89
  if (areTestAndConsequentOnSameLine) {
85
- reportError(node.test, node, true);
90
+ context.report({
91
+ node: node.test,
92
+ loc: {
93
+ start: firstTokenOfTest.loc.start,
94
+ end: lastTokenOfTest.loc.end
95
+ },
96
+ messageId: "expectedTestCons"
97
+ });
86
98
  }
87
99
 
88
100
  if (areConsequentAndAlternateOnSameLine) {
89
- reportError(node.consequent, node, true);
101
+ context.report({
102
+ node: node.consequent,
103
+ loc: {
104
+ start: firstTokenOfConsequent.loc.start,
105
+ end: lastTokenOfConsequent.loc.end
106
+ },
107
+ messageId: "expectedConsAlt"
108
+ });
90
109
  }
91
110
  }
92
111
  }
@@ -35,7 +35,7 @@ const collector = new (class {
35
35
  try {
36
36
  this._source = regexpStr;
37
37
  this._validator.validatePattern(regexpStr); // Call onCharacter hook
38
- } catch (err) {
38
+ } catch {
39
39
 
40
40
  // Ignore syntax errors in RegExp.
41
41
  }
@@ -172,6 +172,9 @@ module.exports = {
172
172
  case "UnaryExpression":
173
173
  return precedence(node) < precedence(parent);
174
174
  case "LogicalExpression":
175
+ if (astUtils.isMixedLogicalAndCoalesceExpressions(node, parent)) {
176
+ return true;
177
+ }
175
178
  if (previousNode === parent.left) {
176
179
  return precedence(node) < precedence(parent);
177
180
  }
@@ -51,7 +51,8 @@ module.exports = {
51
51
  ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] },
52
52
  enforceForArrowConditionals: { type: "boolean" },
53
53
  enforceForSequenceExpressions: { type: "boolean" },
54
- enforceForNewInMemberExpressions: { type: "boolean" }
54
+ enforceForNewInMemberExpressions: { type: "boolean" },
55
+ enforceForFunctionPrototypeMethods: { type: "boolean" }
55
56
  },
56
57
  additionalProperties: false
57
58
  }
@@ -83,12 +84,28 @@ module.exports = {
83
84
  context.options[1].enforceForSequenceExpressions === false;
84
85
  const IGNORE_NEW_IN_MEMBER_EXPR = ALL_NODES && context.options[1] &&
85
86
  context.options[1].enforceForNewInMemberExpressions === false;
87
+ const IGNORE_FUNCTION_PROTOTYPE_METHODS = ALL_NODES && context.options[1] &&
88
+ context.options[1].enforceForFunctionPrototypeMethods === false;
86
89
 
87
90
  const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
88
91
  const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
89
92
 
90
93
  let reportsBuffer;
91
94
 
95
+ /**
96
+ * Determines whether the given node is a `call` or `apply` method call, invoked directly on a `FunctionExpression` node.
97
+ * Example: function(){}.call()
98
+ * @param {ASTNode} node The node to be checked.
99
+ * @returns {boolean} True if the node is an immediate `call` or `apply` method call.
100
+ * @private
101
+ */
102
+ function isImmediateFunctionPrototypeMethodCall(node) {
103
+ return node.type === "CallExpression" &&
104
+ node.callee.type === "MemberExpression" &&
105
+ node.callee.object.type === "FunctionExpression" &&
106
+ ["call", "apply"].includes(astUtils.getStaticPropertyName(node.callee));
107
+ }
108
+
92
109
  /**
93
110
  * Determines if this rule should be enforced for a node given the current configuration.
94
111
  * @param {ASTNode} node The node to be checked.
@@ -125,6 +142,10 @@ module.exports = {
125
142
  return false;
126
143
  }
127
144
 
145
+ if (isImmediateFunctionPrototypeMethodCall(node) && IGNORE_FUNCTION_PROTOTYPE_METHODS) {
146
+ return false;
147
+ }
148
+
128
149
  return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
129
150
  }
130
151
 
@@ -478,6 +499,7 @@ module.exports = {
478
499
  if (!shouldSkipLeft && hasExcessParens(node.left)) {
479
500
  if (
480
501
  !(node.left.type === "UnaryExpression" && isExponentiation) &&
502
+ !astUtils.isMixedLogicalAndCoalesceExpressions(node.left, node) &&
481
503
  (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation)) ||
482
504
  isParenthesisedTwice(node.left)
483
505
  ) {
@@ -487,6 +509,7 @@ module.exports = {
487
509
 
488
510
  if (!shouldSkipRight && hasExcessParens(node.right)) {
489
511
  if (
512
+ !astUtils.isMixedLogicalAndCoalesceExpressions(node.right, node) &&
490
513
  (rightPrecedence > prec || (rightPrecedence === prec && isExponentiation)) ||
491
514
  isParenthesisedTwice(node.right)
492
515
  ) {
@@ -687,6 +710,20 @@ module.exports = {
687
710
  reportsBuffer.reports = reportsBuffer.reports.filter(r => r.node !== node);
688
711
  }
689
712
 
713
+ /**
714
+ * Checks whether a node is a MemberExpression at NewExpression's callee.
715
+ * @param {ASTNode} node node to check.
716
+ * @returns {boolean} True if the node is a MemberExpression at NewExpression's callee. false otherwise.
717
+ */
718
+ function isMemberExpInNewCallee(node) {
719
+ if (node.type === "MemberExpression") {
720
+ return node.parent.type === "NewExpression" && node.parent.callee === node
721
+ ? true
722
+ : node.parent.object === node && isMemberExpInNewCallee(node.parent);
723
+ }
724
+ return false;
725
+ }
726
+
690
727
  return {
691
728
  ArrayExpression(node) {
692
729
  node.elements
@@ -927,7 +964,16 @@ module.exports = {
927
964
  LogicalExpression: checkBinaryLogical,
928
965
 
929
966
  MemberExpression(node) {
930
- const nodeObjHasExcessParens = hasExcessParens(node.object);
967
+ const shouldAllowWrapOnce = isMemberExpInNewCallee(node) &&
968
+ doesMemberExpressionContainCallExpression(node);
969
+ const nodeObjHasExcessParens = shouldAllowWrapOnce
970
+ ? hasDoubleExcessParens(node.object)
971
+ : hasExcessParens(node.object) &&
972
+ !(
973
+ isImmediateFunctionPrototypeMethodCall(node.parent) &&
974
+ node.parent.callee === node &&
975
+ IGNORE_FUNCTION_PROTOTYPE_METHODS
976
+ );
931
977
 
932
978
  if (
933
979
  nodeObjHasExcessParens &&
@@ -946,8 +992,8 @@ module.exports = {
946
992
  }
947
993
 
948
994
  if (nodeObjHasExcessParens &&
949
- node.object.type === "CallExpression" &&
950
- node.parent.type !== "NewExpression") {
995
+ node.object.type === "CallExpression"
996
+ ) {
951
997
  report(node.object);
952
998
  }
953
999
 
@@ -93,7 +93,7 @@ module.exports = {
93
93
  try {
94
94
  validator.validateFlags(flags);
95
95
  return null;
96
- } catch (err) {
96
+ } catch {
97
97
  return `Invalid flags supplied to RegExp constructor '${flags}'`;
98
98
  }
99
99
  }
@@ -147,7 +147,7 @@ module.exports = {
147
147
  pattern.length,
148
148
  flags.includes("u")
149
149
  );
150
- } catch (e) {
150
+ } catch {
151
151
 
152
152
  // Ignore regular expressions with syntax errors
153
153
  return;
@@ -21,13 +21,15 @@ const COMPARISON_OPERATORS = ["==", "!=", "===", "!==", ">", ">=", "<", "<="];
21
21
  const LOGICAL_OPERATORS = ["&&", "||"];
22
22
  const RELATIONAL_OPERATORS = ["in", "instanceof"];
23
23
  const TERNARY_OPERATOR = ["?:"];
24
+ const COALESCE_OPERATOR = ["??"];
24
25
  const ALL_OPERATORS = [].concat(
25
26
  ARITHMETIC_OPERATORS,
26
27
  BITWISE_OPERATORS,
27
28
  COMPARISON_OPERATORS,
28
29
  LOGICAL_OPERATORS,
29
30
  RELATIONAL_OPERATORS,
30
- TERNARY_OPERATOR
31
+ TERNARY_OPERATOR,
32
+ COALESCE_OPERATOR
31
33
  );
32
34
  const DEFAULT_GROUPS = [
33
35
  ARITHMETIC_OPERATORS,
@@ -236,7 +238,6 @@ module.exports = {
236
238
  return {
237
239
  BinaryExpression: check,
238
240
  LogicalExpression: check
239
-
240
241
  };
241
242
  }
242
243
  };
@@ -67,7 +67,7 @@ module.exports = {
67
67
  * or the reverse before non-tab/-space
68
68
  * characters begin.
69
69
  */
70
- let regex = /^(?=[\t ]*(\t | \t))/u;
70
+ let regex = /^(?=( +|\t+))\1(?:\t| )/u;
71
71
 
72
72
  if (smartTabs) {
73
73
 
@@ -75,19 +75,27 @@ module.exports = {
75
75
  * At least one space followed by a tab
76
76
  * before non-tab/-space characters begin.
77
77
  */
78
- regex = /^(?=[\t ]* \t)/u;
78
+ regex = /^(?=(\t*))\1(?=( +))\2\t/u;
79
79
  }
80
80
 
81
81
  lines.forEach((line, i) => {
82
82
  const match = regex.exec(line);
83
83
 
84
84
  if (match) {
85
- const lineNumber = i + 1,
86
- column = match.index + 1,
87
- loc = { line: lineNumber, column };
85
+ const lineNumber = i + 1;
86
+ const loc = {
87
+ start: {
88
+ line: lineNumber,
89
+ column: match[0].length - 2
90
+ },
91
+ end: {
92
+ line: lineNumber,
93
+ column: match[0].length
94
+ }
95
+ };
88
96
 
89
97
  if (!ignoredCommentLines.has(lineNumber)) {
90
- const containingNode = sourceCode.getNodeByRangeIndex(sourceCode.getIndexFromLoc(loc));
98
+ const containingNode = sourceCode.getNodeByRangeIndex(sourceCode.getIndexFromLoc(loc.start));
91
99
 
92
100
  if (!(containingNode && ["Literal", "TemplateElement"].includes(containingNode.type))) {
93
101
  context.report({
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @fileoverview Rule to disallow returning values from Promise executor functions
3
+ * @author Milos Djermanovic
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const { findVariable } = require("eslint-utils");
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Helpers
16
+ //------------------------------------------------------------------------------
17
+
18
+ const functionTypesToCheck = new Set(["ArrowFunctionExpression", "FunctionExpression"]);
19
+
20
+ /**
21
+ * Determines whether the given identifier node is a reference to a global variable.
22
+ * @param {ASTNode} node `Identifier` node to check.
23
+ * @param {Scope} scope Scope to which the node belongs.
24
+ * @returns {boolean} True if the identifier is a reference to a global variable.
25
+ */
26
+ function isGlobalReference(node, scope) {
27
+ const variable = findVariable(scope, node);
28
+
29
+ return variable !== null && variable.scope.type === "global" && variable.defs.length === 0;
30
+ }
31
+
32
+ /**
33
+ * Finds function's outer scope.
34
+ * @param {Scope} scope Function's own scope.
35
+ * @returns {Scope} Function's outer scope.
36
+ */
37
+ function getOuterScope(scope) {
38
+ const upper = scope.upper;
39
+
40
+ if (upper.type === "function-expression-name") {
41
+ return upper.upper;
42
+ }
43
+ return upper;
44
+ }
45
+
46
+ /**
47
+ * Determines whether the given function node is used as a Promise executor.
48
+ * @param {ASTNode} node The node to check.
49
+ * @param {Scope} scope Function's own scope.
50
+ * @returns {boolean} `true` if the node is a Promise executor.
51
+ */
52
+ function isPromiseExecutor(node, scope) {
53
+ const parent = node.parent;
54
+
55
+ return parent.type === "NewExpression" &&
56
+ parent.arguments[0] === node &&
57
+ parent.callee.type === "Identifier" &&
58
+ parent.callee.name === "Promise" &&
59
+ isGlobalReference(parent.callee, getOuterScope(scope));
60
+ }
61
+
62
+ //------------------------------------------------------------------------------
63
+ // Rule Definition
64
+ //------------------------------------------------------------------------------
65
+
66
+ module.exports = {
67
+ meta: {
68
+ type: "problem",
69
+
70
+ docs: {
71
+ description: "disallow returning values from Promise executor functions",
72
+ category: "Possible Errors",
73
+ recommended: false,
74
+ url: "https://eslint.org/docs/rules/no-promise-executor-return"
75
+ },
76
+
77
+ schema: [],
78
+
79
+ messages: {
80
+ returnsValue: "Return values from promise executor functions cannot be read."
81
+ }
82
+ },
83
+
84
+ create(context) {
85
+
86
+ let funcInfo = null;
87
+
88
+ /**
89
+ * Reports the given node.
90
+ * @param {ASTNode} node Node to report.
91
+ * @returns {void}
92
+ */
93
+ function report(node) {
94
+ context.report({ node, messageId: "returnsValue" });
95
+ }
96
+
97
+ return {
98
+
99
+ onCodePathStart(_, node) {
100
+ funcInfo = {
101
+ upper: funcInfo,
102
+ shouldCheck: functionTypesToCheck.has(node.type) && isPromiseExecutor(node, context.getScope())
103
+ };
104
+
105
+ if (funcInfo.shouldCheck && node.type === "ArrowFunctionExpression" && node.expression) {
106
+ report(node.body);
107
+ }
108
+ },
109
+
110
+ onCodePathEnd() {
111
+ funcInfo = funcInfo.upper;
112
+ },
113
+
114
+ ReturnStatement(node) {
115
+ if (funcInfo.shouldCheck && node.argument) {
116
+ report(node);
117
+ }
118
+ }
119
+ };
120
+ }
121
+ };
@@ -76,7 +76,7 @@ module.exports = {
76
76
 
77
77
  try {
78
78
  regExpAST = regExpParser.parsePattern(pattern, 0, pattern.length, flags.includes("u"));
79
- } catch (e) {
79
+ } catch {
80
80
 
81
81
  // Ignore regular expressions with syntax errors
82
82
  return;
@@ -61,6 +61,12 @@ module.exports = {
61
61
  }
62
62
 
63
63
  return {
64
+ ExportAllDeclaration(node) {
65
+ if (node.exported) {
66
+ checkExportedName(node.exported);
67
+ }
68
+ },
69
+
64
70
  ExportNamedDeclaration(node) {
65
71
  const declaration = node.declaration;
66
72
 
@@ -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);