eslint 3.15.0 → 3.17.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 +79 -0
- package/conf/{eslint.json → eslint-recommended.js} +87 -71
- package/lib/ast-utils.js +182 -80
- package/lib/code-path-analysis/code-path-state.js +2 -2
- package/lib/config/autoconfig.js +3 -3
- package/lib/config/config-file.js +14 -7
- package/lib/config/config-initializer.js +1 -1
- package/lib/config.js +3 -2
- package/lib/eslint.js +4 -4
- package/lib/internal-rules/internal-no-invalid-meta.js +2 -40
- package/lib/rules/array-callback-return.js +15 -5
- 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 +2 -6
- package/lib/rules/capitalized-comments.js +8 -7
- package/lib/rules/comma-spacing.js +3 -3
- package/lib/rules/complexity.js +14 -8
- package/lib/rules/consistent-return.js +18 -11
- 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-names.js +20 -5
- package/lib/rules/generator-star-spacing.js +18 -19
- 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 +7 -6
- package/lib/rules/key-spacing.js +12 -16
- package/lib/rules/keyword-spacing.js +21 -17
- package/lib/rules/line-comment-position.js +16 -6
- package/lib/rules/linebreak-style.js +7 -1
- package/lib/rules/lines-around-comment.js +23 -4
- package/lib/rules/lines-around-directive.js +3 -3
- package/lib/rules/max-lines.js +2 -2
- package/lib/rules/max-params.js +17 -4
- package/lib/rules/max-statements-per-line.js +7 -6
- package/lib/rules/max-statements.js +11 -10
- package/lib/rules/newline-after-var.js +7 -2
- package/lib/rules/newline-per-chained-call.js +3 -1
- package/lib/rules/no-compare-neg-zero.js +53 -0
- package/lib/rules/no-cond-assign.js +3 -3
- package/lib/rules/no-else-return.js +13 -1
- package/lib/rules/no-empty-function.js +9 -16
- 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 +1 -2
- package/lib/rules/no-extra-semi.js +13 -1
- package/lib/rules/no-inner-declarations.js +4 -4
- package/lib/rules/no-invalid-regexp.js +2 -1
- 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-multi-spaces.js +4 -1
- package/lib/rules/no-multi-str.js +7 -3
- package/lib/rules/no-multiple-empty-lines.js +2 -4
- package/lib/rules/no-param-reassign.js +29 -6
- package/lib/rules/no-restricted-properties.js +2 -0
- package/lib/rules/no-return-assign.js +7 -14
- package/lib/rules/no-return-await.js +1 -1
- 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 +13 -27
- package/lib/rules/no-use-before-define.js +1 -1
- 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 +2 -2
- package/lib/rules/no-useless-return.js +14 -9
- package/lib/rules/no-var.js +1 -3
- package/lib/rules/no-whitespace-before-property.js +5 -16
- package/lib/rules/nonblock-statement-body-position.js +114 -0
- 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 +4 -3
- package/lib/rules/operator-assignment.js +2 -2
- package/lib/rules/operator-linebreak.js +8 -10
- package/lib/rules/padded-blocks.js +39 -30
- package/lib/rules/prefer-destructuring.js +1 -1
- 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/semi.js +13 -1
- package/lib/rules/space-before-function-paren.js +8 -5
- package/lib/rules/space-unary-ops.js +19 -1
- package/lib/rules/spaced-comment.js +2 -2
- package/lib/rules/strict.js +10 -4
- package/lib/rules/unicode-bom.js +1 -1
- package/lib/rules/wrap-iife.js +5 -5
- package/lib/rules/yoda.js +4 -9
- package/lib/testers/rule-tester.js +46 -9
- 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/fix-tracker.js +121 -0
- package/lib/util/source-code-fixer.js +35 -39
- package/lib/util/source-code.js +129 -16
- package/messages/extend-config-missing.txt +3 -0
- package/package.json +5 -6
- package/lib/token-store.js +0 -203
@@ -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
|
|
@@ -62,9 +62,11 @@ module.exports = {
|
|
62
62
|
},
|
63
63
|
|
64
64
|
create(context) {
|
65
|
+
const sourceCode = context.getSourceCode();
|
65
66
|
|
66
67
|
const DEFINED_MESSAGE = "'{{name}}' is defined but never used.";
|
67
68
|
const ASSIGNED_MESSAGE = "'{{name}}' is assigned a value but never used.";
|
69
|
+
const REST_PROPERTY_TYPE = /^(?:Experimental)?RestProperty$/;
|
68
70
|
|
69
71
|
const config = {
|
70
72
|
vars: "all",
|
@@ -138,17 +140,16 @@ module.exports = {
|
|
138
140
|
*/
|
139
141
|
function hasRestSpreadSibling(variable) {
|
140
142
|
if (config.ignoreRestSiblings) {
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
));
|
143
|
+
return variable.defs.some(def => {
|
144
|
+
const propertyNode = def.name.parent;
|
145
|
+
const patternNode = propertyNode.parent;
|
146
|
+
|
147
|
+
return (
|
148
|
+
propertyNode.type === "Property" &&
|
149
|
+
patternNode.type === "ObjectPattern" &&
|
150
|
+
REST_PROPERTY_TYPE.test(patternNode.properties[patternNode.properties.length - 1].type)
|
151
|
+
);
|
152
|
+
});
|
152
153
|
}
|
153
154
|
|
154
155
|
return false;
|
@@ -566,23 +567,8 @@ module.exports = {
|
|
566
567
|
*/
|
567
568
|
function getLocation(variable) {
|
568
569
|
const comment = variable.eslintExplicitGlobalComment;
|
569
|
-
const baseLoc = comment.loc.start;
|
570
|
-
let column = getColumnInComment(variable, comment);
|
571
|
-
const prefix = comment.value.slice(0, column);
|
572
|
-
const lineInComment = (prefix.match(/\n/g) || []).length;
|
573
570
|
|
574
|
-
|
575
|
-
column -= 1 + prefix.lastIndexOf("\n");
|
576
|
-
} else {
|
577
|
-
|
578
|
-
// 2 is for `/*`
|
579
|
-
column += baseLoc.column + 2;
|
580
|
-
}
|
581
|
-
|
582
|
-
return {
|
583
|
-
line: baseLoc.line + lineInComment,
|
584
|
-
column
|
585
|
-
};
|
571
|
+
return sourceCode.getLocFromIndex(comment.range[0] + 2 + getColumnInComment(variable, comment));
|
586
572
|
}
|
587
573
|
|
588
574
|
//--------------------------------------------------------------------------
|
@@ -166,7 +166,7 @@ module.exports = {
|
|
166
166
|
const options = parseOptions(context.options[0]);
|
167
167
|
|
168
168
|
/**
|
169
|
-
* Determines whether a given use-before-define case should be
|
169
|
+
* Determines whether a given use-before-define case should be reported according to the options.
|
170
170
|
* @param {escope.Variable} variable The variable that gets used before being defined
|
171
171
|
* @param {escope.Reference} reference The reference to the variable
|
172
172
|
* @returns {boolean} `true` if the usage should be reported
|
@@ -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
|
//------------------------------------------------------------------------------
|
@@ -40,9 +46,8 @@ module.exports = {
|
|
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
|
|
@@ -94,7 +94,7 @@ module.exports = {
|
|
94
94
|
function report(node, startOffset, character) {
|
95
95
|
context.report({
|
96
96
|
node,
|
97
|
-
loc:
|
97
|
+
loc: sourceCode.getLocFromIndex(sourceCode.getIndexFromLoc(node.loc.start) + startOffset),
|
98
98
|
message: "Unnecessary escape character: \\{{character}}.",
|
99
99
|
data: { character }
|
100
100
|
});
|
@@ -8,7 +8,8 @@
|
|
8
8
|
// Requirements
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
|
-
const astUtils = require("../ast-utils")
|
11
|
+
const astUtils = require("../ast-utils"),
|
12
|
+
FixTracker = require("../util/fix-tracker");
|
12
13
|
|
13
14
|
//------------------------------------------------------------------------------
|
14
15
|
// Helpers
|
@@ -45,13 +46,7 @@ function remove(array, element) {
|
|
45
46
|
* @returns {boolean} `true` if the node is removeable.
|
46
47
|
*/
|
47
48
|
function isRemovable(node) {
|
48
|
-
|
49
|
-
|
50
|
-
return (
|
51
|
-
parent.type === "Program" ||
|
52
|
-
parent.type === "BlockStatement" ||
|
53
|
-
parent.type === "SwitchCase"
|
54
|
-
);
|
49
|
+
return astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type);
|
55
50
|
}
|
56
51
|
|
57
52
|
/**
|
@@ -225,7 +220,17 @@ module.exports = {
|
|
225
220
|
loc: node.loc,
|
226
221
|
message: "Unnecessary return statement.",
|
227
222
|
fix(fixer) {
|
228
|
-
|
223
|
+
if (isRemovable(node)) {
|
224
|
+
|
225
|
+
// Extend the replacement range to include the
|
226
|
+
// entire function to avoid conflicting with
|
227
|
+
// no-else-return.
|
228
|
+
// https://github.com/eslint/eslint/issues/8026
|
229
|
+
return new FixTracker(fixer, context.getSourceCode())
|
230
|
+
.retainEnclosingFunction(node)
|
231
|
+
.remove(node);
|
232
|
+
}
|
233
|
+
return null;
|
229
234
|
}
|
230
235
|
});
|
231
236
|
}
|
package/lib/rules/no-var.js
CHANGED
@@ -274,9 +274,7 @@ module.exports = {
|
|
274
274
|
if (
|
275
275
|
!isLoopAssignee(node) &&
|
276
276
|
!(node.parent.type === "ForStatement" && node.parent.init === node) &&
|
277
|
-
node.parent.type
|
278
|
-
node.parent.type !== "Program" &&
|
279
|
-
node.parent.type !== "SwitchCase"
|
277
|
+
!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)
|
280
278
|
) {
|
281
279
|
|
282
280
|
// If the declaration is not in a block, e.g. `if (foo) var bar = 1;`, then it can't be fixed.
|
@@ -4,6 +4,10 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
7
11
|
const astUtils = require("../ast-utils");
|
8
12
|
|
9
13
|
//------------------------------------------------------------------------------
|
@@ -29,21 +33,6 @@ module.exports = {
|
|
29
33
|
// Helpers
|
30
34
|
//--------------------------------------------------------------------------
|
31
35
|
|
32
|
-
/**
|
33
|
-
* Finds opening bracket token of node's computed property
|
34
|
-
* @param {ASTNode} node - the node to check
|
35
|
-
* @returns {Token} opening bracket token of node's computed property
|
36
|
-
* @private
|
37
|
-
*/
|
38
|
-
function findOpeningBracket(node) {
|
39
|
-
let token = sourceCode.getTokenBefore(node.property);
|
40
|
-
|
41
|
-
while (token.value !== "[") {
|
42
|
-
token = sourceCode.getTokenBefore(token);
|
43
|
-
}
|
44
|
-
return token;
|
45
|
-
}
|
46
|
-
|
47
36
|
/**
|
48
37
|
* Reports whitespace before property token
|
49
38
|
* @param {ASTNode} node - the node to report in the event of an error
|
@@ -87,7 +76,7 @@ module.exports = {
|
|
87
76
|
}
|
88
77
|
|
89
78
|
if (node.computed) {
|
90
|
-
rightToken =
|
79
|
+
rightToken = sourceCode.getTokenBefore(node.property, astUtils.isOpeningBracketToken);
|
91
80
|
leftToken = sourceCode.getTokenBefore(rightToken);
|
92
81
|
} else {
|
93
82
|
rightToken = sourceCode.getFirstToken(node.property);
|
@@ -0,0 +1,114 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview enforce the location of single-line statements
|
3
|
+
* @author Teddy Katz
|
4
|
+
*/
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Rule Definition
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const POSITION_SCHEMA = { enum: ["beside", "below", "any"] };
|
12
|
+
|
13
|
+
module.exports = {
|
14
|
+
meta: {
|
15
|
+
docs: {
|
16
|
+
description: "enforce the location of single-line statements",
|
17
|
+
category: "Stylistic Issues",
|
18
|
+
recommended: false
|
19
|
+
},
|
20
|
+
fixable: "whitespace",
|
21
|
+
schema: [
|
22
|
+
POSITION_SCHEMA,
|
23
|
+
{
|
24
|
+
properties: {
|
25
|
+
overrides: {
|
26
|
+
properties: {
|
27
|
+
if: POSITION_SCHEMA,
|
28
|
+
else: POSITION_SCHEMA,
|
29
|
+
while: POSITION_SCHEMA,
|
30
|
+
do: POSITION_SCHEMA,
|
31
|
+
for: POSITION_SCHEMA
|
32
|
+
},
|
33
|
+
additionalProperties: false
|
34
|
+
}
|
35
|
+
},
|
36
|
+
additionalProperties: false
|
37
|
+
}
|
38
|
+
]
|
39
|
+
},
|
40
|
+
|
41
|
+
create(context) {
|
42
|
+
const sourceCode = context.getSourceCode();
|
43
|
+
|
44
|
+
//----------------------------------------------------------------------
|
45
|
+
// Helpers
|
46
|
+
//----------------------------------------------------------------------
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Gets the applicable preference for a particular keyword
|
50
|
+
* @param {string} keywordName The name of a keyword, e.g. 'if'
|
51
|
+
* @returns {string} The applicable option for the keyword, e.g. 'beside'
|
52
|
+
*/
|
53
|
+
function getOption(keywordName) {
|
54
|
+
return context.options[1] && context.options[1].overrides && context.options[1].overrides[keywordName] ||
|
55
|
+
context.options[0] ||
|
56
|
+
"beside";
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Validates the location of a single-line statement
|
61
|
+
* @param {ASTNode} node The single-line statement
|
62
|
+
* @param {string} keywordName The applicable keyword name for the single-line statement
|
63
|
+
* @returns {void}
|
64
|
+
*/
|
65
|
+
function validateStatement(node, keywordName) {
|
66
|
+
const option = getOption(keywordName);
|
67
|
+
|
68
|
+
if (node.type === "BlockStatement" || option === "any") {
|
69
|
+
return;
|
70
|
+
}
|
71
|
+
|
72
|
+
const tokenBefore = sourceCode.getTokenBefore(node);
|
73
|
+
|
74
|
+
if (tokenBefore.loc.end.line === node.loc.start.line && option === "below") {
|
75
|
+
context.report({
|
76
|
+
node,
|
77
|
+
message: "Expected a linebreak before this statement.",
|
78
|
+
fix: fixer => fixer.insertTextBefore(node, "\n")
|
79
|
+
});
|
80
|
+
} else if (tokenBefore.loc.end.line !== node.loc.start.line && option === "beside") {
|
81
|
+
context.report({
|
82
|
+
node,
|
83
|
+
message: "Expected no linebreak before this statement.",
|
84
|
+
fix(fixer) {
|
85
|
+
if (sourceCode.getText().slice(tokenBefore.range[1], node.range[0]).trim()) {
|
86
|
+
return null;
|
87
|
+
}
|
88
|
+
return fixer.replaceTextRange([tokenBefore.range[1], node.range[0]], " ");
|
89
|
+
}
|
90
|
+
});
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
//----------------------------------------------------------------------
|
95
|
+
// Public
|
96
|
+
//----------------------------------------------------------------------
|
97
|
+
|
98
|
+
return {
|
99
|
+
IfStatement(node) {
|
100
|
+
validateStatement(node.consequent, "if");
|
101
|
+
|
102
|
+
// Check the `else` node, but don't check 'else if' statements.
|
103
|
+
if (node.alternate && node.alternate.type !== "IfStatement") {
|
104
|
+
validateStatement(node.alternate, "else");
|
105
|
+
}
|
106
|
+
},
|
107
|
+
WhileStatement: node => validateStatement(node.body, "while"),
|
108
|
+
DoWhileStatement: node => validateStatement(node.body, "do"),
|
109
|
+
ForStatement: node => validateStatement(node.body, "for"),
|
110
|
+
ForInStatement: node => validateStatement(node.body, "for"),
|
111
|
+
ForOfStatement: node => validateStatement(node.body, "for")
|
112
|
+
};
|
113
|
+
}
|
114
|
+
};
|
@@ -128,8 +128,8 @@ module.exports = {
|
|
128
128
|
const options = normalizedOptions[node.type];
|
129
129
|
const openBrace = sourceCode.getFirstToken(node);
|
130
130
|
const closeBrace = sourceCode.getLastToken(node);
|
131
|
-
let first = sourceCode.
|
132
|
-
let last = sourceCode.
|
131
|
+
let first = sourceCode.getTokenAfter(openBrace, { includeComments: true });
|
132
|
+
let last = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
|
133
133
|
const needsLinebreaks = (
|
134
134
|
node.properties.length >= options.minProperties ||
|
135
135
|
(
|
@@ -206,14 +206,8 @@ module.exports = {
|
|
206
206
|
*/
|
207
207
|
function getClosingBraceOfObject(node) {
|
208
208
|
const lastProperty = node.properties[node.properties.length - 1];
|
209
|
-
let token = sourceCode.getTokenAfter(lastProperty);
|
210
209
|
|
211
|
-
|
212
|
-
while (token.type !== "Punctuator" || token.value !== "}") {
|
213
|
-
token = sourceCode.getTokenAfter(token);
|
214
|
-
}
|
215
|
-
|
216
|
-
return token;
|
210
|
+
return sourceCode.getTokenAfter(lastProperty, astUtils.isClosingBraceToken);
|
217
211
|
}
|
218
212
|
|
219
213
|
/**
|
@@ -254,15 +248,9 @@ module.exports = {
|
|
254
248
|
firstSpecifier = node.specifiers[1];
|
255
249
|
}
|
256
250
|
|
257
|
-
const first = sourceCode.getTokenBefore(firstSpecifier)
|
258
|
-
|
259
|
-
|
260
|
-
// to support a trailing comma.
|
261
|
-
if (last.value === ",") {
|
262
|
-
last = sourceCode.getTokenAfter(last);
|
263
|
-
}
|
264
|
-
|
265
|
-
const second = sourceCode.getTokenAfter(first),
|
251
|
+
const first = sourceCode.getTokenBefore(firstSpecifier),
|
252
|
+
last = sourceCode.getTokenAfter(lastSpecifier, astUtils.isNotCommaToken),
|
253
|
+
second = sourceCode.getTokenAfter(first),
|
266
254
|
penultimate = sourceCode.getTokenBefore(last);
|
267
255
|
|
268
256
|
validateBraceSpacing(node, first, second, penultimate, last);
|
@@ -280,15 +268,9 @@ module.exports = {
|
|
280
268
|
|
281
269
|
const firstSpecifier = node.specifiers[0],
|
282
270
|
lastSpecifier = node.specifiers[node.specifiers.length - 1],
|
283
|
-
first = sourceCode.getTokenBefore(firstSpecifier)
|
284
|
-
|
285
|
-
|
286
|
-
// to support a trailing comma.
|
287
|
-
if (last.value === ",") {
|
288
|
-
last = sourceCode.getTokenAfter(last);
|
289
|
-
}
|
290
|
-
|
291
|
-
const second = sourceCode.getTokenAfter(first),
|
271
|
+
first = sourceCode.getTokenBefore(firstSpecifier),
|
272
|
+
last = sourceCode.getTokenAfter(lastSpecifier, astUtils.isNotCommaToken),
|
273
|
+
second = sourceCode.getTokenAfter(first),
|
292
274
|
penultimate = sourceCode.getTokenBefore(last);
|
293
275
|
|
294
276
|
validateBraceSpacing(node, first, second, penultimate, last);
|
@@ -34,9 +34,9 @@ module.exports = {
|
|
34
34
|
|
35
35
|
create(context) {
|
36
36
|
const allowSameLine = context.options[0] && Boolean(context.options[0].allowMultiplePropertiesPerLine);
|
37
|
-
const errorMessage = allowSameLine
|
38
|
-
"Object properties must go on a new line if they aren't all on the same line."
|
39
|
-
"Object properties must go on a new line.";
|
37
|
+
const errorMessage = allowSameLine
|
38
|
+
? "Object properties must go on a new line if they aren't all on the same line."
|
39
|
+
: "Object properties must go on a new line.";
|
40
40
|
|
41
41
|
const sourceCode = context.getSourceCode();
|
42
42
|
|
@@ -215,8 +215,8 @@ module.exports = {
|
|
215
215
|
* @returns {Object} A fix for this node
|
216
216
|
*/
|
217
217
|
function makeFunctionShorthand(fixer, node) {
|
218
|
-
const firstKeyToken = node.computed ? sourceCode.
|
219
|
-
const lastKeyToken = node.computed ? sourceCode.
|
218
|
+
const firstKeyToken = node.computed ? sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken) : sourceCode.getFirstToken(node.key);
|
219
|
+
const lastKeyToken = node.computed ? sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken) : sourceCode.getLastToken(node.key);
|
220
220
|
const keyText = sourceCode.text.slice(firstKeyToken.range[0], lastKeyToken.range[1]);
|
221
221
|
let keyPrefix = "";
|
222
222
|
|
@@ -368,11 +368,12 @@ module.exports = {
|
|
368
368
|
// Checks for property/method shorthand.
|
369
369
|
if (isConciseProperty) {
|
370
370
|
if (node.method && (APPLY_NEVER || AVOID_QUOTES && isStringLiteral(node.key))) {
|
371
|
+
const message = APPLY_NEVER ? "Expected longform method syntax." : "Expected longform method syntax for string literal keys.";
|
371
372
|
|
372
373
|
// { x() {} } should be written as { x: function() {} }
|
373
374
|
context.report({
|
374
375
|
node,
|
375
|
-
message
|
376
|
+
message,
|
376
377
|
fix: fixer => makeFunctionLongform(fixer, node)
|
377
378
|
});
|
378
379
|
} else if (APPLY_NEVER) {
|
@@ -108,7 +108,7 @@ module.exports = {
|
|
108
108
|
* @returns {Token} The operator token in the node
|
109
109
|
*/
|
110
110
|
function getOperatorToken(node) {
|
111
|
-
return sourceCode.
|
111
|
+
return sourceCode.getFirstTokenBetween(node.left, node.right, token => token.value === node.operator);
|
112
112
|
}
|
113
113
|
|
114
114
|
/**
|
@@ -135,7 +135,7 @@ module.exports = {
|
|
135
135
|
const equalsToken = getOperatorToken(node);
|
136
136
|
const operatorToken = getOperatorToken(expr);
|
137
137
|
const leftText = sourceCode.getText().slice(node.range[0], equalsToken.range[0]);
|
138
|
-
const rightText = sourceCode.getText().slice(operatorToken.range[1],
|
138
|
+
const rightText = sourceCode.getText().slice(operatorToken.range[1], expr.right.range[1]);
|
139
139
|
|
140
140
|
return fixer.replaceText(node, `${leftText}${expr.operator}=${rightText}`);
|
141
141
|
}
|