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.
Files changed (118) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/conf/{eslint.json → eslint-recommended.js} +87 -71
  3. package/lib/ast-utils.js +182 -80
  4. package/lib/code-path-analysis/code-path-state.js +2 -2
  5. package/lib/config/autoconfig.js +3 -3
  6. package/lib/config/config-file.js +14 -7
  7. package/lib/config/config-initializer.js +1 -1
  8. package/lib/config.js +3 -2
  9. package/lib/eslint.js +4 -4
  10. package/lib/internal-rules/internal-no-invalid-meta.js +2 -40
  11. package/lib/rules/array-callback-return.js +15 -5
  12. package/lib/rules/arrow-body-style.js +7 -4
  13. package/lib/rules/arrow-spacing.js +7 -6
  14. package/lib/rules/block-spacing.js +2 -2
  15. package/lib/rules/brace-style.js +2 -6
  16. package/lib/rules/capitalized-comments.js +8 -7
  17. package/lib/rules/comma-spacing.js +3 -3
  18. package/lib/rules/complexity.js +14 -8
  19. package/lib/rules/consistent-return.js +18 -11
  20. package/lib/rules/constructor-super.js +3 -3
  21. package/lib/rules/curly.js +11 -7
  22. package/lib/rules/default-case.js +3 -3
  23. package/lib/rules/eqeqeq.js +15 -6
  24. package/lib/rules/func-call-spacing.js +10 -13
  25. package/lib/rules/func-names.js +20 -5
  26. package/lib/rules/generator-star-spacing.js +18 -19
  27. package/lib/rules/id-blacklist.js +2 -2
  28. package/lib/rules/id-length.js +3 -3
  29. package/lib/rules/id-match.js +2 -2
  30. package/lib/rules/indent.js +7 -6
  31. package/lib/rules/key-spacing.js +12 -16
  32. package/lib/rules/keyword-spacing.js +21 -17
  33. package/lib/rules/line-comment-position.js +16 -6
  34. package/lib/rules/linebreak-style.js +7 -1
  35. package/lib/rules/lines-around-comment.js +23 -4
  36. package/lib/rules/lines-around-directive.js +3 -3
  37. package/lib/rules/max-lines.js +2 -2
  38. package/lib/rules/max-params.js +17 -4
  39. package/lib/rules/max-statements-per-line.js +7 -6
  40. package/lib/rules/max-statements.js +11 -10
  41. package/lib/rules/newline-after-var.js +7 -2
  42. package/lib/rules/newline-per-chained-call.js +3 -1
  43. package/lib/rules/no-compare-neg-zero.js +53 -0
  44. package/lib/rules/no-cond-assign.js +3 -3
  45. package/lib/rules/no-else-return.js +13 -1
  46. package/lib/rules/no-empty-function.js +9 -16
  47. package/lib/rules/no-extend-native.js +3 -3
  48. package/lib/rules/no-extra-bind.js +3 -4
  49. package/lib/rules/no-extra-boolean-cast.js +8 -0
  50. package/lib/rules/no-extra-parens.js +1 -2
  51. package/lib/rules/no-extra-semi.js +13 -1
  52. package/lib/rules/no-inner-declarations.js +4 -4
  53. package/lib/rules/no-invalid-regexp.js +2 -1
  54. package/lib/rules/no-irregular-whitespace.js +7 -1
  55. package/lib/rules/no-lone-blocks.js +10 -10
  56. package/lib/rules/no-mixed-operators.js +1 -7
  57. package/lib/rules/no-multi-spaces.js +4 -1
  58. package/lib/rules/no-multi-str.js +7 -3
  59. package/lib/rules/no-multiple-empty-lines.js +2 -4
  60. package/lib/rules/no-param-reassign.js +29 -6
  61. package/lib/rules/no-restricted-properties.js +2 -0
  62. package/lib/rules/no-return-assign.js +7 -14
  63. package/lib/rules/no-return-await.js +1 -1
  64. package/lib/rules/no-sequences.js +7 -6
  65. package/lib/rules/no-trailing-spaces.js +8 -2
  66. package/lib/rules/no-undefined.js +45 -6
  67. package/lib/rules/no-unexpected-multiline.js +9 -8
  68. package/lib/rules/no-unneeded-ternary.js +5 -1
  69. package/lib/rules/no-unused-labels.js +17 -2
  70. package/lib/rules/no-unused-vars.js +13 -27
  71. package/lib/rules/no-use-before-define.js +1 -1
  72. package/lib/rules/no-useless-computed-key.js +8 -3
  73. package/lib/rules/no-useless-concat.js +10 -7
  74. package/lib/rules/no-useless-escape.js +2 -2
  75. package/lib/rules/no-useless-return.js +14 -9
  76. package/lib/rules/no-var.js +1 -3
  77. package/lib/rules/no-whitespace-before-property.js +5 -16
  78. package/lib/rules/nonblock-statement-body-position.js +114 -0
  79. package/lib/rules/object-curly-newline.js +2 -2
  80. package/lib/rules/object-curly-spacing.js +7 -25
  81. package/lib/rules/object-property-newline.js +3 -3
  82. package/lib/rules/object-shorthand.js +4 -3
  83. package/lib/rules/operator-assignment.js +2 -2
  84. package/lib/rules/operator-linebreak.js +8 -10
  85. package/lib/rules/padded-blocks.js +39 -30
  86. package/lib/rules/prefer-destructuring.js +1 -1
  87. package/lib/rules/prefer-spread.js +1 -1
  88. package/lib/rules/prefer-template.js +1 -1
  89. package/lib/rules/quotes.js +10 -6
  90. package/lib/rules/semi-spacing.js +4 -0
  91. package/lib/rules/semi.js +13 -1
  92. package/lib/rules/space-before-function-paren.js +8 -5
  93. package/lib/rules/space-unary-ops.js +19 -1
  94. package/lib/rules/spaced-comment.js +2 -2
  95. package/lib/rules/strict.js +10 -4
  96. package/lib/rules/unicode-bom.js +1 -1
  97. package/lib/rules/wrap-iife.js +5 -5
  98. package/lib/rules/yoda.js +4 -9
  99. package/lib/testers/rule-tester.js +46 -9
  100. package/lib/token-store/backward-token-comment-cursor.js +57 -0
  101. package/lib/token-store/backward-token-cursor.js +56 -0
  102. package/lib/token-store/cursor.js +76 -0
  103. package/lib/token-store/cursors.js +92 -0
  104. package/lib/token-store/decorative-cursor.js +39 -0
  105. package/lib/token-store/filter-cursor.js +43 -0
  106. package/lib/token-store/forward-token-comment-cursor.js +57 -0
  107. package/lib/token-store/forward-token-cursor.js +61 -0
  108. package/lib/token-store/index.js +604 -0
  109. package/lib/token-store/limit-cursor.js +40 -0
  110. package/lib/token-store/padded-token-cursor.js +38 -0
  111. package/lib/token-store/skip-cursor.js +42 -0
  112. package/lib/token-store/utils.js +100 -0
  113. package/lib/util/fix-tracker.js +121 -0
  114. package/lib/util/source-code-fixer.js +35 -39
  115. package/lib/util/source-code.js +129 -16
  116. package/messages/extend-config-missing.txt +3 -0
  117. package/package.json +5 -6
  118. 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
- Identifier(node) {
27
- if (node.name === "undefined") {
28
- const parent = context.getAncestors().pop();
67
+ while (stack.length) {
68
+ const scope = stack.pop();
29
69
 
30
- if (!parent || parent.type !== "MemberExpression" || node !== parent.property || parent.computed) {
31
- context.report({ node, message: "Unexpected use of undefined." });
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
- let nodeExpressionEnd = node;
39
- let openParen = sourceCode.getTokenAfter(node);
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.getTokensBetween(node.left, node.right).find(token => token.value === node.operator);
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
- const restProperties = new Set(["ExperimentalRestProperty", "RestProperty"]);
142
-
143
- return variable.defs
144
- .filter(def => def.name.type === "Identifier")
145
- .some(def => (
146
- def.node.id &&
147
- def.node.id.type === "ObjectPattern" &&
148
- def.node.id.properties.length &&
149
- restProperties.has(def.node.id.properties[def.node.id.properties.length - 1].type) && // last property is a rest property
150
- !restProperties.has(def.name.parent.type) // variable is sibling of the rest property
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
- if (lineInComment > 0) {
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 reportedaccording to the options.
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, node.value.generator || node.value.async ? 1 : 0);
44
- const rightSquareBracket = sourceCode.getTokensBetween(node.key, node.value).find(token => token.value === "]");
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\n\r\u2028\u2029");
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: astUtils.getLocationFromRangeIndex(sourceCode, astUtils.getRangeIndexFromLocation(sourceCode, node.loc.start) + startOffset),
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
- const parent = node.parent;
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
- return isRemovable(node) ? fixer.remove(node) : null;
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
  }
@@ -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 !== "BlockStatement" &&
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 = findOpeningBracket(node);
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.getTokenOrCommentAfter(openBrace);
132
- let last = sourceCode.getTokenOrCommentBefore(closeBrace);
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
- // skip ')' and trailing commas.
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
- let last = sourceCode.getTokenAfter(lastSpecifier);
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
- let last = sourceCode.getTokenAfter(lastSpecifier);
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.getTokens(node).find(token => token.value === "[") : sourceCode.getFirstToken(node.key);
219
- const lastKeyToken = node.computed ? sourceCode.getTokensBetween(node.key, node.value).find(token => token.value === "]") : sourceCode.getLastToken(node.key);
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: `Expected longform method syntax${APPLY_NEVER ? "" : " for string literal keys"}.`,
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.getTokensBetween(node.left, node.right).find(token => token.value === node.operator);
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], node.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
  }