eslint 3.13.1 → 3.16.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/CHANGELOG.md +81 -0
- package/README.md +1 -1
- package/conf/{eslint.json → eslint-recommended.js} +88 -71
- package/lib/ast-utils.js +247 -28
- package/lib/cli.js +2 -2
- package/lib/code-path-analysis/code-path-state.js +2 -2
- package/lib/config/autoconfig.js +24 -20
- package/lib/config/config-file.js +31 -24
- package/lib/config/config-initializer.js +2 -2
- package/lib/config/config-rule.js +15 -16
- 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/codeframe.js +1 -1
- 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/internal-rules/internal-consistent-docs-description.js +1 -1
- package/lib/rule-context.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 +93 -202
- package/lib/rules/capitalized-comments.js +6 -6
- package/lib/rules/comma-dangle.js +6 -6
- package/lib/rules/comma-spacing.js +16 -16
- package/lib/rules/comma-style.js +1 -1
- 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 +12 -15
- 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 +4 -4
- package/lib/rules/max-lines.js +3 -3
- package/lib/rules/max-statements-per-line.js +8 -7
- 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-await-in-loop.js +5 -5
- package/lib/rules/no-cond-assign.js +3 -3
- package/lib/rules/no-dupe-keys.js +1 -1
- package/lib/rules/no-else-return.js +88 -25
- 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 +22 -1
- package/lib/rules/no-extra-parens.js +57 -9
- 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-assign.js +41 -0
- 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-return-await.js +2 -2
- package/lib/rules/no-sequences.js +7 -6
- package/lib/rules/no-throw-literal.js +2 -39
- 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 +9 -4
- 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 +5 -11
- package/lib/rules/no-var.js +69 -3
- 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 +4 -4
- package/lib/rules/prefer-promise-reject-errors.js +124 -0
- package/lib/rules/prefer-spread.js +1 -1
- package/lib/rules/prefer-template.js +1 -1
- package/lib/rules/quotes.js +11 -7
- package/lib/rules/require-await.js +1 -1
- package/lib/rules/semi-spacing.js +4 -0
- package/lib/rules/sort-imports.js +4 -4
- package/lib/rules/sort-keys.js +2 -2
- package/lib/rules/sort-vars.js +2 -2
- package/lib/rules/space-before-function-paren.js +9 -6
- 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 +28 -21
- 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/comment-event-generator.js +17 -16
- package/lib/util/glob-util.js +1 -1
- package/lib/util/glob.js +1 -1
- package/lib/util/rule-fixer.js +3 -8
- package/lib/util/source-code-fixer.js +41 -45
- 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
@@ -5,44 +5,7 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
-
|
9
|
-
// Helpers
|
10
|
-
//------------------------------------------------------------------------------
|
11
|
-
|
12
|
-
/**
|
13
|
-
* Determine if a node has a possiblity to be an Error object
|
14
|
-
* @param {ASTNode} node ASTNode to check
|
15
|
-
* @returns {boolean} True if there is a chance it contains an Error obj
|
16
|
-
*/
|
17
|
-
function couldBeError(node) {
|
18
|
-
switch (node.type) {
|
19
|
-
case "Identifier":
|
20
|
-
case "CallExpression":
|
21
|
-
case "NewExpression":
|
22
|
-
case "MemberExpression":
|
23
|
-
case "TaggedTemplateExpression":
|
24
|
-
case "YieldExpression":
|
25
|
-
return true; // possibly an error object.
|
26
|
-
|
27
|
-
case "AssignmentExpression":
|
28
|
-
return couldBeError(node.right);
|
29
|
-
|
30
|
-
case "SequenceExpression": {
|
31
|
-
const exprs = node.expressions;
|
32
|
-
|
33
|
-
return exprs.length !== 0 && couldBeError(exprs[exprs.length - 1]);
|
34
|
-
}
|
35
|
-
|
36
|
-
case "LogicalExpression":
|
37
|
-
return couldBeError(node.left) || couldBeError(node.right);
|
38
|
-
|
39
|
-
case "ConditionalExpression":
|
40
|
-
return couldBeError(node.consequent) || couldBeError(node.alternate);
|
41
|
-
|
42
|
-
default:
|
43
|
-
return false;
|
44
|
-
}
|
45
|
-
}
|
8
|
+
const astUtils = require("../ast-utils");
|
46
9
|
|
47
10
|
//------------------------------------------------------------------------------
|
48
11
|
// Rule Definition
|
@@ -64,7 +27,7 @@ module.exports = {
|
|
64
27
|
return {
|
65
28
|
|
66
29
|
ThrowStatement(node) {
|
67
|
-
if (!couldBeError(node.argument)) {
|
30
|
+
if (!astUtils.couldBeError(node.argument)) {
|
68
31
|
context.report({ node, message: "Expected an object to be thrown." });
|
69
32
|
} else if (node.argument.type === "Identifier") {
|
70
33
|
if (node.argument.name === "undefined") {
|
@@ -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
|
//--------------------------------------------------------------------------
|
@@ -21,22 +21,17 @@ const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/;
|
|
21
21
|
function parseOptions(options) {
|
22
22
|
let functions = true;
|
23
23
|
let classes = true;
|
24
|
+
let variables = true;
|
24
25
|
|
25
26
|
if (typeof options === "string") {
|
26
27
|
functions = (options !== "nofunc");
|
27
28
|
} else if (typeof options === "object" && options !== null) {
|
28
29
|
functions = options.functions !== false;
|
29
30
|
classes = options.classes !== false;
|
31
|
+
variables = options.variables !== false;
|
30
32
|
}
|
31
33
|
|
32
|
-
return { functions, classes };
|
33
|
-
}
|
34
|
-
|
35
|
-
/**
|
36
|
-
* @returns {boolean} `false`.
|
37
|
-
*/
|
38
|
-
function alwaysFalse() {
|
39
|
-
return false;
|
34
|
+
return { functions, classes, variables };
|
40
35
|
}
|
41
36
|
|
42
37
|
/**
|
@@ -64,14 +59,16 @@ function isOuterClass(variable, reference) {
|
|
64
59
|
}
|
65
60
|
|
66
61
|
/**
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
62
|
+
* Checks whether or not a given variable is a variable declaration in an upper function scope.
|
63
|
+
* @param {escope.Variable} variable - A variable to check.
|
64
|
+
* @param {escope.Reference} reference - A reference to check.
|
65
|
+
* @returns {boolean} `true` if the variable is a variable declaration.
|
66
|
+
*/
|
67
|
+
function isOuterVariable(variable, reference) {
|
68
|
+
return (
|
69
|
+
variable.defs[0].type === "Variable" &&
|
70
|
+
variable.scope.variableScope !== reference.from.variableScope
|
71
|
+
);
|
75
72
|
}
|
76
73
|
|
77
74
|
/**
|
@@ -155,7 +152,8 @@ module.exports = {
|
|
155
152
|
type: "object",
|
156
153
|
properties: {
|
157
154
|
functions: { type: "boolean" },
|
158
|
-
classes: { type: "boolean" }
|
155
|
+
classes: { type: "boolean" },
|
156
|
+
variables: { type: "boolean" }
|
159
157
|
},
|
160
158
|
additionalProperties: false
|
161
159
|
}
|
@@ -167,17 +165,23 @@ module.exports = {
|
|
167
165
|
create(context) {
|
168
166
|
const options = parseOptions(context.options[0]);
|
169
167
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
168
|
+
/**
|
169
|
+
* Determines whether a given use-before-define case should be reportedaccording to the options.
|
170
|
+
* @param {escope.Variable} variable The variable that gets used before being defined
|
171
|
+
* @param {escope.Reference} reference The reference to the variable
|
172
|
+
* @returns {boolean} `true` if the usage should be reported
|
173
|
+
*/
|
174
|
+
function isForbidden(variable, reference) {
|
175
|
+
if (isFunction(variable)) {
|
176
|
+
return options.functions;
|
177
|
+
}
|
178
|
+
if (isOuterClass(variable, reference)) {
|
179
|
+
return options.classes;
|
180
|
+
}
|
181
|
+
if (isOuterVariable(variable, reference)) {
|
182
|
+
return options.variables;
|
183
|
+
}
|
184
|
+
return true;
|
181
185
|
}
|
182
186
|
|
183
187
|
/**
|
@@ -200,7 +204,7 @@ module.exports = {
|
|
200
204
|
!variable ||
|
201
205
|
variable.identifiers.length === 0 ||
|
202
206
|
(variable.identifiers[0].range[1] < reference.identifier.range[1] && !isInInitializer(variable, reference)) ||
|
203
|
-
|
207
|
+
!isForbidden(variable, reference)
|
204
208
|
) {
|
205
209
|
return;
|
206
210
|
}
|
@@ -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,15 +40,14 @@ module.exports = {
|
|
34
40
|
const key = node.key,
|
35
41
|
nodeType = typeof key.value;
|
36
42
|
|
37
|
-
if (key.type === "Literal" && (nodeType === "string" || nodeType === "number")) {
|
43
|
+
if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== "__proto__") {
|
38
44
|
context.report({
|
39
45
|
node,
|
40
46
|
message: MESSAGE_UNNECESSARY_COMPUTED,
|
41
47
|
data: { property: sourceCode.getText(key) },
|
42
48
|
fix(fixer) {
|
43
|
-
const leftSquareBracket = sourceCode.getFirstToken(node,
|
44
|
-
const rightSquareBracket = sourceCode.
|
45
|
-
|
49
|
+
const leftSquareBracket = sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken);
|
50
|
+
const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken);
|
46
51
|
const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1);
|
47
52
|
|
48
53
|
if (tokensBetween.slice(0, -1).some((token, index) => sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) {
|
@@ -23,6 +23,15 @@ function isConcatenation(node) {
|
|
23
23
|
return node.type === "BinaryExpression" && node.operator === "+";
|
24
24
|
}
|
25
25
|
|
26
|
+
/**
|
27
|
+
* Checks if the given token is a `+` token or not.
|
28
|
+
* @param {Token} token - The token to check.
|
29
|
+
* @returns {boolean} `true` if the token is a `+` token.
|
30
|
+
*/
|
31
|
+
function isConcatOperatorToken(token) {
|
32
|
+
return token.value === "+" && token.type === "Punctuator";
|
33
|
+
}
|
34
|
+
|
26
35
|
/**
|
27
36
|
* Get's the right most node on the left side of a BinaryExpression with + operator.
|
28
37
|
* @param {ASTNode} node - A BinaryExpression node to check.
|
@@ -85,13 +94,7 @@ module.exports = {
|
|
85
94
|
astUtils.isStringLiteral(right) &&
|
86
95
|
astUtils.isTokenOnSameLine(left, right)
|
87
96
|
) {
|
88
|
-
|
89
|
-
// move warning location to operator
|
90
|
-
let operatorToken = sourceCode.getTokenAfter(left);
|
91
|
-
|
92
|
-
while (operatorToken.value !== "+") {
|
93
|
-
operatorToken = sourceCode.getTokenAfter(operatorToken);
|
94
|
-
}
|
97
|
+
const operatorToken = sourceCode.getFirstTokenBetween(left, right, isConcatOperatorToken);
|
95
98
|
|
96
99
|
context.report({
|
97
100
|
node,
|
@@ -24,7 +24,7 @@ function union(setA, setB) {
|
|
24
24
|
}());
|
25
25
|
}
|
26
26
|
|
27
|
-
const VALID_STRING_ESCAPES = new Set("\\nrvtbfux
|
27
|
+
const VALID_STRING_ESCAPES = union(new Set("\\nrvtbfux"), astUtils.LINEBREAKS);
|
28
28
|
const REGEX_GENERAL_ESCAPES = new Set("\\bcdDfnrsStvwWxu0123456789]");
|
29
29
|
const REGEX_NON_CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("^/.$*+?[{}|()B"));
|
30
30
|
|
@@ -45,13 +45,7 @@ function remove(array, element) {
|
|
45
45
|
* @returns {boolean} `true` if the node is removeable.
|
46
46
|
*/
|
47
47
|
function isRemovable(node) {
|
48
|
-
|
49
|
-
|
50
|
-
return (
|
51
|
-
parent.type === "Program" ||
|
52
|
-
parent.type === "BlockStatement" ||
|
53
|
-
parent.type === "SwitchCase"
|
54
|
-
);
|
48
|
+
return astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type);
|
55
49
|
}
|
56
50
|
|
57
51
|
/**
|
@@ -213,7 +207,7 @@ module.exports = {
|
|
213
207
|
scopeInfo = {
|
214
208
|
upper: scopeInfo,
|
215
209
|
uselessReturns: [],
|
216
|
-
codePath
|
210
|
+
codePath
|
217
211
|
};
|
218
212
|
},
|
219
213
|
|
@@ -226,7 +220,7 @@ module.exports = {
|
|
226
220
|
message: "Unnecessary return statement.",
|
227
221
|
fix(fixer) {
|
228
222
|
return isRemovable(node) ? fixer.remove(node) : null;
|
229
|
-
}
|
223
|
+
}
|
230
224
|
});
|
231
225
|
}
|
232
226
|
|
@@ -238,7 +232,7 @@ module.exports = {
|
|
238
232
|
onCodePathSegmentStart(segment) {
|
239
233
|
const info = {
|
240
234
|
uselessReturns: getUselessReturns([], segment.allPrevSegments),
|
241
|
-
returned: false
|
235
|
+
returned: false
|
242
236
|
};
|
243
237
|
|
244
238
|
// Stores the info.
|
@@ -287,7 +281,7 @@ module.exports = {
|
|
287
281
|
WithStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
|
288
282
|
ExportNamedDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
|
289
283
|
ExportDefaultDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
|
290
|
-
ExportAllDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed
|
284
|
+
ExportAllDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed
|
291
285
|
};
|
292
286
|
}
|
293
287
|
};
|
package/lib/rules/no-var.js
CHANGED
@@ -128,6 +128,43 @@ function isUsedFromOutsideOf(scopeNode) {
|
|
128
128
|
};
|
129
129
|
}
|
130
130
|
|
131
|
+
/**
|
132
|
+
* Creates the predicate function which checks whether a variable has their references in TDZ.
|
133
|
+
*
|
134
|
+
* The predicate function would return `true`:
|
135
|
+
*
|
136
|
+
* - if a reference is before the declarator. E.g. (var a = b, b = 1;)(var {a = b, b} = {};)
|
137
|
+
* - if a reference is in the expression of their default value. E.g. (var {a = a} = {};)
|
138
|
+
* - if a reference is in the expression of their initializer. E.g. (var a = a;)
|
139
|
+
*
|
140
|
+
* @param {ASTNode} node - The initializer node of VariableDeclarator.
|
141
|
+
* @returns {Function} The predicate function.
|
142
|
+
* @private
|
143
|
+
*/
|
144
|
+
function hasReferenceInTDZ(node) {
|
145
|
+
const initStart = node.range[0];
|
146
|
+
const initEnd = node.range[1];
|
147
|
+
|
148
|
+
return variable => {
|
149
|
+
const id = variable.defs[0].name;
|
150
|
+
const idStart = id.range[0];
|
151
|
+
const defaultValue = (id.parent.type === "AssignmentPattern" ? id.parent.right : null);
|
152
|
+
const defaultStart = defaultValue && defaultValue.range[0];
|
153
|
+
const defaultEnd = defaultValue && defaultValue.range[1];
|
154
|
+
|
155
|
+
return variable.references.some(reference => {
|
156
|
+
const start = reference.identifier.range[0];
|
157
|
+
const end = reference.identifier.range[1];
|
158
|
+
|
159
|
+
return !reference.init && (
|
160
|
+
start < idStart ||
|
161
|
+
(defaultValue !== null && start >= defaultStart && end <= defaultEnd) ||
|
162
|
+
(start >= initStart && end <= initEnd)
|
163
|
+
);
|
164
|
+
});
|
165
|
+
};
|
166
|
+
}
|
167
|
+
|
131
168
|
//------------------------------------------------------------------------------
|
132
169
|
// Rule Definition
|
133
170
|
//------------------------------------------------------------------------------
|
@@ -147,6 +184,21 @@ module.exports = {
|
|
147
184
|
create(context) {
|
148
185
|
const sourceCode = context.getSourceCode();
|
149
186
|
|
187
|
+
/**
|
188
|
+
* Checks whether the variables which are defined by the given declarator node have their references in TDZ.
|
189
|
+
*
|
190
|
+
* @param {ASTNode} declarator - The VariableDeclarator node to check.
|
191
|
+
* @returns {boolean} `true` if one of the variables which are defined by the given declarator node have their references in TDZ.
|
192
|
+
*/
|
193
|
+
function hasSelfReferenceInTDZ(declarator) {
|
194
|
+
if (!declarator.init) {
|
195
|
+
return false;
|
196
|
+
}
|
197
|
+
const variables = context.getDeclaredVariables(declarator);
|
198
|
+
|
199
|
+
return variables.some(hasReferenceInTDZ(declarator.init));
|
200
|
+
}
|
201
|
+
|
150
202
|
/**
|
151
203
|
* Checks whether it can fix a given variable declaration or not.
|
152
204
|
* It cannot fix if the following cases:
|
@@ -156,6 +208,8 @@ module.exports = {
|
|
156
208
|
* - A variable is used from outside the scope.
|
157
209
|
* - A variable is used from a closure within a loop.
|
158
210
|
* - A variable might be used before it is assigned within a loop.
|
211
|
+
* - A variable might be used in TDZ.
|
212
|
+
* - A variable is declared in statement position (e.g. a single-line `IfStatement`)
|
159
213
|
*
|
160
214
|
* ## A variable is declared on a SwitchCase node.
|
161
215
|
*
|
@@ -201,8 +255,10 @@ module.exports = {
|
|
201
255
|
const scopeNode = getScopeNode(node);
|
202
256
|
|
203
257
|
if (node.parent.type === "SwitchCase" ||
|
204
|
-
|
205
|
-
|
258
|
+
node.declarations.some(hasSelfReferenceInTDZ) ||
|
259
|
+
variables.some(isRedeclared) ||
|
260
|
+
variables.some(isUsedFromOutsideOf(scopeNode))
|
261
|
+
) {
|
206
262
|
return false;
|
207
263
|
}
|
208
264
|
|
@@ -215,6 +271,16 @@ module.exports = {
|
|
215
271
|
}
|
216
272
|
}
|
217
273
|
|
274
|
+
if (
|
275
|
+
!isLoopAssignee(node) &&
|
276
|
+
!(node.parent.type === "ForStatement" && node.parent.init === node) &&
|
277
|
+
!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)
|
278
|
+
) {
|
279
|
+
|
280
|
+
// If the declaration is not in a block, e.g. `if (foo) var bar = 1;`, then it can't be fixed.
|
281
|
+
return false;
|
282
|
+
}
|
283
|
+
|
218
284
|
return true;
|
219
285
|
}
|
220
286
|
|
@@ -241,7 +307,7 @@ module.exports = {
|
|
241
307
|
}
|
242
308
|
|
243
309
|
return {
|
244
|
-
VariableDeclaration(node) {
|
310
|
+
"VariableDeclaration:exit"(node) {
|
245
311
|
if (node.kind === "var") {
|
246
312
|
report(node);
|
247
313
|
}
|