eslint 3.13.1 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +1 -1
  3. package/conf/{eslint.json → eslint-recommended.js} +88 -71
  4. package/lib/ast-utils.js +247 -28
  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 +24 -20
  8. package/lib/config/config-file.js +31 -24
  9. package/lib/config/config-initializer.js +2 -2
  10. package/lib/config/config-rule.js +15 -16
  11. package/lib/config/config-validator.js +6 -6
  12. package/lib/config.js +3 -2
  13. package/lib/eslint.js +18 -18
  14. package/lib/formatters/checkstyle.js +2 -2
  15. package/lib/formatters/codeframe.js +1 -1
  16. package/lib/formatters/compact.js +2 -2
  17. package/lib/formatters/junit.js +2 -2
  18. package/lib/formatters/tap.js +2 -2
  19. package/lib/formatters/unix.js +2 -2
  20. package/lib/formatters/visualstudio.js +2 -2
  21. package/lib/internal-rules/internal-consistent-docs-description.js +1 -1
  22. package/lib/rule-context.js +2 -2
  23. package/lib/rules/arrow-body-style.js +7 -4
  24. package/lib/rules/arrow-spacing.js +7 -6
  25. package/lib/rules/block-spacing.js +2 -2
  26. package/lib/rules/brace-style.js +93 -202
  27. package/lib/rules/capitalized-comments.js +6 -6
  28. package/lib/rules/comma-dangle.js +6 -6
  29. package/lib/rules/comma-spacing.js +16 -16
  30. package/lib/rules/comma-style.js +1 -1
  31. package/lib/rules/consistent-return.js +1 -1
  32. package/lib/rules/constructor-super.js +3 -3
  33. package/lib/rules/curly.js +11 -7
  34. package/lib/rules/default-case.js +3 -3
  35. package/lib/rules/eqeqeq.js +15 -6
  36. package/lib/rules/func-call-spacing.js +12 -15
  37. package/lib/rules/func-name-matching.js +1 -1
  38. package/lib/rules/generator-star-spacing.js +18 -19
  39. package/lib/rules/global-require.js +2 -2
  40. package/lib/rules/id-blacklist.js +2 -2
  41. package/lib/rules/id-length.js +3 -3
  42. package/lib/rules/id-match.js +2 -2
  43. package/lib/rules/indent.js +21 -20
  44. package/lib/rules/key-spacing.js +20 -23
  45. package/lib/rules/keyword-spacing.js +2 -13
  46. package/lib/rules/line-comment-position.js +1 -1
  47. package/lib/rules/linebreak-style.js +7 -1
  48. package/lib/rules/lines-around-comment.js +4 -4
  49. package/lib/rules/lines-around-directive.js +4 -4
  50. package/lib/rules/max-lines.js +3 -3
  51. package/lib/rules/max-statements-per-line.js +8 -7
  52. package/lib/rules/new-cap.js +2 -2
  53. package/lib/rules/newline-after-var.js +7 -2
  54. package/lib/rules/newline-before-return.js +2 -2
  55. package/lib/rules/newline-per-chained-call.js +3 -1
  56. package/lib/rules/no-await-in-loop.js +5 -5
  57. package/lib/rules/no-cond-assign.js +3 -3
  58. package/lib/rules/no-dupe-keys.js +1 -1
  59. package/lib/rules/no-else-return.js +88 -25
  60. package/lib/rules/no-extend-native.js +3 -3
  61. package/lib/rules/no-extra-bind.js +3 -4
  62. package/lib/rules/no-extra-boolean-cast.js +22 -1
  63. package/lib/rules/no-extra-parens.js +57 -9
  64. package/lib/rules/no-inner-declarations.js +4 -4
  65. package/lib/rules/no-irregular-whitespace.js +7 -1
  66. package/lib/rules/no-lone-blocks.js +10 -10
  67. package/lib/rules/no-mixed-operators.js +1 -7
  68. package/lib/rules/no-mixed-requires.js +4 -4
  69. package/lib/rules/no-multi-assign.js +41 -0
  70. package/lib/rules/no-multi-spaces.js +4 -1
  71. package/lib/rules/no-multi-str.js +7 -3
  72. package/lib/rules/no-redeclare.js +7 -7
  73. package/lib/rules/no-return-assign.js +7 -14
  74. package/lib/rules/no-return-await.js +2 -2
  75. package/lib/rules/no-sequences.js +7 -6
  76. package/lib/rules/no-throw-literal.js +2 -39
  77. package/lib/rules/no-trailing-spaces.js +8 -2
  78. package/lib/rules/no-undefined.js +45 -6
  79. package/lib/rules/no-unexpected-multiline.js +9 -8
  80. package/lib/rules/no-unneeded-ternary.js +5 -1
  81. package/lib/rules/no-unused-labels.js +17 -2
  82. package/lib/rules/no-unused-vars.js +34 -19
  83. package/lib/rules/no-use-before-define.js +33 -29
  84. package/lib/rules/no-useless-computed-key.js +9 -4
  85. package/lib/rules/no-useless-concat.js +10 -7
  86. package/lib/rules/no-useless-escape.js +1 -1
  87. package/lib/rules/no-useless-return.js +5 -11
  88. package/lib/rules/no-var.js +69 -3
  89. package/lib/rules/no-whitespace-before-property.js +5 -16
  90. package/lib/rules/object-curly-newline.js +2 -2
  91. package/lib/rules/object-curly-spacing.js +7 -25
  92. package/lib/rules/object-property-newline.js +3 -3
  93. package/lib/rules/object-shorthand.js +10 -10
  94. package/lib/rules/operator-assignment.js +2 -2
  95. package/lib/rules/operator-linebreak.js +8 -10
  96. package/lib/rules/padded-blocks.js +4 -4
  97. package/lib/rules/prefer-promise-reject-errors.js +124 -0
  98. package/lib/rules/prefer-spread.js +1 -1
  99. package/lib/rules/prefer-template.js +1 -1
  100. package/lib/rules/quotes.js +11 -7
  101. package/lib/rules/require-await.js +1 -1
  102. package/lib/rules/semi-spacing.js +4 -0
  103. package/lib/rules/sort-imports.js +4 -4
  104. package/lib/rules/sort-keys.js +2 -2
  105. package/lib/rules/sort-vars.js +2 -2
  106. package/lib/rules/space-before-function-paren.js +9 -6
  107. package/lib/rules/space-in-parens.js +8 -8
  108. package/lib/rules/spaced-comment.js +10 -10
  109. package/lib/rules/strict.js +2 -2
  110. package/lib/rules/template-tag-spacing.js +77 -0
  111. package/lib/rules/unicode-bom.js +1 -1
  112. package/lib/rules/wrap-iife.js +5 -5
  113. package/lib/rules/yoda.js +2 -7
  114. package/lib/rules.js +2 -2
  115. package/lib/testers/rule-tester.js +28 -21
  116. package/lib/token-store/backward-token-comment-cursor.js +57 -0
  117. package/lib/token-store/backward-token-cursor.js +56 -0
  118. package/lib/token-store/cursor.js +76 -0
  119. package/lib/token-store/cursors.js +92 -0
  120. package/lib/token-store/decorative-cursor.js +39 -0
  121. package/lib/token-store/filter-cursor.js +43 -0
  122. package/lib/token-store/forward-token-comment-cursor.js +57 -0
  123. package/lib/token-store/forward-token-cursor.js +61 -0
  124. package/lib/token-store/index.js +604 -0
  125. package/lib/token-store/limit-cursor.js +40 -0
  126. package/lib/token-store/padded-token-cursor.js +38 -0
  127. package/lib/token-store/skip-cursor.js +42 -0
  128. package/lib/token-store/utils.js +100 -0
  129. package/lib/util/comment-event-generator.js +17 -16
  130. package/lib/util/glob-util.js +1 -1
  131. package/lib/util/glob.js +1 -1
  132. package/lib/util/rule-fixer.js +3 -8
  133. package/lib/util/source-code-fixer.js +41 -45
  134. package/lib/util/source-code.js +35 -19
  135. package/messages/extend-config-missing.txt +3 -0
  136. package/package.json +3 -3
  137. package/lib/token-store.js +0 -203
@@ -17,7 +17,9 @@ module.exports = {
17
17
  recommended: false
18
18
  },
19
19
 
20
- schema: []
20
+ schema: [],
21
+
22
+ fixable: "code"
21
23
  },
22
24
 
23
25
  create(context) {
@@ -33,7 +35,60 @@ module.exports = {
33
35
  * @returns {void}
34
36
  */
35
37
  function displayReport(node) {
36
- context.report({ node, message: "Unnecessary 'else' after 'return'." });
38
+ context.report({
39
+ node,
40
+ message: "Unnecessary 'else' after 'return'.",
41
+ fix: fixer => {
42
+ const sourceCode = context.getSourceCode();
43
+ const startToken = sourceCode.getFirstToken(node);
44
+ const elseToken = sourceCode.getTokenBefore(startToken);
45
+ const source = sourceCode.getText(node);
46
+ const lastIfToken = sourceCode.getTokenBefore(elseToken);
47
+ let fixedSource, firstTokenOfElseBlock;
48
+
49
+ if (startToken.type === "Punctuator" && startToken.value === "{") {
50
+ firstTokenOfElseBlock = sourceCode.getTokenAfter(startToken);
51
+ } else {
52
+ firstTokenOfElseBlock = startToken;
53
+ }
54
+
55
+ // If the if block does not have curly braces and does not end in a semicolon
56
+ // and the else block starts with (, [, /, +, ` or -, then it is not
57
+ // safe to remove the else keyword, because ASI will not add a semicolon
58
+ // after the if block
59
+ const ifBlockMaybeUnsafe = node.parent.consequent.type !== "BlockStatement" && lastIfToken.value !== ";";
60
+ const elseBlockUnsafe = /^[([/+`-]/.test(firstTokenOfElseBlock.value);
61
+
62
+ if (ifBlockMaybeUnsafe && elseBlockUnsafe) {
63
+ return null;
64
+ }
65
+
66
+ const endToken = sourceCode.getLastToken(node);
67
+ const lastTokenOfElseBlock = sourceCode.getTokenBefore(endToken);
68
+
69
+ if (lastTokenOfElseBlock.value !== ";") {
70
+ const nextToken = sourceCode.getTokenAfter(endToken);
71
+
72
+ const nextTokenUnsafe = nextToken && /^[([/+`-]/.test(nextToken.value);
73
+ const nextTokenOnSameLine = nextToken && nextToken.loc.start.line === lastTokenOfElseBlock.loc.start.line;
74
+
75
+ // If the else block contents does not end in a semicolon,
76
+ // and the else block starts with (, [, /, +, ` or -, then it is not
77
+ // safe to remove the else block, because ASI will not add a semicolon
78
+ // after the remaining else block contents
79
+ if (nextTokenUnsafe || (nextTokenOnSameLine && nextToken.value !== "}")) {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ if (startToken.type === "Punctuator" && startToken.value === "{") {
85
+ fixedSource = source.slice(1, -1);
86
+ } else {
87
+ fixedSource = source;
88
+ }
89
+ return fixer.replaceTextRange([elseToken.start, node.end], fixedSource);
90
+ }
91
+ });
37
92
  }
38
93
 
39
94
  /**
@@ -121,35 +176,43 @@ module.exports = {
121
176
  return checkForReturnOrIf(node);
122
177
  }
123
178
 
179
+ /**
180
+ * Check the if statement
181
+ * @returns {void}
182
+ * @param {Node} node The node for the if statement to check
183
+ * @private
184
+ */
185
+ function IfStatement(node) {
186
+ const parent = context.getAncestors().pop();
187
+ let consequents,
188
+ alternate;
189
+
190
+ // Only "top-level" if statements are checked, meaning the first `if`
191
+ // in a `if-else-if-...` chain.
192
+ if (parent.type === "IfStatement" && parent.alternate === node) {
193
+ return;
194
+ }
195
+
196
+ for (consequents = []; node.type === "IfStatement"; node = node.alternate) {
197
+ if (!node.alternate) {
198
+ return;
199
+ }
200
+ consequents.push(node.consequent);
201
+ alternate = node.alternate;
202
+ }
203
+
204
+ if (consequents.every(alwaysReturns)) {
205
+ displayReport(alternate);
206
+ }
207
+ }
208
+
124
209
  //--------------------------------------------------------------------------
125
210
  // Public API
126
211
  //--------------------------------------------------------------------------
127
212
 
128
213
  return {
129
214
 
130
- IfStatement(node) {
131
- const parent = context.getAncestors().pop();
132
- let consequents,
133
- alternate;
134
-
135
- // Only "top-level" if statements are checked, meaning the first `if`
136
- // in a `if-else-if-...` chain.
137
- if (parent.type === "IfStatement" && parent.alternate === node) {
138
- return;
139
- }
140
-
141
- for (consequents = []; node.type === "IfStatement"; node = node.alternate) {
142
- if (!node.alternate) {
143
- return;
144
- }
145
- consequents.push(node.consequent);
146
- alternate = node.alternate;
147
- }
148
-
149
- if (consequents.every(alwaysReturns)) {
150
- displayReport(alternate);
151
- }
152
- }
215
+ "IfStatement:exit": IfStatement
153
216
 
154
217
  };
155
218
 
@@ -60,9 +60,9 @@ module.exports = {
60
60
  return;
61
61
  }
62
62
 
63
- const affectsProto = lhs.object.computed ?
64
- lhs.object.property.type === "Literal" && lhs.object.property.value === "prototype" :
65
- lhs.object.property.name === "prototype";
63
+ const affectsProto = lhs.object.computed
64
+ ? lhs.object.property.type === "Literal" && lhs.object.property.value === "prototype"
65
+ : lhs.object.property.name === "prototype";
66
66
 
67
67
  if (!affectsProto) {
68
68
  return;
@@ -8,7 +8,7 @@
8
8
  // Requirements
9
9
  //------------------------------------------------------------------------------
10
10
 
11
- const getPropertyName = require("../ast-utils").getStaticPropertyName;
11
+ const astUtils = require("../ast-utils");
12
12
 
13
13
  //------------------------------------------------------------------------------
14
14
  // Rule Definition
@@ -44,8 +44,7 @@ module.exports = {
44
44
  loc: node.parent.property.loc.start,
45
45
  fix(fixer) {
46
46
  const firstTokenToRemove = context.getSourceCode()
47
- .getTokensBetween(node.parent.object, node.parent.property)
48
- .find(token => token.value !== ")");
47
+ .getFirstTokenBetween(node.parent.object, node.parent.property, astUtils.isNotClosingParenToken);
49
48
 
50
49
  return fixer.removeRange([firstTokenToRemove.range[0], node.parent.parent.range[1]]);
51
50
  }
@@ -73,7 +72,7 @@ module.exports = {
73
72
  grandparent.arguments.length === 1 &&
74
73
  parent.type === "MemberExpression" &&
75
74
  parent.object === node &&
76
- getPropertyName(parent) === "bind"
75
+ astUtils.getStaticPropertyName(parent) === "bind"
77
76
  );
78
77
  }
79
78
 
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("../ast-utils");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -91,7 +97,22 @@ module.exports = {
91
97
  context.report({
92
98
  node,
93
99
  message: "Redundant Boolean call.",
94
- fix: fixer => fixer.replaceText(node, sourceCode.getText(node.arguments[0]))
100
+ fix: fixer => {
101
+ if (!node.arguments.length) {
102
+ return fixer.replaceText(parent, "true");
103
+ }
104
+
105
+ if (node.arguments.length > 1 || node.arguments[0].type === "SpreadElement") {
106
+ return null;
107
+ }
108
+
109
+ const argument = node.arguments[0];
110
+
111
+ if (astUtils.getPrecedence(argument) < astUtils.getPrecedence(node.parent)) {
112
+ return fixer.replaceText(node, `(${sourceCode.getText(argument)})`);
113
+ }
114
+ return fixer.replaceText(node, sourceCode.getText(argument));
115
+ }
95
116
  });
96
117
  }
97
118
  }
@@ -44,7 +44,8 @@ module.exports = {
44
44
  properties: {
45
45
  conditionalAssign: { type: "boolean" },
46
46
  nestedBinaryExpressions: { type: "boolean" },
47
- returnAssign: { type: "boolean" }
47
+ returnAssign: { type: "boolean" },
48
+ ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] }
48
49
  },
49
50
  additionalProperties: false
50
51
  }
@@ -65,6 +66,7 @@ module.exports = {
65
66
  const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false;
66
67
  const NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false;
67
68
  const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false;
69
+ const IGNORE_JSX = ALL_NODES && context.options[1] && context.options[1].ignoreJSX;
68
70
 
69
71
  /**
70
72
  * Determines if this rule should be enforced for a node given the current configuration.
@@ -73,6 +75,31 @@ module.exports = {
73
75
  * @private
74
76
  */
75
77
  function ruleApplies(node) {
78
+ if (node.type === "JSXElement") {
79
+ const isSingleLine = node.loc.start.line === node.loc.end.line;
80
+
81
+ switch (IGNORE_JSX) {
82
+
83
+ // Exclude this JSX element from linting
84
+ case "all":
85
+ return false;
86
+
87
+ // Exclude this JSX element if it is multi-line element
88
+ case "multi-line":
89
+ return isSingleLine;
90
+
91
+ // Exclude this JSX element if it is single-line element
92
+ case "single-line":
93
+ return !isSingleLine;
94
+
95
+ // Nothing special to be done for JSX elements
96
+ case "none":
97
+ break;
98
+
99
+ // no default
100
+ }
101
+ }
102
+
76
103
  return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
77
104
  }
78
105
 
@@ -140,6 +167,19 @@ module.exports = {
140
167
  return false;
141
168
  }
142
169
 
170
+ /**
171
+ * Determines if a constructor function is newed-up with parens
172
+ * @param {ASTNode} newExpression - The NewExpression node to be checked.
173
+ * @returns {boolean} True if the constructor is called with parens.
174
+ * @private
175
+ */
176
+ function isNewExpressionWithParens(newExpression) {
177
+ const lastToken = sourceCode.getLastToken(newExpression);
178
+ const penultimateToken = sourceCode.getTokenBefore(lastToken);
179
+
180
+ return newExpression.arguments.length > 0 || penultimateToken.value === "(" && lastToken.value === ")";
181
+ }
182
+
143
183
  /**
144
184
  * Determines if a node is or contains an assignment expression
145
185
  * @param {ASTNode} node - The node to be checked.
@@ -175,9 +215,9 @@ module.exports = {
175
215
  return node.argument && containsAssignment(node.argument);
176
216
  } else if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") {
177
217
  return containsAssignment(node.body);
178
- } else {
179
- return containsAssignment(node);
180
218
  }
219
+ return containsAssignment(node);
220
+
181
221
  }
182
222
 
183
223
  /**
@@ -326,6 +366,10 @@ module.exports = {
326
366
  * @private
327
367
  */
328
368
  function dryUnaryUpdate(node) {
369
+ if (node.type === "UnaryExpression" && node.argument.type === "BinaryExpression" && node.argument.operator === "**") {
370
+ return;
371
+ }
372
+
329
373
  if (hasExcessParens(node.argument) && precedence(node.argument) >= precedence(node)) {
330
374
  report(node.argument);
331
375
  }
@@ -340,7 +384,8 @@ module.exports = {
340
384
  function dryCallNew(node) {
341
385
  if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node) && !(
342
386
  node.type === "CallExpression" &&
343
- node.callee.type === "FunctionExpression" &&
387
+ (node.callee.type === "FunctionExpression" ||
388
+ node.callee.type === "NewExpression" && !isNewExpressionWithParens(node.callee)) &&
344
389
 
345
390
  // One set of parentheses are allowed for a function expression
346
391
  !hasDoubleExcessParens(node.callee)
@@ -368,13 +413,17 @@ module.exports = {
368
413
  */
369
414
  function dryBinaryLogical(node) {
370
415
  const prec = precedence(node);
371
- const shouldSkipLeft = NESTED_BINARY && (node.left.type === "BinaryExpression" || node.left.type === "LogicalExpression");
416
+ const leftPrecedence = precedence(node.left);
417
+ const rightPrecedence = precedence(node.right);
418
+ const isExponentiation = node.operator === "**";
419
+ const shouldSkipLeft = (NESTED_BINARY && (node.left.type === "BinaryExpression" || node.left.type === "LogicalExpression")) ||
420
+ node.left.type === "UnaryExpression" && isExponentiation;
372
421
  const shouldSkipRight = NESTED_BINARY && (node.right.type === "BinaryExpression" || node.right.type === "LogicalExpression");
373
422
 
374
- if (!shouldSkipLeft && hasExcessParens(node.left) && precedence(node.left) >= prec) {
423
+ if (!shouldSkipLeft && hasExcessParens(node.left) && (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation))) {
375
424
  report(node.left);
376
425
  }
377
- if (!shouldSkipRight && hasExcessParens(node.right) && precedence(node.right) > prec) {
426
+ if (!shouldSkipRight && hasExcessParens(node.right) && (rightPrecedence > prec || (rightPrecedence === prec && isExponentiation))) {
378
427
  report(node.right);
379
428
  }
380
429
  }
@@ -508,8 +557,7 @@ module.exports = {
508
557
  !(
509
558
  (node.object.type === "Literal" &&
510
559
  typeof node.object.value === "number" &&
511
- astUtils.isDecimalInteger(node.object))
512
- ||
560
+ astUtils.isDecimalInteger(node.object)) ||
513
561
 
514
562
  // RegExp literal is allowed to have parens (#1589)
515
563
  (node.object.type === "Literal" && node.object.regex)
@@ -64,10 +64,10 @@ module.exports = {
64
64
 
65
65
  if (!valid) {
66
66
  context.report({ node, message: "Move {{type}} declaration to {{body}} root.", data: {
67
- type: (node.type === "FunctionDeclaration" ?
68
- "function" : "variable"),
69
- body: (body.type === "Program" ?
70
- "program" : "function body")
67
+ type: (node.type === "FunctionDeclaration"
68
+ ? "function" : "variable"),
69
+ body: (body.type === "Program"
70
+ ? "program" : "function body")
71
71
  } });
72
72
  }
73
73
  }
@@ -6,6 +6,12 @@
6
6
 
7
7
  "use strict";
8
8
 
9
+ //------------------------------------------------------------------------------
10
+ // Requirements
11
+ //------------------------------------------------------------------------------
12
+
13
+ const astUtils = require("../ast-utils");
14
+
9
15
  //------------------------------------------------------------------------------
10
16
  // Constants
11
17
  //------------------------------------------------------------------------------
@@ -13,7 +19,7 @@
13
19
  const ALL_IRREGULARS = /[\f\v\u0085\u00A0\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000\u2028\u2029]/;
14
20
  const IRREGULAR_WHITESPACE = /[\f\v\u0085\u00A0\ufeff\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u202f\u205f\u3000]+/mg;
15
21
  const IRREGULAR_LINE_TERMINATORS = /[\u2028\u2029]/mg;
16
- const LINE_BREAK = /\r\n|\r|\n|\u2028|\u2029/g;
22
+ const LINE_BREAK = astUtils.createGlobalLinebreakMatcher();
17
23
 
18
24
  //------------------------------------------------------------------------------
19
25
  // Rule Definition
@@ -32,22 +32,22 @@ module.exports = {
32
32
  * @returns {void}
33
33
  */
34
34
  function report(node) {
35
- const parent = context.getAncestors().pop();
35
+ const message = node.parent.type === "BlockStatement" ? "Nested block is redundant." : "Block is redundant.";
36
36
 
37
- context.report({ node, message: parent.type === "Program" ?
38
- "Block is redundant." :
39
- "Nested block is redundant."
40
- });
37
+ context.report({ node, message });
41
38
  }
42
39
 
43
40
  /**
44
- * Checks for any ocurrence of BlockStatement > BlockStatement or Program > BlockStatement
45
- * @returns {boolean} True if the current node is a lone block.
41
+ * Checks for any ocurrence of a BlockStatement in a place where lists of statements can appear
42
+ * @param {ASTNode} node The node to check
43
+ * @returns {boolean} True if the node is a lone block.
46
44
  */
47
- function isLoneBlock() {
48
- const parent = context.getAncestors().pop();
45
+ function isLoneBlock(node) {
46
+ return node.parent.type === "BlockStatement" ||
47
+ node.parent.type === "Program" ||
49
48
 
50
- return parent.type === "BlockStatement" || parent.type === "Program";
49
+ // Don't report blocks in switch cases if the block is the only statement of the case.
50
+ node.parent.type === "SwitchCase" && !(node.parent.consequent[0] === node && node.parent.consequent.length === 1);
51
51
  }
52
52
 
53
53
  /**
@@ -148,13 +148,7 @@ module.exports = {
148
148
  * @returns {Token} The operator token of the node.
149
149
  */
150
150
  function getOperatorToken(node) {
151
- let token = sourceCode.getTokenAfter(node.left);
152
-
153
- while (token.value === ")") {
154
- token = sourceCode.getTokenAfter(token);
155
- }
156
-
157
- return token;
151
+ return sourceCode.getTokenAfter(node.left, astUtils.isNotClosingParenToken);
158
152
  }
159
153
 
160
154
  /**
@@ -153,11 +153,11 @@ module.exports = {
153
153
 
154
154
  // "var utils = require('./utils');"
155
155
  return REQ_FILE;
156
- } else {
157
-
158
- // "var async = require('async');"
159
- return REQ_MODULE;
160
156
  }
157
+
158
+ // "var async = require('async');"
159
+ return REQ_MODULE;
160
+
161
161
  }
162
162
 
163
163
  /**
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @fileoverview Rule to check use of chained assignment expressions
3
+ * @author Stewart Rand
4
+ */
5
+
6
+ "use strict";
7
+
8
+
9
+ //------------------------------------------------------------------------------
10
+ // Rule Definition
11
+ //------------------------------------------------------------------------------
12
+
13
+ module.exports = {
14
+ meta: {
15
+ docs: {
16
+ description: "disallow use of chained assignment expressions",
17
+ category: "Stylistic Issues",
18
+ recommended: false
19
+ },
20
+ schema: []
21
+ },
22
+
23
+ create(context) {
24
+
25
+ //--------------------------------------------------------------------------
26
+ // Public
27
+ //--------------------------------------------------------------------------
28
+
29
+ return {
30
+ AssignmentExpression(node) {
31
+ if (["AssignmentExpression", "VariableDeclarator"].indexOf(node.parent.type) !== -1) {
32
+ context.report({
33
+ node,
34
+ message: "Unexpected chained assignment."
35
+ });
36
+ }
37
+ }
38
+ };
39
+
40
+ }
41
+ };
@@ -5,6 +5,8 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ const astUtils = require("../ast-utils");
9
+
8
10
  //------------------------------------------------------------------------------
9
11
  // Rule Definition
10
12
  //------------------------------------------------------------------------------
@@ -93,7 +95,8 @@ module.exports = {
93
95
  const sourceCode = context.getSourceCode(),
94
96
  source = sourceCode.getText(),
95
97
  allComments = sourceCode.getAllComments(),
96
- pattern = /[^\n\r\u2028\u2029\t ].? {2,}/g; // note: repeating space
98
+ JOINED_LINEBEAKS = Array.from(astUtils.LINEBREAKS).join(""),
99
+ pattern = new RegExp(String.raw`[^ \t${JOINED_LINEBEAKS}].? {2,}`, "g"); // note: repeating space
97
100
  let parent;
98
101
 
99
102
 
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("../ast-utils");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -39,9 +45,7 @@ module.exports = {
39
45
  return {
40
46
 
41
47
  Literal(node) {
42
- const lineBreak = /\n/;
43
-
44
- if (lineBreak.test(node.raw) && !isJSXElement(node.parent)) {
48
+ if (astUtils.LINEBREAK_MATCHER.test(node.raw) && !isJSXElement(node.parent)) {
45
49
  context.report({ node, message: "Multiline support is limited to browsers supporting ES5 only." });
46
50
  }
47
51
  }
@@ -89,13 +89,13 @@ module.exports = {
89
89
  BlockStatement: checkForBlock,
90
90
  SwitchStatement: checkForBlock
91
91
  };
92
- } else {
93
- return {
94
- Program: checkForGlobal,
95
- FunctionDeclaration: checkForBlock,
96
- FunctionExpression: checkForBlock,
97
- ArrowFunctionExpression: checkForBlock
98
- };
99
92
  }
93
+ return {
94
+ Program: checkForGlobal,
95
+ FunctionDeclaration: checkForBlock,
96
+ FunctionExpression: checkForBlock,
97
+ ArrowFunctionExpression: checkForBlock
98
+ };
99
+
100
100
  }
101
101
  };
@@ -5,23 +5,16 @@
5
5
  "use strict";
6
6
 
7
7
  //------------------------------------------------------------------------------
8
- // Helpers
8
+ // Requirements
9
9
  //------------------------------------------------------------------------------
10
10
 
11
- const SENTINEL_TYPE = /^(?:[a-zA-Z]+?Statement|ArrowFunctionExpression|FunctionExpression|ClassExpression)$/;
11
+ const astUtils = require("../ast-utils");
12
12
 
13
- /**
14
- * Checks whether or not a node is enclosed in parentheses.
15
- * @param {Node|null} node - A node to check.
16
- * @param {sourceCode} sourceCode - The ESLint SourceCode object.
17
- * @returns {boolean} Whether or not the node is enclosed in parentheses.
18
- */
19
- function isEnclosedInParens(node, sourceCode) {
20
- const prevToken = sourceCode.getTokenBefore(node);
21
- const nextToken = sourceCode.getTokenAfter(node);
13
+ //------------------------------------------------------------------------------
14
+ // Helpers
15
+ //------------------------------------------------------------------------------
22
16
 
23
- return prevToken && prevToken.value === "(" && nextToken && nextToken.value === ")";
24
- }
17
+ const SENTINEL_TYPE = /^(?:[a-zA-Z]+?Statement|ArrowFunctionExpression|FunctionExpression|ClassExpression)$/;
25
18
 
26
19
  //------------------------------------------------------------------------------
27
20
  // Rule Definition
@@ -48,7 +41,7 @@ module.exports = {
48
41
 
49
42
  return {
50
43
  AssignmentExpression(node) {
51
- if (!always && isEnclosedInParens(node, sourceCode)) {
44
+ if (!always && astUtils.isParenthesised(sourceCode, node)) {
52
45
  return;
53
46
  }
54
47
 
@@ -35,7 +35,7 @@ module.exports = {
35
35
  context.report({
36
36
  node: context.getSourceCode().getFirstToken(node),
37
37
  loc: node.loc,
38
- message,
38
+ message
39
39
  });
40
40
  }
41
41
 
@@ -88,7 +88,7 @@ module.exports = {
88
88
  if (isInTailCallPosition(node) && !hasErrorHandler(node)) {
89
89
  reportUnnecessaryAwait(node);
90
90
  }
91
- },
91
+ }
92
92
  };
93
93
  }
94
94
  };
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("../ast-utils");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -57,12 +63,7 @@ module.exports = {
57
63
  * @returns {boolean} True if the node has a paren on each side.
58
64
  */
59
65
  function isParenthesised(node) {
60
- const previousToken = sourceCode.getTokenBefore(node),
61
- nextToken = sourceCode.getTokenAfter(node);
62
-
63
- return previousToken && nextToken &&
64
- previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
65
- nextToken.value === ")" && nextToken.range[0] >= node.range[1];
66
+ return astUtils.isParenthesised(sourceCode, node);
66
67
  }
67
68
 
68
69
  /**