eslint 3.14.0 → 3.16.1
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/CHANGELOG.md +68 -0
- package/README.md +1 -1
- package/conf/{eslint.json → eslint-recommended.js} +86 -71
- package/lib/ast-utils.js +192 -24
- package/lib/cli.js +2 -2
- package/lib/code-path-analysis/code-path-state.js +2 -2
- package/lib/config/autoconfig.js +3 -3
- package/lib/config/config-file.js +31 -24
- package/lib/config/config-initializer.js +1 -1
- package/lib/config/config-validator.js +6 -6
- package/lib/config.js +3 -2
- package/lib/eslint.js +18 -18
- package/lib/formatters/checkstyle.js +2 -2
- package/lib/formatters/compact.js +2 -2
- package/lib/formatters/junit.js +2 -2
- package/lib/formatters/tap.js +2 -2
- package/lib/formatters/unix.js +2 -2
- package/lib/formatters/visualstudio.js +2 -2
- package/lib/rules/arrow-body-style.js +7 -4
- package/lib/rules/arrow-spacing.js +7 -6
- package/lib/rules/block-spacing.js +2 -2
- package/lib/rules/brace-style.js +42 -22
- package/lib/rules/capitalized-comments.js +6 -6
- package/lib/rules/comma-spacing.js +16 -16
- package/lib/rules/consistent-return.js +1 -1
- package/lib/rules/constructor-super.js +3 -3
- package/lib/rules/curly.js +11 -7
- package/lib/rules/default-case.js +3 -3
- package/lib/rules/eqeqeq.js +15 -6
- package/lib/rules/func-call-spacing.js +10 -13
- package/lib/rules/func-name-matching.js +1 -1
- package/lib/rules/generator-star-spacing.js +18 -19
- package/lib/rules/global-require.js +2 -2
- package/lib/rules/id-blacklist.js +2 -2
- package/lib/rules/id-length.js +3 -3
- package/lib/rules/id-match.js +2 -2
- package/lib/rules/indent.js +21 -20
- package/lib/rules/key-spacing.js +20 -23
- package/lib/rules/keyword-spacing.js +2 -13
- package/lib/rules/line-comment-position.js +1 -1
- package/lib/rules/linebreak-style.js +7 -1
- package/lib/rules/lines-around-comment.js +4 -4
- package/lib/rules/lines-around-directive.js +3 -3
- package/lib/rules/max-lines.js +2 -2
- package/lib/rules/max-statements-per-line.js +7 -6
- package/lib/rules/new-cap.js +2 -2
- package/lib/rules/newline-after-var.js +7 -2
- package/lib/rules/newline-before-return.js +2 -2
- package/lib/rules/newline-per-chained-call.js +3 -1
- package/lib/rules/no-cond-assign.js +3 -3
- package/lib/rules/no-extend-native.js +3 -3
- package/lib/rules/no-extra-bind.js +3 -4
- package/lib/rules/no-extra-boolean-cast.js +8 -0
- package/lib/rules/no-extra-parens.js +29 -8
- package/lib/rules/no-inner-declarations.js +4 -4
- package/lib/rules/no-irregular-whitespace.js +7 -1
- package/lib/rules/no-lone-blocks.js +10 -10
- package/lib/rules/no-mixed-operators.js +1 -7
- package/lib/rules/no-mixed-requires.js +4 -4
- package/lib/rules/no-multi-spaces.js +4 -1
- package/lib/rules/no-multi-str.js +7 -3
- package/lib/rules/no-redeclare.js +7 -7
- package/lib/rules/no-return-assign.js +7 -14
- package/lib/rules/no-sequences.js +7 -6
- package/lib/rules/no-trailing-spaces.js +8 -2
- package/lib/rules/no-undefined.js +45 -6
- package/lib/rules/no-unexpected-multiline.js +9 -8
- package/lib/rules/no-unneeded-ternary.js +5 -1
- package/lib/rules/no-unused-labels.js +17 -2
- package/lib/rules/no-unused-vars.js +34 -19
- package/lib/rules/no-use-before-define.js +33 -29
- package/lib/rules/no-useless-computed-key.js +8 -3
- package/lib/rules/no-useless-concat.js +10 -7
- package/lib/rules/no-useless-escape.js +1 -1
- package/lib/rules/no-useless-return.js +1 -7
- package/lib/rules/no-var.js +11 -0
- package/lib/rules/no-whitespace-before-property.js +5 -16
- package/lib/rules/object-curly-newline.js +2 -2
- package/lib/rules/object-curly-spacing.js +7 -25
- package/lib/rules/object-property-newline.js +3 -3
- package/lib/rules/object-shorthand.js +10 -10
- package/lib/rules/operator-assignment.js +2 -2
- package/lib/rules/operator-linebreak.js +8 -10
- package/lib/rules/padded-blocks.js +7 -4
- package/lib/rules/prefer-spread.js +1 -1
- package/lib/rules/prefer-template.js +1 -1
- package/lib/rules/quotes.js +10 -6
- package/lib/rules/semi-spacing.js +4 -0
- package/lib/rules/sort-imports.js +4 -4
- package/lib/rules/sort-vars.js +2 -2
- package/lib/rules/space-before-function-paren.js +8 -5
- package/lib/rules/space-in-parens.js +8 -8
- package/lib/rules/spaced-comment.js +10 -10
- package/lib/rules/strict.js +2 -2
- package/lib/rules/template-tag-spacing.js +77 -0
- package/lib/rules/unicode-bom.js +1 -1
- package/lib/rules/wrap-iife.js +5 -5
- package/lib/rules/yoda.js +2 -7
- package/lib/rules.js +2 -2
- package/lib/testers/rule-tester.js +25 -18
- package/lib/token-store/backward-token-comment-cursor.js +57 -0
- package/lib/token-store/backward-token-cursor.js +56 -0
- package/lib/token-store/cursor.js +76 -0
- package/lib/token-store/cursors.js +92 -0
- package/lib/token-store/decorative-cursor.js +39 -0
- package/lib/token-store/filter-cursor.js +43 -0
- package/lib/token-store/forward-token-comment-cursor.js +57 -0
- package/lib/token-store/forward-token-cursor.js +61 -0
- package/lib/token-store/index.js +604 -0
- package/lib/token-store/limit-cursor.js +40 -0
- package/lib/token-store/padded-token-cursor.js +38 -0
- package/lib/token-store/skip-cursor.js +42 -0
- package/lib/token-store/utils.js +100 -0
- package/lib/util/glob.js +1 -1
- package/lib/util/source-code-fixer.js +46 -44
- package/lib/util/source-code.js +35 -19
- package/messages/extend-config-missing.txt +3 -0
- package/package.json +3 -3
- package/lib/token-store.js +0 -203
@@ -8,7 +8,7 @@
|
|
8
8
|
// Requirements
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
|
-
const
|
11
|
+
const astUtils = require("../ast-utils");
|
12
12
|
|
13
13
|
//------------------------------------------------------------------------------
|
14
14
|
// Rule Definition
|
@@ -44,8 +44,7 @@ module.exports = {
|
|
44
44
|
loc: node.parent.property.loc.start,
|
45
45
|
fix(fixer) {
|
46
46
|
const firstTokenToRemove = context.getSourceCode()
|
47
|
-
.
|
48
|
-
.find(token => token.value !== ")");
|
47
|
+
.getFirstTokenBetween(node.parent.object, node.parent.property, astUtils.isNotClosingParenToken);
|
49
48
|
|
50
49
|
return fixer.removeRange([firstTokenToRemove.range[0], node.parent.parent.range[1]]);
|
51
50
|
}
|
@@ -73,7 +72,7 @@ module.exports = {
|
|
73
72
|
grandparent.arguments.length === 1 &&
|
74
73
|
parent.type === "MemberExpression" &&
|
75
74
|
parent.object === node &&
|
76
|
-
|
75
|
+
astUtils.getStaticPropertyName(parent) === "bind"
|
77
76
|
);
|
78
77
|
}
|
79
78
|
|
@@ -98,6 +98,14 @@ module.exports = {
|
|
98
98
|
node,
|
99
99
|
message: "Redundant Boolean call.",
|
100
100
|
fix: fixer => {
|
101
|
+
if (!node.arguments.length) {
|
102
|
+
return fixer.replaceText(parent, "true");
|
103
|
+
}
|
104
|
+
|
105
|
+
if (node.arguments.length > 1 || node.arguments[0].type === "SpreadElement") {
|
106
|
+
return null;
|
107
|
+
}
|
108
|
+
|
101
109
|
const argument = node.arguments[0];
|
102
110
|
|
103
111
|
if (astUtils.getPrecedence(argument) < astUtils.getPrecedence(node.parent)) {
|
@@ -167,6 +167,19 @@ module.exports = {
|
|
167
167
|
return false;
|
168
168
|
}
|
169
169
|
|
170
|
+
/**
|
171
|
+
* Determines if a constructor function is newed-up with parens
|
172
|
+
* @param {ASTNode} newExpression - The NewExpression node to be checked.
|
173
|
+
* @returns {boolean} True if the constructor is called with parens.
|
174
|
+
* @private
|
175
|
+
*/
|
176
|
+
function isNewExpressionWithParens(newExpression) {
|
177
|
+
const lastToken = sourceCode.getLastToken(newExpression);
|
178
|
+
const penultimateToken = sourceCode.getTokenBefore(lastToken);
|
179
|
+
|
180
|
+
return newExpression.arguments.length > 0 || penultimateToken.value === "(" && lastToken.value === ")";
|
181
|
+
}
|
182
|
+
|
170
183
|
/**
|
171
184
|
* Determines if a node is or contains an assignment expression
|
172
185
|
* @param {ASTNode} node - The node to be checked.
|
@@ -202,9 +215,9 @@ module.exports = {
|
|
202
215
|
return node.argument && containsAssignment(node.argument);
|
203
216
|
} else if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") {
|
204
217
|
return containsAssignment(node.body);
|
205
|
-
} else {
|
206
|
-
return containsAssignment(node);
|
207
218
|
}
|
219
|
+
return containsAssignment(node);
|
220
|
+
|
208
221
|
}
|
209
222
|
|
210
223
|
/**
|
@@ -353,6 +366,10 @@ module.exports = {
|
|
353
366
|
* @private
|
354
367
|
*/
|
355
368
|
function dryUnaryUpdate(node) {
|
369
|
+
if (node.type === "UnaryExpression" && node.argument.type === "BinaryExpression" && node.argument.operator === "**") {
|
370
|
+
return;
|
371
|
+
}
|
372
|
+
|
356
373
|
if (hasExcessParens(node.argument) && precedence(node.argument) >= precedence(node)) {
|
357
374
|
report(node.argument);
|
358
375
|
}
|
@@ -367,7 +384,8 @@ module.exports = {
|
|
367
384
|
function dryCallNew(node) {
|
368
385
|
if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node) && !(
|
369
386
|
node.type === "CallExpression" &&
|
370
|
-
node.callee.type === "FunctionExpression"
|
387
|
+
(node.callee.type === "FunctionExpression" ||
|
388
|
+
node.callee.type === "NewExpression" && !isNewExpressionWithParens(node.callee)) &&
|
371
389
|
|
372
390
|
// One set of parentheses are allowed for a function expression
|
373
391
|
!hasDoubleExcessParens(node.callee)
|
@@ -395,13 +413,17 @@ module.exports = {
|
|
395
413
|
*/
|
396
414
|
function dryBinaryLogical(node) {
|
397
415
|
const prec = precedence(node);
|
398
|
-
const
|
416
|
+
const leftPrecedence = precedence(node.left);
|
417
|
+
const rightPrecedence = precedence(node.right);
|
418
|
+
const isExponentiation = node.operator === "**";
|
419
|
+
const shouldSkipLeft = (NESTED_BINARY && (node.left.type === "BinaryExpression" || node.left.type === "LogicalExpression")) ||
|
420
|
+
node.left.type === "UnaryExpression" && isExponentiation;
|
399
421
|
const shouldSkipRight = NESTED_BINARY && (node.right.type === "BinaryExpression" || node.right.type === "LogicalExpression");
|
400
422
|
|
401
|
-
if (!shouldSkipLeft && hasExcessParens(node.left) &&
|
423
|
+
if (!shouldSkipLeft && hasExcessParens(node.left) && (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation))) {
|
402
424
|
report(node.left);
|
403
425
|
}
|
404
|
-
if (!shouldSkipRight && hasExcessParens(node.right) &&
|
426
|
+
if (!shouldSkipRight && hasExcessParens(node.right) && (rightPrecedence > prec || (rightPrecedence === prec && isExponentiation))) {
|
405
427
|
report(node.right);
|
406
428
|
}
|
407
429
|
}
|
@@ -535,8 +557,7 @@ module.exports = {
|
|
535
557
|
!(
|
536
558
|
(node.object.type === "Literal" &&
|
537
559
|
typeof node.object.value === "number" &&
|
538
|
-
astUtils.isDecimalInteger(node.object))
|
539
|
-
||
|
560
|
+
astUtils.isDecimalInteger(node.object)) ||
|
540
561
|
|
541
562
|
// RegExp literal is allowed to have parens (#1589)
|
542
563
|
(node.object.type === "Literal" && node.object.regex)
|
@@ -64,10 +64,10 @@ module.exports = {
|
|
64
64
|
|
65
65
|
if (!valid) {
|
66
66
|
context.report({ node, message: "Move {{type}} declaration to {{body}} root.", data: {
|
67
|
-
type: (node.type === "FunctionDeclaration"
|
68
|
-
"function" : "variable"),
|
69
|
-
body: (body.type === "Program"
|
70
|
-
"program" : "function body")
|
67
|
+
type: (node.type === "FunctionDeclaration"
|
68
|
+
? "function" : "variable"),
|
69
|
+
body: (body.type === "Program"
|
70
|
+
? "program" : "function body")
|
71
71
|
} });
|
72
72
|
}
|
73
73
|
}
|
@@ -6,6 +6,12 @@
|
|
6
6
|
|
7
7
|
"use strict";
|
8
8
|
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
// Requirements
|
11
|
+
//------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
const astUtils = require("../ast-utils");
|
14
|
+
|
9
15
|
//------------------------------------------------------------------------------
|
10
16
|
// Constants
|
11
17
|
//------------------------------------------------------------------------------
|
@@ -13,7 +19,7 @@
|
|
13
19
|
const ALL_IRREGULARS = /[\f\v\u0085\u00A0\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000\u2028\u2029]/;
|
14
20
|
const IRREGULAR_WHITESPACE = /[\f\v\u0085\u00A0\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+/mg;
|
15
21
|
const IRREGULAR_LINE_TERMINATORS = /[\u2028\u2029]/mg;
|
16
|
-
const LINE_BREAK =
|
22
|
+
const LINE_BREAK = astUtils.createGlobalLinebreakMatcher();
|
17
23
|
|
18
24
|
//------------------------------------------------------------------------------
|
19
25
|
// Rule Definition
|
@@ -32,22 +32,22 @@ module.exports = {
|
|
32
32
|
* @returns {void}
|
33
33
|
*/
|
34
34
|
function report(node) {
|
35
|
-
const
|
35
|
+
const message = node.parent.type === "BlockStatement" ? "Nested block is redundant." : "Block is redundant.";
|
36
36
|
|
37
|
-
context.report({ node, message
|
38
|
-
"Block is redundant." :
|
39
|
-
"Nested block is redundant."
|
40
|
-
});
|
37
|
+
context.report({ node, message });
|
41
38
|
}
|
42
39
|
|
43
40
|
/**
|
44
|
-
* Checks for any ocurrence of BlockStatement
|
45
|
-
* @
|
41
|
+
* Checks for any ocurrence of a BlockStatement in a place where lists of statements can appear
|
42
|
+
* @param {ASTNode} node The node to check
|
43
|
+
* @returns {boolean} True if the node is a lone block.
|
46
44
|
*/
|
47
|
-
function isLoneBlock() {
|
48
|
-
|
45
|
+
function isLoneBlock(node) {
|
46
|
+
return node.parent.type === "BlockStatement" ||
|
47
|
+
node.parent.type === "Program" ||
|
49
48
|
|
50
|
-
|
49
|
+
// Don't report blocks in switch cases if the block is the only statement of the case.
|
50
|
+
node.parent.type === "SwitchCase" && !(node.parent.consequent[0] === node && node.parent.consequent.length === 1);
|
51
51
|
}
|
52
52
|
|
53
53
|
/**
|
@@ -148,13 +148,7 @@ module.exports = {
|
|
148
148
|
* @returns {Token} The operator token of the node.
|
149
149
|
*/
|
150
150
|
function getOperatorToken(node) {
|
151
|
-
|
152
|
-
|
153
|
-
while (token.value === ")") {
|
154
|
-
token = sourceCode.getTokenAfter(token);
|
155
|
-
}
|
156
|
-
|
157
|
-
return token;
|
151
|
+
return sourceCode.getTokenAfter(node.left, astUtils.isNotClosingParenToken);
|
158
152
|
}
|
159
153
|
|
160
154
|
/**
|
@@ -153,11 +153,11 @@ module.exports = {
|
|
153
153
|
|
154
154
|
// "var utils = require('./utils');"
|
155
155
|
return REQ_FILE;
|
156
|
-
} else {
|
157
|
-
|
158
|
-
// "var async = require('async');"
|
159
|
-
return REQ_MODULE;
|
160
156
|
}
|
157
|
+
|
158
|
+
// "var async = require('async');"
|
159
|
+
return REQ_MODULE;
|
160
|
+
|
161
161
|
}
|
162
162
|
|
163
163
|
/**
|
@@ -5,6 +5,8 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
const astUtils = require("../ast-utils");
|
9
|
+
|
8
10
|
//------------------------------------------------------------------------------
|
9
11
|
// Rule Definition
|
10
12
|
//------------------------------------------------------------------------------
|
@@ -93,7 +95,8 @@ module.exports = {
|
|
93
95
|
const sourceCode = context.getSourceCode(),
|
94
96
|
source = sourceCode.getText(),
|
95
97
|
allComments = sourceCode.getAllComments(),
|
96
|
-
|
98
|
+
JOINED_LINEBEAKS = Array.from(astUtils.LINEBREAKS).join(""),
|
99
|
+
pattern = new RegExp(String.raw`[^ \t${JOINED_LINEBEAKS}].? {2,}`, "g"); // note: repeating space
|
97
100
|
let parent;
|
98
101
|
|
99
102
|
|
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("../ast-utils");
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Rule Definition
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -39,9 +45,7 @@ module.exports = {
|
|
39
45
|
return {
|
40
46
|
|
41
47
|
Literal(node) {
|
42
|
-
|
43
|
-
|
44
|
-
if (lineBreak.test(node.raw) && !isJSXElement(node.parent)) {
|
48
|
+
if (astUtils.LINEBREAK_MATCHER.test(node.raw) && !isJSXElement(node.parent)) {
|
45
49
|
context.report({ node, message: "Multiline support is limited to browsers supporting ES5 only." });
|
46
50
|
}
|
47
51
|
}
|
@@ -89,13 +89,13 @@ module.exports = {
|
|
89
89
|
BlockStatement: checkForBlock,
|
90
90
|
SwitchStatement: checkForBlock
|
91
91
|
};
|
92
|
-
} else {
|
93
|
-
return {
|
94
|
-
Program: checkForGlobal,
|
95
|
-
FunctionDeclaration: checkForBlock,
|
96
|
-
FunctionExpression: checkForBlock,
|
97
|
-
ArrowFunctionExpression: checkForBlock
|
98
|
-
};
|
99
92
|
}
|
93
|
+
return {
|
94
|
+
Program: checkForGlobal,
|
95
|
+
FunctionDeclaration: checkForBlock,
|
96
|
+
FunctionExpression: checkForBlock,
|
97
|
+
ArrowFunctionExpression: checkForBlock
|
98
|
+
};
|
99
|
+
|
100
100
|
}
|
101
101
|
};
|
@@ -5,23 +5,16 @@
|
|
5
5
|
"use strict";
|
6
6
|
|
7
7
|
//------------------------------------------------------------------------------
|
8
|
-
//
|
8
|
+
// Requirements
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
|
-
const
|
11
|
+
const astUtils = require("../ast-utils");
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
* @param {sourceCode} sourceCode - The ESLint SourceCode object.
|
17
|
-
* @returns {boolean} Whether or not the node is enclosed in parentheses.
|
18
|
-
*/
|
19
|
-
function isEnclosedInParens(node, sourceCode) {
|
20
|
-
const prevToken = sourceCode.getTokenBefore(node);
|
21
|
-
const nextToken = sourceCode.getTokenAfter(node);
|
13
|
+
//------------------------------------------------------------------------------
|
14
|
+
// Helpers
|
15
|
+
//------------------------------------------------------------------------------
|
22
16
|
|
23
|
-
|
24
|
-
}
|
17
|
+
const SENTINEL_TYPE = /^(?:[a-zA-Z]+?Statement|ArrowFunctionExpression|FunctionExpression|ClassExpression)$/;
|
25
18
|
|
26
19
|
//------------------------------------------------------------------------------
|
27
20
|
// Rule Definition
|
@@ -48,7 +41,7 @@ module.exports = {
|
|
48
41
|
|
49
42
|
return {
|
50
43
|
AssignmentExpression(node) {
|
51
|
-
if (!always &&
|
44
|
+
if (!always && astUtils.isParenthesised(sourceCode, node)) {
|
52
45
|
return;
|
53
46
|
}
|
54
47
|
|
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("../ast-utils");
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Rule Definition
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -57,12 +63,7 @@ module.exports = {
|
|
57
63
|
* @returns {boolean} True if the node has a paren on each side.
|
58
64
|
*/
|
59
65
|
function isParenthesised(node) {
|
60
|
-
|
61
|
-
nextToken = sourceCode.getTokenAfter(node);
|
62
|
-
|
63
|
-
return previousToken && nextToken &&
|
64
|
-
previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
|
65
|
-
nextToken.value === ")" && nextToken.range[0] >= node.range[1];
|
66
|
+
return astUtils.isParenthesised(sourceCode, node);
|
66
67
|
}
|
67
68
|
|
68
69
|
/**
|
@@ -4,6 +4,12 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const astUtils = require("../ast-utils");
|
12
|
+
|
7
13
|
//------------------------------------------------------------------------------
|
8
14
|
// Rule Definition
|
9
15
|
//------------------------------------------------------------------------------
|
@@ -34,7 +40,7 @@ module.exports = {
|
|
34
40
|
create(context) {
|
35
41
|
const sourceCode = context.getSourceCode();
|
36
42
|
|
37
|
-
const BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\
|
43
|
+
const BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u3000]",
|
38
44
|
SKIP_BLANK = `^${BLANK_CLASS}*$`,
|
39
45
|
NONBLANK = `${BLANK_CLASS}+$`;
|
40
46
|
|
@@ -81,7 +87,7 @@ module.exports = {
|
|
81
87
|
const re = new RegExp(NONBLANK),
|
82
88
|
skipMatch = new RegExp(SKIP_BLANK),
|
83
89
|
lines = sourceCode.lines,
|
84
|
-
linebreaks = sourceCode.getText().match(
|
90
|
+
linebreaks = sourceCode.getText().match(astUtils.createGlobalLinebreakMatcher());
|
85
91
|
let totalLength = 0,
|
86
92
|
fixRange = [];
|
87
93
|
|
@@ -21,15 +21,54 @@ module.exports = {
|
|
21
21
|
|
22
22
|
create(context) {
|
23
23
|
|
24
|
+
/**
|
25
|
+
* Report an invalid "undefined" identifier node.
|
26
|
+
* @param {ASTNode} node The node to report.
|
27
|
+
* @returns {void}
|
28
|
+
*/
|
29
|
+
function report(node) {
|
30
|
+
context.report({
|
31
|
+
node,
|
32
|
+
message: "Unexpected use of undefined."
|
33
|
+
});
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Checks the given scope for references to `undefined` and reports
|
38
|
+
* all references found.
|
39
|
+
* @param {escope.Scope} scope The scope to check.
|
40
|
+
* @returns {void}
|
41
|
+
*/
|
42
|
+
function checkScope(scope) {
|
43
|
+
const undefinedVar = scope.set.get("undefined");
|
44
|
+
|
45
|
+
if (!undefinedVar) {
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
|
49
|
+
const references = undefinedVar.references;
|
50
|
+
|
51
|
+
const defs = undefinedVar.defs;
|
52
|
+
|
53
|
+
// Report non-initializing references (those are covered in defs below)
|
54
|
+
references
|
55
|
+
.filter(ref => !ref.init)
|
56
|
+
.forEach(ref => report(ref.identifier));
|
57
|
+
|
58
|
+
defs.forEach(def => report(def.name));
|
59
|
+
}
|
60
|
+
|
24
61
|
return {
|
62
|
+
"Program:exit"() {
|
63
|
+
const globalScope = context.getScope();
|
64
|
+
|
65
|
+
const stack = [globalScope];
|
25
66
|
|
26
|
-
|
27
|
-
|
28
|
-
const parent = context.getAncestors().pop();
|
67
|
+
while (stack.length) {
|
68
|
+
const scope = stack.pop();
|
29
69
|
|
30
|
-
|
31
|
-
|
32
|
-
}
|
70
|
+
stack.push.apply(stack, scope.childScopes);
|
71
|
+
checkScope(scope);
|
33
72
|
}
|
34
73
|
}
|
35
74
|
};
|
@@ -4,9 +4,16 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const astUtils = require("../ast-utils");
|
12
|
+
|
7
13
|
//------------------------------------------------------------------------------
|
8
14
|
// Rule Definition
|
9
15
|
//------------------------------------------------------------------------------
|
16
|
+
|
10
17
|
module.exports = {
|
11
18
|
meta: {
|
12
19
|
docs: {
|
@@ -35,14 +42,8 @@ module.exports = {
|
|
35
42
|
* @private
|
36
43
|
*/
|
37
44
|
function checkForBreakAfter(node, msg) {
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
// Move along until the end of the wrapped expression
|
42
|
-
while (openParen.value === ")") {
|
43
|
-
nodeExpressionEnd = openParen;
|
44
|
-
openParen = sourceCode.getTokenAfter(nodeExpressionEnd);
|
45
|
-
}
|
45
|
+
const openParen = sourceCode.getTokenAfter(node, astUtils.isNotClosingParenToken);
|
46
|
+
const nodeExpressionEnd = sourceCode.getTokenBefore(openParen);
|
46
47
|
|
47
48
|
if (openParen.loc.start.line !== nodeExpressionEnd.loc.end.line) {
|
48
49
|
context.report({ node, loc: openParen.loc.start, message: msg, data: { char: openParen.value } });
|
@@ -67,7 +67,11 @@ module.exports = {
|
|
67
67
|
*/
|
68
68
|
function invertExpression(node) {
|
69
69
|
if (node.type === "BinaryExpression" && Object.prototype.hasOwnProperty.call(OPERATOR_INVERSES, node.operator)) {
|
70
|
-
const operatorToken = sourceCode.
|
70
|
+
const operatorToken = sourceCode.getFirstTokenBetween(
|
71
|
+
node.left,
|
72
|
+
node.right,
|
73
|
+
token => token.value === node.operator
|
74
|
+
);
|
71
75
|
|
72
76
|
return sourceCode.getText().slice(node.range[0], operatorToken.range[0]) + OPERATOR_INVERSES[node.operator] + sourceCode.getText().slice(operatorToken.range[1], node.range[1]);
|
73
77
|
}
|
@@ -17,10 +17,13 @@ module.exports = {
|
|
17
17
|
recommended: true
|
18
18
|
},
|
19
19
|
|
20
|
-
schema: []
|
20
|
+
schema: [],
|
21
|
+
|
22
|
+
fixable: "code"
|
21
23
|
},
|
22
24
|
|
23
25
|
create(context) {
|
26
|
+
const sourceCode = context.getSourceCode();
|
24
27
|
let scopeInfo = null;
|
25
28
|
|
26
29
|
/**
|
@@ -49,7 +52,19 @@ module.exports = {
|
|
49
52
|
context.report({
|
50
53
|
node: node.label,
|
51
54
|
message: "'{{name}}:' is defined but never used.",
|
52
|
-
data: node.label
|
55
|
+
data: node.label,
|
56
|
+
fix(fixer) {
|
57
|
+
|
58
|
+
/*
|
59
|
+
* Only perform a fix if there are no comments between the label and the body. This will be the case
|
60
|
+
* when there is exactly one token/comment (the ":") between the label and the body.
|
61
|
+
*/
|
62
|
+
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) === sourceCode.getTokenBefore(node.body, { includeComments: true })) {
|
63
|
+
return fixer.removeRange([node.range[0], node.body.range[0]]);
|
64
|
+
}
|
65
|
+
|
66
|
+
return null;
|
67
|
+
}
|
53
68
|
});
|
54
69
|
}
|
55
70
|
|
@@ -42,6 +42,9 @@ module.exports = {
|
|
42
42
|
args: {
|
43
43
|
enum: ["all", "after-used", "none"]
|
44
44
|
},
|
45
|
+
ignoreRestSiblings: {
|
46
|
+
type: "boolean"
|
47
|
+
},
|
45
48
|
argsIgnorePattern: {
|
46
49
|
type: "string"
|
47
50
|
},
|
@@ -59,6 +62,7 @@ module.exports = {
|
|
59
62
|
},
|
60
63
|
|
61
64
|
create(context) {
|
65
|
+
const sourceCode = context.getSourceCode();
|
62
66
|
|
63
67
|
const DEFINED_MESSAGE = "'{{name}}' is defined but never used.";
|
64
68
|
const ASSIGNED_MESSAGE = "'{{name}}' is assigned a value but never used.";
|
@@ -66,6 +70,7 @@ module.exports = {
|
|
66
70
|
const config = {
|
67
71
|
vars: "all",
|
68
72
|
args: "after-used",
|
73
|
+
ignoreRestSiblings: false,
|
69
74
|
caughtErrors: "none"
|
70
75
|
};
|
71
76
|
|
@@ -77,6 +82,7 @@ module.exports = {
|
|
77
82
|
} else {
|
78
83
|
config.vars = firstOption.vars || config.vars;
|
79
84
|
config.args = firstOption.args || config.args;
|
85
|
+
config.ignoreRestSiblings = firstOption.ignoreRestSiblings || config.ignoreRestSiblings;
|
80
86
|
config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;
|
81
87
|
|
82
88
|
if (firstOption.varsIgnorePattern) {
|
@@ -120,9 +126,33 @@ module.exports = {
|
|
120
126
|
}
|
121
127
|
|
122
128
|
return node.parent.type.indexOf("Export") === 0;
|
123
|
-
} else {
|
124
|
-
return false;
|
125
129
|
}
|
130
|
+
return false;
|
131
|
+
|
132
|
+
}
|
133
|
+
|
134
|
+
/**
|
135
|
+
* Determines if a variable has a sibling rest property
|
136
|
+
* @param {Variable} variable - EScope variable object.
|
137
|
+
* @returns {boolean} True if the variable is exported, false if not.
|
138
|
+
* @private
|
139
|
+
*/
|
140
|
+
function hasRestSpreadSibling(variable) {
|
141
|
+
if (config.ignoreRestSiblings) {
|
142
|
+
const restProperties = new Set(["ExperimentalRestProperty", "RestProperty"]);
|
143
|
+
|
144
|
+
return variable.defs
|
145
|
+
.filter(def => def.name.type === "Identifier")
|
146
|
+
.some(def => (
|
147
|
+
def.node.id &&
|
148
|
+
def.node.id.type === "ObjectPattern" &&
|
149
|
+
def.node.id.properties.length &&
|
150
|
+
restProperties.has(def.node.id.properties[def.node.id.properties.length - 1].type) && // last property is a rest property
|
151
|
+
!restProperties.has(def.name.parent.type) // variable is sibling of the rest property
|
152
|
+
));
|
153
|
+
}
|
154
|
+
|
155
|
+
return false;
|
126
156
|
}
|
127
157
|
|
128
158
|
/**
|
@@ -495,7 +525,7 @@ module.exports = {
|
|
495
525
|
}
|
496
526
|
}
|
497
527
|
|
498
|
-
if (!isUsedVariable(variable) && !isExported(variable)) {
|
528
|
+
if (!isUsedVariable(variable) && !isExported(variable) && !hasRestSpreadSibling(variable)) {
|
499
529
|
unusedVars.push(variable);
|
500
530
|
}
|
501
531
|
}
|
@@ -537,23 +567,8 @@ module.exports = {
|
|
537
567
|
*/
|
538
568
|
function getLocation(variable) {
|
539
569
|
const comment = variable.eslintExplicitGlobalComment;
|
540
|
-
const baseLoc = comment.loc.start;
|
541
|
-
let column = getColumnInComment(variable, comment);
|
542
|
-
const prefix = comment.value.slice(0, column);
|
543
|
-
const lineInComment = (prefix.match(/\n/g) || []).length;
|
544
|
-
|
545
|
-
if (lineInComment > 0) {
|
546
|
-
column -= 1 + prefix.lastIndexOf("\n");
|
547
|
-
} else {
|
548
|
-
|
549
|
-
// 2 is for `/*`
|
550
|
-
column += baseLoc.column + 2;
|
551
|
-
}
|
552
570
|
|
553
|
-
return
|
554
|
-
line: baseLoc.line + lineInComment,
|
555
|
-
column
|
556
|
-
};
|
571
|
+
return astUtils.getLocationFromRangeIndex(sourceCode, comment.range[0] + 2 + getColumnInComment(variable, comment));
|
557
572
|
}
|
558
573
|
|
559
574
|
//--------------------------------------------------------------------------
|