eslint 3.16.1 → 3.19.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 (86) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1 -0
  3. package/conf/eslint-recommended.js +2 -0
  4. package/lib/ast-utils.js +3 -67
  5. package/lib/code-path-analysis/code-path-analyzer.js +2 -7
  6. package/lib/code-path-analysis/debug-helpers.js +17 -16
  7. package/lib/config/config-file.js +68 -38
  8. package/lib/config/config-rule.js +14 -10
  9. package/lib/config/plugins.js +19 -8
  10. package/lib/eslint.js +11 -10
  11. package/lib/formatters/codeframe.js +4 -9
  12. package/lib/formatters/stylish.js +5 -4
  13. package/lib/ignored-paths.js +6 -0
  14. package/lib/internal-rules/internal-no-invalid-meta.js +2 -40
  15. package/lib/rules/array-callback-return.js +15 -5
  16. package/lib/rules/arrow-body-style.js +2 -2
  17. package/lib/rules/arrow-parens.js +9 -3
  18. package/lib/rules/capitalized-comments.js +2 -1
  19. package/lib/rules/comma-dangle.js +3 -2
  20. package/lib/rules/comma-spacing.js +4 -14
  21. package/lib/rules/comma-style.js +8 -14
  22. package/lib/rules/complexity.js +14 -8
  23. package/lib/rules/consistent-return.js +17 -10
  24. package/lib/rules/curly.js +2 -2
  25. package/lib/rules/dot-notation.js +12 -6
  26. package/lib/rules/func-name-matching.js +18 -7
  27. package/lib/rules/func-names.js +20 -5
  28. package/lib/rules/keyword-spacing.js +19 -4
  29. package/lib/rules/line-comment-position.js +15 -5
  30. package/lib/rules/lines-around-comment.js +19 -0
  31. package/lib/rules/lines-around-directive.js +1 -1
  32. package/lib/rules/max-params.js +17 -4
  33. package/lib/rules/max-statements.js +11 -10
  34. package/lib/rules/new-parens.js +7 -21
  35. package/lib/rules/no-compare-neg-zero.js +53 -0
  36. package/lib/rules/no-cond-assign.js +4 -17
  37. package/lib/rules/no-else-return.js +19 -4
  38. package/lib/rules/no-empty-function.js +9 -16
  39. package/lib/rules/no-extra-parens.js +110 -121
  40. package/lib/rules/no-extra-semi.js +16 -3
  41. package/lib/rules/no-global-assign.js +1 -1
  42. package/lib/rules/no-implicit-coercion.js +21 -8
  43. package/lib/rules/no-invalid-regexp.js +2 -1
  44. package/lib/rules/no-multiple-empty-lines.js +2 -4
  45. package/lib/rules/no-native-reassign.js +1 -1
  46. package/lib/rules/no-negated-in-lhs.js +1 -1
  47. package/lib/rules/no-new-func.js +6 -8
  48. package/lib/rules/no-new.js +2 -6
  49. package/lib/rules/no-param-reassign.js +37 -7
  50. package/lib/rules/no-process-exit.js +2 -10
  51. package/lib/rules/no-restricted-properties.js +2 -0
  52. package/lib/rules/no-restricted-syntax.js +32 -21
  53. package/lib/rules/no-return-await.js +1 -1
  54. package/lib/rules/no-sequences.js +2 -2
  55. package/lib/rules/no-sync.js +8 -13
  56. package/lib/rules/no-unsafe-negation.js +1 -1
  57. package/lib/rules/no-unused-expressions.js +10 -1
  58. package/lib/rules/no-unused-vars.js +12 -12
  59. package/lib/rules/no-use-before-define.js +1 -1
  60. package/lib/rules/no-useless-computed-key.js +12 -1
  61. package/lib/rules/no-useless-escape.js +8 -2
  62. package/lib/rules/no-useless-return.js +13 -2
  63. package/lib/rules/nonblock-statement-body-position.js +114 -0
  64. package/lib/rules/object-curly-spacing.js +2 -2
  65. package/lib/rules/object-shorthand.js +10 -3
  66. package/lib/rules/operator-assignment.js +20 -3
  67. package/lib/rules/padded-blocks.js +37 -31
  68. package/lib/rules/prefer-const.js +1 -1
  69. package/lib/rules/prefer-destructuring.js +1 -1
  70. package/lib/rules/quotes.js +1 -0
  71. package/lib/rules/semi-spacing.js +2 -15
  72. package/lib/rules/semi.js +17 -13
  73. package/lib/rules/sort-vars.js +3 -5
  74. package/lib/rules/space-before-function-paren.js +53 -77
  75. package/lib/rules/space-in-parens.js +4 -8
  76. package/lib/rules/space-unary-ops.js +19 -1
  77. package/lib/rules/strict.js +8 -2
  78. package/lib/rules/yoda.js +2 -2
  79. package/lib/testers/rule-tester.js +44 -13
  80. package/lib/util/fix-tracker.js +121 -0
  81. package/lib/util/glob-util.js +1 -1
  82. package/lib/util/node-event-generator.js +274 -4
  83. package/lib/util/source-code-fixer.js +3 -9
  84. package/lib/util/source-code.js +99 -2
  85. package/lib/util/traverser.js +16 -25
  86. package/package.json +34 -34
@@ -60,6 +60,7 @@ module.exports = {
60
60
  create(context) {
61
61
  const sourceCode = context.getSourceCode();
62
62
 
63
+ const tokensToIgnore = new WeakSet();
63
64
  const isParenthesised = astUtils.isParenthesised.bind(astUtils, sourceCode);
64
65
  const precedence = astUtils.getPrecedence;
65
66
  const ALL_NODES = context.options[0] !== "functions";
@@ -67,6 +68,8 @@ module.exports = {
67
68
  const NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false;
68
69
  const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false;
69
70
  const IGNORE_JSX = ALL_NODES && context.options[1] && context.options[1].ignoreJSX;
71
+ const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
72
+ const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
70
73
 
71
74
  /**
72
75
  * Determines if this rule should be enforced for a node given the current configuration.
@@ -114,8 +117,8 @@ module.exports = {
114
117
  nextToken = sourceCode.getTokenAfter(node, 1);
115
118
 
116
119
  return isParenthesised(node) && previousToken && nextToken &&
117
- previousToken.value === "(" && previousToken.range[1] <= node.range[0] &&
118
- nextToken.value === ")" && nextToken.range[0] >= node.range[1];
120
+ astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] &&
121
+ astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1];
119
122
  }
120
123
 
121
124
  /**
@@ -177,7 +180,7 @@ module.exports = {
177
180
  const lastToken = sourceCode.getLastToken(newExpression);
178
181
  const penultimateToken = sourceCode.getTokenBefore(lastToken);
179
182
 
180
- return newExpression.arguments.length > 0 || penultimateToken.value === "(" && lastToken.value === ")";
183
+ return newExpression.arguments.length > 0 || astUtils.isOpeningParenToken(penultimateToken) && astUtils.isClosingParenToken(lastToken);
181
184
  }
182
185
 
183
186
  /**
@@ -236,69 +239,6 @@ module.exports = {
236
239
  return hasDoubleExcessParens(node);
237
240
  }
238
241
 
239
- /**
240
- * Checks whether or not a given node is located at the head of ExpressionStatement.
241
- * @param {ASTNode} node - A node to check.
242
- * @returns {boolean} `true` if the node is located at the head of ExpressionStatement.
243
- */
244
- function isHeadOfExpressionStatement(node) {
245
- let parent = node.parent;
246
-
247
- while (parent) {
248
- switch (parent.type) {
249
- case "SequenceExpression":
250
- if (parent.expressions[0] !== node || isParenthesised(node)) {
251
- return false;
252
- }
253
- break;
254
-
255
- case "UnaryExpression":
256
- case "UpdateExpression":
257
- if (parent.prefix || isParenthesised(node)) {
258
- return false;
259
- }
260
- break;
261
-
262
- case "BinaryExpression":
263
- case "LogicalExpression":
264
- if (parent.left !== node || isParenthesised(node)) {
265
- return false;
266
- }
267
- break;
268
-
269
- case "ConditionalExpression":
270
- if (parent.test !== node || isParenthesised(node)) {
271
- return false;
272
- }
273
- break;
274
-
275
- case "CallExpression":
276
- if (parent.callee !== node || isParenthesised(node)) {
277
- return false;
278
- }
279
- break;
280
-
281
- case "MemberExpression":
282
- if (parent.object !== node || isParenthesised(node)) {
283
- return false;
284
- }
285
- break;
286
-
287
- case "ExpressionStatement":
288
- return true;
289
-
290
- default:
291
- return false;
292
- }
293
-
294
- node = parent;
295
- parent = parent.parent;
296
- }
297
-
298
- /* istanbul ignore next */
299
- throw new Error("unreachable");
300
- }
301
-
302
242
  /**
303
243
  * Determines whether a node should be preceded by an additional space when removing parens
304
244
  * @param {ASTNode} node node to evaluate; must be surrounded by parentheses
@@ -316,7 +256,7 @@ module.exports = {
316
256
  }
317
257
 
318
258
  // If the parens are preceded by a keyword (e.g. `typeof(0)`), a space should be inserted (`typeof 0`)
319
- const precededByKeyword = tokenBeforeLeftParen.type === "Keyword";
259
+ const precededByIdentiferPart = esUtils.code.isIdentifierPartES6(tokenBeforeLeftParen.value.slice(-1).charCodeAt(0));
320
260
 
321
261
  // However, a space should not be inserted unless the first character of the token is an identifier part
322
262
  // e.g. `typeof([])` should be fixed to `typeof[]`
@@ -329,7 +269,7 @@ module.exports = {
329
269
  const startsWithUnaryPlus = firstToken.type === "Punctuator" && firstToken.value === "+";
330
270
  const startsWithUnaryMinus = firstToken.type === "Punctuator" && firstToken.value === "-";
331
271
 
332
- return (precededByKeyword && startsWithIdentifierPart) ||
272
+ return (precededByIdentiferPart && startsWithIdentifierPart) ||
333
273
  (precededByUnaryPlus && startsWithUnaryPlus) ||
334
274
  (precededByUnaryMinus && startsWithUnaryMinus);
335
275
  }
@@ -344,6 +284,10 @@ module.exports = {
344
284
  const leftParenToken = sourceCode.getTokenBefore(node);
345
285
  const rightParenToken = sourceCode.getTokenAfter(node);
346
286
 
287
+ if (tokensToIgnore.has(sourceCode.getFirstToken(node)) && !isParenthesisedTwice(node)) {
288
+ return;
289
+ }
290
+
347
291
  context.report({
348
292
  node,
349
293
  loc: leftParenToken.loc.start,
@@ -365,7 +309,7 @@ module.exports = {
365
309
  * @returns {void}
366
310
  * @private
367
311
  */
368
- function dryUnaryUpdate(node) {
312
+ function checkUnaryUpdate(node) {
369
313
  if (node.type === "UnaryExpression" && node.argument.type === "BinaryExpression" && node.argument.operator === "**") {
370
314
  return;
371
315
  }
@@ -381,7 +325,7 @@ module.exports = {
381
325
  * @returns {void}
382
326
  * @private
383
327
  */
384
- function dryCallNew(node) {
328
+ function checkCallNew(node) {
385
329
  if (hasExcessParens(node.callee) && precedence(node.callee) >= precedence(node) && !(
386
330
  node.type === "CallExpression" &&
387
331
  (node.callee.type === "FunctionExpression" ||
@@ -393,12 +337,12 @@ module.exports = {
393
337
  report(node.callee);
394
338
  }
395
339
  if (node.arguments.length === 1) {
396
- if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= precedence({ type: "AssignmentExpression" })) {
340
+ if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
397
341
  report(node.arguments[0]);
398
342
  }
399
343
  } else {
400
344
  [].forEach.call(node.arguments, arg => {
401
- if (hasExcessParens(arg) && precedence(arg) >= precedence({ type: "AssignmentExpression" })) {
345
+ if (hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
402
346
  report(arg);
403
347
  }
404
348
  });
@@ -411,7 +355,7 @@ module.exports = {
411
355
  * @returns {void}
412
356
  * @private
413
357
  */
414
- function dryBinaryLogical(node) {
358
+ function checkBinaryLogical(node) {
415
359
  const prec = precedence(node);
416
360
  const leftPrecedence = precedence(node.left);
417
361
  const rightPrecedence = precedence(node.right);
@@ -428,10 +372,74 @@ module.exports = {
428
372
  }
429
373
  }
430
374
 
375
+ /**
376
+ * Check the parentheses around the super class of the given class definition.
377
+ * @param {ASTNode} node The node of class declarations to check.
378
+ * @returns {void}
379
+ */
380
+ function checkClass(node) {
381
+ if (!node.superClass) {
382
+ return;
383
+ }
384
+
385
+ // If `node.superClass` is a LeftHandSideExpression, parentheses are extra.
386
+ // Otherwise, parentheses are needed.
387
+ const hasExtraParens = precedence(node.superClass) > PRECEDENCE_OF_UPDATE_EXPR
388
+ ? hasExcessParens(node.superClass)
389
+ : hasDoubleExcessParens(node.superClass);
390
+
391
+ if (hasExtraParens) {
392
+ report(node.superClass);
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Check the parentheses around the argument of the given spread operator.
398
+ * @param {ASTNode} node The node of spread elements/properties to check.
399
+ * @returns {void}
400
+ */
401
+ function checkSpreadOperator(node) {
402
+ const hasExtraParens = precedence(node.argument) >= PRECEDENCE_OF_ASSIGNMENT_EXPR
403
+ ? hasExcessParens(node.argument)
404
+ : hasDoubleExcessParens(node.argument);
405
+
406
+ if (hasExtraParens) {
407
+ report(node.argument);
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Checks the parentheses for an ExpressionStatement or ExportDefaultDeclaration
413
+ * @param {ASTNode} node The ExpressionStatement.expression or ExportDefaultDeclaration.declaration node
414
+ * @returns {void}
415
+ */
416
+ function checkExpressionOrExportStatement(node) {
417
+ const firstToken = isParenthesised(node) ? sourceCode.getTokenBefore(node) : sourceCode.getFirstToken(node);
418
+ const secondToken = sourceCode.getTokenAfter(firstToken, astUtils.isNotOpeningParenToken);
419
+
420
+ if (
421
+ astUtils.isOpeningParenToken(firstToken) &&
422
+ (
423
+ astUtils.isOpeningBraceToken(secondToken) ||
424
+ secondToken.type === "Keyword" && (
425
+ secondToken.value === "function" ||
426
+ secondToken.value === "class" ||
427
+ secondToken.value === "let" && astUtils.isOpeningBracketToken(sourceCode.getTokenAfter(secondToken))
428
+ )
429
+ )
430
+ ) {
431
+ tokensToIgnore.add(secondToken);
432
+ }
433
+
434
+ if (hasExcessParens(node)) {
435
+ report(node);
436
+ }
437
+ }
438
+
431
439
  return {
432
440
  ArrayExpression(node) {
433
441
  [].forEach.call(node.elements, e => {
434
- if (e && hasExcessParens(e) && precedence(e) >= precedence({ type: "AssignmentExpression" })) {
442
+ if (e && hasExcessParens(e) && precedence(e) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
435
443
  report(e);
436
444
  }
437
445
  });
@@ -443,13 +451,13 @@ module.exports = {
443
451
  }
444
452
 
445
453
  if (node.body.type !== "BlockStatement") {
446
- if (sourceCode.getFirstToken(node.body).value !== "{" && hasExcessParens(node.body) && precedence(node.body) >= precedence({ type: "AssignmentExpression" })) {
447
- report(node.body);
448
- return;
449
- }
454
+ const firstBodyToken = sourceCode.getFirstToken(node.body, astUtils.isNotOpeningParenToken);
455
+ const tokenBeforeFirst = sourceCode.getTokenBefore(firstBodyToken);
450
456
 
451
- // Object literals *must* be parenthesised
452
- if (node.body.type === "ObjectExpression" && hasDoubleExcessParens(node.body)) {
457
+ if (astUtils.isOpeningParenToken(tokenBeforeFirst) && astUtils.isOpeningBraceToken(firstBodyToken)) {
458
+ tokensToIgnore.add(firstBodyToken);
459
+ }
460
+ if (hasExcessParens(node.body) && precedence(node.body) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
453
461
  report(node.body);
454
462
  }
455
463
  }
@@ -465,8 +473,8 @@ module.exports = {
465
473
  }
466
474
  },
467
475
 
468
- BinaryExpression: dryBinaryLogical,
469
- CallExpression: dryCallNew,
476
+ BinaryExpression: checkBinaryLogical,
477
+ CallExpression: checkCallNew,
470
478
 
471
479
  ConditionalExpression(node) {
472
480
  if (isReturnAssignException(node)) {
@@ -477,11 +485,11 @@ module.exports = {
477
485
  report(node.test);
478
486
  }
479
487
 
480
- if (hasExcessParens(node.consequent) && precedence(node.consequent) >= precedence({ type: "AssignmentExpression" })) {
488
+ if (hasExcessParens(node.consequent) && precedence(node.consequent) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
481
489
  report(node.consequent);
482
490
  }
483
491
 
484
- if (hasExcessParens(node.alternate) && precedence(node.alternate) >= precedence({ type: "AssignmentExpression" })) {
492
+ if (hasExcessParens(node.alternate) && precedence(node.alternate) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
485
493
  report(node.alternate);
486
494
  }
487
495
  },
@@ -492,27 +500,8 @@ module.exports = {
492
500
  }
493
501
  },
494
502
 
495
- ExpressionStatement(node) {
496
- if (hasExcessParens(node.expression)) {
497
- const firstTokens = sourceCode.getFirstTokens(node.expression, 2);
498
- const firstToken = firstTokens[0];
499
- const secondToken = firstTokens[1];
500
-
501
- if (
502
- !firstToken ||
503
- firstToken.value !== "{" &&
504
- firstToken.value !== "function" &&
505
- firstToken.value !== "class" &&
506
- (
507
- firstToken.value !== "let" ||
508
- !secondToken ||
509
- secondToken.value !== "["
510
- )
511
- ) {
512
- report(node.expression);
513
- }
514
- }
515
- },
503
+ ExportDefaultDeclaration: node => checkExpressionOrExportStatement(node.declaration),
504
+ ExpressionStatement: node => checkExpressionOrExportStatement(node.expression),
516
505
 
517
506
  ForInStatement(node) {
518
507
  if (hasExcessParens(node.right)) {
@@ -546,7 +535,7 @@ module.exports = {
546
535
  }
547
536
  },
548
537
 
549
- LogicalExpression: dryBinaryLogical,
538
+ LogicalExpression: checkBinaryLogical,
550
539
 
551
540
  MemberExpression(node) {
552
541
  if (
@@ -555,18 +544,11 @@ module.exports = {
555
544
  (
556
545
  node.computed ||
557
546
  !(
558
- (node.object.type === "Literal" &&
559
- typeof node.object.value === "number" &&
560
- astUtils.isDecimalInteger(node.object)) ||
547
+ astUtils.isDecimalInteger(node.object) ||
561
548
 
562
549
  // RegExp literal is allowed to have parens (#1589)
563
550
  (node.object.type === "Literal" && node.object.regex)
564
551
  )
565
- ) &&
566
- !(
567
- (node.object.type === "FunctionExpression" || node.object.type === "ClassExpression") &&
568
- isHeadOfExpressionStatement(node) &&
569
- !hasDoubleExcessParens(node.object)
570
552
  )
571
553
  ) {
572
554
  report(node.object);
@@ -576,13 +558,13 @@ module.exports = {
576
558
  }
577
559
  },
578
560
 
579
- NewExpression: dryCallNew,
561
+ NewExpression: checkCallNew,
580
562
 
581
563
  ObjectExpression(node) {
582
564
  [].forEach.call(node.properties, e => {
583
565
  const v = e.value;
584
566
 
585
- if (v && hasExcessParens(v) && precedence(v) >= precedence({ type: "AssignmentExpression" })) {
567
+ if (v && hasExcessParens(v) && precedence(v) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) {
586
568
  report(v);
587
569
  }
588
570
  });
@@ -632,13 +614,13 @@ module.exports = {
632
614
  }
633
615
  },
634
616
 
635
- UnaryExpression: dryUnaryUpdate,
636
- UpdateExpression: dryUnaryUpdate,
637
- AwaitExpression: dryUnaryUpdate,
617
+ UnaryExpression: checkUnaryUpdate,
618
+ UpdateExpression: checkUnaryUpdate,
619
+ AwaitExpression: checkUnaryUpdate,
638
620
 
639
621
  VariableDeclarator(node) {
640
622
  if (node.init && hasExcessParens(node.init) &&
641
- precedence(node.init) >= precedence({ type: "AssignmentExpression" }) &&
623
+ precedence(node.init) >= PRECEDENCE_OF_ASSIGNMENT_EXPR &&
642
624
 
643
625
  // RegExp literal is allowed to have parens (#1589)
644
626
  !(node.init.type === "Literal" && node.init.regex)) {
@@ -668,7 +650,14 @@ module.exports = {
668
650
  report(node.argument);
669
651
  }
670
652
  }
671
- }
653
+ },
654
+
655
+ ClassDeclaration: checkClass,
656
+ ClassExpression: checkClass,
657
+
658
+ SpreadElement: checkSpreadOperator,
659
+ SpreadProperty: checkSpreadOperator,
660
+ ExperimentalSpreadProperty: checkSpreadOperator
672
661
  };
673
662
 
674
663
  }
@@ -5,6 +5,13 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const FixTracker = require("../util/fix-tracker");
13
+ const astUtils = require("../ast-utils");
14
+
8
15
  //------------------------------------------------------------------------------
9
16
  // Rule Definition
10
17
  //------------------------------------------------------------------------------
@@ -34,7 +41,13 @@ module.exports = {
34
41
  node: nodeOrToken,
35
42
  message: "Unnecessary semicolon.",
36
43
  fix(fixer) {
37
- return fixer.remove(nodeOrToken);
44
+
45
+ // Expand the replacement range to include the surrounding
46
+ // tokens to avoid conflicting with semi.
47
+ // https://github.com/eslint/eslint/issues/7928
48
+ return new FixTracker(fixer, context.getSourceCode())
49
+ .retainSurroundingTokens(nodeOrToken)
50
+ .remove(nodeOrToken);
38
51
  }
39
52
  });
40
53
  }
@@ -48,10 +61,10 @@ module.exports = {
48
61
  */
49
62
  function checkForPartOfClassBody(firstToken) {
50
63
  for (let token = firstToken;
51
- token.type === "Punctuator" && token.value !== "}";
64
+ token.type === "Punctuator" && !astUtils.isClosingBraceToken(token);
52
65
  token = sourceCode.getTokenAfter(token)
53
66
  ) {
54
- if (token.value === ";") {
67
+ if (astUtils.isSemicolonToken(token)) {
55
68
  report(token);
56
69
  }
57
70
  }
@@ -14,7 +14,7 @@ module.exports = {
14
14
  docs: {
15
15
  description: "disallow assignments to native objects or read-only global variables",
16
16
  category: "Best Practices",
17
- recommended: false
17
+ recommended: true
18
18
  },
19
19
 
20
20
  schema: [
@@ -6,6 +6,7 @@
6
6
  "use strict";
7
7
 
8
8
  const astUtils = require("../ast-utils");
9
+ const esUtils = require("esutils");
9
10
 
10
11
  //------------------------------------------------------------------------------
11
12
  // Helpers
@@ -197,19 +198,31 @@ module.exports = {
197
198
  */
198
199
  function report(node, recommendation, shouldFix) {
199
200
  shouldFix = typeof shouldFix === "undefined" ? true : shouldFix;
200
- const reportObj = {
201
+
202
+ context.report({
201
203
  node,
202
204
  message: "use `{{recommendation}}` instead.",
203
205
  data: {
204
206
  recommendation
207
+ },
208
+ fix(fixer) {
209
+ if (!shouldFix) {
210
+ return null;
211
+ }
212
+
213
+ const tokenBefore = sourceCode.getTokenBefore(node);
214
+
215
+ if (
216
+ tokenBefore &&
217
+ tokenBefore.range[1] === node.range[0] &&
218
+ esUtils.code.isIdentifierPartES6(tokenBefore.value.slice(-1).charCodeAt(0)) &&
219
+ esUtils.code.isIdentifierPartES6(recommendation.charCodeAt(0))
220
+ ) {
221
+ return fixer.replaceText(node, ` ${recommendation}`);
222
+ }
223
+ return fixer.replaceText(node, recommendation);
205
224
  }
206
- };
207
-
208
- if (shouldFix) {
209
- reportObj.fix = fixer => fixer.replaceText(node, recommendation);
210
- }
211
-
212
- context.report(reportObj);
225
+ });
213
226
  }
214
227
 
215
228
  return {
@@ -74,7 +74,8 @@ module.exports = {
74
74
  } catch (e) {
75
75
  context.report({
76
76
  node,
77
- message: `${e.message}.`
77
+ message: "{{message}}.",
78
+ data: e
78
79
  });
79
80
  }
80
81
 
@@ -5,8 +5,6 @@
5
5
  */
6
6
  "use strict";
7
7
 
8
- const astUtils = require("../ast-utils");
9
-
10
8
  //------------------------------------------------------------------------------
11
9
  // Rule Definition
12
10
  //------------------------------------------------------------------------------
@@ -114,8 +112,8 @@ module.exports = {
114
112
  data: { max: maxAllowed, pluralizedLines: maxAllowed === 1 ? "line" : "lines" },
115
113
  fix(fixer) {
116
114
  return fixer.removeRange([
117
- astUtils.getRangeIndexFromLocation(sourceCode, { line: lastLineNumber + 1, column: 0 }),
118
- astUtils.getRangeIndexFromLocation(sourceCode, { line: lineNumber - maxAllowed, column: 0 })
115
+ sourceCode.getIndexFromLoc({ line: lastLineNumber + 1, column: 0 }),
116
+ sourceCode.getIndexFromLoc({ line: lineNumber - maxAllowed, column: 0 })
119
117
  ]);
120
118
  }
121
119
  });
@@ -15,7 +15,7 @@ module.exports = {
15
15
  docs: {
16
16
  description: "disallow assignments to native objects or read-only global variables",
17
17
  category: "Best Practices",
18
- recommended: true,
18
+ recommended: false,
19
19
  replacedBy: ["no-global-assign"]
20
20
  },
21
21
 
@@ -15,7 +15,7 @@ module.exports = {
15
15
  docs: {
16
16
  description: "disallow negating the left operand in `in` expressions",
17
17
  category: "Possible Errors",
18
- recommended: true,
18
+ recommended: false,
19
19
  replacedBy: ["no-unsafe-negation"]
20
20
  },
21
21
  deprecated: true,
@@ -27,20 +27,18 @@ module.exports = {
27
27
  //--------------------------------------------------------------------------
28
28
 
29
29
  /**
30
- * Checks if the callee is the Function constructor, and if so, reports an issue.
31
- * @param {ASTNode} node The node to check and report on
30
+ * Reports a node.
31
+ * @param {ASTNode} node The node to report
32
32
  * @returns {void}
33
33
  * @private
34
34
  */
35
- function validateCallee(node) {
36
- if (node.callee.name === "Function") {
37
- context.report({ node, message: "The Function constructor is eval." });
38
- }
35
+ function report(node) {
36
+ context.report({ node, message: "The Function constructor is eval." });
39
37
  }
40
38
 
41
39
  return {
42
- NewExpression: validateCallee,
43
- CallExpression: validateCallee
40
+ "NewExpression[callee.name = 'Function']": report,
41
+ "CallExpression[callee.name = 'Function']": report
44
42
  };
45
43
 
46
44
  }
@@ -24,12 +24,8 @@ module.exports = {
24
24
  create(context) {
25
25
 
26
26
  return {
27
-
28
- ExpressionStatement(node) {
29
-
30
- if (node.expression.type === "NewExpression") {
31
- context.report({ node, message: "Do not use 'new' for side effects." });
32
- }
27
+ "ExpressionStatement > NewExpression"(node) {
28
+ context.report({ node: node.parent, message: "Do not use 'new' for side effects." });
33
29
  }
34
30
  };
35
31
 
@@ -20,17 +20,40 @@ module.exports = {
20
20
 
21
21
  schema: [
22
22
  {
23
- type: "object",
24
- properties: {
25
- props: { type: "boolean" }
26
- },
27
- additionalProperties: false
23
+ oneOf: [
24
+ {
25
+ type: "object",
26
+ properties: {
27
+ props: {
28
+ enum: [false]
29
+ }
30
+ },
31
+ additionalProperties: false
32
+ },
33
+ {
34
+ type: "object",
35
+ properties: {
36
+ props: {
37
+ enum: [true]
38
+ },
39
+ ignorePropertyModificationsFor: {
40
+ type: "array",
41
+ items: {
42
+ type: "string"
43
+ },
44
+ uniqueItems: true
45
+ }
46
+ },
47
+ additionalProperties: false
48
+ }
49
+ ]
28
50
  }
29
51
  ]
30
52
  },
31
53
 
32
54
  create(context) {
33
55
  const props = context.options[0] && Boolean(context.options[0].props);
56
+ const ignoredPropertyAssignmentsFor = context.options[0] && context.options[0].ignorePropertyModificationsFor || [];
34
57
 
35
58
  /**
36
59
  * Checks whether or not the reference modifies properties of its variable.
@@ -73,8 +96,15 @@ module.exports = {
73
96
  }
74
97
  break;
75
98
 
76
- default:
99
+ // EXCLUDES: e.g. ({ [foo]: a }) = bar;
100
+ case "Property":
101
+ if (parent.key === node) {
102
+ return false;
103
+ }
104
+
77
105
  break;
106
+
107
+ // no default
78
108
  }
79
109
 
80
110
  node = parent;
@@ -103,7 +133,7 @@ module.exports = {
103
133
  ) {
104
134
  if (reference.isWrite()) {
105
135
  context.report({ node: identifier, message: "Assignment to function parameter '{{name}}'.", data: { name: identifier.name } });
106
- } else if (props && isModifyingProp(reference)) {
136
+ } else if (props && isModifyingProp(reference) && ignoredPropertyAssignmentsFor.indexOf(identifier.name) === -1) {
107
137
  context.report({ node: identifier, message: "Assignment to property of function parameter '{{name}}'.", data: { name: identifier.name } });
108
138
  }
109
139
  }