eslint 9.27.0 → 9.28.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/lib/cli.js +14 -12
- package/lib/config/config-loader.js +32 -21
- package/lib/config/config.js +34 -11
- package/lib/eslint/eslint.js +5 -4
- package/lib/languages/js/source-code/source-code.js +30 -0
- package/lib/linter/linter.js +27 -58
- package/lib/linter/{node-event-generator.js → source-code-traverser.js} +143 -87
- package/lib/options.js +7 -0
- package/lib/rules/func-style.js +57 -7
- package/lib/rules/no-implicit-globals.js +31 -15
- package/lib/rules/no-magic-numbers.js +98 -5
- package/lib/rules/no-shadow.js +262 -6
- package/lib/rules/no-unassigned-vars.js +14 -6
- package/lib/rules/no-use-before-define.js +97 -1
- package/lib/rules/prefer-arrow-callback.js +9 -0
- package/lib/services/warning-service.js +85 -0
- package/lib/types/index.d.ts +10 -5
- package/lib/types/rules.d.ts +47 -2
- package/package.json +7 -5
package/lib/rules/no-shadow.js
CHANGED
@@ -24,6 +24,23 @@ const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/u;
|
|
24
24
|
const SENTINEL_TYPE =
|
25
25
|
/^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/u;
|
26
26
|
|
27
|
+
// TS-specific node types
|
28
|
+
const TYPES_HOISTED_NODES = new Set([
|
29
|
+
"TSInterfaceDeclaration",
|
30
|
+
"TSTypeAliasDeclaration",
|
31
|
+
]);
|
32
|
+
|
33
|
+
// TS-specific function variable def types
|
34
|
+
const ALLOWED_FUNCTION_VARIABLE_DEF_TYPES = new Set([
|
35
|
+
"TSCallSignatureDeclaration",
|
36
|
+
"TSFunctionType",
|
37
|
+
"TSMethodSignature",
|
38
|
+
"TSEmptyBodyFunctionExpression",
|
39
|
+
"TSDeclareFunction",
|
40
|
+
"TSConstructSignatureDeclaration",
|
41
|
+
"TSConstructorType",
|
42
|
+
]);
|
43
|
+
|
27
44
|
//------------------------------------------------------------------------------
|
28
45
|
// Rule Definition
|
29
46
|
//------------------------------------------------------------------------------
|
@@ -32,6 +49,8 @@ const SENTINEL_TYPE =
|
|
32
49
|
module.exports = {
|
33
50
|
meta: {
|
34
51
|
type: "suggestion",
|
52
|
+
dialects: ["typescript", "javascript"],
|
53
|
+
language: "javascript",
|
35
54
|
|
36
55
|
defaultOptions: [
|
37
56
|
{
|
@@ -39,6 +58,8 @@ module.exports = {
|
|
39
58
|
builtinGlobals: false,
|
40
59
|
hoist: "functions",
|
41
60
|
ignoreOnInitialization: false,
|
61
|
+
ignoreTypeValueShadow: true,
|
62
|
+
ignoreFunctionTypeParameterNameValueShadow: true,
|
42
63
|
},
|
43
64
|
],
|
44
65
|
|
@@ -54,7 +75,15 @@ module.exports = {
|
|
54
75
|
type: "object",
|
55
76
|
properties: {
|
56
77
|
builtinGlobals: { type: "boolean" },
|
57
|
-
hoist: {
|
78
|
+
hoist: {
|
79
|
+
enum: [
|
80
|
+
"all",
|
81
|
+
"functions",
|
82
|
+
"never",
|
83
|
+
"types",
|
84
|
+
"functions-and-types",
|
85
|
+
],
|
86
|
+
},
|
58
87
|
allow: {
|
59
88
|
type: "array",
|
60
89
|
items: {
|
@@ -62,6 +91,10 @@ module.exports = {
|
|
62
91
|
},
|
63
92
|
},
|
64
93
|
ignoreOnInitialization: { type: "boolean" },
|
94
|
+
ignoreTypeValueShadow: { type: "boolean" },
|
95
|
+
ignoreFunctionTypeParameterNameValueShadow: {
|
96
|
+
type: "boolean",
|
97
|
+
},
|
65
98
|
},
|
66
99
|
additionalProperties: false,
|
67
100
|
},
|
@@ -75,10 +108,112 @@ module.exports = {
|
|
75
108
|
},
|
76
109
|
|
77
110
|
create(context) {
|
78
|
-
const [
|
79
|
-
|
111
|
+
const [
|
112
|
+
{
|
113
|
+
builtinGlobals,
|
114
|
+
hoist,
|
115
|
+
allow,
|
116
|
+
ignoreOnInitialization,
|
117
|
+
ignoreTypeValueShadow,
|
118
|
+
ignoreFunctionTypeParameterNameValueShadow,
|
119
|
+
},
|
120
|
+
] = context.options;
|
80
121
|
const sourceCode = context.sourceCode;
|
81
122
|
|
123
|
+
/**
|
124
|
+
* Check if a scope is a TypeScript module augmenting the global namespace.
|
125
|
+
* @param {Scope} scope The scope to check
|
126
|
+
* @returns {boolean} Whether the scope is a global augmentation
|
127
|
+
*/
|
128
|
+
function isGlobalAugmentation(scope) {
|
129
|
+
return (
|
130
|
+
scope.block.kind === "global" ||
|
131
|
+
(!!scope.upper && isGlobalAugmentation(scope.upper))
|
132
|
+
);
|
133
|
+
}
|
134
|
+
|
135
|
+
/**
|
136
|
+
* Check if variable is a `this` parameter.
|
137
|
+
* @param {Object} variable The variable to check
|
138
|
+
* @returns {boolean} Whether the variable is a this parameter
|
139
|
+
*/
|
140
|
+
function isThisParam(variable) {
|
141
|
+
return variable.name === "this";
|
142
|
+
}
|
143
|
+
|
144
|
+
/**
|
145
|
+
* Checks if type and value shadows each other
|
146
|
+
* @param {Object} variable The variable to check
|
147
|
+
* @param {Object} shadowedVariable The shadowed variable
|
148
|
+
* @returns {boolean} Whether it's a type/value shadow case to ignore
|
149
|
+
*/
|
150
|
+
function isTypeValueShadow(variable, shadowedVariable) {
|
151
|
+
if (ignoreTypeValueShadow !== true) {
|
152
|
+
return false;
|
153
|
+
}
|
154
|
+
|
155
|
+
if (!("isValueVariable" in variable)) {
|
156
|
+
return false;
|
157
|
+
}
|
158
|
+
|
159
|
+
const firstDefinition = shadowedVariable.defs[0];
|
160
|
+
|
161
|
+
// Check if shadowedVariable is a type import
|
162
|
+
const isTypeImport =
|
163
|
+
firstDefinition &&
|
164
|
+
firstDefinition.parent?.type === "ImportDeclaration" &&
|
165
|
+
(firstDefinition.parent.importKind === "type" ||
|
166
|
+
firstDefinition.parent.specifiers.some(
|
167
|
+
s => s.importKind === "type",
|
168
|
+
));
|
169
|
+
|
170
|
+
const isShadowedValue =
|
171
|
+
!firstDefinition ||
|
172
|
+
(isTypeImport ? false : shadowedVariable.isValueVariable);
|
173
|
+
|
174
|
+
return variable.isValueVariable !== isShadowedValue;
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* Checks if it's a function type parameter shadow
|
179
|
+
* @param {Object} variable The variable to check
|
180
|
+
* @returns {boolean} Whether it's a function type parameter shadow case to ignore
|
181
|
+
*/
|
182
|
+
function isFunctionTypeParameterNameValueShadow(variable) {
|
183
|
+
if (ignoreFunctionTypeParameterNameValueShadow !== true) {
|
184
|
+
return false;
|
185
|
+
}
|
186
|
+
|
187
|
+
return variable.defs.some(def =>
|
188
|
+
ALLOWED_FUNCTION_VARIABLE_DEF_TYPES.has(def.node.type),
|
189
|
+
);
|
190
|
+
}
|
191
|
+
|
192
|
+
/**
|
193
|
+
* Checks if the variable is a generic of a static method
|
194
|
+
* @param {Object} variable The variable to check
|
195
|
+
* @returns {boolean} Whether the variable is a generic of a static method
|
196
|
+
*/
|
197
|
+
function isTypeParameterOfStaticMethod(variable) {
|
198
|
+
const typeParameter = variable.identifiers[0].parent;
|
199
|
+
const typeParameterDecl = typeParameter.parent;
|
200
|
+
if (typeParameterDecl.type !== "TSTypeParameterDeclaration") {
|
201
|
+
return false;
|
202
|
+
}
|
203
|
+
const functionExpr = typeParameterDecl.parent;
|
204
|
+
const methodDefinition = functionExpr.parent;
|
205
|
+
return methodDefinition.static;
|
206
|
+
}
|
207
|
+
|
208
|
+
/**
|
209
|
+
* Checks for static method generic shadowing class generic
|
210
|
+
* @param {Object} variable The variable to check
|
211
|
+
* @returns {boolean} Whether it's a static method generic shadowing class generic
|
212
|
+
*/
|
213
|
+
function isGenericOfAStaticMethodShadow(variable) {
|
214
|
+
return isTypeParameterOfStaticMethod(variable);
|
215
|
+
}
|
216
|
+
|
82
217
|
/**
|
83
218
|
* Checks whether or not a given location is inside of the range of a given node.
|
84
219
|
* @param {ASTNode} node An node to check.
|
@@ -114,7 +249,7 @@ module.exports = {
|
|
114
249
|
function getOuterScope(scope) {
|
115
250
|
const upper = scope.upper;
|
116
251
|
|
117
|
-
if (upper.type === "function-expression-name") {
|
252
|
+
if (upper && upper.type === "function-expression-name") {
|
118
253
|
return upper.upper;
|
119
254
|
}
|
120
255
|
return upper;
|
@@ -284,6 +419,21 @@ module.exports = {
|
|
284
419
|
const inner = getNameRange(variable);
|
285
420
|
const outer = getNameRange(scopeVar);
|
286
421
|
|
422
|
+
if (!outer || inner[1] >= outer[0]) {
|
423
|
+
return false;
|
424
|
+
}
|
425
|
+
|
426
|
+
if (hoist === "types") {
|
427
|
+
return !TYPES_HOISTED_NODES.has(outerDef.node.type);
|
428
|
+
}
|
429
|
+
|
430
|
+
if (hoist === "functions-and-types") {
|
431
|
+
return (
|
432
|
+
outerDef.node.type !== "FunctionDeclaration" &&
|
433
|
+
!TYPES_HOISTED_NODES.has(outerDef.node.type)
|
434
|
+
);
|
435
|
+
}
|
436
|
+
|
287
437
|
return (
|
288
438
|
inner &&
|
289
439
|
outer &&
|
@@ -295,12 +445,111 @@ module.exports = {
|
|
295
445
|
);
|
296
446
|
}
|
297
447
|
|
448
|
+
/**
|
449
|
+
* Checks if the initialization of a variable has the declare modifier in a
|
450
|
+
* definition file.
|
451
|
+
* @param {Object} variable The variable to check
|
452
|
+
* @returns {boolean} Whether the variable is declared in a definition file
|
453
|
+
*/
|
454
|
+
function isDeclareInDTSFile(variable) {
|
455
|
+
const fileName = context.filename;
|
456
|
+
if (
|
457
|
+
!fileName.endsWith(".d.ts") &&
|
458
|
+
!fileName.endsWith(".d.cts") &&
|
459
|
+
!fileName.endsWith(".d.mts")
|
460
|
+
) {
|
461
|
+
return false;
|
462
|
+
}
|
463
|
+
return variable.defs.some(
|
464
|
+
def =>
|
465
|
+
(def.type === "Variable" && def.parent.declare) ||
|
466
|
+
(def.type === "ClassName" && def.node.declare) ||
|
467
|
+
(def.type === "TSEnumName" && def.node.declare) ||
|
468
|
+
(def.type === "TSModuleName" && def.node.declare),
|
469
|
+
);
|
470
|
+
}
|
471
|
+
|
472
|
+
/**
|
473
|
+
* Checks if a variable is a duplicate of an enum name in the enum scope
|
474
|
+
* @param {Object} variable The variable to check
|
475
|
+
* @returns {boolean} Whether it's a duplicate enum name variable
|
476
|
+
*/
|
477
|
+
function isDuplicatedEnumNameVariable(variable) {
|
478
|
+
const block = variable.scope.block;
|
479
|
+
|
480
|
+
return (
|
481
|
+
block.type === "TSEnumDeclaration" &&
|
482
|
+
block.id === variable.identifiers[0]
|
483
|
+
);
|
484
|
+
}
|
485
|
+
|
486
|
+
/**
|
487
|
+
* Check if this is an external module declaration merging with a type import
|
488
|
+
* @param {Scope} scope Current scope
|
489
|
+
* @param {Object} variable Current variable
|
490
|
+
* @param {Object} shadowedVariable Shadowed variable
|
491
|
+
* @returns {boolean} Whether it's an external declaration merging
|
492
|
+
*/
|
493
|
+
function isExternalDeclarationMerging(
|
494
|
+
scope,
|
495
|
+
variable,
|
496
|
+
shadowedVariable,
|
497
|
+
) {
|
498
|
+
const firstDefinition = shadowedVariable.defs[0];
|
499
|
+
|
500
|
+
if (!firstDefinition || !firstDefinition.parent) {
|
501
|
+
return false;
|
502
|
+
}
|
503
|
+
|
504
|
+
// Check if the shadowed variable is a type import
|
505
|
+
const isTypeImport =
|
506
|
+
firstDefinition.parent.type === "ImportDeclaration" &&
|
507
|
+
(firstDefinition.parent.importKind === "type" ||
|
508
|
+
firstDefinition.parent.specifiers?.some(
|
509
|
+
s =>
|
510
|
+
s.type === "ImportSpecifier" &&
|
511
|
+
s.importKind === "type" &&
|
512
|
+
s.local.name === shadowedVariable.name,
|
513
|
+
));
|
514
|
+
|
515
|
+
if (!isTypeImport) {
|
516
|
+
return false;
|
517
|
+
}
|
518
|
+
|
519
|
+
// Check if the current variable is within a module declaration
|
520
|
+
const moduleDecl = findSelfOrAncestor(
|
521
|
+
variable.identifiers[0]?.parent,
|
522
|
+
node => node.type === "TSModuleDeclaration",
|
523
|
+
);
|
524
|
+
|
525
|
+
if (!moduleDecl) {
|
526
|
+
return false;
|
527
|
+
}
|
528
|
+
|
529
|
+
/*
|
530
|
+
* Module declaration merging should only happen within the same module
|
531
|
+
* Check if the module name matches the import source
|
532
|
+
*/
|
533
|
+
const importSource = firstDefinition.parent.source.value;
|
534
|
+
const moduleName =
|
535
|
+
moduleDecl.id.type === "Literal"
|
536
|
+
? moduleDecl.id.value
|
537
|
+
: moduleDecl.id.name;
|
538
|
+
|
539
|
+
return importSource === moduleName;
|
540
|
+
}
|
541
|
+
|
298
542
|
/**
|
299
543
|
* Checks the current context for shadowed variables.
|
300
544
|
* @param {Scope} scope Fixme
|
301
545
|
* @returns {void}
|
302
546
|
*/
|
303
547
|
function checkForShadows(scope) {
|
548
|
+
// ignore global augmentation
|
549
|
+
if (isGlobalAugmentation(scope)) {
|
550
|
+
return;
|
551
|
+
}
|
552
|
+
|
304
553
|
const variables = scope.variables;
|
305
554
|
|
306
555
|
for (let i = 0; i < variables.length; ++i) {
|
@@ -310,7 +559,10 @@ module.exports = {
|
|
310
559
|
if (
|
311
560
|
variable.identifiers.length === 0 ||
|
312
561
|
isDuplicatedClassNameVariable(variable) ||
|
313
|
-
|
562
|
+
isDuplicatedEnumNameVariable(variable) ||
|
563
|
+
isAllowed(variable) ||
|
564
|
+
isDeclareInDTSFile(variable) ||
|
565
|
+
isThisParam(variable)
|
314
566
|
) {
|
315
567
|
continue;
|
316
568
|
}
|
@@ -330,7 +582,11 @@ module.exports = {
|
|
330
582
|
ignoreOnInitialization &&
|
331
583
|
isInitPatternNode(variable, shadowed)
|
332
584
|
) &&
|
333
|
-
!(hoist !== "all" && isInTdz(variable, shadowed))
|
585
|
+
!(hoist !== "all" && isInTdz(variable, shadowed)) &&
|
586
|
+
!isTypeValueShadow(variable, shadowed) &&
|
587
|
+
!isFunctionTypeParameterNameValueShadow(variable) &&
|
588
|
+
!isGenericOfAStaticMethodShadow(variable, shadowed) &&
|
589
|
+
!isExternalDeclarationMerging(scope, variable, shadowed)
|
334
590
|
) {
|
335
591
|
const location = getDeclaredLocation(shadowed);
|
336
592
|
const messageId = location.global
|
@@ -31,17 +31,25 @@ module.exports = {
|
|
31
31
|
|
32
32
|
create(context) {
|
33
33
|
const sourceCode = context.sourceCode;
|
34
|
+
let insideDeclareModule = false;
|
34
35
|
|
35
36
|
return {
|
37
|
+
"TSModuleDeclaration[declare=true]"() {
|
38
|
+
insideDeclareModule = true;
|
39
|
+
},
|
40
|
+
"TSModuleDeclaration[declare=true]:exit"() {
|
41
|
+
insideDeclareModule = false;
|
42
|
+
},
|
36
43
|
VariableDeclarator(node) {
|
37
44
|
/** @type {import('estree').VariableDeclaration} */
|
38
45
|
const declaration = node.parent;
|
39
|
-
const
|
40
|
-
|
41
|
-
node.id.type
|
42
|
-
declaration.kind
|
43
|
-
|
44
|
-
|
46
|
+
const shouldSkip =
|
47
|
+
node.init ||
|
48
|
+
node.id.type !== "Identifier" ||
|
49
|
+
declaration.kind === "const" ||
|
50
|
+
declaration.declare ||
|
51
|
+
insideDeclareModule;
|
52
|
+
if (shouldSkip) {
|
45
53
|
return;
|
46
54
|
}
|
47
55
|
const [variable] = sourceCode.getDeclaredVariables(node);
|
@@ -30,6 +30,9 @@ function parseOptions(options) {
|
|
30
30
|
classes: true,
|
31
31
|
variables: true,
|
32
32
|
allowNamedExports: false,
|
33
|
+
enums: true,
|
34
|
+
typedefs: true,
|
35
|
+
ignoreTypeReferences: true,
|
33
36
|
};
|
34
37
|
}
|
35
38
|
|
@@ -208,6 +211,57 @@ function isEvaluatedDuringInitialization(reference) {
|
|
208
211
|
return false;
|
209
212
|
}
|
210
213
|
|
214
|
+
/**
|
215
|
+
* check whether the reference contains a type query.
|
216
|
+
* @param {ASTNode} node Identifier node to check.
|
217
|
+
* @returns {boolean} true if reference contains type query.
|
218
|
+
*/
|
219
|
+
function referenceContainsTypeQuery(node) {
|
220
|
+
switch (node.type) {
|
221
|
+
case "TSTypeQuery":
|
222
|
+
return true;
|
223
|
+
|
224
|
+
case "TSQualifiedName":
|
225
|
+
case "Identifier":
|
226
|
+
return referenceContainsTypeQuery(node.parent);
|
227
|
+
|
228
|
+
default:
|
229
|
+
// if we find a different node, there's no chance that we're in a TSTypeQuery
|
230
|
+
return false;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
/**
|
235
|
+
* Decorators are transpiled such that the decorator is placed after the class declaration
|
236
|
+
* So it is considered safe
|
237
|
+
* @param {Variable} variable The variable to check.
|
238
|
+
* @param {Reference} reference The reference to check.
|
239
|
+
* @returns {boolean} `true` if the reference is in a class decorator.
|
240
|
+
*/
|
241
|
+
function isClassRefInClassDecorator(variable, reference) {
|
242
|
+
if (variable.defs[0].type !== "ClassName") {
|
243
|
+
return false;
|
244
|
+
}
|
245
|
+
|
246
|
+
if (
|
247
|
+
!variable.defs[0].node.decorators ||
|
248
|
+
variable.defs[0].node.decorators.length === 0
|
249
|
+
) {
|
250
|
+
return false;
|
251
|
+
}
|
252
|
+
|
253
|
+
for (const deco of variable.defs[0].node.decorators) {
|
254
|
+
if (
|
255
|
+
reference.identifier.range[0] >= deco.range[0] &&
|
256
|
+
reference.identifier.range[1] <= deco.range[1]
|
257
|
+
) {
|
258
|
+
return true;
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
return false;
|
263
|
+
}
|
264
|
+
|
211
265
|
//------------------------------------------------------------------------------
|
212
266
|
// Rule Definition
|
213
267
|
//------------------------------------------------------------------------------
|
@@ -237,6 +291,9 @@ module.exports = {
|
|
237
291
|
classes: { type: "boolean" },
|
238
292
|
variables: { type: "boolean" },
|
239
293
|
allowNamedExports: { type: "boolean" },
|
294
|
+
enums: { type: "boolean" },
|
295
|
+
typedefs: { type: "boolean" },
|
296
|
+
ignoreTypeReferences: { type: "boolean" },
|
240
297
|
},
|
241
298
|
additionalProperties: false,
|
242
299
|
},
|
@@ -250,6 +307,9 @@ module.exports = {
|
|
250
307
|
functions: true,
|
251
308
|
variables: true,
|
252
309
|
allowNamedExports: false,
|
310
|
+
enums: true,
|
311
|
+
typedefs: true,
|
312
|
+
ignoreTypeReferences: true,
|
253
313
|
},
|
254
314
|
],
|
255
315
|
|
@@ -310,6 +370,41 @@ module.exports = {
|
|
310
370
|
return false;
|
311
371
|
}
|
312
372
|
|
373
|
+
if (!options.enums && definitionType === "TSEnumName") {
|
374
|
+
return false;
|
375
|
+
}
|
376
|
+
|
377
|
+
if (!options.typedefs && definitionType === "Type") {
|
378
|
+
return false;
|
379
|
+
}
|
380
|
+
|
381
|
+
if (
|
382
|
+
options.ignoreTypeReferences &&
|
383
|
+
(referenceContainsTypeQuery(identifier) ||
|
384
|
+
identifier.parent.type === "TSTypeReference")
|
385
|
+
) {
|
386
|
+
return false;
|
387
|
+
}
|
388
|
+
|
389
|
+
// skip nested namespace aliases as variable references
|
390
|
+
if (identifier.parent.type === "TSQualifiedName") {
|
391
|
+
let currentNode = identifier.parent;
|
392
|
+
|
393
|
+
while (currentNode.type === "TSQualifiedName") {
|
394
|
+
currentNode = currentNode.left;
|
395
|
+
}
|
396
|
+
|
397
|
+
if (currentNode === identifier) {
|
398
|
+
return true;
|
399
|
+
}
|
400
|
+
|
401
|
+
return false;
|
402
|
+
}
|
403
|
+
|
404
|
+
if (isClassRefInClassDecorator(variable, reference)) {
|
405
|
+
return false;
|
406
|
+
}
|
407
|
+
|
313
408
|
return true;
|
314
409
|
}
|
315
410
|
|
@@ -326,7 +421,8 @@ module.exports = {
|
|
326
421
|
if (
|
327
422
|
reference.identifier.range[1] <
|
328
423
|
definitionIdentifier.range[1] ||
|
329
|
-
isEvaluatedDuringInitialization(reference)
|
424
|
+
(isEvaluatedDuringInitialization(reference) &&
|
425
|
+
reference.identifier.parent.type !== "TSTypeReference")
|
330
426
|
) {
|
331
427
|
context.report({
|
332
428
|
node: reference.identifier,
|
@@ -151,6 +151,8 @@ function hasDuplicateParams(paramsList) {
|
|
151
151
|
module.exports = {
|
152
152
|
meta: {
|
153
153
|
type: "suggestion",
|
154
|
+
dialects: ["javascript", "typescript"],
|
155
|
+
language: "javascript",
|
154
156
|
|
155
157
|
defaultOptions: [
|
156
158
|
{ allowNamedFunctions: false, allowUnboundThis: true },
|
@@ -308,6 +310,13 @@ module.exports = {
|
|
308
310
|
return;
|
309
311
|
}
|
310
312
|
|
313
|
+
if (
|
314
|
+
node.params.length &&
|
315
|
+
node.params[0].name === "this"
|
316
|
+
) {
|
317
|
+
return;
|
318
|
+
}
|
319
|
+
|
311
320
|
// Remove `.bind(this)` if exists.
|
312
321
|
if (callbackInfo.isLexicalThis) {
|
313
322
|
const memberNode = node.parent;
|
@@ -0,0 +1,85 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Emits warnings for ESLint.
|
3
|
+
* @author Francesco Trotta
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
//-----------------------------------------------------------------------------
|
9
|
+
// Exports
|
10
|
+
//-----------------------------------------------------------------------------
|
11
|
+
|
12
|
+
/**
|
13
|
+
* A service that emits warnings for ESLint.
|
14
|
+
*/
|
15
|
+
class WarningService {
|
16
|
+
/**
|
17
|
+
* Creates a new instance of the service.
|
18
|
+
* @param {{ emitWarning?: ((warning: string, type: string) => void) | undefined }} [options] A function called internally to emit warnings using API provided by the runtime.
|
19
|
+
*/
|
20
|
+
constructor({
|
21
|
+
emitWarning = globalThis.process?.emitWarning ?? (() => {}),
|
22
|
+
} = {}) {
|
23
|
+
this.emitWarning = emitWarning;
|
24
|
+
}
|
25
|
+
|
26
|
+
/**
|
27
|
+
* Emits a warning when circular fixes are detected while fixing a file.
|
28
|
+
* This method is used by the Linter and is safe to call outside Node.js.
|
29
|
+
* @param {string} filename The name of the file being fixed.
|
30
|
+
* @returns {void}
|
31
|
+
*/
|
32
|
+
emitCircularFixesWarning(filename) {
|
33
|
+
this.emitWarning(
|
34
|
+
`Circular fixes detected while fixing ${filename}. It is likely that you have conflicting rules in your configuration.`,
|
35
|
+
"ESLintCircularFixesWarning",
|
36
|
+
);
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Emits a warning when an empty config file has been loaded.
|
41
|
+
* @param {string} configFilePath The path to the config file.
|
42
|
+
* @returns {void}
|
43
|
+
*/
|
44
|
+
emitEmptyConfigWarning(configFilePath) {
|
45
|
+
this.emitWarning(
|
46
|
+
`Running ESLint with an empty config (from ${configFilePath}). Please double-check that this is what you want. If you want to run ESLint with an empty config, export [{}] to remove this warning.`,
|
47
|
+
"ESLintEmptyConfigWarning",
|
48
|
+
);
|
49
|
+
}
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Emits a warning when an ".eslintignore" file is found.
|
53
|
+
* @returns {void}
|
54
|
+
*/
|
55
|
+
emitESLintIgnoreWarning() {
|
56
|
+
this.emitWarning(
|
57
|
+
'The ".eslintignore" file is no longer supported. Switch to using the "ignores" property in "eslint.config.js": https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files',
|
58
|
+
"ESLintIgnoreWarning",
|
59
|
+
);
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Emits a warning when the ESLINT_USE_FLAT_CONFIG environment variable is set to "false".
|
64
|
+
* @returns {void}
|
65
|
+
*/
|
66
|
+
emitESLintRCWarning() {
|
67
|
+
this.emitWarning(
|
68
|
+
"You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details. An eslintrc configuration file is used because you have the ESLINT_USE_FLAT_CONFIG environment variable set to false. If you want to use an eslint.config.js file, remove the environment variable. If you want to find the location of the eslintrc configuration file, use the --debug flag.",
|
69
|
+
"ESLintRCWarning",
|
70
|
+
);
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Emits a warning when an inactive flag is used.
|
75
|
+
* This method is used by the Linter and is safe to call outside Node.js.
|
76
|
+
* @param {string} flag The name of the flag.
|
77
|
+
* @param {string} message The warning message.
|
78
|
+
* @returns {void}
|
79
|
+
*/
|
80
|
+
emitInactiveFlagWarning(flag, message) {
|
81
|
+
this.emitWarning(message, `ESLintInactiveFlag_${flag}`);
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
module.exports = { WarningService };
|
package/lib/types/index.d.ts
CHANGED
@@ -584,6 +584,11 @@ export namespace SourceCode {
|
|
584
584
|
|
585
585
|
// #endregion
|
586
586
|
|
587
|
+
export type JSSyntaxElement = {
|
588
|
+
type: string;
|
589
|
+
loc?: ESTree.SourceLocation | null | undefined;
|
590
|
+
};
|
591
|
+
|
587
592
|
export namespace Rule {
|
588
593
|
interface RuleModule
|
589
594
|
extends RuleDefinition<{
|
@@ -591,7 +596,7 @@ export namespace Rule {
|
|
591
596
|
Code: SourceCode;
|
592
597
|
RuleOptions: any[];
|
593
598
|
Visitor: NodeListener;
|
594
|
-
Node:
|
599
|
+
Node: JSSyntaxElement;
|
595
600
|
MessageIds: string;
|
596
601
|
ExtRuleDocs: {};
|
597
602
|
}> {
|
@@ -1159,10 +1164,10 @@ export namespace Rule {
|
|
1159
1164
|
/**
|
1160
1165
|
* Indicates the type of rule:
|
1161
1166
|
* - `"problem"` means the rule is identifying code that either will cause an error or may cause a confusing behavior. Developers should consider this a high priority to resolve.
|
1162
|
-
* - `"suggestion"` means the rule is identifying something that could be done in a better way but no errors will occur if the code isn
|
1167
|
+
* - `"suggestion"` means the rule is identifying something that could be done in a better way but no errors will occur if the code isn't changed.
|
1163
1168
|
* - `"layout"` means the rule cares primarily about whitespace, semicolons, commas, and parentheses,
|
1164
1169
|
* all the parts of the program that determine how the code looks rather than how it executes.
|
1165
|
-
* These rules work on parts of the code that aren
|
1170
|
+
* These rules work on parts of the code that aren't specified in the AST.
|
1166
1171
|
*/
|
1167
1172
|
type?: "problem" | "suggestion" | "layout" | undefined;
|
1168
1173
|
/**
|
@@ -1177,7 +1182,7 @@ export namespace Rule {
|
|
1177
1182
|
LangOptions: Linter.LanguageOptions;
|
1178
1183
|
Code: SourceCode;
|
1179
1184
|
RuleOptions: any[];
|
1180
|
-
Node:
|
1185
|
+
Node: JSSyntaxElement;
|
1181
1186
|
MessageIds: string;
|
1182
1187
|
}> {
|
1183
1188
|
/*
|
@@ -1275,7 +1280,7 @@ export type JSRuleDefinition<
|
|
1275
1280
|
LangOptions: Linter.LanguageOptions;
|
1276
1281
|
Code: SourceCode;
|
1277
1282
|
Visitor: Rule.NodeListener;
|
1278
|
-
Node:
|
1283
|
+
Node: JSSyntaxElement;
|
1279
1284
|
} & Required<
|
1280
1285
|
// Rule specific type options (custom)
|
1281
1286
|
Options &
|