eslint 9.27.0 → 9.29.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 +1 -1
- package/conf/ecma-version.js +1 -1
- package/conf/globals.js +10 -0
- package/lib/cli.js +20 -23
- package/lib/config/config-loader.js +32 -21
- package/lib/config/config.js +34 -11
- package/lib/eslint/eslint.js +18 -21
- package/lib/languages/js/source-code/source-code.js +104 -27
- package/lib/linter/apply-disable-directives.js +2 -4
- package/lib/linter/code-path-analysis/code-path-analyzer.js +8 -9
- package/lib/linter/linter.js +30 -61
- package/lib/linter/source-code-traverser.js +327 -0
- package/lib/linter/source-code-visitor.js +81 -0
- package/lib/options.js +7 -0
- package/lib/rules/class-methods-use-this.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-promise-executor-return.js +4 -35
- package/lib/rules/no-restricted-globals.js +35 -2
- package/lib/rules/no-restricted-properties.js +24 -10
- package/lib/rules/no-setter-return.js +13 -48
- 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 +99 -1
- package/lib/rules/no-var.js +14 -2
- package/lib/rules/prefer-arrow-callback.js +9 -0
- package/lib/rules/prefer-regex-literals.js +1 -18
- package/lib/services/suppressions-service.js +8 -0
- package/lib/services/warning-service.js +85 -0
- package/lib/shared/naming.js +109 -0
- package/lib/shared/relative-module-resolver.js +28 -0
- package/lib/types/index.d.ts +18 -7
- package/lib/types/rules.d.ts +52 -2
- package/package.json +12 -10
- package/lib/linter/node-event-generator.js +0 -256
- package/lib/linter/safe-emitter.js +0 -52
@@ -111,6 +111,7 @@ module.exports = {
|
|
111
111
|
switch (node.type) {
|
112
112
|
case "MethodDefinition":
|
113
113
|
return !node.static && node.kind !== "constructor";
|
114
|
+
case "AccessorProperty":
|
114
115
|
case "PropertyDefinition":
|
115
116
|
return !node.static && enforceForClassFields;
|
116
117
|
default:
|
@@ -218,6 +219,8 @@ module.exports = {
|
|
218
219
|
/*
|
219
220
|
* Class field value are implicit functions.
|
220
221
|
*/
|
222
|
+
"AccessorProperty > *.key:exit": pushContext,
|
223
|
+
"AccessorProperty:exit": popContext,
|
221
224
|
"PropertyDefinition > *.key:exit": pushContext,
|
222
225
|
"PropertyDefinition:exit": popContext,
|
223
226
|
|
@@ -233,6 +236,10 @@ module.exports = {
|
|
233
236
|
ThisExpression: markThisUsed,
|
234
237
|
Super: markThisUsed,
|
235
238
|
...(enforceForClassFields && {
|
239
|
+
"AccessorProperty > ArrowFunctionExpression.value":
|
240
|
+
enterFunction,
|
241
|
+
"AccessorProperty > ArrowFunctionExpression.value:exit":
|
242
|
+
exitFunction,
|
236
243
|
"PropertyDefinition > ArrowFunctionExpression.value":
|
237
244
|
enterFunction,
|
238
245
|
"PropertyDefinition > ArrowFunctionExpression.value:exit":
|
package/lib/rules/func-style.js
CHANGED
@@ -11,12 +11,15 @@
|
|
11
11
|
/** @type {import('../types').Rule.RuleModule} */
|
12
12
|
module.exports = {
|
13
13
|
meta: {
|
14
|
+
dialects: ["javascript", "typescript"],
|
15
|
+
language: "javascript",
|
14
16
|
type: "suggestion",
|
15
17
|
|
16
18
|
defaultOptions: [
|
17
19
|
"expression",
|
18
20
|
{
|
19
21
|
allowArrowFunctions: false,
|
22
|
+
allowTypeAnnotation: false,
|
20
23
|
overrides: {},
|
21
24
|
},
|
22
25
|
],
|
@@ -39,6 +42,9 @@ module.exports = {
|
|
39
42
|
allowArrowFunctions: {
|
40
43
|
type: "boolean",
|
41
44
|
},
|
45
|
+
allowTypeAnnotation: {
|
46
|
+
type: "boolean",
|
47
|
+
},
|
42
48
|
overrides: {
|
43
49
|
type: "object",
|
44
50
|
properties: {
|
@@ -60,11 +66,49 @@ module.exports = {
|
|
60
66
|
},
|
61
67
|
|
62
68
|
create(context) {
|
63
|
-
const [style, { allowArrowFunctions, overrides }] =
|
69
|
+
const [style, { allowArrowFunctions, allowTypeAnnotation, overrides }] =
|
70
|
+
context.options;
|
64
71
|
const enforceDeclarations = style === "declaration";
|
65
72
|
const { namedExports: exportFunctionStyle } = overrides;
|
66
73
|
const stack = [];
|
67
74
|
|
75
|
+
/**
|
76
|
+
* Checks if a function declaration is part of an overloaded function
|
77
|
+
* @param {ASTNode} node The function declaration node to check
|
78
|
+
* @returns {boolean} True if the function is overloaded
|
79
|
+
*/
|
80
|
+
function isOverloadedFunction(node) {
|
81
|
+
const functionName = node.id.name;
|
82
|
+
|
83
|
+
if (node.parent.type === "ExportNamedDeclaration") {
|
84
|
+
return node.parent.parent.body.some(
|
85
|
+
member =>
|
86
|
+
member.type === "ExportNamedDeclaration" &&
|
87
|
+
member.declaration?.type === "TSDeclareFunction" &&
|
88
|
+
member.declaration.id.name === functionName,
|
89
|
+
);
|
90
|
+
}
|
91
|
+
|
92
|
+
if (node.parent.type === "SwitchCase") {
|
93
|
+
return node.parent.parent.cases.some(switchCase =>
|
94
|
+
switchCase.consequent.some(
|
95
|
+
member =>
|
96
|
+
member.type === "TSDeclareFunction" &&
|
97
|
+
member.id.name === functionName,
|
98
|
+
),
|
99
|
+
);
|
100
|
+
}
|
101
|
+
|
102
|
+
return (
|
103
|
+
Array.isArray(node.parent.body) &&
|
104
|
+
node.parent.body.some(
|
105
|
+
member =>
|
106
|
+
member.type === "TSDeclareFunction" &&
|
107
|
+
member.id.name === functionName,
|
108
|
+
)
|
109
|
+
);
|
110
|
+
}
|
111
|
+
|
68
112
|
const nodesToCheck = {
|
69
113
|
FunctionDeclaration(node) {
|
70
114
|
stack.push(false);
|
@@ -73,14 +117,16 @@ module.exports = {
|
|
73
117
|
!enforceDeclarations &&
|
74
118
|
node.parent.type !== "ExportDefaultDeclaration" &&
|
75
119
|
(typeof exportFunctionStyle === "undefined" ||
|
76
|
-
node.parent.type !== "ExportNamedDeclaration")
|
120
|
+
node.parent.type !== "ExportNamedDeclaration") &&
|
121
|
+
!isOverloadedFunction(node)
|
77
122
|
) {
|
78
123
|
context.report({ node, messageId: "expression" });
|
79
124
|
}
|
80
125
|
|
81
126
|
if (
|
82
127
|
node.parent.type === "ExportNamedDeclaration" &&
|
83
|
-
exportFunctionStyle === "expression"
|
128
|
+
exportFunctionStyle === "expression" &&
|
129
|
+
!isOverloadedFunction(node)
|
84
130
|
) {
|
85
131
|
context.report({ node, messageId: "expression" });
|
86
132
|
}
|
@@ -97,7 +143,8 @@ module.exports = {
|
|
97
143
|
node.parent.type === "VariableDeclarator" &&
|
98
144
|
(typeof exportFunctionStyle === "undefined" ||
|
99
145
|
node.parent.parent.parent.type !==
|
100
|
-
"ExportNamedDeclaration")
|
146
|
+
"ExportNamedDeclaration") &&
|
147
|
+
!(allowTypeAnnotation && node.parent.id.typeAnnotation)
|
101
148
|
) {
|
102
149
|
context.report({
|
103
150
|
node: node.parent,
|
@@ -109,7 +156,8 @@ module.exports = {
|
|
109
156
|
node.parent.type === "VariableDeclarator" &&
|
110
157
|
node.parent.parent.parent.type ===
|
111
158
|
"ExportNamedDeclaration" &&
|
112
|
-
exportFunctionStyle === "declaration"
|
159
|
+
exportFunctionStyle === "declaration" &&
|
160
|
+
!(allowTypeAnnotation && node.parent.id.typeAnnotation)
|
113
161
|
) {
|
114
162
|
context.report({
|
115
163
|
node: node.parent,
|
@@ -144,7 +192,8 @@ module.exports = {
|
|
144
192
|
enforceDeclarations &&
|
145
193
|
(typeof exportFunctionStyle === "undefined" ||
|
146
194
|
node.parent.parent.parent.type !==
|
147
|
-
"ExportNamedDeclaration")
|
195
|
+
"ExportNamedDeclaration") &&
|
196
|
+
!(allowTypeAnnotation && node.parent.id.typeAnnotation)
|
148
197
|
) {
|
149
198
|
context.report({
|
150
199
|
node: node.parent,
|
@@ -155,7 +204,8 @@ module.exports = {
|
|
155
204
|
if (
|
156
205
|
node.parent.parent.parent.type ===
|
157
206
|
"ExportNamedDeclaration" &&
|
158
|
-
exportFunctionStyle === "declaration"
|
207
|
+
exportFunctionStyle === "declaration" &&
|
208
|
+
!(allowTypeAnnotation && node.parent.id.typeAnnotation)
|
159
209
|
) {
|
160
210
|
context.report({
|
161
211
|
node: node.parent,
|
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
const ASSIGNMENT_NODES = new Set([
|
9
|
+
"AssignmentExpression",
|
10
|
+
"ForInStatement",
|
11
|
+
"ForOfStatement",
|
12
|
+
]);
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Rule Definition
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -142,27 +148,37 @@ module.exports = {
|
|
142
148
|
}
|
143
149
|
}
|
144
150
|
});
|
145
|
-
});
|
146
151
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
152
|
+
if (
|
153
|
+
isReadonlyEslintGlobalVariable &&
|
154
|
+
variable.defs.length === 0
|
155
|
+
) {
|
156
|
+
variable.references.forEach(reference => {
|
157
|
+
if (reference.isWrite() && !reference.isRead()) {
|
158
|
+
let assignmentParent =
|
159
|
+
reference.identifier.parent;
|
160
|
+
|
161
|
+
while (
|
162
|
+
assignmentParent &&
|
163
|
+
!ASSIGNMENT_NODES.has(assignmentParent.type)
|
164
|
+
) {
|
165
|
+
assignmentParent = assignmentParent.parent;
|
166
|
+
}
|
151
167
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
} else {
|
159
|
-
// Reference to an unknown variable, possible global leak.
|
160
|
-
messageId = "globalVariableLeak";
|
168
|
+
report(
|
169
|
+
assignmentParent ?? reference.identifier,
|
170
|
+
"assignmentToReadonlyGlobal",
|
171
|
+
);
|
172
|
+
}
|
173
|
+
});
|
161
174
|
}
|
175
|
+
});
|
162
176
|
|
177
|
+
// Undeclared assigned variables.
|
178
|
+
scope.implicit.variables.forEach(variable => {
|
163
179
|
// def.node is an AssignmentExpression, ForInStatement or ForOfStatement.
|
164
180
|
variable.defs.forEach(def => {
|
165
|
-
report(def.node,
|
181
|
+
report(def.node, "globalVariableLeak");
|
166
182
|
});
|
167
183
|
});
|
168
184
|
},
|
@@ -26,10 +26,73 @@ function normalizeIgnoreValue(x) {
|
|
26
26
|
return x;
|
27
27
|
}
|
28
28
|
|
29
|
+
/**
|
30
|
+
* Checks if the node parent is a TypeScript enum member
|
31
|
+
* @param {ASTNode} node The node to be validated
|
32
|
+
* @returns {boolean} True if the node parent is a TypeScript enum member
|
33
|
+
*/
|
34
|
+
function isParentTSEnumDeclaration(node) {
|
35
|
+
return node.parent.type === "TSEnumMember";
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Checks if the node is a valid TypeScript numeric literal type.
|
40
|
+
* @param {ASTNode} node The node to be validated
|
41
|
+
* @returns {boolean} True if the node is a TypeScript numeric literal type
|
42
|
+
*/
|
43
|
+
function isTSNumericLiteralType(node) {
|
44
|
+
let ancestor = node.parent;
|
45
|
+
|
46
|
+
// Go up while we're part of a type union
|
47
|
+
while (ancestor.parent.type === "TSUnionType") {
|
48
|
+
ancestor = ancestor.parent;
|
49
|
+
}
|
50
|
+
|
51
|
+
// Check if the final ancestor is in a type alias declaration
|
52
|
+
return ancestor.parent.type === "TSTypeAliasDeclaration";
|
53
|
+
}
|
54
|
+
|
55
|
+
/**
|
56
|
+
* Checks if the node parent is a readonly class property
|
57
|
+
* @param {ASTNode} node The node to be validated
|
58
|
+
* @returns {boolean} True if the node parent is a readonly class property
|
59
|
+
*/
|
60
|
+
function isParentTSReadonlyPropertyDefinition(node) {
|
61
|
+
if (node.parent?.type === "PropertyDefinition" && node.parent.readonly) {
|
62
|
+
return true;
|
63
|
+
}
|
64
|
+
|
65
|
+
return false;
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Checks if the node is part of a type indexed access (eg. Foo[4])
|
70
|
+
* @param {ASTNode} node The node to be validated
|
71
|
+
* @returns {boolean} True if the node is part of an indexed access
|
72
|
+
*/
|
73
|
+
function isAncestorTSIndexedAccessType(node) {
|
74
|
+
let ancestor = node.parent;
|
75
|
+
|
76
|
+
/*
|
77
|
+
* Go up another level while we're part of a type union (eg. 1 | 2) or
|
78
|
+
* intersection (eg. 1 & 2)
|
79
|
+
*/
|
80
|
+
while (
|
81
|
+
ancestor.parent.type === "TSUnionType" ||
|
82
|
+
ancestor.parent.type === "TSIntersectionType"
|
83
|
+
) {
|
84
|
+
ancestor = ancestor.parent;
|
85
|
+
}
|
86
|
+
|
87
|
+
return ancestor.parent.type === "TSIndexedAccessType";
|
88
|
+
}
|
89
|
+
|
29
90
|
/** @type {import('../types').Rule.RuleModule} */
|
30
91
|
module.exports = {
|
31
92
|
meta: {
|
32
93
|
type: "suggestion",
|
94
|
+
dialects: ["typescript", "javascript"],
|
95
|
+
language: "javascript",
|
33
96
|
|
34
97
|
docs: {
|
35
98
|
description: "Disallow magic numbers",
|
@@ -75,6 +138,22 @@ module.exports = {
|
|
75
138
|
type: "boolean",
|
76
139
|
default: false,
|
77
140
|
},
|
141
|
+
ignoreEnums: {
|
142
|
+
type: "boolean",
|
143
|
+
default: false,
|
144
|
+
},
|
145
|
+
ignoreNumericLiteralTypes: {
|
146
|
+
type: "boolean",
|
147
|
+
default: false,
|
148
|
+
},
|
149
|
+
ignoreReadonlyClassProperties: {
|
150
|
+
type: "boolean",
|
151
|
+
default: false,
|
152
|
+
},
|
153
|
+
ignoreTypeIndexes: {
|
154
|
+
type: "boolean",
|
155
|
+
default: false,
|
156
|
+
},
|
78
157
|
},
|
79
158
|
additionalProperties: false,
|
80
159
|
},
|
@@ -94,7 +173,12 @@ module.exports = {
|
|
94
173
|
ignoreArrayIndexes = !!config.ignoreArrayIndexes,
|
95
174
|
ignoreDefaultValues = !!config.ignoreDefaultValues,
|
96
175
|
ignoreClassFieldInitialValues =
|
97
|
-
!!config.ignoreClassFieldInitialValues
|
176
|
+
!!config.ignoreClassFieldInitialValues,
|
177
|
+
ignoreEnums = !!config.ignoreEnums,
|
178
|
+
ignoreNumericLiteralTypes = !!config.ignoreNumericLiteralTypes,
|
179
|
+
ignoreReadonlyClassProperties =
|
180
|
+
!!config.ignoreReadonlyClassProperties,
|
181
|
+
ignoreTypeIndexes = !!config.ignoreTypeIndexes;
|
98
182
|
|
99
183
|
const okTypes = detectObjects
|
100
184
|
? []
|
@@ -217,14 +301,15 @@ module.exports = {
|
|
217
301
|
let value;
|
218
302
|
let raw;
|
219
303
|
|
220
|
-
// Treat unary minus as a part of the number
|
304
|
+
// Treat unary minus/plus as a part of the number
|
221
305
|
if (
|
222
306
|
node.parent.type === "UnaryExpression" &&
|
223
|
-
node.parent.operator
|
307
|
+
["-", "+"].includes(node.parent.operator)
|
224
308
|
) {
|
225
309
|
fullNumberNode = node.parent;
|
226
|
-
value =
|
227
|
-
|
310
|
+
value =
|
311
|
+
node.parent.operator === "-" ? -node.value : node.value;
|
312
|
+
raw = `${node.parent.operator}${node.raw}`;
|
228
313
|
} else {
|
229
314
|
fullNumberNode = node;
|
230
315
|
value = node.value;
|
@@ -239,6 +324,14 @@ module.exports = {
|
|
239
324
|
(ignoreDefaultValues && isDefaultValue(fullNumberNode)) ||
|
240
325
|
(ignoreClassFieldInitialValues &&
|
241
326
|
isClassFieldInitialValue(fullNumberNode)) ||
|
327
|
+
(ignoreEnums &&
|
328
|
+
isParentTSEnumDeclaration(fullNumberNode)) ||
|
329
|
+
(ignoreNumericLiteralTypes &&
|
330
|
+
isTSNumericLiteralType(fullNumberNode)) ||
|
331
|
+
(ignoreTypeIndexes &&
|
332
|
+
isAncestorTSIndexedAccessType(fullNumberNode)) ||
|
333
|
+
(ignoreReadonlyClassProperties &&
|
334
|
+
isParentTSReadonlyPropertyDefinition(fullNumberNode)) ||
|
242
335
|
isParseIntRadix(fullNumberNode) ||
|
243
336
|
isJSXNumber(fullNumberNode) ||
|
244
337
|
(ignoreArrayIndexes && isArrayIndex(fullNumberNode, value))
|
@@ -9,7 +9,6 @@
|
|
9
9
|
// Requirements
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
|
-
const { findVariable } = require("@eslint-community/eslint-utils");
|
13
12
|
const astUtils = require("./utils/ast-utils");
|
14
13
|
|
15
14
|
//------------------------------------------------------------------------------
|
@@ -21,43 +20,13 @@ const functionTypesToCheck = new Set([
|
|
21
20
|
"FunctionExpression",
|
22
21
|
]);
|
23
22
|
|
24
|
-
/**
|
25
|
-
* Determines whether the given identifier node is a reference to a global variable.
|
26
|
-
* @param {ASTNode} node `Identifier` node to check.
|
27
|
-
* @param {Scope} scope Scope to which the node belongs.
|
28
|
-
* @returns {boolean} True if the identifier is a reference to a global variable.
|
29
|
-
*/
|
30
|
-
function isGlobalReference(node, scope) {
|
31
|
-
const variable = findVariable(scope, node);
|
32
|
-
|
33
|
-
return (
|
34
|
-
variable !== null &&
|
35
|
-
variable.scope.type === "global" &&
|
36
|
-
variable.defs.length === 0
|
37
|
-
);
|
38
|
-
}
|
39
|
-
|
40
|
-
/**
|
41
|
-
* Finds function's outer scope.
|
42
|
-
* @param {Scope} scope Function's own scope.
|
43
|
-
* @returns {Scope} Function's outer scope.
|
44
|
-
*/
|
45
|
-
function getOuterScope(scope) {
|
46
|
-
const upper = scope.upper;
|
47
|
-
|
48
|
-
if (upper.type === "function-expression-name") {
|
49
|
-
return upper.upper;
|
50
|
-
}
|
51
|
-
return upper;
|
52
|
-
}
|
53
|
-
|
54
23
|
/**
|
55
24
|
* Determines whether the given function node is used as a Promise executor.
|
56
25
|
* @param {ASTNode} node The node to check.
|
57
|
-
* @param {
|
26
|
+
* @param {SourceCode} sourceCode Source code to which the node belongs.
|
58
27
|
* @returns {boolean} `true` if the node is a Promise executor.
|
59
28
|
*/
|
60
|
-
function isPromiseExecutor(node,
|
29
|
+
function isPromiseExecutor(node, sourceCode) {
|
61
30
|
const parent = node.parent;
|
62
31
|
|
63
32
|
return (
|
@@ -65,7 +34,7 @@ function isPromiseExecutor(node, scope) {
|
|
65
34
|
parent.arguments[0] === node &&
|
66
35
|
parent.callee.type === "Identifier" &&
|
67
36
|
parent.callee.name === "Promise" &&
|
68
|
-
isGlobalReference(parent.callee
|
37
|
+
sourceCode.isGlobalReference(parent.callee)
|
69
38
|
);
|
70
39
|
}
|
71
40
|
|
@@ -203,7 +172,7 @@ module.exports = {
|
|
203
172
|
upper: funcInfo,
|
204
173
|
shouldCheck:
|
205
174
|
functionTypesToCheck.has(node.type) &&
|
206
|
-
isPromiseExecutor(node, sourceCode
|
175
|
+
isPromiseExecutor(node, sourceCode),
|
207
176
|
};
|
208
177
|
|
209
178
|
if (
|
@@ -4,6 +4,18 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Helpers
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const TYPE_NODES = new Set([
|
12
|
+
"TSTypeReference",
|
13
|
+
"TSInterfaceHeritage",
|
14
|
+
"TSClassImplements",
|
15
|
+
"TSTypeQuery",
|
16
|
+
"TSQualifiedName",
|
17
|
+
]);
|
18
|
+
|
7
19
|
//------------------------------------------------------------------------------
|
8
20
|
// Rule Definition
|
9
21
|
//------------------------------------------------------------------------------
|
@@ -11,6 +23,8 @@
|
|
11
23
|
/** @type {import('../types').Rule.RuleModule} */
|
12
24
|
module.exports = {
|
13
25
|
meta: {
|
26
|
+
dialects: ["javascript", "typescript"],
|
27
|
+
language: "javascript",
|
14
28
|
type: "suggestion",
|
15
29
|
|
16
30
|
docs: {
|
@@ -100,6 +114,18 @@ module.exports = {
|
|
100
114
|
return Object.hasOwn(restrictedGlobalMessages, name);
|
101
115
|
}
|
102
116
|
|
117
|
+
/**
|
118
|
+
* Check if the given reference occurs within a TypeScript type context.
|
119
|
+
* @param {Reference} reference The variable reference to check.
|
120
|
+
* @returns {boolean} Whether the reference is in a type context.
|
121
|
+
* @private
|
122
|
+
*/
|
123
|
+
function isInTypeContext(reference) {
|
124
|
+
const parent = reference.identifier.parent;
|
125
|
+
|
126
|
+
return TYPE_NODES.has(parent.type);
|
127
|
+
}
|
128
|
+
|
103
129
|
return {
|
104
130
|
Program(node) {
|
105
131
|
const scope = sourceCode.getScope(node);
|
@@ -107,13 +133,20 @@ module.exports = {
|
|
107
133
|
// Report variables declared elsewhere (ex: variables defined as "global" by eslint)
|
108
134
|
scope.variables.forEach(variable => {
|
109
135
|
if (!variable.defs.length && isRestricted(variable.name)) {
|
110
|
-
variable.references.forEach(
|
136
|
+
variable.references.forEach(reference => {
|
137
|
+
if (!isInTypeContext(reference)) {
|
138
|
+
reportReference(reference);
|
139
|
+
}
|
140
|
+
});
|
111
141
|
}
|
112
142
|
});
|
113
143
|
|
114
144
|
// Report variables not declared at all
|
115
145
|
scope.through.forEach(reference => {
|
116
|
-
if (
|
146
|
+
if (
|
147
|
+
isRestricted(reference.identifier.name) &&
|
148
|
+
!isInTypeContext(reference)
|
149
|
+
) {
|
117
150
|
reportReference(reference);
|
118
151
|
}
|
119
152
|
});
|
@@ -40,6 +40,13 @@ module.exports = {
|
|
40
40
|
},
|
41
41
|
uniqueItems: true,
|
42
42
|
},
|
43
|
+
allowProperties: {
|
44
|
+
type: "array",
|
45
|
+
items: {
|
46
|
+
type: "string",
|
47
|
+
},
|
48
|
+
uniqueItems: true,
|
49
|
+
},
|
43
50
|
message: {
|
44
51
|
type: "string",
|
45
52
|
},
|
@@ -53,7 +60,10 @@ module.exports = {
|
|
53
60
|
},
|
54
61
|
],
|
55
62
|
not: {
|
56
|
-
|
63
|
+
anyOf: [
|
64
|
+
{ required: ["allowObjects", "object"] },
|
65
|
+
{ required: ["allowProperties", "property"] },
|
66
|
+
],
|
57
67
|
},
|
58
68
|
additionalProperties: false,
|
59
69
|
},
|
@@ -92,6 +102,7 @@ module.exports = {
|
|
92
102
|
});
|
93
103
|
} else if (typeof propertyName === "undefined") {
|
94
104
|
globallyRestrictedObjects.set(objectName, {
|
105
|
+
allowProperties: option.allowProperties,
|
95
106
|
message: option.message,
|
96
107
|
});
|
97
108
|
} else {
|
@@ -106,17 +117,17 @@ module.exports = {
|
|
106
117
|
});
|
107
118
|
|
108
119
|
/**
|
109
|
-
* Checks if
|
110
|
-
* @param {string}
|
111
|
-
* @param {string[]} [
|
112
|
-
* @returns {boolean} True if the
|
120
|
+
* Checks if a name is in the allowed list.
|
121
|
+
* @param {string} name The name to check
|
122
|
+
* @param {string[]} [allowedList] The list of allowed names
|
123
|
+
* @returns {boolean} True if the name is allowed, false otherwise
|
113
124
|
*/
|
114
|
-
function
|
115
|
-
if (!
|
125
|
+
function isAllowed(name, allowedList) {
|
126
|
+
if (!allowedList) {
|
116
127
|
return false;
|
117
128
|
}
|
118
129
|
|
119
|
-
return
|
130
|
+
return allowedList.includes(name);
|
120
131
|
}
|
121
132
|
|
122
133
|
/**
|
@@ -137,7 +148,10 @@ module.exports = {
|
|
137
148
|
const globalMatchedProperty =
|
138
149
|
globallyRestrictedProperties.get(propertyName);
|
139
150
|
|
140
|
-
if (
|
151
|
+
if (
|
152
|
+
matchedObjectProperty &&
|
153
|
+
!isAllowed(propertyName, matchedObjectProperty.allowProperties)
|
154
|
+
) {
|
141
155
|
const message = matchedObjectProperty.message
|
142
156
|
? ` ${matchedObjectProperty.message}`
|
143
157
|
: "";
|
@@ -153,7 +167,7 @@ module.exports = {
|
|
153
167
|
});
|
154
168
|
} else if (
|
155
169
|
globalMatchedProperty &&
|
156
|
-
!
|
170
|
+
!isAllowed(objectName, globalMatchedProperty.allowObjects)
|
157
171
|
) {
|
158
172
|
const message = globalMatchedProperty.message
|
159
173
|
? ` ${globalMatchedProperty.message}`
|