eslint 9.31.0 → 9.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 +2 -3
- package/lib/linter/{report-translator.js → file-report.js} +255 -61
- package/lib/linter/linter.js +140 -293
- package/lib/rules/accessor-pairs.js +35 -0
- package/lib/rules/grouped-accessor-pairs.js +36 -6
- package/lib/rules/no-await-in-loop.js +12 -1
- package/lib/rules/no-implied-eval.js +12 -2
- package/lib/rules/no-restricted-globals.js +131 -21
- package/lib/rules/no-unused-vars.js +27 -0
- package/lib/rules/one-var.js +63 -30
- package/lib/rules/prefer-destructuring.js +8 -0
- package/lib/rules/require-await.js +9 -0
- package/lib/rules/utils/ast-utils.js +27 -7
- package/lib/types/index.d.ts +12 -20
- package/lib/types/rules.d.ts +38 -9
- package/package.json +6 -7
@@ -148,6 +148,7 @@ module.exports = {
|
|
148
148
|
|
149
149
|
defaultOptions: [
|
150
150
|
{
|
151
|
+
enforceForTSTypes: false,
|
151
152
|
enforceForClassMembers: true,
|
152
153
|
getWithoutSet: false,
|
153
154
|
setWithoutGet: true,
|
@@ -174,6 +175,9 @@ module.exports = {
|
|
174
175
|
enforceForClassMembers: {
|
175
176
|
type: "boolean",
|
176
177
|
},
|
178
|
+
enforceForTSTypes: {
|
179
|
+
type: "boolean",
|
180
|
+
},
|
177
181
|
},
|
178
182
|
additionalProperties: false,
|
179
183
|
},
|
@@ -190,6 +194,8 @@ module.exports = {
|
|
190
194
|
"Setter is not present for {{ name }}.",
|
191
195
|
missingGetterInClass: "Getter is not present for class {{ name }}.",
|
192
196
|
missingSetterInClass: "Setter is not present for class {{ name }}.",
|
197
|
+
missingGetterInType: "Getter is not present for type {{ name }}.",
|
198
|
+
missingSetterInType: "Setter is not present for type {{ name }}.",
|
193
199
|
},
|
194
200
|
},
|
195
201
|
create(context) {
|
@@ -198,6 +204,7 @@ module.exports = {
|
|
198
204
|
getWithoutSet: checkGetWithoutSet,
|
199
205
|
setWithoutGet: checkSetWithoutGet,
|
200
206
|
enforceForClassMembers,
|
207
|
+
enforceForTSTypes,
|
201
208
|
},
|
202
209
|
] = context.options;
|
203
210
|
const sourceCode = context.sourceCode;
|
@@ -228,6 +235,15 @@ module.exports = {
|
|
228
235
|
name: astUtils.getFunctionNameWithKind(node.value),
|
229
236
|
},
|
230
237
|
});
|
238
|
+
} else if (node.type === "TSMethodSignature") {
|
239
|
+
context.report({
|
240
|
+
node,
|
241
|
+
messageId: `${messageKind}InType`,
|
242
|
+
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
243
|
+
data: {
|
244
|
+
name: astUtils.getFunctionNameWithKind(node),
|
245
|
+
},
|
246
|
+
});
|
231
247
|
} else {
|
232
248
|
context.report({
|
233
249
|
node,
|
@@ -371,6 +387,22 @@ module.exports = {
|
|
371
387
|
checkList(methodDefinitions.filter(m => !m.static));
|
372
388
|
}
|
373
389
|
|
390
|
+
/**
|
391
|
+
* Checks the given type.
|
392
|
+
* @param {ASTNode} node `TSTypeLiteral` or `TSInterfaceBody` node to check.
|
393
|
+
* @returns {void}
|
394
|
+
* @private
|
395
|
+
*/
|
396
|
+
function checkType(node) {
|
397
|
+
const members =
|
398
|
+
node.type === "TSTypeLiteral" ? node.members : node.body;
|
399
|
+
const methodDefinitions = members.filter(
|
400
|
+
m => m.type === "TSMethodSignature",
|
401
|
+
);
|
402
|
+
|
403
|
+
checkList(methodDefinitions);
|
404
|
+
}
|
405
|
+
|
374
406
|
const listeners = {};
|
375
407
|
|
376
408
|
if (checkSetWithoutGet || checkGetWithoutSet) {
|
@@ -378,6 +410,9 @@ module.exports = {
|
|
378
410
|
if (enforceForClassMembers) {
|
379
411
|
listeners.ClassBody = checkClassBody;
|
380
412
|
}
|
413
|
+
if (enforceForTSTypes) {
|
414
|
+
listeners["TSTypeLiteral, TSInterfaceBody"] = checkType;
|
415
|
+
}
|
381
416
|
}
|
382
417
|
|
383
418
|
return listeners;
|
@@ -87,6 +87,8 @@ function isAccessorKind(node) {
|
|
87
87
|
return node.kind === "get" || node.kind === "set";
|
88
88
|
}
|
89
89
|
|
90
|
+
const DEFAULT_ORDER = "anyOrder";
|
91
|
+
|
90
92
|
//------------------------------------------------------------------------------
|
91
93
|
// Rule Definition
|
92
94
|
//------------------------------------------------------------------------------
|
@@ -96,7 +98,7 @@ module.exports = {
|
|
96
98
|
meta: {
|
97
99
|
type: "suggestion",
|
98
100
|
|
99
|
-
defaultOptions: [
|
101
|
+
defaultOptions: [DEFAULT_ORDER],
|
100
102
|
|
101
103
|
docs: {
|
102
104
|
description:
|
@@ -106,8 +108,15 @@ module.exports = {
|
|
106
108
|
},
|
107
109
|
|
108
110
|
schema: [
|
111
|
+
{ enum: ["anyOrder", "getBeforeSet", "setBeforeGet"] },
|
109
112
|
{
|
110
|
-
|
113
|
+
type: "object",
|
114
|
+
properties: {
|
115
|
+
enforceForTSTypes: {
|
116
|
+
type: "boolean",
|
117
|
+
},
|
118
|
+
},
|
119
|
+
additionalProperties: false,
|
111
120
|
},
|
112
121
|
],
|
113
122
|
|
@@ -120,7 +129,9 @@ module.exports = {
|
|
120
129
|
},
|
121
130
|
|
122
131
|
create(context) {
|
123
|
-
const
|
132
|
+
const order = context.options[0] ?? DEFAULT_ORDER;
|
133
|
+
const enforceForTSTypes =
|
134
|
+
context.options[1]?.enforceForTSTypes ?? false;
|
124
135
|
const sourceCode = context.sourceCode;
|
125
136
|
|
126
137
|
/**
|
@@ -135,13 +146,22 @@ module.exports = {
|
|
135
146
|
context.report({
|
136
147
|
node: latterNode,
|
137
148
|
messageId,
|
138
|
-
loc: astUtils.getFunctionHeadLoc(
|
149
|
+
loc: astUtils.getFunctionHeadLoc(
|
150
|
+
latterNode.type !== "TSMethodSignature"
|
151
|
+
? latterNode.value
|
152
|
+
: latterNode,
|
153
|
+
sourceCode,
|
154
|
+
),
|
139
155
|
data: {
|
140
156
|
formerName: astUtils.getFunctionNameWithKind(
|
141
|
-
formerNode.
|
157
|
+
formerNode.type !== "TSMethodSignature"
|
158
|
+
? formerNode.value
|
159
|
+
: formerNode,
|
142
160
|
),
|
143
161
|
latterName: astUtils.getFunctionNameWithKind(
|
144
|
-
latterNode.
|
162
|
+
latterNode.type !== "TSMethodSignature"
|
163
|
+
? latterNode.value
|
164
|
+
: latterNode,
|
145
165
|
),
|
146
166
|
},
|
147
167
|
});
|
@@ -232,6 +252,16 @@ module.exports = {
|
|
232
252
|
n => n.type === "MethodDefinition" && n.static,
|
233
253
|
);
|
234
254
|
},
|
255
|
+
"TSTypeLiteral, TSInterfaceBody"(node) {
|
256
|
+
if (enforceForTSTypes) {
|
257
|
+
checkList(
|
258
|
+
node.type === "TSTypeLiteral"
|
259
|
+
? node.members
|
260
|
+
: node.body,
|
261
|
+
n => n.type === "TSMethodSignature",
|
262
|
+
);
|
263
|
+
}
|
264
|
+
},
|
235
265
|
};
|
236
266
|
},
|
237
267
|
};
|
@@ -41,7 +41,10 @@ function isLooped(node, parent) {
|
|
41
41
|
|
42
42
|
case "ForOfStatement":
|
43
43
|
case "ForInStatement":
|
44
|
-
return
|
44
|
+
return (
|
45
|
+
node === parent.body ||
|
46
|
+
(node === parent.left && node.kind === "await using")
|
47
|
+
);
|
45
48
|
|
46
49
|
case "WhileStatement":
|
47
50
|
case "DoWhileStatement":
|
@@ -76,6 +79,13 @@ module.exports = {
|
|
76
79
|
* @returns {void}
|
77
80
|
*/
|
78
81
|
function validate(awaitNode) {
|
82
|
+
if (
|
83
|
+
awaitNode.type === "VariableDeclaration" &&
|
84
|
+
awaitNode.kind !== "await using"
|
85
|
+
) {
|
86
|
+
return;
|
87
|
+
}
|
88
|
+
|
79
89
|
if (awaitNode.type === "ForOfStatement" && !awaitNode.await) {
|
80
90
|
return;
|
81
91
|
}
|
@@ -99,6 +109,7 @@ module.exports = {
|
|
99
109
|
return {
|
100
110
|
AwaitExpression: validate,
|
101
111
|
ForOfStatement: validate,
|
112
|
+
VariableDeclaration: validate,
|
102
113
|
};
|
103
114
|
},
|
104
115
|
};
|
@@ -32,6 +32,7 @@ module.exports = {
|
|
32
32
|
messages: {
|
33
33
|
impliedEval:
|
34
34
|
"Implied eval. Consider passing a function instead of a string.",
|
35
|
+
execScript: "Implied eval. Do not use execScript().",
|
35
36
|
},
|
36
37
|
},
|
37
38
|
|
@@ -85,9 +86,14 @@ module.exports = {
|
|
85
86
|
isStaticString || isEvaluatedString(firstArgument);
|
86
87
|
|
87
88
|
if (isString) {
|
89
|
+
const calleeName =
|
90
|
+
node.callee.type === "Identifier"
|
91
|
+
? node.callee.name
|
92
|
+
: astUtils.getStaticPropertyName(node.callee);
|
93
|
+
const isExecScript = calleeName === "execScript";
|
88
94
|
context.report({
|
89
95
|
node,
|
90
|
-
messageId: "impliedEval",
|
96
|
+
messageId: isExecScript ? "execScript" : "impliedEval",
|
91
97
|
});
|
92
98
|
}
|
93
99
|
}
|
@@ -139,7 +145,11 @@ module.exports = {
|
|
139
145
|
return {
|
140
146
|
CallExpression(node) {
|
141
147
|
if (
|
142
|
-
astUtils.isSpecificId(
|
148
|
+
astUtils.isSpecificId(
|
149
|
+
node.callee,
|
150
|
+
EVAL_LIKE_FUNC_PATTERN,
|
151
|
+
) &&
|
152
|
+
sourceCode.isGlobalReference(node.callee)
|
143
153
|
) {
|
144
154
|
reportImpliedEvalCallExpression(node);
|
145
155
|
}
|
@@ -4,6 +4,12 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const astUtils = require("./utils/ast-utils");
|
12
|
+
|
7
13
|
//------------------------------------------------------------------------------
|
8
14
|
// Helpers
|
9
15
|
//------------------------------------------------------------------------------
|
@@ -16,10 +22,34 @@ const TYPE_NODES = new Set([
|
|
16
22
|
"TSQualifiedName",
|
17
23
|
]);
|
18
24
|
|
25
|
+
const GLOBAL_OBJECTS = new Set(["globalThis", "self", "window"]);
|
26
|
+
|
19
27
|
//------------------------------------------------------------------------------
|
20
28
|
// Rule Definition
|
21
29
|
//------------------------------------------------------------------------------
|
22
30
|
|
31
|
+
const arrayOfGlobals = {
|
32
|
+
type: "array",
|
33
|
+
items: {
|
34
|
+
oneOf: [
|
35
|
+
{
|
36
|
+
type: "string",
|
37
|
+
},
|
38
|
+
{
|
39
|
+
type: "object",
|
40
|
+
properties: {
|
41
|
+
name: { type: "string" },
|
42
|
+
message: { type: "string" },
|
43
|
+
},
|
44
|
+
required: ["name"],
|
45
|
+
additionalProperties: false,
|
46
|
+
},
|
47
|
+
],
|
48
|
+
},
|
49
|
+
uniqueItems: true,
|
50
|
+
minItems: 0,
|
51
|
+
};
|
52
|
+
|
23
53
|
/** @type {import('../types').Rule.RuleModule} */
|
24
54
|
module.exports = {
|
25
55
|
meta: {
|
@@ -34,25 +64,33 @@ module.exports = {
|
|
34
64
|
},
|
35
65
|
|
36
66
|
schema: {
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
67
|
+
anyOf: [
|
68
|
+
arrayOfGlobals,
|
69
|
+
{
|
70
|
+
type: "array",
|
71
|
+
items: [
|
72
|
+
{
|
73
|
+
type: "object",
|
74
|
+
properties: {
|
75
|
+
globals: arrayOfGlobals,
|
76
|
+
checkGlobalObject: {
|
77
|
+
type: "boolean",
|
78
|
+
},
|
79
|
+
globalObjects: {
|
80
|
+
type: "array",
|
81
|
+
items: {
|
82
|
+
type: "string",
|
83
|
+
},
|
84
|
+
uniqueItems: true,
|
85
|
+
},
|
86
|
+
},
|
87
|
+
required: ["globals"],
|
88
|
+
additionalProperties: false,
|
48
89
|
},
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
},
|
54
|
-
uniqueItems: true,
|
55
|
-
minItems: 0,
|
90
|
+
],
|
91
|
+
additionalItems: false,
|
92
|
+
},
|
93
|
+
],
|
56
94
|
},
|
57
95
|
|
58
96
|
messages: {
|
@@ -63,14 +101,33 @@ module.exports = {
|
|
63
101
|
},
|
64
102
|
|
65
103
|
create(context) {
|
66
|
-
const sourceCode = context
|
104
|
+
const { sourceCode, options } = context;
|
105
|
+
|
106
|
+
const isGlobalsObject =
|
107
|
+
typeof options[0] === "object" &&
|
108
|
+
Object.hasOwn(options[0], "globals");
|
109
|
+
|
110
|
+
const restrictedGlobals = isGlobalsObject
|
111
|
+
? options[0].globals
|
112
|
+
: options;
|
113
|
+
const checkGlobalObject = isGlobalsObject
|
114
|
+
? options[0].checkGlobalObject
|
115
|
+
: false;
|
116
|
+
const userGlobalObjects = isGlobalsObject
|
117
|
+
? options[0].globalObjects || []
|
118
|
+
: [];
|
119
|
+
|
120
|
+
const globalObjects = new Set([
|
121
|
+
...GLOBAL_OBJECTS,
|
122
|
+
...userGlobalObjects,
|
123
|
+
]);
|
67
124
|
|
68
125
|
// If no globals are restricted, we don't need to do anything
|
69
|
-
if (
|
126
|
+
if (restrictedGlobals.length === 0) {
|
70
127
|
return {};
|
71
128
|
}
|
72
129
|
|
73
|
-
const restrictedGlobalMessages =
|
130
|
+
const restrictedGlobalMessages = restrictedGlobals.reduce(
|
74
131
|
(memo, option) => {
|
75
132
|
if (typeof option === "string") {
|
76
133
|
memo[option] = null;
|
@@ -151,6 +208,59 @@ module.exports = {
|
|
151
208
|
}
|
152
209
|
});
|
153
210
|
},
|
211
|
+
|
212
|
+
"Program:exit"(node) {
|
213
|
+
if (!checkGlobalObject) {
|
214
|
+
return;
|
215
|
+
}
|
216
|
+
|
217
|
+
const globalScope = sourceCode.getScope(node);
|
218
|
+
globalObjects.forEach(globalObjectName => {
|
219
|
+
const variable = astUtils.getVariableByName(
|
220
|
+
globalScope,
|
221
|
+
globalObjectName,
|
222
|
+
);
|
223
|
+
|
224
|
+
if (!variable) {
|
225
|
+
return;
|
226
|
+
}
|
227
|
+
|
228
|
+
variable.references.forEach(reference => {
|
229
|
+
const identifier = reference.identifier;
|
230
|
+
let parent = identifier.parent;
|
231
|
+
|
232
|
+
// To detect code like `window.window.Promise`.
|
233
|
+
while (
|
234
|
+
astUtils.isSpecificMemberAccess(
|
235
|
+
parent,
|
236
|
+
null,
|
237
|
+
globalObjectName,
|
238
|
+
)
|
239
|
+
) {
|
240
|
+
parent = parent.parent;
|
241
|
+
}
|
242
|
+
|
243
|
+
const propertyName =
|
244
|
+
astUtils.getStaticPropertyName(parent);
|
245
|
+
if (propertyName && isRestricted(propertyName)) {
|
246
|
+
const customMessage =
|
247
|
+
restrictedGlobalMessages[propertyName];
|
248
|
+
const messageId = customMessage
|
249
|
+
? "customMessage"
|
250
|
+
: "defaultMessage";
|
251
|
+
|
252
|
+
context.report({
|
253
|
+
node: parent.property,
|
254
|
+
messageId,
|
255
|
+
data: {
|
256
|
+
name: propertyName,
|
257
|
+
customMessage,
|
258
|
+
},
|
259
|
+
});
|
260
|
+
}
|
261
|
+
});
|
262
|
+
});
|
263
|
+
},
|
154
264
|
};
|
155
265
|
},
|
156
266
|
};
|
@@ -88,6 +88,9 @@ module.exports = {
|
|
88
88
|
ignoreClassWithStaticInitBlock: {
|
89
89
|
type: "boolean",
|
90
90
|
},
|
91
|
+
ignoreUsingDeclarations: {
|
92
|
+
type: "boolean",
|
93
|
+
},
|
91
94
|
reportUsedIgnorePattern: {
|
92
95
|
type: "boolean",
|
93
96
|
},
|
@@ -119,6 +122,7 @@ module.exports = {
|
|
119
122
|
ignoreRestSiblings: false,
|
120
123
|
caughtErrors: "all",
|
121
124
|
ignoreClassWithStaticInitBlock: false,
|
125
|
+
ignoreUsingDeclarations: false,
|
122
126
|
reportUsedIgnorePattern: false,
|
123
127
|
};
|
124
128
|
|
@@ -137,6 +141,9 @@ module.exports = {
|
|
137
141
|
config.ignoreClassWithStaticInitBlock =
|
138
142
|
firstOption.ignoreClassWithStaticInitBlock ||
|
139
143
|
config.ignoreClassWithStaticInitBlock;
|
144
|
+
config.ignoreUsingDeclarations =
|
145
|
+
firstOption.ignoreUsingDeclarations ||
|
146
|
+
config.ignoreUsingDeclarations;
|
140
147
|
config.reportUsedIgnorePattern =
|
141
148
|
firstOption.reportUsedIgnorePattern ||
|
142
149
|
config.reportUsedIgnorePattern;
|
@@ -357,6 +364,22 @@ module.exports = {
|
|
357
364
|
return false;
|
358
365
|
}
|
359
366
|
|
367
|
+
/**
|
368
|
+
* Determines if a given variable uses the explicit resource management protocol.
|
369
|
+
* @param {Variable} variable eslint-scope variable object.
|
370
|
+
* @returns {boolean} True if the variable is declared with "using" or "await using"
|
371
|
+
* @private
|
372
|
+
*/
|
373
|
+
function usesExplicitResourceManagement(variable) {
|
374
|
+
const [definition] = variable.defs;
|
375
|
+
|
376
|
+
return (
|
377
|
+
definition?.type === "Variable" &&
|
378
|
+
(definition.parent.kind === "using" ||
|
379
|
+
definition.parent.kind === "await using")
|
380
|
+
);
|
381
|
+
}
|
382
|
+
|
360
383
|
/**
|
361
384
|
* Checks whether a node is a sibling of the rest property or not.
|
362
385
|
* @param {ASTNode} node a node to check
|
@@ -922,6 +945,10 @@ module.exports = {
|
|
922
945
|
if (
|
923
946
|
!isUsedVariable(variable) &&
|
924
947
|
!isExported(variable) &&
|
948
|
+
!(
|
949
|
+
config.ignoreUsingDeclarations &&
|
950
|
+
usesExplicitResourceManagement(variable)
|
951
|
+
) &&
|
925
952
|
!hasRestSpreadSibling(variable)
|
926
953
|
) {
|
927
954
|
unusedVars.push(variable);
|