eslint 8.13.0 → 8.17.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 +17 -12
- package/bin/eslint.js +1 -1
- package/lib/cli-engine/cli-engine.js +2 -4
- package/lib/cli-engine/lint-result-cache.js +1 -1
- package/lib/eslint/eslint.js +3 -3
- package/lib/linter/code-path-analysis/code-path-segment.js +1 -1
- package/lib/linter/code-path-analysis/code-path-state.js +1 -1
- package/lib/linter/code-path-analysis/code-path.js +1 -1
- package/lib/rules/accessor-pairs.js +4 -4
- package/lib/rules/callback-return.js +2 -2
- package/lib/rules/capitalized-comments.js +1 -1
- package/lib/rules/consistent-this.js +1 -1
- package/lib/rules/dot-notation.js +2 -2
- package/lib/rules/function-paren-newline.js +8 -5
- package/lib/rules/global-require.js +3 -3
- package/lib/rules/indent-legacy.js +2 -2
- package/lib/rules/indent.js +45 -13
- package/lib/rules/index.js +1 -0
- package/lib/rules/jsx-quotes.js +1 -1
- package/lib/rules/lines-around-comment.js +3 -3
- package/lib/rules/max-lines.js +2 -2
- package/lib/rules/max-statements.js +1 -1
- package/lib/rules/newline-before-return.js +1 -1
- package/lib/rules/no-bitwise.js +2 -2
- package/lib/rules/no-console.js +1 -1
- package/lib/rules/no-constant-binary-expression.js +500 -0
- package/lib/rules/no-constant-condition.js +4 -197
- package/lib/rules/no-control-regex.js +23 -10
- package/lib/rules/no-empty-function.js +1 -1
- package/lib/rules/no-extra-boolean-cast.js +3 -3
- package/lib/rules/no-extra-semi.js +1 -1
- package/lib/rules/no-global-assign.js +1 -1
- package/lib/rules/no-implicit-coercion.js +8 -8
- package/lib/rules/no-loop-func.js +1 -1
- package/lib/rules/no-magic-numbers.js +3 -3
- package/lib/rules/no-misleading-character-class.js +90 -17
- package/lib/rules/no-mixed-operators.js +1 -1
- package/lib/rules/no-mixed-requires.js +1 -1
- package/lib/rules/no-multi-spaces.js +1 -1
- package/lib/rules/no-native-reassign.js +1 -1
- package/lib/rules/no-new-object.js +1 -1
- package/lib/rules/no-new-wrappers.js +1 -1
- package/lib/rules/no-octal.js +2 -2
- package/lib/rules/no-prototype-builtins.js +3 -3
- package/lib/rules/no-shadow.js +5 -5
- package/lib/rules/no-sparse-arrays.js +1 -1
- package/lib/rules/no-underscore-dangle.js +31 -2
- package/lib/rules/no-unused-expressions.js +1 -1
- package/lib/rules/no-unused-vars.js +1 -1
- package/lib/rules/no-use-before-define.js +15 -2
- package/lib/rules/operator-assignment.js +2 -2
- package/lib/rules/prefer-const.js +1 -1
- package/lib/rules/prefer-reflect.js +2 -2
- package/lib/rules/prefer-regex-literals.js +3 -3
- package/lib/rules/quote-props.js +2 -2
- package/lib/rules/quotes.js +1 -1
- package/lib/rules/spaced-comment.js +1 -1
- package/lib/rules/utils/ast-utils.js +203 -7
- package/lib/rules/valid-jsdoc.js +1 -1
- package/lib/rules/valid-typeof.js +4 -4
- package/lib/rules/yoda.js +1 -1
- package/lib/shared/types.js +1 -1
- package/package.json +25 -8
@@ -34,7 +34,7 @@ module.exports = {
|
|
34
34
|
NewExpression(node) {
|
35
35
|
const wrapperObjects = ["String", "Number", "Boolean"];
|
36
36
|
|
37
|
-
if (wrapperObjects.
|
37
|
+
if (wrapperObjects.includes(node.callee.name)) {
|
38
38
|
context.report({
|
39
39
|
node,
|
40
40
|
messageId: "noConstructor",
|
package/lib/rules/no-octal.js
CHANGED
@@ -23,7 +23,7 @@ module.exports = {
|
|
23
23
|
schema: [],
|
24
24
|
|
25
25
|
messages: {
|
26
|
-
|
26
|
+
noOctal: "Octal literals should not be used."
|
27
27
|
}
|
28
28
|
},
|
29
29
|
|
@@ -35,7 +35,7 @@ module.exports = {
|
|
35
35
|
if (typeof node.value === "number" && /^0[0-9]/u.test(node.raw)) {
|
36
36
|
context.report({
|
37
37
|
node,
|
38
|
-
messageId: "
|
38
|
+
messageId: "noOctal"
|
39
39
|
});
|
40
40
|
}
|
41
41
|
}
|
@@ -33,11 +33,11 @@ module.exports = {
|
|
33
33
|
},
|
34
34
|
|
35
35
|
create(context) {
|
36
|
-
const DISALLOWED_PROPS = [
|
36
|
+
const DISALLOWED_PROPS = new Set([
|
37
37
|
"hasOwnProperty",
|
38
38
|
"isPrototypeOf",
|
39
39
|
"propertyIsEnumerable"
|
40
|
-
];
|
40
|
+
]);
|
41
41
|
|
42
42
|
/**
|
43
43
|
* Reports if a disallowed property is used in a CallExpression
|
@@ -54,7 +54,7 @@ module.exports = {
|
|
54
54
|
|
55
55
|
const propName = astUtils.getStaticPropertyName(callee);
|
56
56
|
|
57
|
-
if (propName !== null && DISALLOWED_PROPS.
|
57
|
+
if (propName !== null && DISALLOWED_PROPS.has(propName)) {
|
58
58
|
context.report({
|
59
59
|
messageId: "prototypeBuildIn",
|
60
60
|
loc: callee.property.loc,
|
package/lib/rules/no-shadow.js
CHANGED
@@ -15,8 +15,8 @@ const astUtils = require("./utils/ast-utils");
|
|
15
15
|
// Helpers
|
16
16
|
//------------------------------------------------------------------------------
|
17
17
|
|
18
|
-
const FUNC_EXPR_NODE_TYPES = ["ArrowFunctionExpression", "FunctionExpression"];
|
19
|
-
const CALL_EXPR_NODE_TYPE = ["CallExpression"];
|
18
|
+
const FUNC_EXPR_NODE_TYPES = new Set(["ArrowFunctionExpression", "FunctionExpression"]);
|
19
|
+
const CALL_EXPR_NODE_TYPE = new Set(["CallExpression"]);
|
20
20
|
const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/u;
|
21
21
|
const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/u;
|
22
22
|
|
@@ -123,7 +123,7 @@ module.exports = {
|
|
123
123
|
const { variableScope } = variable.scope;
|
124
124
|
|
125
125
|
|
126
|
-
if (!(FUNC_EXPR_NODE_TYPES.
|
126
|
+
if (!(FUNC_EXPR_NODE_TYPES.has(variableScope.block.type) && getOuterScope(variableScope) === shadowedVariable.scope)) {
|
127
127
|
return false;
|
128
128
|
}
|
129
129
|
|
@@ -132,7 +132,7 @@ module.exports = {
|
|
132
132
|
|
133
133
|
const callExpression = findSelfOrAncestor(
|
134
134
|
parent,
|
135
|
-
node => CALL_EXPR_NODE_TYPE.
|
135
|
+
node => CALL_EXPR_NODE_TYPE.has(node.type)
|
136
136
|
);
|
137
137
|
|
138
138
|
if (!callExpression) {
|
@@ -173,7 +173,7 @@ module.exports = {
|
|
173
173
|
* @returns {boolean} Whether or not the variable name is allowed.
|
174
174
|
*/
|
175
175
|
function isAllowed(variable) {
|
176
|
-
return options.allow.
|
176
|
+
return options.allow.includes(variable.name);
|
177
177
|
}
|
178
178
|
|
179
179
|
/**
|
@@ -49,6 +49,10 @@ module.exports = {
|
|
49
49
|
allowFunctionParams: {
|
50
50
|
type: "boolean",
|
51
51
|
default: true
|
52
|
+
},
|
53
|
+
enforceInClassFields: {
|
54
|
+
type: "boolean",
|
55
|
+
default: false
|
52
56
|
}
|
53
57
|
},
|
54
58
|
additionalProperties: false
|
@@ -68,6 +72,7 @@ module.exports = {
|
|
68
72
|
const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false;
|
69
73
|
const allowAfterThisConstructor = typeof options.allowAfterThisConstructor !== "undefined" ? options.allowAfterThisConstructor : false;
|
70
74
|
const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;
|
75
|
+
const enforceInClassFields = typeof options.enforceInClassFields !== "undefined" ? options.enforceInClassFields : false;
|
71
76
|
const allowFunctionParams = typeof options.allowFunctionParams !== "undefined" ? options.allowFunctionParams : true;
|
72
77
|
|
73
78
|
//-------------------------------------------------------------------------
|
@@ -81,7 +86,7 @@ module.exports = {
|
|
81
86
|
* @private
|
82
87
|
*/
|
83
88
|
function isAllowed(identifier) {
|
84
|
-
return ALLOWED_VARIABLES.
|
89
|
+
return ALLOWED_VARIABLES.includes(identifier);
|
85
90
|
}
|
86
91
|
|
87
92
|
/**
|
@@ -261,6 +266,30 @@ module.exports = {
|
|
261
266
|
}
|
262
267
|
}
|
263
268
|
|
269
|
+
/**
|
270
|
+
* Check if a class field has a dangling underscore
|
271
|
+
* @param {ASTNode} node node to evaluate
|
272
|
+
* @returns {void}
|
273
|
+
* @private
|
274
|
+
*/
|
275
|
+
function checkForDanglingUnderscoreInClassField(node) {
|
276
|
+
const identifier = node.key.name;
|
277
|
+
|
278
|
+
if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) &&
|
279
|
+
enforceInClassFields &&
|
280
|
+
!isAllowed(identifier)) {
|
281
|
+
context.report({
|
282
|
+
node,
|
283
|
+
messageId: "unexpectedUnderscore",
|
284
|
+
data: {
|
285
|
+
identifier: node.key.type === "PrivateIdentifier"
|
286
|
+
? `#${identifier}`
|
287
|
+
: identifier
|
288
|
+
}
|
289
|
+
});
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
264
293
|
//--------------------------------------------------------------------------
|
265
294
|
// Public API
|
266
295
|
//--------------------------------------------------------------------------
|
@@ -270,7 +299,7 @@ module.exports = {
|
|
270
299
|
VariableDeclarator: checkForDanglingUnderscoreInVariableExpression,
|
271
300
|
MemberExpression: checkForDanglingUnderscoreInMemberExpression,
|
272
301
|
MethodDefinition: checkForDanglingUnderscoreInMethod,
|
273
|
-
PropertyDefinition:
|
302
|
+
PropertyDefinition: checkForDanglingUnderscoreInClassField,
|
274
303
|
Property: checkForDanglingUnderscoreInMethod,
|
275
304
|
FunctionExpression: checkForDanglingUnderscoreInFunction,
|
276
305
|
ArrowFunctionExpression: checkForDanglingUnderscoreInFunction
|
@@ -21,6 +21,7 @@ function parseOptions(options) {
|
|
21
21
|
let functions = true;
|
22
22
|
let classes = true;
|
23
23
|
let variables = true;
|
24
|
+
let allowNamedExports = false;
|
24
25
|
|
25
26
|
if (typeof options === "string") {
|
26
27
|
functions = (options !== "nofunc");
|
@@ -28,9 +29,10 @@ function parseOptions(options) {
|
|
28
29
|
functions = options.functions !== false;
|
29
30
|
classes = options.classes !== false;
|
30
31
|
variables = options.variables !== false;
|
32
|
+
allowNamedExports = !!options.allowNamedExports;
|
31
33
|
}
|
32
34
|
|
33
|
-
return { functions, classes, variables };
|
35
|
+
return { functions, classes, variables, allowNamedExports };
|
34
36
|
}
|
35
37
|
|
36
38
|
/**
|
@@ -240,7 +242,8 @@ module.exports = {
|
|
240
242
|
properties: {
|
241
243
|
functions: { type: "boolean" },
|
242
244
|
classes: { type: "boolean" },
|
243
|
-
variables: { type: "boolean" }
|
245
|
+
variables: { type: "boolean" },
|
246
|
+
allowNamedExports: { type: "boolean" }
|
244
247
|
},
|
245
248
|
additionalProperties: false
|
246
249
|
}
|
@@ -273,6 +276,16 @@ module.exports = {
|
|
273
276
|
return false;
|
274
277
|
}
|
275
278
|
|
279
|
+
const { identifier } = reference;
|
280
|
+
|
281
|
+
if (
|
282
|
+
options.allowNamedExports &&
|
283
|
+
identifier.parent.type === "ExportSpecifier" &&
|
284
|
+
identifier.parent.local === identifier
|
285
|
+
) {
|
286
|
+
return false;
|
287
|
+
}
|
288
|
+
|
276
289
|
const variable = reference.resolved;
|
277
290
|
|
278
291
|
if (!variable || variable.defs.length === 0) {
|
@@ -22,7 +22,7 @@ const astUtils = require("./utils/ast-utils");
|
|
22
22
|
* shorthand form.
|
23
23
|
*/
|
24
24
|
function isCommutativeOperatorWithShorthand(operator) {
|
25
|
-
return ["*", "&", "^", "|"].
|
25
|
+
return ["*", "&", "^", "|"].includes(operator);
|
26
26
|
}
|
27
27
|
|
28
28
|
/**
|
@@ -33,7 +33,7 @@ function isCommutativeOperatorWithShorthand(operator) {
|
|
33
33
|
* a shorthand form.
|
34
34
|
*/
|
35
35
|
function isNonCommutativeOperatorWithShorthand(operator) {
|
36
|
-
return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].
|
36
|
+
return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].includes(operator);
|
37
37
|
}
|
38
38
|
|
39
39
|
//------------------------------------------------------------------------------
|
@@ -60,7 +60,7 @@ function canBecomeVariableDeclaration(identifier) {
|
|
60
60
|
*/
|
61
61
|
function isOuterVariableInDestructing(name, initScope) {
|
62
62
|
|
63
|
-
if (initScope.through.
|
63
|
+
if (initScope.through.some(ref => ref.resolved && ref.resolved.name === name)) {
|
64
64
|
return true;
|
65
65
|
}
|
66
66
|
|
@@ -106,7 +106,7 @@ module.exports = {
|
|
106
106
|
const methodName = (node.callee.property || {}).name;
|
107
107
|
const isReflectCall = (node.callee.object || {}).name === "Reflect";
|
108
108
|
const hasReflectSubstitute = Object.prototype.hasOwnProperty.call(reflectSubstitutes, methodName);
|
109
|
-
const userConfiguredException = exceptions.
|
109
|
+
const userConfiguredException = exceptions.includes(methodName);
|
110
110
|
|
111
111
|
if (hasReflectSubstitute && !isReflectCall && !userConfiguredException) {
|
112
112
|
report(node, existingNames[methodName], reflectSubstitutes[methodName]);
|
@@ -115,7 +115,7 @@ module.exports = {
|
|
115
115
|
UnaryExpression(node) {
|
116
116
|
const isDeleteOperator = node.operator === "delete";
|
117
117
|
const targetsIdentifier = node.argument.type === "Identifier";
|
118
|
-
const userConfiguredException = exceptions.
|
118
|
+
const userConfiguredException = exceptions.includes("delete");
|
119
119
|
|
120
120
|
if (isDeleteOperator && !targetsIdentifier && !userConfiguredException) {
|
121
121
|
report(node, "the delete keyword", "Reflect.deleteProperty");
|
@@ -47,7 +47,7 @@ function isStaticTemplateLiteral(node) {
|
|
47
47
|
return node.type === "TemplateLiteral" && node.expressions.length === 0;
|
48
48
|
}
|
49
49
|
|
50
|
-
const validPrecedingTokens = [
|
50
|
+
const validPrecedingTokens = new Set([
|
51
51
|
"(",
|
52
52
|
";",
|
53
53
|
"[",
|
@@ -110,7 +110,7 @@ const validPrecedingTokens = [
|
|
110
110
|
"debugger",
|
111
111
|
"case",
|
112
112
|
"throw"
|
113
|
-
];
|
113
|
+
]);
|
114
114
|
|
115
115
|
|
116
116
|
//------------------------------------------------------------------------------
|
@@ -334,7 +334,7 @@ module.exports = {
|
|
334
334
|
|
335
335
|
const tokenBefore = sourceCode.getTokenBefore(node);
|
336
336
|
|
337
|
-
if (tokenBefore && !validPrecedingTokens.
|
337
|
+
if (tokenBefore && !validPrecedingTokens.has(tokenBefore.value)) {
|
338
338
|
noFix = true;
|
339
339
|
}
|
340
340
|
|
package/lib/rules/quote-props.js
CHANGED
@@ -95,7 +95,7 @@ module.exports = {
|
|
95
95
|
* @returns {boolean} `true` if it is an ES3 token.
|
96
96
|
*/
|
97
97
|
function isKeyword(tokenStr) {
|
98
|
-
return keywords.
|
98
|
+
return keywords.includes(tokenStr);
|
99
99
|
}
|
100
100
|
|
101
101
|
/**
|
@@ -108,7 +108,7 @@ module.exports = {
|
|
108
108
|
*/
|
109
109
|
function areQuotesRedundant(rawKey, tokens, skipNumberLiterals) {
|
110
110
|
return tokens.length === 1 && tokens[0].start === 0 && tokens[0].end === rawKey.length &&
|
111
|
-
(["Identifier", "Keyword", "Null", "Boolean"].
|
111
|
+
(["Identifier", "Keyword", "Null", "Boolean"].includes(tokens[0].type) ||
|
112
112
|
(tokens[0].type === "Numeric" && !skipNumberLiterals && String(+tokens[0].value) === tokens[0].value));
|
113
113
|
}
|
114
114
|
|
package/lib/rules/quotes.js
CHANGED
@@ -283,7 +283,7 @@ module.exports = {
|
|
283
283
|
astUtils.isSurroundedBy(rawVal, settings.quote);
|
284
284
|
|
285
285
|
if (!isValid && avoidEscape) {
|
286
|
-
isValid = astUtils.isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.
|
286
|
+
isValid = astUtils.isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.includes(settings.quote);
|
287
287
|
}
|
288
288
|
|
289
289
|
if (!isValid) {
|
@@ -32,6 +32,7 @@ const thisTagPattern = /^[\s*]*@this/mu;
|
|
32
32
|
|
33
33
|
|
34
34
|
const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u;
|
35
|
+
const ESLINT_DIRECTIVE_PATTERN = /^(?:eslint[- ]|(?:globals?|exported) )/u;
|
35
36
|
const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
|
36
37
|
|
37
38
|
// A set of node types that can contain a list of statements
|
@@ -788,6 +789,203 @@ function getModuleExportName(node) {
|
|
788
789
|
return node.value;
|
789
790
|
}
|
790
791
|
|
792
|
+
/**
|
793
|
+
* Returns literal's value converted to the Boolean type
|
794
|
+
* @param {ASTNode} node any `Literal` node
|
795
|
+
* @returns {boolean | null} `true` when node is truthy, `false` when node is falsy,
|
796
|
+
* `null` when it cannot be determined.
|
797
|
+
*/
|
798
|
+
function getBooleanValue(node) {
|
799
|
+
if (node.value === null) {
|
800
|
+
|
801
|
+
/*
|
802
|
+
* it might be a null literal or bigint/regex literal in unsupported environments .
|
803
|
+
* https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es5.md#regexpliteral
|
804
|
+
* https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es2020.md#bigintliteral
|
805
|
+
*/
|
806
|
+
|
807
|
+
if (node.raw === "null") {
|
808
|
+
return false;
|
809
|
+
}
|
810
|
+
|
811
|
+
// regex is always truthy
|
812
|
+
if (typeof node.regex === "object") {
|
813
|
+
return true;
|
814
|
+
}
|
815
|
+
|
816
|
+
return null;
|
817
|
+
}
|
818
|
+
|
819
|
+
return !!node.value;
|
820
|
+
}
|
821
|
+
|
822
|
+
/**
|
823
|
+
* Checks if a branch node of LogicalExpression short circuits the whole condition
|
824
|
+
* @param {ASTNode} node The branch of main condition which needs to be checked
|
825
|
+
* @param {string} operator The operator of the main LogicalExpression.
|
826
|
+
* @returns {boolean} true when condition short circuits whole condition
|
827
|
+
*/
|
828
|
+
function isLogicalIdentity(node, operator) {
|
829
|
+
switch (node.type) {
|
830
|
+
case "Literal":
|
831
|
+
return (operator === "||" && getBooleanValue(node) === true) ||
|
832
|
+
(operator === "&&" && getBooleanValue(node) === false);
|
833
|
+
|
834
|
+
case "UnaryExpression":
|
835
|
+
return (operator === "&&" && node.operator === "void");
|
836
|
+
|
837
|
+
case "LogicalExpression":
|
838
|
+
|
839
|
+
/*
|
840
|
+
* handles `a && false || b`
|
841
|
+
* `false` is an identity element of `&&` but not `||`
|
842
|
+
*/
|
843
|
+
return operator === node.operator &&
|
844
|
+
(
|
845
|
+
isLogicalIdentity(node.left, operator) ||
|
846
|
+
isLogicalIdentity(node.right, operator)
|
847
|
+
);
|
848
|
+
|
849
|
+
case "AssignmentExpression":
|
850
|
+
return ["||=", "&&="].includes(node.operator) &&
|
851
|
+
operator === node.operator.slice(0, -1) &&
|
852
|
+
isLogicalIdentity(node.right, operator);
|
853
|
+
|
854
|
+
// no default
|
855
|
+
}
|
856
|
+
return false;
|
857
|
+
}
|
858
|
+
|
859
|
+
/**
|
860
|
+
* Checks if an identifier is a reference to a global variable.
|
861
|
+
* @param {Scope} scope The scope in which the identifier is referenced.
|
862
|
+
* @param {ASTNode} node An identifier node to check.
|
863
|
+
* @returns {boolean} `true` if the identifier is a reference to a global variable.
|
864
|
+
*/
|
865
|
+
function isReferenceToGlobalVariable(scope, node) {
|
866
|
+
const reference = scope.references.find(ref => ref.identifier === node);
|
867
|
+
|
868
|
+
return Boolean(
|
869
|
+
reference &&
|
870
|
+
reference.resolved &&
|
871
|
+
reference.resolved.scope.type === "global" &&
|
872
|
+
reference.resolved.defs.length === 0
|
873
|
+
);
|
874
|
+
}
|
875
|
+
|
876
|
+
|
877
|
+
/**
|
878
|
+
* Checks if a node has a constant truthiness value.
|
879
|
+
* @param {Scope} scope Scope in which the node appears.
|
880
|
+
* @param {ASTNode} node The AST node to check.
|
881
|
+
* @param {boolean} inBooleanPosition `true` if checking the test of a
|
882
|
+
* condition. `false` in all other cases. When `false`, checks if -- for
|
883
|
+
* both string and number -- if coerced to that type, the value will
|
884
|
+
* be constant.
|
885
|
+
* @returns {boolean} true when node's truthiness is constant
|
886
|
+
* @private
|
887
|
+
*/
|
888
|
+
function isConstant(scope, node, inBooleanPosition) {
|
889
|
+
|
890
|
+
// node.elements can return null values in the case of sparse arrays ex. [,]
|
891
|
+
if (!node) {
|
892
|
+
return true;
|
893
|
+
}
|
894
|
+
switch (node.type) {
|
895
|
+
case "Literal":
|
896
|
+
case "ArrowFunctionExpression":
|
897
|
+
case "FunctionExpression":
|
898
|
+
return true;
|
899
|
+
case "ClassExpression":
|
900
|
+
case "ObjectExpression":
|
901
|
+
|
902
|
+
/**
|
903
|
+
* In theory objects like:
|
904
|
+
*
|
905
|
+
* `{toString: () => a}`
|
906
|
+
* `{valueOf: () => a}`
|
907
|
+
*
|
908
|
+
* Or a classes like:
|
909
|
+
*
|
910
|
+
* `class { static toString() { return a } }`
|
911
|
+
* `class { static valueOf() { return a } }`
|
912
|
+
*
|
913
|
+
* Are not constant verifiably when `inBooleanPosition` is
|
914
|
+
* false, but it's an edge case we've opted not to handle.
|
915
|
+
*/
|
916
|
+
return true;
|
917
|
+
case "TemplateLiteral":
|
918
|
+
return (inBooleanPosition && node.quasis.some(quasi => quasi.value.cooked.length)) ||
|
919
|
+
node.expressions.every(exp => isConstant(scope, exp, false));
|
920
|
+
|
921
|
+
case "ArrayExpression": {
|
922
|
+
if (!inBooleanPosition) {
|
923
|
+
return node.elements.every(element => isConstant(scope, element, false));
|
924
|
+
}
|
925
|
+
return true;
|
926
|
+
}
|
927
|
+
|
928
|
+
case "UnaryExpression":
|
929
|
+
if (
|
930
|
+
node.operator === "void" ||
|
931
|
+
node.operator === "typeof" && inBooleanPosition
|
932
|
+
) {
|
933
|
+
return true;
|
934
|
+
}
|
935
|
+
|
936
|
+
if (node.operator === "!") {
|
937
|
+
return isConstant(scope, node.argument, true);
|
938
|
+
}
|
939
|
+
|
940
|
+
return isConstant(scope, node.argument, false);
|
941
|
+
|
942
|
+
case "BinaryExpression":
|
943
|
+
return isConstant(scope, node.left, false) &&
|
944
|
+
isConstant(scope, node.right, false) &&
|
945
|
+
node.operator !== "in";
|
946
|
+
|
947
|
+
case "LogicalExpression": {
|
948
|
+
const isLeftConstant = isConstant(scope, node.left, inBooleanPosition);
|
949
|
+
const isRightConstant = isConstant(scope, node.right, inBooleanPosition);
|
950
|
+
const isLeftShortCircuit = (isLeftConstant && isLogicalIdentity(node.left, node.operator));
|
951
|
+
const isRightShortCircuit = (inBooleanPosition && isRightConstant && isLogicalIdentity(node.right, node.operator));
|
952
|
+
|
953
|
+
return (isLeftConstant && isRightConstant) ||
|
954
|
+
isLeftShortCircuit ||
|
955
|
+
isRightShortCircuit;
|
956
|
+
}
|
957
|
+
case "NewExpression":
|
958
|
+
return inBooleanPosition;
|
959
|
+
case "AssignmentExpression":
|
960
|
+
if (node.operator === "=") {
|
961
|
+
return isConstant(scope, node.right, inBooleanPosition);
|
962
|
+
}
|
963
|
+
|
964
|
+
if (["||=", "&&="].includes(node.operator) && inBooleanPosition) {
|
965
|
+
return isLogicalIdentity(node.right, node.operator.slice(0, -1));
|
966
|
+
}
|
967
|
+
|
968
|
+
return false;
|
969
|
+
|
970
|
+
case "SequenceExpression":
|
971
|
+
return isConstant(scope, node.expressions[node.expressions.length - 1], inBooleanPosition);
|
972
|
+
case "SpreadElement":
|
973
|
+
return isConstant(scope, node.argument, inBooleanPosition);
|
974
|
+
case "CallExpression":
|
975
|
+
if (node.callee.type === "Identifier" && node.callee.name === "Boolean") {
|
976
|
+
if (node.arguments.length === 0 || isConstant(scope, node.arguments[0], true)) {
|
977
|
+
return isReferenceToGlobalVariable(scope, node.callee);
|
978
|
+
}
|
979
|
+
}
|
980
|
+
return false;
|
981
|
+
case "Identifier":
|
982
|
+
return node.name === "undefined" && isReferenceToGlobalVariable(scope, node);
|
983
|
+
|
984
|
+
// no default
|
985
|
+
}
|
986
|
+
return false;
|
987
|
+
}
|
988
|
+
|
791
989
|
//------------------------------------------------------------------------------
|
792
990
|
// Public Interface
|
793
991
|
//------------------------------------------------------------------------------
|
@@ -908,12 +1106,8 @@ module.exports = {
|
|
908
1106
|
const comment = node.value.trim();
|
909
1107
|
|
910
1108
|
return (
|
911
|
-
node.type === "Line" && comment.
|
912
|
-
node.type === "Block" && (
|
913
|
-
comment.indexOf("global ") === 0 ||
|
914
|
-
comment.indexOf("eslint ") === 0 ||
|
915
|
-
comment.indexOf("eslint-") === 0
|
916
|
-
)
|
1109
|
+
node.type === "Line" && comment.startsWith("eslint-") ||
|
1110
|
+
node.type === "Block" && ESLINT_DIRECTIVE_PATTERN.test(comment)
|
917
1111
|
);
|
918
1112
|
},
|
919
1113
|
|
@@ -1905,6 +2099,7 @@ module.exports = {
|
|
1905
2099
|
return OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN.test(rawString);
|
1906
2100
|
},
|
1907
2101
|
|
2102
|
+
isReferenceToGlobalVariable,
|
1908
2103
|
isLogicalExpression,
|
1909
2104
|
isCoalesceExpression,
|
1910
2105
|
isMixedLogicalAndCoalesceExpressions,
|
@@ -1918,5 +2113,6 @@ module.exports = {
|
|
1918
2113
|
isSameReference,
|
1919
2114
|
isLogicalAssignmentOperator,
|
1920
2115
|
getSwitchCaseColonToken,
|
1921
|
-
getModuleExportName
|
2116
|
+
getModuleExportName,
|
2117
|
+
isConstant
|
1922
2118
|
};
|
package/lib/rules/valid-jsdoc.js
CHANGED
@@ -42,8 +42,8 @@ module.exports = {
|
|
42
42
|
|
43
43
|
create(context) {
|
44
44
|
|
45
|
-
const VALID_TYPES = ["symbol", "undefined", "object", "boolean", "number", "string", "function", "bigint"],
|
46
|
-
OPERATORS = ["==", "===", "!=", "!=="];
|
45
|
+
const VALID_TYPES = new Set(["symbol", "undefined", "object", "boolean", "number", "string", "function", "bigint"]),
|
46
|
+
OPERATORS = new Set(["==", "===", "!=", "!=="]);
|
47
47
|
|
48
48
|
const requireStringLiterals = context.options[0] && context.options[0].requireStringLiterals;
|
49
49
|
|
@@ -85,13 +85,13 @@ module.exports = {
|
|
85
85
|
if (isTypeofExpression(node)) {
|
86
86
|
const parent = context.getAncestors().pop();
|
87
87
|
|
88
|
-
if (parent.type === "BinaryExpression" && OPERATORS.
|
88
|
+
if (parent.type === "BinaryExpression" && OPERATORS.has(parent.operator)) {
|
89
89
|
const sibling = parent.left === node ? parent.right : parent.left;
|
90
90
|
|
91
91
|
if (sibling.type === "Literal" || sibling.type === "TemplateLiteral" && !sibling.expressions.length) {
|
92
92
|
const value = sibling.type === "Literal" ? sibling.value : sibling.quasis[0].value.cooked;
|
93
93
|
|
94
|
-
if (VALID_TYPES.
|
94
|
+
if (!VALID_TYPES.has(value)) {
|
95
95
|
context.report({ node: sibling, messageId: "invalidValue" });
|
96
96
|
}
|
97
97
|
} else if (sibling.type === "Identifier" && sibling.name === "undefined" && isReferenceToGlobalVariable(sibling)) {
|
package/lib/rules/yoda.js
CHANGED
@@ -39,7 +39,7 @@ function isEqualityOperator(operator) {
|
|
39
39
|
* @returns {boolean} Whether the operator is used in range tests.
|
40
40
|
*/
|
41
41
|
function isRangeTestOperator(operator) {
|
42
|
-
return ["<", "<="].
|
42
|
+
return ["<", "<="].includes(operator);
|
43
43
|
}
|
44
44
|
|
45
45
|
/**
|
package/lib/shared/types.js
CHANGED
@@ -136,7 +136,6 @@ module.exports = {};
|
|
136
136
|
|
137
137
|
/**
|
138
138
|
* @typedef {Object} RuleMetaDocs
|
139
|
-
* @property {string} category The category of the rule.
|
140
139
|
* @property {string} description The description of the rule.
|
141
140
|
* @property {boolean} recommended If `true` then the rule is included in `eslint:recommended` preset.
|
142
141
|
* @property {string} url The URL of the rule documentation.
|
@@ -147,6 +146,7 @@ module.exports = {};
|
|
147
146
|
* @property {boolean} [deprecated] If `true` then the rule has been deprecated.
|
148
147
|
* @property {RuleMetaDocs} docs The document information of the rule.
|
149
148
|
* @property {"code"|"whitespace"} [fixable] The autofix type.
|
149
|
+
* @property {boolean} [hasSuggestions] If `true` then the rule provides suggestions.
|
150
150
|
* @property {Record<string,string>} [messages] The messages the rule reports.
|
151
151
|
* @property {string[]} [replacedBy] The IDs of the alternative rules.
|
152
152
|
* @property {Array|Object} schema The option schema of the rule.
|