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.
- package/README.md +51 -45
- package/bin/eslint.js +2 -4
- package/conf/globals.js +6 -1
- package/conf/rule-type-list.json +2 -2
- package/lib/cli-engine/file-enumerator.js +4 -2
- package/lib/cli-engine/formatters/formatters-meta.json +46 -0
- package/lib/cli-engine/formatters/html.js +76 -51
- package/lib/cli.js +163 -40
- package/lib/config/default-config.js +2 -2
- package/lib/config/flat-config-array.js +1 -1
- package/lib/eslint/eslint-helpers.js +409 -87
- package/lib/eslint/eslint.js +5 -2
- package/lib/eslint/flat-eslint.js +113 -110
- package/lib/linter/code-path-analysis/code-path-segment.js +2 -2
- package/lib/linter/code-path-analysis/code-path-state.js +7 -7
- package/lib/linter/code-path-analysis/debug-helpers.js +3 -3
- package/lib/linter/code-path-analysis/id-generator.js +2 -2
- package/lib/linter/config-comment-parser.js +1 -2
- package/lib/linter/linter.js +17 -7
- package/lib/linter/timing.js +4 -4
- package/lib/options.js +293 -239
- package/lib/rule-tester/flat-rule-tester.js +13 -11
- package/lib/rule-tester/rule-tester.js +15 -11
- package/lib/rules/array-callback-return.js +2 -2
- package/lib/rules/comma-dangle.js +3 -3
- package/lib/rules/for-direction.js +1 -1
- package/lib/rules/func-name-matching.js +2 -2
- package/lib/rules/getter-return.js +14 -8
- package/lib/rules/global-require.js +2 -1
- package/lib/rules/id-length.js +43 -2
- package/lib/rules/indent-legacy.js +4 -4
- package/lib/rules/indent.js +23 -15
- package/lib/rules/index.js +3 -0
- package/lib/rules/key-spacing.js +50 -38
- package/lib/rules/lines-around-comment.js +2 -2
- package/lib/rules/logical-assignment-operators.js +474 -0
- package/lib/rules/multiline-ternary.js +2 -2
- package/lib/rules/new-cap.js +2 -2
- package/lib/rules/no-else-return.js +1 -1
- package/lib/rules/no-empty-static-block.js +47 -0
- package/lib/rules/no-empty.js +19 -2
- package/lib/rules/no-extra-boolean-cast.js +1 -1
- package/lib/rules/no-extra-parens.js +18 -3
- package/lib/rules/no-fallthrough.js +26 -5
- package/lib/rules/no-implicit-coercion.js +20 -1
- package/lib/rules/no-implicit-globals.js +5 -0
- package/lib/rules/no-invalid-regexp.js +40 -18
- package/lib/rules/no-labels.js +1 -1
- package/lib/rules/no-lone-blocks.js +1 -1
- package/lib/rules/no-loss-of-precision.js +2 -2
- package/lib/rules/no-magic-numbers.js +18 -1
- package/lib/rules/no-misleading-character-class.js +4 -4
- package/lib/rules/no-new-native-nonconstructor.js +64 -0
- package/lib/rules/no-obj-calls.js +1 -1
- package/lib/rules/no-restricted-exports.js +106 -10
- package/lib/rules/no-return-await.js +28 -1
- package/lib/rules/no-underscore-dangle.js +36 -11
- package/lib/rules/no-unneeded-ternary.js +1 -1
- package/lib/rules/no-use-before-define.js +1 -1
- package/lib/rules/no-useless-computed-key.js +1 -1
- package/lib/rules/no-var.js +2 -2
- package/lib/rules/no-warning-comments.js +24 -5
- package/lib/rules/padded-blocks.js +1 -1
- package/lib/rules/prefer-arrow-callback.js +4 -3
- package/lib/rules/prefer-const.js +13 -1
- package/lib/rules/prefer-named-capture-group.js +71 -6
- package/lib/rules/prefer-object-spread.js +1 -1
- package/lib/rules/prefer-regex-literals.js +147 -32
- package/lib/rules/prefer-rest-params.js +1 -1
- package/lib/rules/require-yield.js +0 -1
- package/lib/rules/strict.js +1 -1
- package/lib/rules/utils/ast-utils.js +10 -4
- package/lib/shared/directives.js +15 -0
- package/lib/shared/logging.js +1 -1
- package/lib/shared/runtime-info.js +1 -1
- package/lib/shared/traverser.js +1 -1
- package/lib/shared/types.js +15 -2
- package/lib/source-code/token-store/cursor.js +1 -1
- package/messages/print-config-with-directory-path.js +1 -1
- 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 &&
|
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 &&
|
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
|
-
|
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) &&
|
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)})`;
|
@@ -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"
|
142
|
+
if (node.callee.type !== "Identifier" || node.callee.name !== "RegExp") {
|
126
143
|
return;
|
127
144
|
}
|
128
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
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
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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
|
-
|
149
|
-
node,
|
150
|
-
messageId: "regexMessage",
|
151
|
-
data: { message }
|
152
|
-
});
|
174
|
+
report(node, message);
|
153
175
|
}
|
154
176
|
}
|
155
177
|
};
|
package/lib/rules/no-labels.js
CHANGED
@@ -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.
|
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
|
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
|
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.
|
196
|
+
const { ecmaVersion } = context.languageOptions;
|
197
197
|
|
198
|
-
// ecmaVersion
|
199
|
-
if (
|
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
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
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
|
|