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.
Files changed (119) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +1 -1
  3. package/conf/{eslint.json → eslint-recommended.js} +86 -71
  4. package/lib/ast-utils.js +192 -24
  5. package/lib/cli.js +2 -2
  6. package/lib/code-path-analysis/code-path-state.js +2 -2
  7. package/lib/config/autoconfig.js +3 -3
  8. package/lib/config/config-file.js +31 -24
  9. package/lib/config/config-initializer.js +1 -1
  10. package/lib/config/config-validator.js +6 -6
  11. package/lib/config.js +3 -2
  12. package/lib/eslint.js +18 -18
  13. package/lib/formatters/checkstyle.js +2 -2
  14. package/lib/formatters/compact.js +2 -2
  15. package/lib/formatters/junit.js +2 -2
  16. package/lib/formatters/tap.js +2 -2
  17. package/lib/formatters/unix.js +2 -2
  18. package/lib/formatters/visualstudio.js +2 -2
  19. package/lib/rules/arrow-body-style.js +7 -4
  20. package/lib/rules/arrow-spacing.js +7 -6
  21. package/lib/rules/block-spacing.js +2 -2
  22. package/lib/rules/brace-style.js +42 -22
  23. package/lib/rules/capitalized-comments.js +6 -6
  24. package/lib/rules/comma-spacing.js +16 -16
  25. package/lib/rules/consistent-return.js +1 -1
  26. package/lib/rules/constructor-super.js +3 -3
  27. package/lib/rules/curly.js +11 -7
  28. package/lib/rules/default-case.js +3 -3
  29. package/lib/rules/eqeqeq.js +15 -6
  30. package/lib/rules/func-call-spacing.js +10 -13
  31. package/lib/rules/func-name-matching.js +1 -1
  32. package/lib/rules/generator-star-spacing.js +18 -19
  33. package/lib/rules/global-require.js +2 -2
  34. package/lib/rules/id-blacklist.js +2 -2
  35. package/lib/rules/id-length.js +3 -3
  36. package/lib/rules/id-match.js +2 -2
  37. package/lib/rules/indent.js +21 -20
  38. package/lib/rules/key-spacing.js +20 -23
  39. package/lib/rules/keyword-spacing.js +2 -13
  40. package/lib/rules/line-comment-position.js +1 -1
  41. package/lib/rules/linebreak-style.js +7 -1
  42. package/lib/rules/lines-around-comment.js +4 -4
  43. package/lib/rules/lines-around-directive.js +3 -3
  44. package/lib/rules/max-lines.js +2 -2
  45. package/lib/rules/max-statements-per-line.js +7 -6
  46. package/lib/rules/new-cap.js +2 -2
  47. package/lib/rules/newline-after-var.js +7 -2
  48. package/lib/rules/newline-before-return.js +2 -2
  49. package/lib/rules/newline-per-chained-call.js +3 -1
  50. package/lib/rules/no-cond-assign.js +3 -3
  51. package/lib/rules/no-extend-native.js +3 -3
  52. package/lib/rules/no-extra-bind.js +3 -4
  53. package/lib/rules/no-extra-boolean-cast.js +8 -0
  54. package/lib/rules/no-extra-parens.js +29 -8
  55. package/lib/rules/no-inner-declarations.js +4 -4
  56. package/lib/rules/no-irregular-whitespace.js +7 -1
  57. package/lib/rules/no-lone-blocks.js +10 -10
  58. package/lib/rules/no-mixed-operators.js +1 -7
  59. package/lib/rules/no-mixed-requires.js +4 -4
  60. package/lib/rules/no-multi-spaces.js +4 -1
  61. package/lib/rules/no-multi-str.js +7 -3
  62. package/lib/rules/no-redeclare.js +7 -7
  63. package/lib/rules/no-return-assign.js +7 -14
  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 +34 -19
  71. package/lib/rules/no-use-before-define.js +33 -29
  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 +1 -1
  75. package/lib/rules/no-useless-return.js +1 -7
  76. package/lib/rules/no-var.js +11 -0
  77. package/lib/rules/no-whitespace-before-property.js +5 -16
  78. package/lib/rules/object-curly-newline.js +2 -2
  79. package/lib/rules/object-curly-spacing.js +7 -25
  80. package/lib/rules/object-property-newline.js +3 -3
  81. package/lib/rules/object-shorthand.js +10 -10
  82. package/lib/rules/operator-assignment.js +2 -2
  83. package/lib/rules/operator-linebreak.js +8 -10
  84. package/lib/rules/padded-blocks.js +7 -4
  85. package/lib/rules/prefer-spread.js +1 -1
  86. package/lib/rules/prefer-template.js +1 -1
  87. package/lib/rules/quotes.js +10 -6
  88. package/lib/rules/semi-spacing.js +4 -0
  89. package/lib/rules/sort-imports.js +4 -4
  90. package/lib/rules/sort-vars.js +2 -2
  91. package/lib/rules/space-before-function-paren.js +8 -5
  92. package/lib/rules/space-in-parens.js +8 -8
  93. package/lib/rules/spaced-comment.js +10 -10
  94. package/lib/rules/strict.js +2 -2
  95. package/lib/rules/template-tag-spacing.js +77 -0
  96. package/lib/rules/unicode-bom.js +1 -1
  97. package/lib/rules/wrap-iife.js +5 -5
  98. package/lib/rules/yoda.js +2 -7
  99. package/lib/rules.js +2 -2
  100. package/lib/testers/rule-tester.js +25 -18
  101. package/lib/token-store/backward-token-comment-cursor.js +57 -0
  102. package/lib/token-store/backward-token-cursor.js +56 -0
  103. package/lib/token-store/cursor.js +76 -0
  104. package/lib/token-store/cursors.js +92 -0
  105. package/lib/token-store/decorative-cursor.js +39 -0
  106. package/lib/token-store/filter-cursor.js +43 -0
  107. package/lib/token-store/forward-token-comment-cursor.js +57 -0
  108. package/lib/token-store/forward-token-cursor.js +61 -0
  109. package/lib/token-store/index.js +604 -0
  110. package/lib/token-store/limit-cursor.js +40 -0
  111. package/lib/token-store/padded-token-cursor.js +38 -0
  112. package/lib/token-store/skip-cursor.js +42 -0
  113. package/lib/token-store/utils.js +100 -0
  114. package/lib/util/glob.js +1 -1
  115. package/lib/util/source-code-fixer.js +46 -44
  116. package/lib/util/source-code.js +35 -19
  117. package/messages/extend-config-missing.txt +3 -0
  118. package/package.json +3 -3
  119. package/lib/token-store.js +0 -203
@@ -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
- * Checks whether or not a given variable is a function declaration or a class declaration in an upper function scope.
68
- *
69
- * @param {escope.Variable} variable - A variable to check.
70
- * @param {escope.Reference} reference - A reference to check.
71
- * @returns {boolean} `true` if the variable is a function declaration or a class declaration.
72
- */
73
- function isFunctionOrOuterClass(variable, reference) {
74
- return isFunction(variable, reference) || isOuterClass(variable, reference);
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
- // Defines a function which checks whether or not a reference is allowed according to the option.
171
- let isAllowed;
172
-
173
- if (options.functions && options.classes) {
174
- isAllowed = alwaysFalse;
175
- } else if (options.functions) {
176
- isAllowed = isOuterClass;
177
- } else if (options.classes) {
178
- isAllowed = isFunction;
179
- } else {
180
- isAllowed = isFunctionOrOuterClass;
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
- isAllowed(variable, reference)
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
  //------------------------------------------------------------------------------
@@ -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
 
@@ -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
- const parent = node.parent;
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
  /**
@@ -209,6 +209,7 @@ module.exports = {
209
209
  * - A variable is used from a closure within a loop.
210
210
  * - A variable might be used before it is assigned within a loop.
211
211
  * - A variable might be used in TDZ.
212
+ * - A variable is declared in statement position (e.g. a single-line `IfStatement`)
212
213
  *
213
214
  * ## A variable is declared on a SwitchCase node.
214
215
  *
@@ -270,6 +271,16 @@ module.exports = {
270
271
  }
271
272
  }
272
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
+
273
284
  return true;
274
285
  }
275
286
 
@@ -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);
@@ -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
 
@@ -231,15 +231,15 @@ module.exports = {
231
231
  const tokenBeforeParams = node.value.generator ? sourceCode.getTokenAfter(functionToken) : functionToken;
232
232
 
233
233
  return fixer.replaceTextRange([firstKeyToken.range[0], tokenBeforeParams.range[1]], keyPrefix + keyText);
234
- } else {
235
- const arrowToken = sourceCode.getTokens(node.value).find(token => token.value === "=>");
236
- const tokenBeforeArrow = sourceCode.getTokenBefore(arrowToken);
237
- const hasParensAroundParameters = tokenBeforeArrow.type === "Punctuator" && tokenBeforeArrow.value === ")";
238
- const oldParamText = sourceCode.text.slice(sourceCode.getFirstToken(node.value, node.value.async ? 1 : 0).range[0], tokenBeforeArrow.range[1]);
239
- const newParamText = hasParensAroundParameters ? oldParamText : `(${oldParamText})`;
240
-
241
- return fixer.replaceTextRange([firstKeyToken.range[0], arrowToken.range[1]], keyPrefix + keyText + newParamText);
242
234
  }
235
+ const arrowToken = sourceCode.getTokens(node.value).find(token => token.value === "=>");
236
+ const tokenBeforeArrow = sourceCode.getTokenBefore(arrowToken);
237
+ const hasParensAroundParameters = tokenBeforeArrow.type === "Punctuator" && tokenBeforeArrow.value === ")";
238
+ const oldParamText = sourceCode.text.slice(sourceCode.getFirstToken(node.value, node.value.async ? 1 : 0).range[0], tokenBeforeArrow.range[1]);
239
+ const newParamText = hasParensAroundParameters ? oldParamText : `(${oldParamText})`;
240
+
241
+ return fixer.replaceTextRange([firstKeyToken.range[0], arrowToken.range[1]], keyPrefix + keyText + newParamText);
242
+
243
243
  }
244
244
 
245
245
  /**
@@ -27,7 +27,7 @@ function isCommutativeOperatorWithShorthand(operator) {
27
27
  * a shorthand form.
28
28
  */
29
29
  function isNonCommutativeOperatorWithShorthand(operator) {
30
- return ["+", "-", "/", "%", "<<", ">>", ">>>"].indexOf(operator) >= 0;
30
+ return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].indexOf(operator) >= 0;
31
31
  }
32
32
 
33
33
  //------------------------------------------------------------------------------
@@ -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
  /**
@@ -5,14 +5,16 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
8
12
  const astUtils = require("../ast-utils");
9
13
 
10
14
  //------------------------------------------------------------------------------
11
15
  // Rule Definition
12
16
  //------------------------------------------------------------------------------
13
17
 
14
- const LINEBREAK_REGEX = /\r\n|\r|\n|\u2028|\u2029/g;
15
-
16
18
  module.exports = {
17
19
  meta: {
18
20
  docs: {
@@ -85,7 +87,7 @@ module.exports = {
85
87
  if (hasLinebreakBefore !== hasLinebreakAfter && desiredStyle !== "none") {
86
88
 
87
89
  // If there is a comment before and after the operator, don't do a fix.
88
- if (sourceCode.getTokenOrCommentBefore(operatorToken) !== tokenBefore && sourceCode.getTokenOrCommentAfter(operatorToken) !== tokenAfter) {
90
+ if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore && sourceCode.getTokenAfter(operatorToken, { includeComments: true }) !== tokenAfter) {
89
91
  return null;
90
92
  }
91
93
 
@@ -100,6 +102,7 @@ module.exports = {
100
102
  newTextBefore = textAfter;
101
103
  newTextAfter = textBefore;
102
104
  } else {
105
+ const LINEBREAK_REGEX = astUtils.createGlobalLinebreakMatcher();
103
106
 
104
107
  // Otherwise, if no linebreak is desired and no comments interfere, replace the linebreaks with empty strings.
105
108
  newTextBefore = desiredStyle === "before" || textBefore.trim() ? textBefore : textBefore.replace(LINEBREAK_REGEX, "");
@@ -129,19 +132,14 @@ module.exports = {
129
132
  * @returns {void}
130
133
  */
131
134
  function validateNode(node, leftSide) {
132
- let leftToken = sourceCode.getLastToken(leftSide);
133
- let operatorToken = sourceCode.getTokenAfter(leftToken);
134
135
 
135
136
  // When the left part of a binary expression is a single expression wrapped in
136
137
  // parentheses (ex: `(a) + b`), leftToken will be the last token of the expression
137
138
  // and operatorToken will be the closing parenthesis.
138
139
  // The leftToken should be the last closing parenthesis, and the operatorToken
139
140
  // should be the token right after that.
140
- while (operatorToken.value === ")") {
141
- leftToken = operatorToken;
142
- operatorToken = sourceCode.getTokenAfter(operatorToken);
143
- }
144
-
141
+ const operatorToken = sourceCode.getTokenAfter(leftSide, astUtils.isNotClosingParenToken);
142
+ const leftToken = sourceCode.getTokenBefore(operatorToken);
145
143
  const rightToken = sourceCode.getTokenAfter(operatorToken);
146
144
  const operator = operatorToken.value;
147
145
  const operatorStyleOverride = styleOverrides[operator];
@@ -101,7 +101,7 @@ module.exports = {
101
101
  let first = token;
102
102
 
103
103
  do {
104
- first = sourceCode.getTokenOrCommentAfter(first);
104
+ first = sourceCode.getTokenAfter(first, { includeComments: true });
105
105
  } while (isComment(first) && first.loc.start.line === tokenStartLine);
106
106
 
107
107
  const firstLine = first.loc.start.line;
@@ -120,7 +120,7 @@ module.exports = {
120
120
  let last = token;
121
121
 
122
122
  do {
123
- last = sourceCode.getTokenOrCommentBefore(last);
123
+ last = sourceCode.getTokenBefore(last, { includeComments: true });
124
124
  } while (isComment(last) && last.loc.end.line === blockEnd);
125
125
 
126
126
  const lastLine = last.loc.end.line;
@@ -182,12 +182,15 @@ module.exports = {
182
182
  }
183
183
  } else {
184
184
  if (blockHasTopPadding) {
185
- const nextToken = sourceCode.getTokenOrCommentAfter(openBrace);
185
+ const nextToken = sourceCode.getTokenAfter(openBrace, { includeComments: true });
186
186
 
187
187
  context.report({
188
188
  node,
189
189
  loc: { line: openBrace.loc.start.line, column: openBrace.loc.start.column },
190
190
  fix(fixer) {
191
+
192
+ // FIXME: The start of this range is sometimes larger than the end.
193
+ // https://github.com/eslint/eslint/issues/8116
191
194
  return fixer.replaceTextRange([openBrace.end, nextToken.start - nextToken.loc.start.column], "\n");
192
195
  },
193
196
  message: NEVER_MESSAGE
@@ -195,7 +198,7 @@ module.exports = {
195
198
  }
196
199
 
197
200
  if (blockHasBottomPadding) {
198
- const previousToken = sourceCode.getTokenOrCommentBefore(closeBrace);
201
+ const previousToken = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
199
202
 
200
203
  context.report({
201
204
  node,
@@ -108,7 +108,7 @@ module.exports = {
108
108
  return null;
109
109
  }
110
110
 
111
- const propertyDot = sourceCode.getTokensBetween(applied, node.callee.property).find(token => token.value === ".");
111
+ const propertyDot = sourceCode.getFirstTokenBetween(applied, node.callee.property, token => token.value === ".");
112
112
 
113
113
  return fixer.replaceTextRange([propertyDot.range[0], node.range[1]], `(...${sourceCode.getText(node.arguments[1])})`);
114
114
  }
@@ -157,7 +157,7 @@ module.exports = {
157
157
  }
158
158
 
159
159
  if (isConcatenation(currentNode) && hasStringLiteral(currentNode) && hasNonStringLiteral(currentNode)) {
160
- const plusSign = sourceCode.getTokensBetween(currentNode.left, currentNode.right).find(token => token.value === "+");
160
+ const plusSign = sourceCode.getFirstTokenBetween(currentNode.left, currentNode.right, token => token.value === "+");
161
161
  const textBeforePlus = getTextBetween(currentNode.left, plusSign);
162
162
  const textAfterPlus = getTextBetween(plusSign, currentNode.right);
163
163
  const leftEndsWithCurly = endsWithTemplateCurly(currentNode.left);
@@ -33,6 +33,9 @@ const QUOTE_SETTINGS = {
33
33
  }
34
34
  };
35
35
 
36
+ // An unescaped newline is a newline preceded by an even number of backslashes.
37
+ const UNESCAPED_LINEBREAK_PATTERN = new RegExp(String.raw`(^|[^\\])(\\\\)*[${Array.from(astUtils.LINEBREAKS).join("")}]`);
38
+
36
39
  /**
37
40
  * Switches quoting of javascript string between ' " and `
38
41
  * escaping and unescaping as necessary.
@@ -254,15 +257,16 @@ module.exports = {
254
257
  TemplateLiteral(node) {
255
258
 
256
259
  // If backticks are expected or it's a tagged template, then this shouldn't throw an errors
257
- if (allowTemplateLiterals || quoteOption === "backtick" || node.parent.type === "TaggedTemplateExpression") {
260
+ if (
261
+ allowTemplateLiterals ||
262
+ quoteOption === "backtick" ||
263
+ node.parent.type === "TaggedTemplateExpression" && node === node.parent.quasi
264
+ ) {
258
265
  return;
259
266
  }
260
267
 
261
- /*
262
- * A warning should be produced if the template literal only has one TemplateElement, and has no unescaped newlines.
263
- * An unescaped newline is a newline preceded by an even number of backslashes.
264
- */
265
- const shouldWarn = node.quasis.length === 1 && !/(^|[^\\])(\\\\)*[\r\n\u2028\u2029]/.test(node.quasis[0].value.raw);
268
+ // A warning should be produced if the template literal only has one TemplateElement, and has no unescaped newlines.
269
+ const shouldWarn = node.quasis.length === 1 && !UNESCAPED_LINEBREAK_PATTERN.test(node.quasis[0].value.raw);
266
270
 
267
271
  if (shouldWarn) {
268
272
  context.report({
@@ -206,6 +206,10 @@ module.exports = {
206
206
  DebuggerStatement: checkNode,
207
207
  ReturnStatement: checkNode,
208
208
  ThrowStatement: checkNode,
209
+ ImportDeclaration: checkNode,
210
+ ExportNamedDeclaration: checkNode,
211
+ ExportAllDeclaration: checkNode,
212
+ ExportDefaultDeclaration: checkNode,
209
213
  ForStatement(node) {
210
214
  if (node.init) {
211
215
  checkSemicolonSpacing(sourceCode.getTokenAfter(node.init), node);
@@ -71,9 +71,9 @@ module.exports = {
71
71
  return "all";
72
72
  } else if (node.specifiers.length === 1) {
73
73
  return "single";
74
- } else {
75
- return "multiple";
76
74
  }
75
+ return "multiple";
76
+
77
77
  }
78
78
 
79
79
  /**
@@ -93,9 +93,9 @@ module.exports = {
93
93
  function getFirstLocalMemberName(node) {
94
94
  if (node.specifiers[0]) {
95
95
  return node.specifiers[0].local.name;
96
- } else {
97
- return null;
98
96
  }
97
+ return null;
98
+
99
99
  }
100
100
 
101
101
  return {
@@ -53,9 +53,9 @@ module.exports = {
53
53
  if (currenVariableName < lastVariableName) {
54
54
  context.report({ node: decl, message: "Variables within the same declaration block should be sorted alphabetically." });
55
55
  return memo;
56
- } else {
57
- return decl;
58
56
  }
57
+ return decl;
58
+
59
59
  }, node.declarations[0]);
60
60
  }
61
61
  };
@@ -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
  //------------------------------------------------------------------------------
@@ -104,7 +110,7 @@ module.exports = {
104
110
  const isAnonymousGenerator = node.generator && !isNamed;
105
111
  const isNormalArrow = isArrow && !node.async;
106
112
  const isArrowWithoutParens = isArrow && sourceCode.getFirstToken(node, 1).value !== "(";
107
- let forbidSpacing, requireSpacing, rightToken;
113
+ let forbidSpacing, requireSpacing;
108
114
 
109
115
  // isAnonymousGenerator → `generator-star-spacing` should warn it. E.g. `function* () {}`
110
116
  // isNormalArrow → ignore always.
@@ -124,10 +130,7 @@ module.exports = {
124
130
  requireSpacing = requireAnonymousFunctionSpacing;
125
131
  }
126
132
 
127
- rightToken = sourceCode.getFirstToken(node);
128
- while (rightToken.value !== "(") {
129
- rightToken = sourceCode.getTokenAfter(rightToken);
130
- }
133
+ const rightToken = sourceCode.getFirstToken(node, astUtils.isOpeningParenToken);
131
134
  const leftToken = sourceCode.getTokenBefore(rightToken);
132
135
  const location = leftToken.loc.end;
133
136