eslint 8.43.0 → 8.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/conf/globals.js +6 -1
- package/lib/cli-engine/cli-engine.js +27 -18
- package/lib/eslint/flat-eslint.js +15 -110
- package/lib/linter/report-translator.js +18 -2
- package/lib/rule-tester/flat-rule-tester.js +1 -2
- package/lib/rule-tester/rule-tester.js +1 -2
- package/lib/rules/accessor-pairs.js +33 -41
- package/lib/rules/array-element-newline.js +10 -4
- package/lib/rules/dot-notation.js +1 -2
- package/lib/rules/grouped-accessor-pairs.js +33 -42
- package/lib/rules/indent.js +44 -32
- package/lib/rules/logical-assignment-operators.js +5 -2
- package/lib/rules/max-len.js +17 -13
- package/lib/rules/no-extra-parens.js +12 -2
- package/lib/rules/no-extra-semi.js +29 -11
- package/lib/rules/no-loss-of-precision.js +14 -6
- package/lib/rules/no-restricted-modules.js +7 -10
- package/lib/rules/no-unused-labels.js +46 -13
- package/lib/rules/no-unused-vars.js +2 -1
- package/lib/rules/no-useless-escape.js +5 -1
- package/lib/rules/padding-line-between-statements.js +4 -42
- package/lib/rules/prefer-exponentiation-operator.js +2 -1
- package/lib/rules/prefer-regex-literals.js +3 -12
- package/lib/rules/utils/ast-utils.js +31 -2
- package/lib/rules/valid-typeof.js +7 -1
- package/lib/rules/yoda.js +2 -11
- package/lib/shared/types.js +1 -1
- package/lib/unsupported-api.js +3 -1
- package/package.json +8 -11
package/lib/rules/indent.js
CHANGED
@@ -188,15 +188,19 @@ class TokenInfo {
|
|
188
188
|
*/
|
189
189
|
constructor(sourceCode) {
|
190
190
|
this.sourceCode = sourceCode;
|
191
|
-
this.firstTokensByLineNumber =
|
192
|
-
|
193
|
-
|
191
|
+
this.firstTokensByLineNumber = new Map();
|
192
|
+
const tokens = sourceCode.tokensAndComments;
|
193
|
+
|
194
|
+
for (let i = 0; i < tokens.length; i++) {
|
195
|
+
const token = tokens[i];
|
196
|
+
|
197
|
+
if (!this.firstTokensByLineNumber.has(token.loc.start.line)) {
|
198
|
+
this.firstTokensByLineNumber.set(token.loc.start.line, token);
|
194
199
|
}
|
195
|
-
if (!
|
196
|
-
|
200
|
+
if (!this.firstTokensByLineNumber.has(token.loc.end.line) && sourceCode.text.slice(token.range[1] - token.loc.end.column, token.range[1]).trim()) {
|
201
|
+
this.firstTokensByLineNumber.set(token.loc.end.line, token);
|
197
202
|
}
|
198
|
-
|
199
|
-
}, new Map());
|
203
|
+
}
|
200
204
|
}
|
201
205
|
|
202
206
|
/**
|
@@ -964,19 +968,19 @@ module.exports = {
|
|
964
968
|
const parenStack = [];
|
965
969
|
const parenPairs = [];
|
966
970
|
|
967
|
-
tokens.
|
971
|
+
for (let i = 0; i < tokens.length; i++) {
|
972
|
+
const nextToken = tokens[i];
|
968
973
|
|
969
|
-
// Accumulate a list of parenthesis pairs
|
970
974
|
if (astUtils.isOpeningParenToken(nextToken)) {
|
971
975
|
parenStack.push(nextToken);
|
972
976
|
} else if (astUtils.isClosingParenToken(nextToken)) {
|
973
|
-
parenPairs.
|
977
|
+
parenPairs.push({ left: parenStack.pop(), right: nextToken });
|
974
978
|
}
|
975
|
-
}
|
979
|
+
}
|
976
980
|
|
977
|
-
parenPairs.
|
978
|
-
const leftParen =
|
979
|
-
const rightParen =
|
981
|
+
for (let i = parenPairs.length - 1; i >= 0; i--) {
|
982
|
+
const leftParen = parenPairs[i].left;
|
983
|
+
const rightParen = parenPairs[i].right;
|
980
984
|
|
981
985
|
// We only want to handle parens around expressions, so exclude parentheses that are in function parameters and function call arguments.
|
982
986
|
if (!parameterParens.has(leftParen) && !parameterParens.has(rightParen)) {
|
@@ -990,7 +994,7 @@ module.exports = {
|
|
990
994
|
}
|
991
995
|
|
992
996
|
offsets.setDesiredOffset(rightParen, leftParen, 0);
|
993
|
-
}
|
997
|
+
}
|
994
998
|
}
|
995
999
|
|
996
1000
|
/**
|
@@ -1246,7 +1250,7 @@ module.exports = {
|
|
1246
1250
|
|
1247
1251
|
IfStatement(node) {
|
1248
1252
|
addBlocklessNodeIndent(node.consequent);
|
1249
|
-
if (node.alternate
|
1253
|
+
if (node.alternate) {
|
1250
1254
|
addBlocklessNodeIndent(node.alternate);
|
1251
1255
|
}
|
1252
1256
|
},
|
@@ -1711,9 +1715,13 @@ module.exports = {
|
|
1711
1715
|
}
|
1712
1716
|
|
1713
1717
|
// Invoke the queued offset listeners for the nodes that aren't ignored.
|
1714
|
-
listenerCallQueue
|
1715
|
-
|
1716
|
-
|
1718
|
+
for (let i = 0; i < listenerCallQueue.length; i++) {
|
1719
|
+
const nodeInfo = listenerCallQueue[i];
|
1720
|
+
|
1721
|
+
if (!ignoredNodes.has(nodeInfo.node)) {
|
1722
|
+
nodeInfo.listener(nodeInfo.node);
|
1723
|
+
}
|
1724
|
+
}
|
1717
1725
|
|
1718
1726
|
// Update the offsets for ignored nodes to prevent their child tokens from being reported.
|
1719
1727
|
ignoredNodes.forEach(ignoreNode);
|
@@ -1724,27 +1732,31 @@ module.exports = {
|
|
1724
1732
|
* Create a Map from (tokenOrComment) => (precedingToken).
|
1725
1733
|
* This is necessary because sourceCode.getTokenBefore does not handle a comment as an argument correctly.
|
1726
1734
|
*/
|
1727
|
-
const precedingTokens =
|
1735
|
+
const precedingTokens = new WeakMap();
|
1736
|
+
|
1737
|
+
for (let i = 0; i < sourceCode.ast.comments.length; i++) {
|
1738
|
+
const comment = sourceCode.ast.comments[i];
|
1739
|
+
|
1728
1740
|
const tokenOrCommentBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
|
1741
|
+
const hasToken = precedingTokens.has(tokenOrCommentBefore) ? precedingTokens.get(tokenOrCommentBefore) : tokenOrCommentBefore;
|
1729
1742
|
|
1730
|
-
|
1731
|
-
}
|
1743
|
+
precedingTokens.set(comment, hasToken);
|
1744
|
+
}
|
1732
1745
|
|
1733
|
-
sourceCode.lines.
|
1734
|
-
const lineNumber = lineIndex + 1;
|
1746
|
+
for (let i = 1; i < sourceCode.lines.length + 1; i++) {
|
1735
1747
|
|
1736
|
-
if (!tokenInfo.firstTokensByLineNumber.has(
|
1748
|
+
if (!tokenInfo.firstTokensByLineNumber.has(i)) {
|
1737
1749
|
|
1738
1750
|
// Don't check indentation on blank lines
|
1739
|
-
|
1751
|
+
continue;
|
1740
1752
|
}
|
1741
1753
|
|
1742
|
-
const firstTokenOfLine = tokenInfo.firstTokensByLineNumber.get(
|
1754
|
+
const firstTokenOfLine = tokenInfo.firstTokensByLineNumber.get(i);
|
1743
1755
|
|
1744
|
-
if (firstTokenOfLine.loc.start.line !==
|
1756
|
+
if (firstTokenOfLine.loc.start.line !== i) {
|
1745
1757
|
|
1746
1758
|
// Don't check the indentation of multi-line tokens (e.g. template literals or block comments) twice.
|
1747
|
-
|
1759
|
+
continue;
|
1748
1760
|
}
|
1749
1761
|
|
1750
1762
|
if (astUtils.isCommentToken(firstTokenOfLine)) {
|
@@ -1769,18 +1781,18 @@ module.exports = {
|
|
1769
1781
|
mayAlignWithBefore && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenBefore)) ||
|
1770
1782
|
mayAlignWithAfter && validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(tokenAfter))
|
1771
1783
|
) {
|
1772
|
-
|
1784
|
+
continue;
|
1773
1785
|
}
|
1774
1786
|
}
|
1775
1787
|
|
1776
1788
|
// If the token matches the expected indentation, don't report it.
|
1777
1789
|
if (validateTokenIndent(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine))) {
|
1778
|
-
|
1790
|
+
continue;
|
1779
1791
|
}
|
1780
1792
|
|
1781
1793
|
// Otherwise, report the token/comment.
|
1782
1794
|
report(firstTokenOfLine, offsets.getDesiredIndent(firstTokenOfLine));
|
1783
|
-
}
|
1795
|
+
}
|
1784
1796
|
}
|
1785
1797
|
}
|
1786
1798
|
);
|
@@ -370,8 +370,11 @@ module.exports = {
|
|
370
370
|
return;
|
371
371
|
}
|
372
372
|
|
373
|
-
const
|
374
|
-
|
373
|
+
const parentPrecedence = astUtils.getPrecedence(logical.parent);
|
374
|
+
const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" && (
|
375
|
+
parentPrecedence === -1 ||
|
376
|
+
astUtils.getPrecedence({ type: "AssignmentExpression" }) < parentPrecedence
|
377
|
+
);
|
375
378
|
|
376
379
|
if (!astUtils.isParenthesised(sourceCode, logical) && requiresOuterParenthesis) {
|
377
380
|
yield ruleFixer.insertTextBefore(logical, "(");
|
package/lib/rules/max-len.js
CHANGED
@@ -252,19 +252,23 @@ module.exports = {
|
|
252
252
|
return sourceCode.ast.tokens.filter(token => token.type === "RegularExpression");
|
253
253
|
}
|
254
254
|
|
255
|
-
|
256
255
|
/**
|
257
|
-
*
|
258
|
-
*
|
259
|
-
* @param {ASTNode}
|
260
|
-
* @returns {Object}
|
261
|
-
* @private
|
256
|
+
*
|
257
|
+
* reduce an array of AST nodes by line number, both start and end.
|
258
|
+
* @param {ASTNode[]} arr array of AST nodes
|
259
|
+
* @returns {Object} accululated AST nodes
|
262
260
|
*/
|
263
|
-
function
|
264
|
-
|
265
|
-
|
261
|
+
function groupArrayByLineNumber(arr) {
|
262
|
+
const obj = {};
|
263
|
+
|
264
|
+
for (let i = 0; i < arr.length; i++) {
|
265
|
+
const node = arr[i];
|
266
|
+
|
267
|
+
for (let j = node.loc.start.line; j <= node.loc.end.line; ++j) {
|
268
|
+
ensureArrayAndPush(obj, j, node);
|
269
|
+
}
|
266
270
|
}
|
267
|
-
return
|
271
|
+
return obj;
|
268
272
|
}
|
269
273
|
|
270
274
|
/**
|
@@ -312,13 +316,13 @@ module.exports = {
|
|
312
316
|
let commentsIndex = 0;
|
313
317
|
|
314
318
|
const strings = getAllStrings();
|
315
|
-
const stringsByLine = strings
|
319
|
+
const stringsByLine = groupArrayByLineNumber(strings);
|
316
320
|
|
317
321
|
const templateLiterals = getAllTemplateLiterals();
|
318
|
-
const templateLiteralsByLine = templateLiterals
|
322
|
+
const templateLiteralsByLine = groupArrayByLineNumber(templateLiterals);
|
319
323
|
|
320
324
|
const regExpLiterals = getAllRegExpLiterals();
|
321
|
-
const regExpLiteralsByLine = regExpLiterals
|
325
|
+
const regExpLiteralsByLine = groupArrayByLineNumber(regExpLiterals);
|
322
326
|
|
323
327
|
lines.forEach((line, i) => {
|
324
328
|
|
@@ -46,6 +46,7 @@ module.exports = {
|
|
46
46
|
type: "object",
|
47
47
|
properties: {
|
48
48
|
conditionalAssign: { type: "boolean" },
|
49
|
+
ternaryOperandBinaryExpressions: { type: "boolean" },
|
49
50
|
nestedBinaryExpressions: { type: "boolean" },
|
50
51
|
returnAssign: { type: "boolean" },
|
51
52
|
ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] },
|
@@ -76,6 +77,7 @@ module.exports = {
|
|
76
77
|
const precedence = astUtils.getPrecedence;
|
77
78
|
const ALL_NODES = context.options[0] !== "functions";
|
78
79
|
const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false;
|
80
|
+
const EXCEPT_COND_TERNARY = ALL_NODES && context.options[1] && context.options[1].ternaryOperandBinaryExpressions === false;
|
79
81
|
const NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false;
|
80
82
|
const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false;
|
81
83
|
const IGNORE_JSX = ALL_NODES && context.options[1] && context.options[1].ignoreJSX;
|
@@ -886,18 +888,26 @@ module.exports = {
|
|
886
888
|
if (isReturnAssignException(node)) {
|
887
889
|
return;
|
888
890
|
}
|
891
|
+
|
892
|
+
const availableTypes = new Set(["BinaryExpression", "LogicalExpression"]);
|
893
|
+
|
889
894
|
if (
|
895
|
+
!(EXCEPT_COND_TERNARY && availableTypes.has(node.test.type)) &&
|
890
896
|
!isCondAssignException(node) &&
|
891
897
|
hasExcessParensWithPrecedence(node.test, precedence({ type: "LogicalExpression", operator: "||" }))
|
892
898
|
) {
|
893
899
|
report(node.test);
|
894
900
|
}
|
895
901
|
|
896
|
-
if (
|
902
|
+
if (
|
903
|
+
!(EXCEPT_COND_TERNARY && availableTypes.has(node.consequent.type)) &&
|
904
|
+
hasExcessParensWithPrecedence(node.consequent, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
|
897
905
|
report(node.consequent);
|
898
906
|
}
|
899
907
|
|
900
|
-
if (
|
908
|
+
if (
|
909
|
+
!(EXCEPT_COND_TERNARY && availableTypes.has(node.alternate.type)) &&
|
910
|
+
hasExcessParensWithPrecedence(node.alternate, PRECEDENCE_OF_ASSIGNMENT_EXPR)) {
|
901
911
|
report(node.alternate);
|
902
912
|
}
|
903
913
|
},
|
@@ -38,6 +38,23 @@ module.exports = {
|
|
38
38
|
create(context) {
|
39
39
|
const sourceCode = context.sourceCode;
|
40
40
|
|
41
|
+
/**
|
42
|
+
* Checks if a node or token is fixable.
|
43
|
+
* A node is fixable if it can be removed without turning a subsequent statement into a directive after fixing other nodes.
|
44
|
+
* @param {Token} nodeOrToken The node or token to check.
|
45
|
+
* @returns {boolean} Whether or not the node is fixable.
|
46
|
+
*/
|
47
|
+
function isFixable(nodeOrToken) {
|
48
|
+
const nextToken = sourceCode.getTokenAfter(nodeOrToken);
|
49
|
+
|
50
|
+
if (!nextToken || nextToken.type !== "String") {
|
51
|
+
return true;
|
52
|
+
}
|
53
|
+
const stringNode = sourceCode.getNodeByRangeIndex(nextToken.range[0]);
|
54
|
+
|
55
|
+
return !astUtils.isTopLevelExpressionStatement(stringNode.parent);
|
56
|
+
}
|
57
|
+
|
41
58
|
/**
|
42
59
|
* Reports an unnecessary semicolon error.
|
43
60
|
* @param {Node|Token} nodeOrToken A node or a token to be reported.
|
@@ -47,17 +64,18 @@ module.exports = {
|
|
47
64
|
context.report({
|
48
65
|
node: nodeOrToken,
|
49
66
|
messageId: "unexpected",
|
50
|
-
fix(
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
.
|
59
|
-
|
60
|
-
|
67
|
+
fix: isFixable(nodeOrToken)
|
68
|
+
? fixer =>
|
69
|
+
|
70
|
+
/*
|
71
|
+
* Expand the replacement range to include the surrounding
|
72
|
+
* tokens to avoid conflicting with semi.
|
73
|
+
* https://github.com/eslint/eslint/issues/7928
|
74
|
+
*/
|
75
|
+
new FixTracker(fixer, context.sourceCode)
|
76
|
+
.retainSurroundingTokens(nodeOrToken)
|
77
|
+
.remove(nodeOrToken)
|
78
|
+
: null
|
61
79
|
});
|
62
80
|
}
|
63
81
|
|
@@ -83,7 +83,7 @@ module.exports = {
|
|
83
83
|
* @returns {string} the numeric string with a decimal point in the proper place
|
84
84
|
*/
|
85
85
|
function addDecimalPointToNumber(stringNumber) {
|
86
|
-
return `${stringNumber
|
86
|
+
return `${stringNumber[0]}.${stringNumber.slice(1)}`;
|
87
87
|
}
|
88
88
|
|
89
89
|
/**
|
@@ -92,7 +92,12 @@ module.exports = {
|
|
92
92
|
* @returns {string} the stripped string
|
93
93
|
*/
|
94
94
|
function removeLeadingZeros(numberAsString) {
|
95
|
-
|
95
|
+
for (let i = 0; i < numberAsString.length; i++) {
|
96
|
+
if (numberAsString[i] !== "0") {
|
97
|
+
return numberAsString.slice(i);
|
98
|
+
}
|
99
|
+
}
|
100
|
+
return numberAsString;
|
96
101
|
}
|
97
102
|
|
98
103
|
/**
|
@@ -101,7 +106,12 @@ module.exports = {
|
|
101
106
|
* @returns {string} the stripped string
|
102
107
|
*/
|
103
108
|
function removeTrailingZeros(numberAsString) {
|
104
|
-
|
109
|
+
for (let i = numberAsString.length - 1; i >= 0; i--) {
|
110
|
+
if (numberAsString[i] !== "0") {
|
111
|
+
return numberAsString.slice(0, i + 1);
|
112
|
+
}
|
113
|
+
}
|
114
|
+
return numberAsString;
|
105
115
|
}
|
106
116
|
|
107
117
|
/**
|
@@ -128,7 +138,7 @@ module.exports = {
|
|
128
138
|
const trimmedFloat = removeLeadingZeros(stringFloat);
|
129
139
|
|
130
140
|
if (trimmedFloat.startsWith(".")) {
|
131
|
-
const decimalDigits = trimmedFloat.
|
141
|
+
const decimalDigits = trimmedFloat.slice(1);
|
132
142
|
const significantDigits = removeLeadingZeros(decimalDigits);
|
133
143
|
|
134
144
|
return {
|
@@ -144,7 +154,6 @@ module.exports = {
|
|
144
154
|
};
|
145
155
|
}
|
146
156
|
|
147
|
-
|
148
157
|
/**
|
149
158
|
* Converts a base ten number to proper scientific notation
|
150
159
|
* @param {string} stringNumber the string representation of the base ten number to be converted
|
@@ -160,7 +169,6 @@ module.exports = {
|
|
160
169
|
: normalizedNumber.magnitude;
|
161
170
|
|
162
171
|
return `${normalizedCoefficient}e${magnitude}`;
|
163
|
-
|
164
172
|
}
|
165
173
|
|
166
174
|
/**
|
@@ -5,6 +5,12 @@
|
|
5
5
|
*/
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Rule Definition
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -116,15 +122,6 @@ module.exports = {
|
|
116
122
|
return node && node.type === "Literal" && typeof node.value === "string";
|
117
123
|
}
|
118
124
|
|
119
|
-
/**
|
120
|
-
* Function to check if a node is a static string template literal.
|
121
|
-
* @param {ASTNode} node The node to check.
|
122
|
-
* @returns {boolean} If the node is a string template literal.
|
123
|
-
*/
|
124
|
-
function isStaticTemplateLiteral(node) {
|
125
|
-
return node && node.type === "TemplateLiteral" && node.expressions.length === 0;
|
126
|
-
}
|
127
|
-
|
128
125
|
/**
|
129
126
|
* Function to check if a node is a require call.
|
130
127
|
* @param {ASTNode} node The node to check.
|
@@ -144,7 +141,7 @@ module.exports = {
|
|
144
141
|
return node.value.trim();
|
145
142
|
}
|
146
143
|
|
147
|
-
if (isStaticTemplateLiteral(node)) {
|
144
|
+
if (astUtils.isStaticTemplateLiteral(node)) {
|
148
145
|
return node.quasis[0].value.cooked.trim();
|
149
146
|
}
|
150
147
|
|
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Rule Definition
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -46,6 +52,45 @@ module.exports = {
|
|
46
52
|
};
|
47
53
|
}
|
48
54
|
|
55
|
+
/**
|
56
|
+
* Checks if a `LabeledStatement` node is fixable.
|
57
|
+
* For a node to be fixable, there must be no comments between the label and the body.
|
58
|
+
* Furthermore, is must be possible to remove the label without turning the body statement into a
|
59
|
+
* directive after other fixes are applied.
|
60
|
+
* @param {ASTNode} node The node to evaluate.
|
61
|
+
* @returns {boolean} Whether or not the node is fixable.
|
62
|
+
*/
|
63
|
+
function isFixable(node) {
|
64
|
+
|
65
|
+
/*
|
66
|
+
* Only perform a fix if there are no comments between the label and the body. This will be the case
|
67
|
+
* when there is exactly one token/comment (the ":") between the label and the body.
|
68
|
+
*/
|
69
|
+
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) !==
|
70
|
+
sourceCode.getTokenBefore(node.body, { includeComments: true })) {
|
71
|
+
return false;
|
72
|
+
}
|
73
|
+
|
74
|
+
// Looking for the node's deepest ancestor which is not a `LabeledStatement`.
|
75
|
+
let ancestor = node.parent;
|
76
|
+
|
77
|
+
while (ancestor.type === "LabeledStatement") {
|
78
|
+
ancestor = ancestor.parent;
|
79
|
+
}
|
80
|
+
|
81
|
+
if (ancestor.type === "Program" ||
|
82
|
+
(ancestor.type === "BlockStatement" && astUtils.isFunction(ancestor.parent))) {
|
83
|
+
const { body } = node;
|
84
|
+
|
85
|
+
if (body.type === "ExpressionStatement" &&
|
86
|
+
((body.expression.type === "Literal" && typeof body.expression.value === "string") ||
|
87
|
+
astUtils.isStaticTemplateLiteral(body.expression))) {
|
88
|
+
return false; // potential directive
|
89
|
+
}
|
90
|
+
}
|
91
|
+
return true;
|
92
|
+
}
|
93
|
+
|
49
94
|
/**
|
50
95
|
* Removes the top of the stack.
|
51
96
|
* At the same time, this reports the label if it's never used.
|
@@ -58,19 +103,7 @@ module.exports = {
|
|
58
103
|
node: node.label,
|
59
104
|
messageId: "unused",
|
60
105
|
data: node.label,
|
61
|
-
fix(fixer)
|
62
|
-
|
63
|
-
/*
|
64
|
-
* Only perform a fix if there are no comments between the label and the body. This will be the case
|
65
|
-
* when there is exactly one token/comment (the ":") between the label and the body.
|
66
|
-
*/
|
67
|
-
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) ===
|
68
|
-
sourceCode.getTokenBefore(node.body, { includeComments: true })) {
|
69
|
-
return fixer.removeRange([node.range[0], node.body.range[0]]);
|
70
|
-
}
|
71
|
-
|
72
|
-
return null;
|
73
|
-
}
|
106
|
+
fix: isFixable(node) ? fixer => fixer.removeRange([node.range[0], node.body.range[0]]) : null
|
74
107
|
});
|
75
108
|
}
|
76
109
|
|
@@ -466,7 +466,8 @@ module.exports = {
|
|
466
466
|
(
|
467
467
|
parent.type === "AssignmentExpression" &&
|
468
468
|
parent.left === id &&
|
469
|
-
isUnusedExpression(parent)
|
469
|
+
isUnusedExpression(parent) &&
|
470
|
+
!astUtils.isLogicalAssignmentOperator(parent.operator)
|
470
471
|
) ||
|
471
472
|
(
|
472
473
|
parent.type === "UpdateExpression" &&
|
@@ -94,6 +94,7 @@ module.exports = {
|
|
94
94
|
messages: {
|
95
95
|
unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
|
96
96
|
removeEscape: "Remove the `\\`. This maintains the current functionality.",
|
97
|
+
removeEscapeDoNotKeepSemantics: "Remove the `\\` if it was inserted by mistake.",
|
97
98
|
escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
|
98
99
|
},
|
99
100
|
|
@@ -125,7 +126,10 @@ module.exports = {
|
|
125
126
|
data: { character },
|
126
127
|
suggest: [
|
127
128
|
{
|
128
|
-
|
129
|
+
|
130
|
+
// Removing unnecessary `\` characters in a directive is not guaranteed to maintain functionality.
|
131
|
+
messageId: astUtils.isDirective(node.parent)
|
132
|
+
? "removeEscapeDoNotKeepSemantics" : "removeEscape",
|
129
133
|
fix(fixer) {
|
130
134
|
return fixer.removeRange(range);
|
131
135
|
}
|
@@ -130,42 +130,6 @@ function isBlockLikeStatement(sourceCode, node) {
|
|
130
130
|
);
|
131
131
|
}
|
132
132
|
|
133
|
-
/**
|
134
|
-
* Check whether the given node is a directive or not.
|
135
|
-
* @param {ASTNode} node The node to check.
|
136
|
-
* @param {SourceCode} sourceCode The source code object to get tokens.
|
137
|
-
* @returns {boolean} `true` if the node is a directive.
|
138
|
-
*/
|
139
|
-
function isDirective(node, sourceCode) {
|
140
|
-
return (
|
141
|
-
astUtils.isTopLevelExpressionStatement(node) &&
|
142
|
-
node.expression.type === "Literal" &&
|
143
|
-
typeof node.expression.value === "string" &&
|
144
|
-
!astUtils.isParenthesised(sourceCode, node.expression)
|
145
|
-
);
|
146
|
-
}
|
147
|
-
|
148
|
-
/**
|
149
|
-
* Check whether the given node is a part of directive prologue or not.
|
150
|
-
* @param {ASTNode} node The node to check.
|
151
|
-
* @param {SourceCode} sourceCode The source code object to get tokens.
|
152
|
-
* @returns {boolean} `true` if the node is a part of directive prologue.
|
153
|
-
*/
|
154
|
-
function isDirectivePrologue(node, sourceCode) {
|
155
|
-
if (isDirective(node, sourceCode)) {
|
156
|
-
for (const sibling of node.parent.body) {
|
157
|
-
if (sibling === node) {
|
158
|
-
break;
|
159
|
-
}
|
160
|
-
if (!isDirective(sibling, sourceCode)) {
|
161
|
-
return false;
|
162
|
-
}
|
163
|
-
}
|
164
|
-
return true;
|
165
|
-
}
|
166
|
-
return false;
|
167
|
-
}
|
168
|
-
|
169
133
|
/**
|
170
134
|
* Gets the actual last token.
|
171
135
|
*
|
@@ -359,12 +323,10 @@ const StatementTypes = {
|
|
359
323
|
CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init))
|
360
324
|
},
|
361
325
|
directive: {
|
362
|
-
test:
|
326
|
+
test: astUtils.isDirective
|
363
327
|
},
|
364
328
|
expression: {
|
365
|
-
test: (node
|
366
|
-
node.type === "ExpressionStatement" &&
|
367
|
-
!isDirectivePrologue(node, sourceCode)
|
329
|
+
test: node => node.type === "ExpressionStatement" && !astUtils.isDirective(node)
|
368
330
|
},
|
369
331
|
iife: {
|
370
332
|
test: isIIFEStatement
|
@@ -375,10 +337,10 @@ const StatementTypes = {
|
|
375
337
|
isBlockLikeStatement(sourceCode, node)
|
376
338
|
},
|
377
339
|
"multiline-expression": {
|
378
|
-
test:
|
340
|
+
test: node =>
|
379
341
|
node.loc.start.line !== node.loc.end.line &&
|
380
342
|
node.type === "ExpressionStatement" &&
|
381
|
-
!
|
343
|
+
!astUtils.isDirective(node)
|
382
344
|
},
|
383
345
|
|
384
346
|
"multiline-const": newMultilineKeywordTester("const"),
|
@@ -55,11 +55,12 @@ function doesExponentNeedParens(exponent) {
|
|
55
55
|
function doesExponentiationExpressionNeedParens(node, sourceCode) {
|
56
56
|
const parent = node.parent.type === "ChainExpression" ? node.parent.parent : node.parent;
|
57
57
|
|
58
|
+
const parentPrecedence = astUtils.getPrecedence(parent);
|
58
59
|
const needsParens = (
|
59
60
|
parent.type === "ClassDeclaration" ||
|
60
61
|
(
|
61
62
|
parent.type.endsWith("Expression") &&
|
62
|
-
|
63
|
+
(parentPrecedence === -1 || parentPrecedence >= PRECEDENCE_OF_EXPONENTIATION_EXPR) &&
|
63
64
|
!(parent.type === "BinaryExpression" && parent.operator === "**" && parent.right === node) &&
|
64
65
|
!((parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node)) &&
|
65
66
|
!(parent.type === "MemberExpression" && parent.computed && parent.property === node) &&
|
@@ -37,15 +37,6 @@ function isRegexLiteral(node) {
|
|
37
37
|
return node.type === "Literal" && Object.prototype.hasOwnProperty.call(node, "regex");
|
38
38
|
}
|
39
39
|
|
40
|
-
/**
|
41
|
-
* Determines whether the given node is a template literal without expressions.
|
42
|
-
* @param {ASTNode} node Node to check.
|
43
|
-
* @returns {boolean} True if the node is a template literal without expressions.
|
44
|
-
*/
|
45
|
-
function isStaticTemplateLiteral(node) {
|
46
|
-
return node.type === "TemplateLiteral" && node.expressions.length === 0;
|
47
|
-
}
|
48
|
-
|
49
40
|
const validPrecedingTokens = new Set([
|
50
41
|
"(",
|
51
42
|
";",
|
@@ -178,7 +169,7 @@ module.exports = {
|
|
178
169
|
return node.type === "TaggedTemplateExpression" &&
|
179
170
|
astUtils.isSpecificMemberAccess(node.tag, "String", "raw") &&
|
180
171
|
isGlobalReference(astUtils.skipChainExpression(node.tag).object) &&
|
181
|
-
isStaticTemplateLiteral(node.quasi);
|
172
|
+
astUtils.isStaticTemplateLiteral(node.quasi);
|
182
173
|
}
|
183
174
|
|
184
175
|
/**
|
@@ -191,7 +182,7 @@ module.exports = {
|
|
191
182
|
return node.value;
|
192
183
|
}
|
193
184
|
|
194
|
-
if (isStaticTemplateLiteral(node)) {
|
185
|
+
if (astUtils.isStaticTemplateLiteral(node)) {
|
195
186
|
return node.quasis[0].value.cooked;
|
196
187
|
}
|
197
188
|
|
@@ -209,7 +200,7 @@ module.exports = {
|
|
209
200
|
*/
|
210
201
|
function isStaticString(node) {
|
211
202
|
return isStringLiteral(node) ||
|
212
|
-
isStaticTemplateLiteral(node) ||
|
203
|
+
astUtils.isStaticTemplateLiteral(node) ||
|
213
204
|
isStringRawTaggedStaticTemplateLiteral(node);
|
214
205
|
}
|
215
206
|
|