eslint 9.2.0 → 9.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.
- package/README.md +2 -2
- package/bin/eslint.js +1 -1
- package/lib/cli-engine/cli-engine.js +2 -2
- package/lib/cli-engine/file-enumerator.js +2 -2
- package/lib/cli-engine/lint-result-cache.js +2 -2
- package/lib/cli-engine/load-rules.js +2 -2
- package/lib/cli.js +5 -5
- package/lib/config/flat-config-array.js +1 -1
- package/lib/eslint/eslint-helpers.js +9 -9
- package/lib/eslint/eslint.js +6 -15
- package/lib/eslint/legacy-eslint.js +3 -3
- package/lib/linter/code-path-analysis/code-path-analyzer.js +1 -1
- package/lib/linter/code-path-analysis/fork-context.js +1 -1
- package/lib/linter/linter.js +1 -1
- package/lib/linter/report-translator.js +1 -1
- package/lib/rule-tester/rule-tester.js +4 -4
- package/lib/rules/func-style.js +45 -7
- package/lib/rules/line-comment-position.js +3 -0
- package/lib/rules/multiline-comment-style.js +3 -1
- package/lib/rules/no-constant-binary-expression.js +1 -1
- package/lib/rules/no-constructor-return.js +1 -1
- package/lib/rules/no-extra-boolean-cast.js +79 -27
- package/lib/rules/no-loop-func.js +161 -129
- package/lib/rules/no-misleading-character-class.js +111 -30
- package/lib/rules/no-restricted-exports.js +13 -2
- package/lib/rules/object-shorthand.js +21 -20
- package/lib/shared/runtime-info.js +2 -2
- package/lib/source-code/token-store/index.js +1 -1
- package/messages/config-file-missing.js +16 -0
- package/messages/eslintrc-incompat.js +22 -1
- package/package.json +6 -6
@@ -9,140 +9,16 @@
|
|
9
9
|
// Helpers
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
|
-
/**
|
13
|
-
* Gets the containing loop node of a specified node.
|
14
|
-
*
|
15
|
-
* We don't need to check nested functions, so this ignores those.
|
16
|
-
* `Scope.through` contains references of nested functions.
|
17
|
-
* @param {ASTNode} node An AST node to get.
|
18
|
-
* @returns {ASTNode|null} The containing loop node of the specified node, or
|
19
|
-
* `null`.
|
20
|
-
*/
|
21
|
-
function getContainingLoopNode(node) {
|
22
|
-
for (let currentNode = node; currentNode.parent; currentNode = currentNode.parent) {
|
23
|
-
const parent = currentNode.parent;
|
24
|
-
|
25
|
-
switch (parent.type) {
|
26
|
-
case "WhileStatement":
|
27
|
-
case "DoWhileStatement":
|
28
|
-
return parent;
|
29
|
-
|
30
|
-
case "ForStatement":
|
31
|
-
|
32
|
-
// `init` is outside of the loop.
|
33
|
-
if (parent.init !== currentNode) {
|
34
|
-
return parent;
|
35
|
-
}
|
36
|
-
break;
|
37
|
-
|
38
|
-
case "ForInStatement":
|
39
|
-
case "ForOfStatement":
|
40
|
-
|
41
|
-
// `right` is outside of the loop.
|
42
|
-
if (parent.right !== currentNode) {
|
43
|
-
return parent;
|
44
|
-
}
|
45
|
-
break;
|
46
|
-
|
47
|
-
case "ArrowFunctionExpression":
|
48
|
-
case "FunctionExpression":
|
49
|
-
case "FunctionDeclaration":
|
50
|
-
|
51
|
-
// We don't need to check nested functions.
|
52
|
-
return null;
|
53
|
-
|
54
|
-
default:
|
55
|
-
break;
|
56
|
-
}
|
57
|
-
}
|
58
|
-
|
59
|
-
return null;
|
60
|
-
}
|
61
12
|
|
62
13
|
/**
|
63
|
-
*
|
64
|
-
*
|
65
|
-
* @
|
66
|
-
* @param {ASTNode|null} excludedNode A node that the result node should not
|
67
|
-
* include.
|
68
|
-
* @returns {ASTNode} The most outer loop node.
|
14
|
+
* Identifies is a node is a FunctionExpression which is part of an IIFE
|
15
|
+
* @param {ASTNode} node Node to test
|
16
|
+
* @returns {boolean} True if it's an IIFE
|
69
17
|
*/
|
70
|
-
function
|
71
|
-
|
72
|
-
let retv = node;
|
73
|
-
let containingLoopNode = node;
|
74
|
-
|
75
|
-
while (containingLoopNode && containingLoopNode.range[0] >= border) {
|
76
|
-
retv = containingLoopNode;
|
77
|
-
containingLoopNode = getContainingLoopNode(containingLoopNode);
|
78
|
-
}
|
79
|
-
|
80
|
-
return retv;
|
18
|
+
function isIIFE(node) {
|
19
|
+
return (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
|
81
20
|
}
|
82
21
|
|
83
|
-
/**
|
84
|
-
* Checks whether a given reference which refers to an upper scope's variable is
|
85
|
-
* safe or not.
|
86
|
-
* @param {ASTNode} loopNode A containing loop node.
|
87
|
-
* @param {eslint-scope.Reference} reference A reference to check.
|
88
|
-
* @returns {boolean} `true` if the reference is safe or not.
|
89
|
-
*/
|
90
|
-
function isSafe(loopNode, reference) {
|
91
|
-
const variable = reference.resolved;
|
92
|
-
const definition = variable && variable.defs[0];
|
93
|
-
const declaration = definition && definition.parent;
|
94
|
-
const kind = (declaration && declaration.type === "VariableDeclaration")
|
95
|
-
? declaration.kind
|
96
|
-
: "";
|
97
|
-
|
98
|
-
// Variables which are declared by `const` is safe.
|
99
|
-
if (kind === "const") {
|
100
|
-
return true;
|
101
|
-
}
|
102
|
-
|
103
|
-
/*
|
104
|
-
* Variables which are declared by `let` in the loop is safe.
|
105
|
-
* It's a different instance from the next loop step's.
|
106
|
-
*/
|
107
|
-
if (kind === "let" &&
|
108
|
-
declaration.range[0] > loopNode.range[0] &&
|
109
|
-
declaration.range[1] < loopNode.range[1]
|
110
|
-
) {
|
111
|
-
return true;
|
112
|
-
}
|
113
|
-
|
114
|
-
/*
|
115
|
-
* WriteReferences which exist after this border are unsafe because those
|
116
|
-
* can modify the variable.
|
117
|
-
*/
|
118
|
-
const border = getTopLoopNode(
|
119
|
-
loopNode,
|
120
|
-
(kind === "let") ? declaration : null
|
121
|
-
).range[0];
|
122
|
-
|
123
|
-
/**
|
124
|
-
* Checks whether a given reference is safe or not.
|
125
|
-
* The reference is every reference of the upper scope's variable we are
|
126
|
-
* looking now.
|
127
|
-
*
|
128
|
-
* It's safe if the reference matches one of the following condition.
|
129
|
-
* - is readonly.
|
130
|
-
* - doesn't exist inside a local function and after the border.
|
131
|
-
* @param {eslint-scope.Reference} upperRef A reference to check.
|
132
|
-
* @returns {boolean} `true` if the reference is safe.
|
133
|
-
*/
|
134
|
-
function isSafeReference(upperRef) {
|
135
|
-
const id = upperRef.identifier;
|
136
|
-
|
137
|
-
return (
|
138
|
-
!upperRef.isWrite() ||
|
139
|
-
variable.scope.variableScope === upperRef.from.variableScope &&
|
140
|
-
id.range[0] < border
|
141
|
-
);
|
142
|
-
}
|
143
|
-
|
144
|
-
return Boolean(variable) && variable.references.every(isSafeReference);
|
145
|
-
}
|
146
22
|
|
147
23
|
//------------------------------------------------------------------------------
|
148
24
|
// Rule Definition
|
@@ -168,8 +44,147 @@ module.exports = {
|
|
168
44
|
|
169
45
|
create(context) {
|
170
46
|
|
47
|
+
const SKIPPED_IIFE_NODES = new Set();
|
171
48
|
const sourceCode = context.sourceCode;
|
172
49
|
|
50
|
+
/**
|
51
|
+
* Gets the containing loop node of a specified node.
|
52
|
+
*
|
53
|
+
* We don't need to check nested functions, so this ignores those, with the exception of IIFE.
|
54
|
+
* `Scope.through` contains references of nested functions.
|
55
|
+
* @param {ASTNode} node An AST node to get.
|
56
|
+
* @returns {ASTNode|null} The containing loop node of the specified node, or
|
57
|
+
* `null`.
|
58
|
+
*/
|
59
|
+
function getContainingLoopNode(node) {
|
60
|
+
for (let currentNode = node; currentNode.parent; currentNode = currentNode.parent) {
|
61
|
+
const parent = currentNode.parent;
|
62
|
+
|
63
|
+
switch (parent.type) {
|
64
|
+
case "WhileStatement":
|
65
|
+
case "DoWhileStatement":
|
66
|
+
return parent;
|
67
|
+
|
68
|
+
case "ForStatement":
|
69
|
+
|
70
|
+
// `init` is outside of the loop.
|
71
|
+
if (parent.init !== currentNode) {
|
72
|
+
return parent;
|
73
|
+
}
|
74
|
+
break;
|
75
|
+
|
76
|
+
case "ForInStatement":
|
77
|
+
case "ForOfStatement":
|
78
|
+
|
79
|
+
// `right` is outside of the loop.
|
80
|
+
if (parent.right !== currentNode) {
|
81
|
+
return parent;
|
82
|
+
}
|
83
|
+
break;
|
84
|
+
|
85
|
+
case "ArrowFunctionExpression":
|
86
|
+
case "FunctionExpression":
|
87
|
+
case "FunctionDeclaration":
|
88
|
+
|
89
|
+
// We need to check nested functions only in case of IIFE.
|
90
|
+
if (SKIPPED_IIFE_NODES.has(parent)) {
|
91
|
+
break;
|
92
|
+
}
|
93
|
+
|
94
|
+
return null;
|
95
|
+
default:
|
96
|
+
break;
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
return null;
|
101
|
+
}
|
102
|
+
|
103
|
+
/**
|
104
|
+
* Gets the containing loop node of a given node.
|
105
|
+
* If the loop was nested, this returns the most outer loop.
|
106
|
+
* @param {ASTNode} node A node to get. This is a loop node.
|
107
|
+
* @param {ASTNode|null} excludedNode A node that the result node should not
|
108
|
+
* include.
|
109
|
+
* @returns {ASTNode} The most outer loop node.
|
110
|
+
*/
|
111
|
+
function getTopLoopNode(node, excludedNode) {
|
112
|
+
const border = excludedNode ? excludedNode.range[1] : 0;
|
113
|
+
let retv = node;
|
114
|
+
let containingLoopNode = node;
|
115
|
+
|
116
|
+
while (containingLoopNode && containingLoopNode.range[0] >= border) {
|
117
|
+
retv = containingLoopNode;
|
118
|
+
containingLoopNode = getContainingLoopNode(containingLoopNode);
|
119
|
+
}
|
120
|
+
|
121
|
+
return retv;
|
122
|
+
}
|
123
|
+
|
124
|
+
/**
|
125
|
+
* Checks whether a given reference which refers to an upper scope's variable is
|
126
|
+
* safe or not.
|
127
|
+
* @param {ASTNode} loopNode A containing loop node.
|
128
|
+
* @param {eslint-scope.Reference} reference A reference to check.
|
129
|
+
* @returns {boolean} `true` if the reference is safe or not.
|
130
|
+
*/
|
131
|
+
function isSafe(loopNode, reference) {
|
132
|
+
const variable = reference.resolved;
|
133
|
+
const definition = variable && variable.defs[0];
|
134
|
+
const declaration = definition && definition.parent;
|
135
|
+
const kind = (declaration && declaration.type === "VariableDeclaration")
|
136
|
+
? declaration.kind
|
137
|
+
: "";
|
138
|
+
|
139
|
+
// Variables which are declared by `const` is safe.
|
140
|
+
if (kind === "const") {
|
141
|
+
return true;
|
142
|
+
}
|
143
|
+
|
144
|
+
/*
|
145
|
+
* Variables which are declared by `let` in the loop is safe.
|
146
|
+
* It's a different instance from the next loop step's.
|
147
|
+
*/
|
148
|
+
if (kind === "let" &&
|
149
|
+
declaration.range[0] > loopNode.range[0] &&
|
150
|
+
declaration.range[1] < loopNode.range[1]
|
151
|
+
) {
|
152
|
+
return true;
|
153
|
+
}
|
154
|
+
|
155
|
+
/*
|
156
|
+
* WriteReferences which exist after this border are unsafe because those
|
157
|
+
* can modify the variable.
|
158
|
+
*/
|
159
|
+
const border = getTopLoopNode(
|
160
|
+
loopNode,
|
161
|
+
(kind === "let") ? declaration : null
|
162
|
+
).range[0];
|
163
|
+
|
164
|
+
/**
|
165
|
+
* Checks whether a given reference is safe or not.
|
166
|
+
* The reference is every reference of the upper scope's variable we are
|
167
|
+
* looking now.
|
168
|
+
*
|
169
|
+
* It's safe if the reference matches one of the following condition.
|
170
|
+
* - is readonly.
|
171
|
+
* - doesn't exist inside a local function and after the border.
|
172
|
+
* @param {eslint-scope.Reference} upperRef A reference to check.
|
173
|
+
* @returns {boolean} `true` if the reference is safe.
|
174
|
+
*/
|
175
|
+
function isSafeReference(upperRef) {
|
176
|
+
const id = upperRef.identifier;
|
177
|
+
|
178
|
+
return (
|
179
|
+
!upperRef.isWrite() ||
|
180
|
+
variable.scope.variableScope === upperRef.from.variableScope &&
|
181
|
+
id.range[0] < border
|
182
|
+
);
|
183
|
+
}
|
184
|
+
|
185
|
+
return Boolean(variable) && variable.references.every(isSafeReference);
|
186
|
+
}
|
187
|
+
|
173
188
|
/**
|
174
189
|
* Reports functions which match the following condition:
|
175
190
|
*
|
@@ -186,6 +201,23 @@ module.exports = {
|
|
186
201
|
}
|
187
202
|
|
188
203
|
const references = sourceCode.getScope(node).through;
|
204
|
+
|
205
|
+
// Check if the function is not asynchronous or a generator function
|
206
|
+
if (!(node.async || node.generator)) {
|
207
|
+
if (isIIFE(node)) {
|
208
|
+
|
209
|
+
const isFunctionExpression = node.type === "FunctionExpression";
|
210
|
+
|
211
|
+
// Check if the function is referenced elsewhere in the code
|
212
|
+
const isFunctionReferenced = isFunctionExpression && node.id ? references.some(r => r.identifier.name === node.id.name) : false;
|
213
|
+
|
214
|
+
if (!isFunctionReferenced) {
|
215
|
+
SKIPPED_IIFE_NODES.add(node);
|
216
|
+
return;
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
189
221
|
const unsafeRefs = references.filter(r => r.resolved && !isSafe(loopNode, r)).map(r => r.identifier.name);
|
190
222
|
|
191
223
|
if (unsafeRefs.length > 0) {
|
@@ -85,12 +85,10 @@ function isUnicodeCodePointEscape(char) {
|
|
85
85
|
const findCharacterSequences = {
|
86
86
|
*surrogatePairWithoutUFlag(chars) {
|
87
87
|
for (const [index, char] of chars.entries()) {
|
88
|
-
if (index === 0) {
|
89
|
-
continue;
|
90
|
-
}
|
91
88
|
const previous = chars[index - 1];
|
92
89
|
|
93
90
|
if (
|
91
|
+
previous && char &&
|
94
92
|
isSurrogatePair(previous.value, char.value) &&
|
95
93
|
!isUnicodeCodePointEscape(previous) &&
|
96
94
|
!isUnicodeCodePointEscape(char)
|
@@ -102,12 +100,10 @@ const findCharacterSequences = {
|
|
102
100
|
|
103
101
|
*surrogatePair(chars) {
|
104
102
|
for (const [index, char] of chars.entries()) {
|
105
|
-
if (index === 0) {
|
106
|
-
continue;
|
107
|
-
}
|
108
103
|
const previous = chars[index - 1];
|
109
104
|
|
110
105
|
if (
|
106
|
+
previous && char &&
|
111
107
|
isSurrogatePair(previous.value, char.value) &&
|
112
108
|
(
|
113
109
|
isUnicodeCodePointEscape(previous) ||
|
@@ -119,14 +115,17 @@ const findCharacterSequences = {
|
|
119
115
|
}
|
120
116
|
},
|
121
117
|
|
122
|
-
*combiningClass(chars) {
|
118
|
+
*combiningClass(chars, unfilteredChars) {
|
119
|
+
|
120
|
+
/*
|
121
|
+
* When `allowEscape` is `true`, a combined character should only be allowed if the combining mark appears as an escape sequence.
|
122
|
+
* This means that the base character should be considered even if it's escaped.
|
123
|
+
*/
|
123
124
|
for (const [index, char] of chars.entries()) {
|
124
|
-
|
125
|
-
continue;
|
126
|
-
}
|
127
|
-
const previous = chars[index - 1];
|
125
|
+
const previous = unfilteredChars[index - 1];
|
128
126
|
|
129
127
|
if (
|
128
|
+
previous && char &&
|
130
129
|
isCombiningCharacter(char.value) &&
|
131
130
|
!isCombiningCharacter(previous.value)
|
132
131
|
) {
|
@@ -137,12 +136,10 @@ const findCharacterSequences = {
|
|
137
136
|
|
138
137
|
*emojiModifier(chars) {
|
139
138
|
for (const [index, char] of chars.entries()) {
|
140
|
-
if (index === 0) {
|
141
|
-
continue;
|
142
|
-
}
|
143
139
|
const previous = chars[index - 1];
|
144
140
|
|
145
141
|
if (
|
142
|
+
previous && char &&
|
146
143
|
isEmojiModifier(char.value) &&
|
147
144
|
!isEmojiModifier(previous.value)
|
148
145
|
) {
|
@@ -153,12 +150,10 @@ const findCharacterSequences = {
|
|
153
150
|
|
154
151
|
*regionalIndicatorSymbol(chars) {
|
155
152
|
for (const [index, char] of chars.entries()) {
|
156
|
-
if (index === 0) {
|
157
|
-
continue;
|
158
|
-
}
|
159
153
|
const previous = chars[index - 1];
|
160
154
|
|
161
155
|
if (
|
156
|
+
previous && char &&
|
162
157
|
isRegionalIndicatorSymbol(char.value) &&
|
163
158
|
isRegionalIndicatorSymbol(previous.value)
|
164
159
|
) {
|
@@ -171,17 +166,18 @@ const findCharacterSequences = {
|
|
171
166
|
let sequence = null;
|
172
167
|
|
173
168
|
for (const [index, char] of chars.entries()) {
|
174
|
-
|
175
|
-
|
176
|
-
|
169
|
+
const previous = chars[index - 1];
|
170
|
+
const next = chars[index + 1];
|
171
|
+
|
177
172
|
if (
|
173
|
+
previous && char && next &&
|
178
174
|
char.value === 0x200d &&
|
179
|
-
|
180
|
-
|
175
|
+
previous.value !== 0x200d &&
|
176
|
+
next.value !== 0x200d
|
181
177
|
) {
|
182
178
|
if (sequence) {
|
183
|
-
if (sequence.at(-1) ===
|
184
|
-
sequence.push(char,
|
179
|
+
if (sequence.at(-1) === previous) {
|
180
|
+
sequence.push(char, next); // append to the sequence
|
185
181
|
} else {
|
186
182
|
yield sequence;
|
187
183
|
sequence = chars.slice(index - 1, index + 2);
|
@@ -227,6 +223,41 @@ function getStaticValueOrRegex(node, initialScope) {
|
|
227
223
|
return staticValue;
|
228
224
|
}
|
229
225
|
|
226
|
+
/**
|
227
|
+
* Checks whether a specified regexpp character is represented as an acceptable escape sequence.
|
228
|
+
* This function requires the source text of the character to be known.
|
229
|
+
* @param {Character} char Character to check.
|
230
|
+
* @param {string} charSource Source text of the character to check.
|
231
|
+
* @returns {boolean} Whether the specified regexpp character is represented as an acceptable escape sequence.
|
232
|
+
*/
|
233
|
+
function checkForAcceptableEscape(char, charSource) {
|
234
|
+
if (!charSource.startsWith("\\")) {
|
235
|
+
return false;
|
236
|
+
}
|
237
|
+
const match = /(?<=^\\+).$/su.exec(charSource);
|
238
|
+
|
239
|
+
return match?.[0] !== String.fromCodePoint(char.value);
|
240
|
+
}
|
241
|
+
|
242
|
+
/**
|
243
|
+
* Checks whether a specified regexpp character is represented as an acceptable escape sequence.
|
244
|
+
* This function works with characters that are produced by a string or template literal.
|
245
|
+
* It requires the source text and the CodeUnit list of the literal to be known.
|
246
|
+
* @param {Character} char Character to check.
|
247
|
+
* @param {string} nodeSource Source text of the string or template literal that produces the character.
|
248
|
+
* @param {CodeUnit[]} codeUnits List of CodeUnit objects of the literal that produces the character.
|
249
|
+
* @returns {boolean} Whether the specified regexpp character is represented as an acceptable escape sequence.
|
250
|
+
*/
|
251
|
+
function checkForAcceptableEscapeInString(char, nodeSource, codeUnits) {
|
252
|
+
const firstIndex = char.start;
|
253
|
+
const lastIndex = char.end - 1;
|
254
|
+
const start = codeUnits[firstIndex].start;
|
255
|
+
const end = codeUnits[lastIndex].end;
|
256
|
+
const charSource = nodeSource.slice(start, end);
|
257
|
+
|
258
|
+
return checkForAcceptableEscape(char, charSource);
|
259
|
+
}
|
260
|
+
|
230
261
|
//------------------------------------------------------------------------------
|
231
262
|
// Rule Definition
|
232
263
|
//------------------------------------------------------------------------------
|
@@ -244,7 +275,18 @@ module.exports = {
|
|
244
275
|
|
245
276
|
hasSuggestions: true,
|
246
277
|
|
247
|
-
schema: [
|
278
|
+
schema: [
|
279
|
+
{
|
280
|
+
type: "object",
|
281
|
+
properties: {
|
282
|
+
allowEscape: {
|
283
|
+
type: "boolean",
|
284
|
+
default: false
|
285
|
+
}
|
286
|
+
},
|
287
|
+
additionalProperties: false
|
288
|
+
}
|
289
|
+
],
|
248
290
|
|
249
291
|
messages: {
|
250
292
|
surrogatePairWithoutUFlag: "Unexpected surrogate pair in character class. Use 'u' flag.",
|
@@ -257,6 +299,7 @@ module.exports = {
|
|
257
299
|
}
|
258
300
|
},
|
259
301
|
create(context) {
|
302
|
+
const allowEscape = context.options[0]?.allowEscape;
|
260
303
|
const sourceCode = context.sourceCode;
|
261
304
|
const parser = new RegExpParser();
|
262
305
|
const checkedPatternNodes = new Set();
|
@@ -288,24 +331,62 @@ module.exports = {
|
|
288
331
|
return;
|
289
332
|
}
|
290
333
|
|
334
|
+
let codeUnits = null;
|
335
|
+
|
336
|
+
/**
|
337
|
+
* Checks whether a specified regexpp character is represented as an acceptable escape sequence.
|
338
|
+
* For the purposes of this rule, an escape sequence is considered acceptable if it consists of one or more backslashes followed by the character being escaped.
|
339
|
+
* @param {Character} char Character to check.
|
340
|
+
* @returns {boolean} Whether the specified regexpp character is represented as an acceptable escape sequence.
|
341
|
+
*/
|
342
|
+
function isAcceptableEscapeSequence(char) {
|
343
|
+
if (node.type === "Literal" && node.regex) {
|
344
|
+
return checkForAcceptableEscape(char, char.raw);
|
345
|
+
}
|
346
|
+
if (node.type === "Literal" && typeof node.value === "string") {
|
347
|
+
const nodeSource = node.raw;
|
348
|
+
|
349
|
+
codeUnits ??= parseStringLiteral(nodeSource);
|
350
|
+
|
351
|
+
return checkForAcceptableEscapeInString(char, nodeSource, codeUnits);
|
352
|
+
}
|
353
|
+
if (astUtils.isStaticTemplateLiteral(node)) {
|
354
|
+
const nodeSource = sourceCode.getText(node);
|
355
|
+
|
356
|
+
codeUnits ??= parseTemplateToken(nodeSource);
|
357
|
+
|
358
|
+
return checkForAcceptableEscapeInString(char, nodeSource, codeUnits);
|
359
|
+
}
|
360
|
+
return false;
|
361
|
+
}
|
362
|
+
|
291
363
|
const foundKindMatches = new Map();
|
292
364
|
|
293
365
|
visitRegExpAST(patternNode, {
|
294
366
|
onCharacterClassEnter(ccNode) {
|
295
|
-
for (const
|
367
|
+
for (const unfilteredChars of iterateCharacterSequence(ccNode.elements)) {
|
368
|
+
let chars;
|
369
|
+
|
370
|
+
if (allowEscape) {
|
371
|
+
|
372
|
+
// Replace escape sequences with null to avoid having them flagged.
|
373
|
+
chars = unfilteredChars.map(char => (isAcceptableEscapeSequence(char) ? null : char));
|
374
|
+
} else {
|
375
|
+
chars = unfilteredChars;
|
376
|
+
}
|
296
377
|
for (const kind of kinds) {
|
378
|
+
const matches = findCharacterSequences[kind](chars, unfilteredChars);
|
379
|
+
|
297
380
|
if (foundKindMatches.has(kind)) {
|
298
|
-
foundKindMatches.get(kind).push(...
|
381
|
+
foundKindMatches.get(kind).push(...matches);
|
299
382
|
} else {
|
300
|
-
foundKindMatches.set(kind, [...
|
383
|
+
foundKindMatches.set(kind, [...matches]);
|
301
384
|
}
|
302
385
|
}
|
303
386
|
}
|
304
387
|
}
|
305
388
|
});
|
306
389
|
|
307
|
-
let codeUnits = null;
|
308
|
-
|
309
390
|
/**
|
310
391
|
* Finds the report loc(s) for a range of matches.
|
311
392
|
* Only literals and expression-less templates generate granular errors.
|
@@ -37,7 +37,8 @@ module.exports = {
|
|
37
37
|
type: "string"
|
38
38
|
},
|
39
39
|
uniqueItems: true
|
40
|
-
}
|
40
|
+
},
|
41
|
+
restrictedNamedExportsPattern: { type: "string" }
|
41
42
|
},
|
42
43
|
additionalProperties: false
|
43
44
|
},
|
@@ -52,6 +53,7 @@ module.exports = {
|
|
52
53
|
},
|
53
54
|
uniqueItems: true
|
54
55
|
},
|
56
|
+
restrictedNamedExportsPattern: { type: "string" },
|
55
57
|
restrictDefaultExports: {
|
56
58
|
type: "object",
|
57
59
|
properties: {
|
@@ -98,6 +100,7 @@ module.exports = {
|
|
98
100
|
create(context) {
|
99
101
|
|
100
102
|
const restrictedNames = new Set(context.options[0] && context.options[0].restrictedNamedExports);
|
103
|
+
const restrictedNamePattern = context.options[0] && context.options[0].restrictedNamedExportsPattern;
|
101
104
|
const restrictDefaultExports = context.options[0] && context.options[0].restrictDefaultExports;
|
102
105
|
const sourceCode = context.sourceCode;
|
103
106
|
|
@@ -109,7 +112,15 @@ module.exports = {
|
|
109
112
|
function checkExportedName(node) {
|
110
113
|
const name = astUtils.getModuleExportName(node);
|
111
114
|
|
112
|
-
|
115
|
+
let matchesRestrictedNamePattern = false;
|
116
|
+
|
117
|
+
if (restrictedNamePattern && name !== "default") {
|
118
|
+
const patternRegex = new RegExp(restrictedNamePattern, "u");
|
119
|
+
|
120
|
+
matchesRestrictedNamePattern = patternRegex.test(name);
|
121
|
+
}
|
122
|
+
|
123
|
+
if (matchesRestrictedNamePattern || restrictedNames.has(name)) {
|
113
124
|
context.report({
|
114
125
|
node,
|
115
126
|
messageId: "restrictedNamed",
|
@@ -284,35 +284,22 @@ module.exports = {
|
|
284
284
|
const arrowToken = sourceCode.getTokenBefore(node.value.body, astUtils.isArrowToken);
|
285
285
|
const fnBody = sourceCode.text.slice(arrowToken.range[1], node.value.range[1]);
|
286
286
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
tokenBeforeParams = sourceCode.getFirstToken(node.value, astUtils.isOpeningParenToken);
|
292
|
-
} else {
|
293
|
-
tokenBeforeParams = sourceCode.getTokenBefore(node.value.params[0]);
|
294
|
-
}
|
295
|
-
|
296
|
-
if (node.value.params.length === 1) {
|
297
|
-
const hasParen = astUtils.isOpeningParenToken(tokenBeforeParams);
|
298
|
-
const isTokenOutsideNode = tokenBeforeParams.range[0] < node.range[0];
|
299
|
-
|
300
|
-
shouldAddParensAroundParameters = !hasParen || isTokenOutsideNode;
|
301
|
-
}
|
287
|
+
// First token should not be `async`
|
288
|
+
const firstValueToken = sourceCode.getFirstToken(node.value, {
|
289
|
+
skip: node.value.async ? 1 : 0
|
290
|
+
});
|
302
291
|
|
303
|
-
const sliceStart =
|
304
|
-
? node.value.params[0].range[0]
|
305
|
-
: tokenBeforeParams.range[0];
|
292
|
+
const sliceStart = firstValueToken.range[0];
|
306
293
|
const sliceEnd = sourceCode.getTokenBefore(arrowToken).range[1];
|
294
|
+
const shouldAddParens = node.value.params.length === 1 && node.value.params[0].range[0] === sliceStart;
|
307
295
|
|
308
296
|
const oldParamText = sourceCode.text.slice(sliceStart, sliceEnd);
|
309
|
-
const newParamText =
|
297
|
+
const newParamText = shouldAddParens ? `(${oldParamText})` : oldParamText;
|
310
298
|
|
311
299
|
return fixer.replaceTextRange(
|
312
300
|
fixRange,
|
313
301
|
methodPrefix + newParamText + fnBody
|
314
302
|
);
|
315
|
-
|
316
303
|
}
|
317
304
|
|
318
305
|
/**
|
@@ -497,6 +484,13 @@ module.exports = {
|
|
497
484
|
node,
|
498
485
|
messageId: "expectedPropertyShorthand",
|
499
486
|
fix(fixer) {
|
487
|
+
|
488
|
+
// x: /* */ x
|
489
|
+
// x: (/* */ x)
|
490
|
+
if (sourceCode.getCommentsInside(node).length > 0) {
|
491
|
+
return null;
|
492
|
+
}
|
493
|
+
|
500
494
|
return fixer.replaceText(node, node.value.name);
|
501
495
|
}
|
502
496
|
});
|
@@ -510,6 +504,13 @@ module.exports = {
|
|
510
504
|
node,
|
511
505
|
messageId: "expectedPropertyShorthand",
|
512
506
|
fix(fixer) {
|
507
|
+
|
508
|
+
// "x": /* */ x
|
509
|
+
// "x": (/* */ x)
|
510
|
+
if (sourceCode.getCommentsInside(node).length > 0) {
|
511
|
+
return null;
|
512
|
+
}
|
513
|
+
|
513
514
|
return fixer.replaceText(node, node.value.name);
|
514
515
|
}
|
515
516
|
});
|
@@ -9,9 +9,9 @@
|
|
9
9
|
// Requirements
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
|
-
const path = require("path");
|
12
|
+
const path = require("node:path");
|
13
13
|
const spawn = require("cross-spawn");
|
14
|
-
const os = require("os");
|
14
|
+
const os = require("node:os");
|
15
15
|
const log = require("../shared/logging");
|
16
16
|
const packageJson = require("../../package.json");
|
17
17
|
|