eslint 8.22.0 → 8.33.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 (80) hide show
  1. package/README.md +51 -45
  2. package/bin/eslint.js +2 -4
  3. package/conf/globals.js +6 -1
  4. package/conf/rule-type-list.json +2 -2
  5. package/lib/cli-engine/file-enumerator.js +4 -2
  6. package/lib/cli-engine/formatters/formatters-meta.json +46 -0
  7. package/lib/cli-engine/formatters/html.js +76 -51
  8. package/lib/cli.js +163 -40
  9. package/lib/config/default-config.js +2 -2
  10. package/lib/config/flat-config-array.js +1 -1
  11. package/lib/eslint/eslint-helpers.js +409 -87
  12. package/lib/eslint/eslint.js +5 -2
  13. package/lib/eslint/flat-eslint.js +113 -110
  14. package/lib/linter/code-path-analysis/code-path-segment.js +2 -2
  15. package/lib/linter/code-path-analysis/code-path-state.js +7 -7
  16. package/lib/linter/code-path-analysis/debug-helpers.js +3 -3
  17. package/lib/linter/code-path-analysis/id-generator.js +2 -2
  18. package/lib/linter/config-comment-parser.js +1 -2
  19. package/lib/linter/linter.js +17 -7
  20. package/lib/linter/timing.js +4 -4
  21. package/lib/options.js +293 -239
  22. package/lib/rule-tester/flat-rule-tester.js +13 -11
  23. package/lib/rule-tester/rule-tester.js +15 -11
  24. package/lib/rules/array-callback-return.js +2 -2
  25. package/lib/rules/comma-dangle.js +3 -3
  26. package/lib/rules/for-direction.js +1 -1
  27. package/lib/rules/func-name-matching.js +2 -2
  28. package/lib/rules/getter-return.js +14 -8
  29. package/lib/rules/global-require.js +2 -1
  30. package/lib/rules/id-length.js +43 -2
  31. package/lib/rules/indent-legacy.js +4 -4
  32. package/lib/rules/indent.js +23 -15
  33. package/lib/rules/index.js +3 -0
  34. package/lib/rules/key-spacing.js +50 -38
  35. package/lib/rules/lines-around-comment.js +2 -2
  36. package/lib/rules/logical-assignment-operators.js +474 -0
  37. package/lib/rules/multiline-ternary.js +2 -2
  38. package/lib/rules/new-cap.js +2 -2
  39. package/lib/rules/no-else-return.js +1 -1
  40. package/lib/rules/no-empty-static-block.js +47 -0
  41. package/lib/rules/no-empty.js +19 -2
  42. package/lib/rules/no-extra-boolean-cast.js +1 -1
  43. package/lib/rules/no-extra-parens.js +18 -3
  44. package/lib/rules/no-fallthrough.js +26 -5
  45. package/lib/rules/no-implicit-coercion.js +20 -1
  46. package/lib/rules/no-implicit-globals.js +5 -0
  47. package/lib/rules/no-invalid-regexp.js +40 -18
  48. package/lib/rules/no-labels.js +1 -1
  49. package/lib/rules/no-lone-blocks.js +1 -1
  50. package/lib/rules/no-loss-of-precision.js +2 -2
  51. package/lib/rules/no-magic-numbers.js +18 -1
  52. package/lib/rules/no-misleading-character-class.js +4 -4
  53. package/lib/rules/no-new-native-nonconstructor.js +64 -0
  54. package/lib/rules/no-obj-calls.js +1 -1
  55. package/lib/rules/no-restricted-exports.js +106 -10
  56. package/lib/rules/no-return-await.js +28 -1
  57. package/lib/rules/no-underscore-dangle.js +36 -11
  58. package/lib/rules/no-unneeded-ternary.js +1 -1
  59. package/lib/rules/no-use-before-define.js +1 -1
  60. package/lib/rules/no-useless-computed-key.js +1 -1
  61. package/lib/rules/no-var.js +2 -2
  62. package/lib/rules/no-warning-comments.js +24 -5
  63. package/lib/rules/padded-blocks.js +1 -1
  64. package/lib/rules/prefer-arrow-callback.js +4 -3
  65. package/lib/rules/prefer-const.js +13 -1
  66. package/lib/rules/prefer-named-capture-group.js +71 -6
  67. package/lib/rules/prefer-object-spread.js +1 -1
  68. package/lib/rules/prefer-regex-literals.js +147 -32
  69. package/lib/rules/prefer-rest-params.js +1 -1
  70. package/lib/rules/require-yield.js +0 -1
  71. package/lib/rules/strict.js +1 -1
  72. package/lib/rules/utils/ast-utils.js +10 -4
  73. package/lib/shared/directives.js +15 -0
  74. package/lib/shared/logging.js +1 -1
  75. package/lib/shared/runtime-info.js +1 -1
  76. package/lib/shared/traverser.js +1 -1
  77. package/lib/shared/types.js +15 -2
  78. package/lib/source-code/token-store/cursor.js +1 -1
  79. package/messages/print-config-with-directory-path.js +1 -1
  80. package/package.json +27 -27
@@ -4,12 +4,28 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ const { directivesPattern } = require("../shared/directives");
12
+
7
13
  //------------------------------------------------------------------------------
8
14
  // Helpers
9
15
  //------------------------------------------------------------------------------
10
16
 
11
17
  const DEFAULT_FALLTHROUGH_COMMENT = /falls?\s?through/iu;
12
18
 
19
+ /**
20
+ * Checks whether or not a given comment string is really a fallthrough comment and not an ESLint directive.
21
+ * @param {string} comment The comment string to check.
22
+ * @param {RegExp} fallthroughCommentPattern The regular expression used for checking for fallthrough comments.
23
+ * @returns {boolean} `true` if the comment string is truly a fallthrough comment.
24
+ */
25
+ function isFallThroughComment(comment, fallthroughCommentPattern) {
26
+ return fallthroughCommentPattern.test(comment) && !directivesPattern.test(comment.trim());
27
+ }
28
+
13
29
  /**
14
30
  * Checks whether or not a given case has a fallthrough comment.
15
31
  * @param {ASTNode} caseWhichFallsThrough SwitchCase node which falls through.
@@ -25,14 +41,14 @@ function hasFallthroughComment(caseWhichFallsThrough, subsequentCase, context, f
25
41
  const trailingCloseBrace = sourceCode.getLastToken(caseWhichFallsThrough.consequent[0]);
26
42
  const commentInBlock = sourceCode.getCommentsBefore(trailingCloseBrace).pop();
27
43
 
28
- if (commentInBlock && fallthroughCommentPattern.test(commentInBlock.value)) {
44
+ if (commentInBlock && isFallThroughComment(commentInBlock.value, fallthroughCommentPattern)) {
29
45
  return true;
30
46
  }
31
47
  }
32
48
 
33
49
  const comment = sourceCode.getCommentsBefore(subsequentCase).pop();
34
50
 
35
- return Boolean(comment && fallthroughCommentPattern.test(comment.value));
51
+ return Boolean(comment && isFallThroughComment(comment.value, fallthroughCommentPattern));
36
52
  }
37
53
 
38
54
  /**
@@ -76,6 +92,10 @@ module.exports = {
76
92
  commentPattern: {
77
93
  type: "string",
78
94
  default: ""
95
+ },
96
+ allowEmptyCase: {
97
+ type: "boolean",
98
+ default: false
79
99
  }
80
100
  },
81
101
  additionalProperties: false
@@ -91,6 +111,7 @@ module.exports = {
91
111
  const options = context.options[0] || {};
92
112
  let currentCodePath = null;
93
113
  const sourceCode = context.getSourceCode();
114
+ const allowEmptyCase = options.allowEmptyCase || false;
94
115
 
95
116
  /*
96
117
  * We need to use leading comments of the next SwitchCase node because
@@ -104,7 +125,6 @@ module.exports = {
104
125
  } else {
105
126
  fallthroughCommentPattern = DEFAULT_FALLTHROUGH_COMMENT;
106
127
  }
107
-
108
128
  return {
109
129
  onCodePathStart(codePath) {
110
130
  currentCodePath = codePath;
@@ -119,7 +139,8 @@ module.exports = {
119
139
  * Checks whether or not there is a fallthrough comment.
120
140
  * And reports the previous fallthrough node if that does not exist.
121
141
  */
122
- if (fallthroughCase && !hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern)) {
142
+
143
+ if (fallthroughCase && (!hasFallthroughComment(fallthroughCase, node, context, fallthroughCommentPattern))) {
123
144
  context.report({
124
145
  messageId: node.test ? "case" : "default",
125
146
  node
@@ -137,7 +158,7 @@ module.exports = {
137
158
  * And allows empty cases and the last case.
138
159
  */
139
160
  if (currentCodePath.currentSegments.some(isReachable) &&
140
- (node.consequent.length > 0 || hasBlankLinesBetween(node, nextToken)) &&
161
+ (node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) &&
141
162
  node.parent.cases[node.parent.cases.length - 1] !== node) {
142
163
  fallthroughCase = node;
143
164
  }
@@ -71,6 +71,24 @@ function isMultiplyByOne(node) {
71
71
  );
72
72
  }
73
73
 
74
+ /**
75
+ * Checks whether the given node logically represents multiplication by a fraction of `1`.
76
+ * For example, `a * 1` in `a * 1 / b` is technically multiplication by `1`, but the
77
+ * whole expression can be logically interpreted as `a * (1 / b)` rather than `(a * 1) / b`.
78
+ * @param {BinaryExpression} node A BinaryExpression node to check.
79
+ * @param {SourceCode} sourceCode The source code object.
80
+ * @returns {boolean} Whether or not the node is a multiplying by a fraction of `1`.
81
+ */
82
+ function isMultiplyByFractionOfOne(node, sourceCode) {
83
+ return node.type === "BinaryExpression" &&
84
+ node.operator === "*" &&
85
+ (node.right.type === "Literal" && node.right.value === 1) &&
86
+ node.parent.type === "BinaryExpression" &&
87
+ node.parent.operator === "/" &&
88
+ node.parent.left === node &&
89
+ !astUtils.isParenthesised(sourceCode, node);
90
+ }
91
+
74
92
  /**
75
93
  * Checks whether the result of a node is numeric or not
76
94
  * @param {ASTNode} node The node to test
@@ -290,7 +308,8 @@ module.exports = {
290
308
 
291
309
  // 1 * foo
292
310
  operatorAllowed = options.allow.includes("*");
293
- const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && getNonNumericOperand(node);
311
+ const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && !isMultiplyByFractionOfOne(node, sourceCode) &&
312
+ getNonNumericOperand(node);
294
313
 
295
314
  if (nonNumericOperand) {
296
315
  const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`;
@@ -77,6 +77,11 @@ module.exports = {
77
77
  return;
78
78
  }
79
79
 
80
+ // Variables exported by "exported" block comments
81
+ if (variable.eslintExported) {
82
+ return;
83
+ }
84
+
80
85
  variable.defs.forEach(def => {
81
86
  const defNode = def.node;
82
87
 
@@ -59,6 +59,20 @@ module.exports = {
59
59
  }
60
60
  }
61
61
 
62
+ /**
63
+ * Reports error with the provided message.
64
+ * @param {ASTNode} node The node holding the invalid RegExp
65
+ * @param {string} message The message to report.
66
+ * @returns {void}
67
+ */
68
+ function report(node, message) {
69
+ context.report({
70
+ node,
71
+ messageId: "regexMessage",
72
+ data: { message }
73
+ });
74
+ }
75
+
62
76
  /**
63
77
  * Check if node is a string
64
78
  * @param {ASTNode} node node to evaluate
@@ -108,10 +122,13 @@ module.exports = {
108
122
 
109
123
  /**
110
124
  * Check syntax error in a given flags.
111
- * @param {string} flags The RegExp flags to validate.
125
+ * @param {string|null} flags The RegExp flags to validate.
112
126
  * @returns {string|null} The syntax error.
113
127
  */
114
128
  function validateRegExpFlags(flags) {
129
+ if (!flags) {
130
+ return null;
131
+ }
115
132
  try {
116
133
  validator.validateFlags(flags);
117
134
  return null;
@@ -122,34 +139,39 @@ module.exports = {
122
139
 
123
140
  return {
124
141
  "CallExpression, NewExpression"(node) {
125
- if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp" || !isString(node.arguments[0])) {
142
+ if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp") {
126
143
  return;
127
144
  }
128
- const pattern = node.arguments[0].value;
145
+
129
146
  let flags = getFlags(node);
130
147
 
131
148
  if (flags && allowedFlags) {
132
149
  flags = flags.replace(allowedFlags, "");
133
150
  }
134
151
 
135
- const message =
136
- (
137
- flags && validateRegExpFlags(flags)
138
- ) ||
139
- (
152
+ let message = validateRegExpFlags(flags);
153
+
154
+ if (message) {
155
+ report(node, message);
156
+ return;
157
+ }
158
+
159
+ if (!isString(node.arguments[0])) {
160
+ return;
161
+ }
162
+
163
+ const pattern = node.arguments[0].value;
164
+
165
+ message = (
140
166
 
141
- // If flags are unknown, report the regex only if its pattern is invalid both with and without the "u" flag
142
- flags === null
143
- ? validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
144
- : validateRegExpPattern(pattern, flags.includes("u"))
145
- );
167
+ // If flags are unknown, report the regex only if its pattern is invalid both with and without the "u" flag
168
+ flags === null
169
+ ? validateRegExpPattern(pattern, true) && validateRegExpPattern(pattern, false)
170
+ : validateRegExpPattern(pattern, flags.includes("u"))
171
+ );
146
172
 
147
173
  if (message) {
148
- context.report({
149
- node,
150
- messageId: "regexMessage",
151
- data: { message }
152
- });
174
+ report(node, message);
153
175
  }
154
176
  }
155
177
  };
@@ -98,7 +98,7 @@ module.exports = {
98
98
  info = info.upper;
99
99
  }
100
100
 
101
- /* istanbul ignore next: syntax error */
101
+ /* c8 ignore next */
102
102
  return "other";
103
103
  }
104
104
 
@@ -91,7 +91,7 @@ module.exports = {
91
91
  };
92
92
 
93
93
  // ES6: report blocks without block-level bindings, or that's only child of another block
94
- if (context.parserOptions.ecmaVersion >= 6) {
94
+ if (context.languageOptions.ecmaVersion >= 2015) {
95
95
  ruleDef = {
96
96
  BlockStatement(node) {
97
97
  if (isLoneBlock(node)) {
@@ -105,7 +105,7 @@ module.exports = {
105
105
  }
106
106
 
107
107
  /**
108
- * Converts an integer to to an object containing the integer's coefficient and order of magnitude
108
+ * Converts an integer to an object containing the integer's coefficient and order of magnitude
109
109
  * @param {string} stringInteger the string representation of the integer being converted
110
110
  * @returns {Object} the object containing the integer's coefficient and order of magnitude
111
111
  */
@@ -120,7 +120,7 @@ module.exports = {
120
120
 
121
121
  /**
122
122
  *
123
- * Converts a float to to an object containing the floats's coefficient and order of magnitude
123
+ * Converts a float to an object containing the floats's coefficient and order of magnitude
124
124
  * @param {string} stringFloat the string representation of the float being converted
125
125
  * @returns {Object} the object containing the integer's coefficient and order of magnitude
126
126
  */
@@ -65,6 +65,10 @@ module.exports = {
65
65
  ignoreDefaultValues: {
66
66
  type: "boolean",
67
67
  default: false
68
+ },
69
+ ignoreClassFieldInitialValues: {
70
+ type: "boolean",
71
+ default: false
68
72
  }
69
73
  },
70
74
  additionalProperties: false
@@ -82,7 +86,8 @@ module.exports = {
82
86
  enforceConst = !!config.enforceConst,
83
87
  ignore = new Set((config.ignore || []).map(normalizeIgnoreValue)),
84
88
  ignoreArrayIndexes = !!config.ignoreArrayIndexes,
85
- ignoreDefaultValues = !!config.ignoreDefaultValues;
89
+ ignoreDefaultValues = !!config.ignoreDefaultValues,
90
+ ignoreClassFieldInitialValues = !!config.ignoreClassFieldInitialValues;
86
91
 
87
92
  const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"];
88
93
 
@@ -106,6 +111,17 @@ module.exports = {
106
111
  return parent.type === "AssignmentPattern" && parent.right === fullNumberNode;
107
112
  }
108
113
 
114
+ /**
115
+ * Returns whether the number is the initial value of a class field.
116
+ * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
117
+ * @returns {boolean} true if the number is the initial value of a class field.
118
+ */
119
+ function isClassFieldInitialValue(fullNumberNode) {
120
+ const parent = fullNumberNode.parent;
121
+
122
+ return parent.type === "PropertyDefinition" && parent.value === fullNumberNode;
123
+ }
124
+
109
125
  /**
110
126
  * Returns whether the given node is used as a radix within parseInt() or Number.parseInt()
111
127
  * @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
@@ -194,6 +210,7 @@ module.exports = {
194
210
  if (
195
211
  isIgnoredValue(value) ||
196
212
  (ignoreDefaultValues && isDefaultValue(fullNumberNode)) ||
213
+ (ignoreClassFieldInitialValues && isClassFieldInitialValue(fullNumberNode)) ||
197
214
  isParseIntRadix(fullNumberNode) ||
198
215
  isJSXNumber(fullNumberNode) ||
199
216
  (ignoreArrayIndexes && isArrayIndex(fullNumberNode, value))
@@ -193,15 +193,15 @@ module.exports = {
193
193
  * ecmaVersion doesn't support the `u` flag.
194
194
  */
195
195
  function isValidWithUnicodeFlag(pattern) {
196
- const { ecmaVersion } = context.parserOptions;
196
+ const { ecmaVersion } = context.languageOptions;
197
197
 
198
- // ecmaVersion is unknown or it doesn't support the 'u' flag
199
- if (typeof ecmaVersion !== "number" || ecmaVersion <= 5) {
198
+ // ecmaVersion <= 5 doesn't support the 'u' flag
199
+ if (ecmaVersion <= 5) {
200
200
  return false;
201
201
  }
202
202
 
203
203
  const validator = new RegExpValidator({
204
- ecmaVersion: Math.min(ecmaVersion + 2009, REGEXPP_LATEST_ECMA_VERSION)
204
+ ecmaVersion: Math.min(ecmaVersion, REGEXPP_LATEST_ECMA_VERSION)
205
205
  });
206
206
 
207
207
  try {
@@ -0,0 +1,64 @@
1
+ /**
2
+ * @fileoverview Rule to disallow use of the new operator with global non-constructor functions
3
+ * @author Sosuke Suzuki
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Helpers
10
+ //------------------------------------------------------------------------------
11
+
12
+ const nonConstructorGlobalFunctionNames = ["Symbol", "BigInt"];
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Rule Definition
16
+ //------------------------------------------------------------------------------
17
+
18
+ /** @type {import('../shared/types').Rule} */
19
+ module.exports = {
20
+ meta: {
21
+ type: "problem",
22
+
23
+ docs: {
24
+ description: "Disallow `new` operators with global non-constructor functions",
25
+ recommended: false,
26
+ url: "https://eslint.org/docs/rules/no-new-native-nonconstructor"
27
+ },
28
+
29
+ schema: [],
30
+
31
+ messages: {
32
+ noNewNonconstructor: "`{{name}}` cannot be called as a constructor."
33
+ }
34
+ },
35
+
36
+ create(context) {
37
+
38
+ return {
39
+ "Program:exit"() {
40
+ const globalScope = context.getScope();
41
+
42
+ for (const nonConstructorName of nonConstructorGlobalFunctionNames) {
43
+ const variable = globalScope.set.get(nonConstructorName);
44
+
45
+ if (variable && variable.defs.length === 0) {
46
+ variable.references.forEach(ref => {
47
+ const node = ref.identifier;
48
+ const parent = node.parent;
49
+
50
+ if (parent && parent.type === "NewExpression" && parent.callee === node) {
51
+ context.report({
52
+ node,
53
+ messageId: "noNewNonconstructor",
54
+ data: { name: nonConstructorName }
55
+ });
56
+ }
57
+ });
58
+ }
59
+ }
60
+ }
61
+ };
62
+
63
+ }
64
+ };
@@ -16,7 +16,7 @@ const getPropertyName = require("./utils/ast-utils").getStaticPropertyName;
16
16
  // Helpers
17
17
  //------------------------------------------------------------------------------
18
18
 
19
- const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect"];
19
+ const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect", "Intl"];
20
20
 
21
21
  /**
22
22
  * Returns the name of the node to report
@@ -27,27 +27,78 @@ module.exports = {
27
27
  },
28
28
 
29
29
  schema: [{
30
- type: "object",
31
- properties: {
32
- restrictedNamedExports: {
33
- type: "array",
34
- items: {
35
- type: "string"
30
+ anyOf: [
31
+ {
32
+ type: "object",
33
+ properties: {
34
+ restrictedNamedExports: {
35
+ type: "array",
36
+ items: {
37
+ type: "string"
38
+ },
39
+ uniqueItems: true
40
+ }
36
41
  },
37
- uniqueItems: true
42
+ additionalProperties: false
43
+ },
44
+ {
45
+ type: "object",
46
+ properties: {
47
+ restrictedNamedExports: {
48
+ type: "array",
49
+ items: {
50
+ type: "string",
51
+ pattern: "^(?!default$)"
52
+ },
53
+ uniqueItems: true
54
+ },
55
+ restrictDefaultExports: {
56
+ type: "object",
57
+ properties: {
58
+
59
+ // Allow/Disallow `export default foo; export default 42; export default function foo() {}` format
60
+ direct: {
61
+ type: "boolean"
62
+ },
63
+
64
+ // Allow/Disallow `export { foo as default };` declarations
65
+ named: {
66
+ type: "boolean"
67
+ },
68
+
69
+ // Allow/Disallow `export { default } from "mod"; export { default as default } from "mod";` declarations
70
+ defaultFrom: {
71
+ type: "boolean"
72
+ },
73
+
74
+ // Allow/Disallow `export { foo as default } from "mod";` declarations
75
+ namedFrom: {
76
+ type: "boolean"
77
+ },
78
+
79
+ // Allow/Disallow `export * as default from "mod"`; declarations
80
+ namespaceFrom: {
81
+ type: "boolean"
82
+ }
83
+ },
84
+ additionalProperties: false
85
+ }
86
+ },
87
+ additionalProperties: false
38
88
  }
39
- },
40
- additionalProperties: false
89
+ ]
41
90
  }],
42
91
 
43
92
  messages: {
44
- restrictedNamed: "'{{name}}' is restricted from being used as an exported name."
93
+ restrictedNamed: "'{{name}}' is restricted from being used as an exported name.",
94
+ restrictedDefault: "Exporting 'default' is restricted."
45
95
  }
46
96
  },
47
97
 
48
98
  create(context) {
49
99
 
50
100
  const restrictedNames = new Set(context.options[0] && context.options[0].restrictedNamedExports);
101
+ const restrictDefaultExports = context.options[0] && context.options[0].restrictDefaultExports;
51
102
 
52
103
  /**
53
104
  * Checks and reports given exported name.
@@ -63,6 +114,42 @@ module.exports = {
63
114
  messageId: "restrictedNamed",
64
115
  data: { name }
65
116
  });
117
+ return;
118
+ }
119
+
120
+ if (name === "default") {
121
+ if (node.parent.type === "ExportAllDeclaration") {
122
+ if (restrictDefaultExports && restrictDefaultExports.namespaceFrom) {
123
+ context.report({
124
+ node,
125
+ messageId: "restrictedDefault"
126
+ });
127
+ }
128
+
129
+ } else { // ExportSpecifier
130
+ const isSourceSpecified = !!node.parent.parent.source;
131
+ const specifierLocalName = astUtils.getModuleExportName(node.parent.local);
132
+
133
+ if (!isSourceSpecified && restrictDefaultExports && restrictDefaultExports.named) {
134
+ context.report({
135
+ node,
136
+ messageId: "restrictedDefault"
137
+ });
138
+ return;
139
+ }
140
+
141
+ if (isSourceSpecified && restrictDefaultExports) {
142
+ if (
143
+ (specifierLocalName === "default" && restrictDefaultExports.defaultFrom) ||
144
+ (specifierLocalName !== "default" && restrictDefaultExports.namedFrom)
145
+ ) {
146
+ context.report({
147
+ node,
148
+ messageId: "restrictedDefault"
149
+ });
150
+ }
151
+ }
152
+ }
66
153
  }
67
154
  }
68
155
 
@@ -73,6 +160,15 @@ module.exports = {
73
160
  }
74
161
  },
75
162
 
163
+ ExportDefaultDeclaration(node) {
164
+ if (restrictDefaultExports && restrictDefaultExports.direct) {
165
+ context.report({
166
+ node,
167
+ messageId: "restrictedDefault"
168
+ });
169
+ }
170
+ },
171
+
76
172
  ExportNamedDeclaration(node) {
77
173
  const declaration = node.declaration;
78
174
 
@@ -13,6 +13,7 @@ const astUtils = require("./utils/ast-utils");
13
13
  /** @type {import('../shared/types').Rule} */
14
14
  module.exports = {
15
15
  meta: {
16
+ hasSuggestions: true,
16
17
  type: "suggestion",
17
18
 
18
19
  docs: {
@@ -29,6 +30,7 @@ module.exports = {
29
30
  ],
30
31
 
31
32
  messages: {
33
+ removeAwait: "Remove redundant `await`.",
32
34
  redundantUseOfAwait: "Redundant use of `await` on a return value."
33
35
  }
34
36
  },
@@ -44,7 +46,32 @@ module.exports = {
44
46
  context.report({
45
47
  node: context.getSourceCode().getFirstToken(node),
46
48
  loc: node.loc,
47
- messageId: "redundantUseOfAwait"
49
+ messageId: "redundantUseOfAwait",
50
+ suggest: [
51
+ {
52
+ messageId: "removeAwait",
53
+ fix(fixer) {
54
+ const sourceCode = context.getSourceCode();
55
+ const [awaitToken, tokenAfterAwait] = sourceCode.getFirstTokens(node, 2);
56
+
57
+ const areAwaitAndAwaitedExpressionOnTheSameLine = awaitToken.loc.start.line === tokenAfterAwait.loc.start.line;
58
+
59
+ if (!areAwaitAndAwaitedExpressionOnTheSameLine) {
60
+ return null;
61
+ }
62
+
63
+ const [startOfAwait, endOfAwait] = awaitToken.range;
64
+
65
+ const characterAfterAwait = sourceCode.text[endOfAwait];
66
+ const trimLength = characterAfterAwait === " " ? 1 : 0;
67
+
68
+ const range = [startOfAwait, endOfAwait + trimLength];
69
+
70
+ return fixer.removeRange(range);
71
+ }
72
+ }
73
+ ]
74
+
48
75
  });
49
76
  }
50
77