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
@@ -5,44 +5,7 @@
5
5
 
6
6
  "use strict";
7
7
 
8
- //------------------------------------------------------------------------------
9
- // Helpers
10
- //------------------------------------------------------------------------------
11
-
12
- /**
13
- * Determine if a node has a possiblity to be an Error object
14
- * @param {ASTNode} node ASTNode to check
15
- * @returns {boolean} True if there is a chance it contains an Error obj
16
- */
17
- function couldBeError(node) {
18
- switch (node.type) {
19
- case "Identifier":
20
- case "CallExpression":
21
- case "NewExpression":
22
- case "MemberExpression":
23
- case "TaggedTemplateExpression":
24
- case "YieldExpression":
25
- return true; // possibly an error object.
26
-
27
- case "AssignmentExpression":
28
- return couldBeError(node.right);
29
-
30
- case "SequenceExpression": {
31
- const exprs = node.expressions;
32
-
33
- return exprs.length !== 0 && couldBeError(exprs[exprs.length - 1]);
34
- }
35
-
36
- case "LogicalExpression":
37
- return couldBeError(node.left) || couldBeError(node.right);
38
-
39
- case "ConditionalExpression":
40
- return couldBeError(node.consequent) || couldBeError(node.alternate);
41
-
42
- default:
43
- return false;
44
- }
45
- }
8
+ const astUtils = require("../ast-utils");
46
9
 
47
10
  //------------------------------------------------------------------------------
48
11
  // Rule Definition
@@ -64,7 +27,7 @@ module.exports = {
64
27
  return {
65
28
 
66
29
  ThrowStatement(node) {
67
- if (!couldBeError(node.argument)) {
30
+ if (!astUtils.couldBeError(node.argument)) {
68
31
  context.report({ node, message: "Expected an object to be thrown." });
69
32
  } else if (node.argument.type === "Identifier") {
70
33
  if (node.argument.name === "undefined") {
@@ -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
  //------------------------------------------------------------------------------
@@ -34,7 +40,7 @@ module.exports = {
34
40
  create(context) {
35
41
  const sourceCode = context.getSourceCode();
36
42
 
37
- const BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u2028\u2029\u3000]",
43
+ const BLANK_CLASS = "[ \t\u00a0\u2000-\u200b\u3000]",
38
44
  SKIP_BLANK = `^${BLANK_CLASS}*$`,
39
45
  NONBLANK = `${BLANK_CLASS}+$`;
40
46
 
@@ -81,7 +87,7 @@ module.exports = {
81
87
  const re = new RegExp(NONBLANK),
82
88
  skipMatch = new RegExp(SKIP_BLANK),
83
89
  lines = sourceCode.lines,
84
- linebreaks = sourceCode.getText().match(/\r\n|\r|\n|\u2028|\u2029/g);
90
+ linebreaks = sourceCode.getText().match(astUtils.createGlobalLinebreakMatcher());
85
91
  let totalLength = 0,
86
92
  fixRange = [];
87
93
 
@@ -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
 
@@ -42,6 +42,9 @@ module.exports = {
42
42
  args: {
43
43
  enum: ["all", "after-used", "none"]
44
44
  },
45
+ ignoreRestSiblings: {
46
+ type: "boolean"
47
+ },
45
48
  argsIgnorePattern: {
46
49
  type: "string"
47
50
  },
@@ -59,6 +62,7 @@ module.exports = {
59
62
  },
60
63
 
61
64
  create(context) {
65
+ const sourceCode = context.getSourceCode();
62
66
 
63
67
  const DEFINED_MESSAGE = "'{{name}}' is defined but never used.";
64
68
  const ASSIGNED_MESSAGE = "'{{name}}' is assigned a value but never used.";
@@ -66,6 +70,7 @@ module.exports = {
66
70
  const config = {
67
71
  vars: "all",
68
72
  args: "after-used",
73
+ ignoreRestSiblings: false,
69
74
  caughtErrors: "none"
70
75
  };
71
76
 
@@ -77,6 +82,7 @@ module.exports = {
77
82
  } else {
78
83
  config.vars = firstOption.vars || config.vars;
79
84
  config.args = firstOption.args || config.args;
85
+ config.ignoreRestSiblings = firstOption.ignoreRestSiblings || config.ignoreRestSiblings;
80
86
  config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;
81
87
 
82
88
  if (firstOption.varsIgnorePattern) {
@@ -120,9 +126,33 @@ module.exports = {
120
126
  }
121
127
 
122
128
  return node.parent.type.indexOf("Export") === 0;
123
- } else {
124
- return false;
125
129
  }
130
+ return false;
131
+
132
+ }
133
+
134
+ /**
135
+ * Determines if a variable has a sibling rest property
136
+ * @param {Variable} variable - EScope variable object.
137
+ * @returns {boolean} True if the variable is exported, false if not.
138
+ * @private
139
+ */
140
+ function hasRestSpreadSibling(variable) {
141
+ if (config.ignoreRestSiblings) {
142
+ const restProperties = new Set(["ExperimentalRestProperty", "RestProperty"]);
143
+
144
+ return variable.defs
145
+ .filter(def => def.name.type === "Identifier")
146
+ .some(def => (
147
+ def.node.id &&
148
+ def.node.id.type === "ObjectPattern" &&
149
+ def.node.id.properties.length &&
150
+ restProperties.has(def.node.id.properties[def.node.id.properties.length - 1].type) && // last property is a rest property
151
+ !restProperties.has(def.name.parent.type) // variable is sibling of the rest property
152
+ ));
153
+ }
154
+
155
+ return false;
126
156
  }
127
157
 
128
158
  /**
@@ -495,7 +525,7 @@ module.exports = {
495
525
  }
496
526
  }
497
527
 
498
- if (!isUsedVariable(variable) && !isExported(variable)) {
528
+ if (!isUsedVariable(variable) && !isExported(variable) && !hasRestSpreadSibling(variable)) {
499
529
  unusedVars.push(variable);
500
530
  }
501
531
  }
@@ -537,23 +567,8 @@ module.exports = {
537
567
  */
538
568
  function getLocation(variable) {
539
569
  const comment = variable.eslintExplicitGlobalComment;
540
- const baseLoc = comment.loc.start;
541
- let column = getColumnInComment(variable, comment);
542
- const prefix = comment.value.slice(0, column);
543
- const lineInComment = (prefix.match(/\n/g) || []).length;
544
-
545
- if (lineInComment > 0) {
546
- column -= 1 + prefix.lastIndexOf("\n");
547
- } else {
548
-
549
- // 2 is for `/*`
550
- column += baseLoc.column + 2;
551
- }
552
570
 
553
- return {
554
- line: baseLoc.line + lineInComment,
555
- column
556
- };
571
+ return astUtils.getLocationFromRangeIndex(sourceCode, comment.range[0] + 2 + getColumnInComment(variable, comment));
557
572
  }
558
573
 
559
574
  //--------------------------------------------------------------------------
@@ -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
  //------------------------------------------------------------------------------
@@ -34,15 +40,14 @@ module.exports = {
34
40
  const key = node.key,
35
41
  nodeType = typeof key.value;
36
42
 
37
- if (key.type === "Literal" && (nodeType === "string" || nodeType === "number")) {
43
+ if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== "__proto__") {
38
44
  context.report({
39
45
  node,
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
  /**
@@ -213,7 +207,7 @@ module.exports = {
213
207
  scopeInfo = {
214
208
  upper: scopeInfo,
215
209
  uselessReturns: [],
216
- codePath,
210
+ codePath
217
211
  };
218
212
  },
219
213
 
@@ -226,7 +220,7 @@ module.exports = {
226
220
  message: "Unnecessary return statement.",
227
221
  fix(fixer) {
228
222
  return isRemovable(node) ? fixer.remove(node) : null;
229
- },
223
+ }
230
224
  });
231
225
  }
232
226
 
@@ -238,7 +232,7 @@ module.exports = {
238
232
  onCodePathSegmentStart(segment) {
239
233
  const info = {
240
234
  uselessReturns: getUselessReturns([], segment.allPrevSegments),
241
- returned: false,
235
+ returned: false
242
236
  };
243
237
 
244
238
  // Stores the info.
@@ -287,7 +281,7 @@ module.exports = {
287
281
  WithStatement: markReturnStatementsOnCurrentSegmentsAsUsed,
288
282
  ExportNamedDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
289
283
  ExportDefaultDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
290
- ExportAllDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed,
284
+ ExportAllDeclaration: markReturnStatementsOnCurrentSegmentsAsUsed
291
285
  };
292
286
  }
293
287
  };
@@ -128,6 +128,43 @@ function isUsedFromOutsideOf(scopeNode) {
128
128
  };
129
129
  }
130
130
 
131
+ /**
132
+ * Creates the predicate function which checks whether a variable has their references in TDZ.
133
+ *
134
+ * The predicate function would return `true`:
135
+ *
136
+ * - if a reference is before the declarator. E.g. (var a = b, b = 1;)(var {a = b, b} = {};)
137
+ * - if a reference is in the expression of their default value. E.g. (var {a = a} = {};)
138
+ * - if a reference is in the expression of their initializer. E.g. (var a = a;)
139
+ *
140
+ * @param {ASTNode} node - The initializer node of VariableDeclarator.
141
+ * @returns {Function} The predicate function.
142
+ * @private
143
+ */
144
+ function hasReferenceInTDZ(node) {
145
+ const initStart = node.range[0];
146
+ const initEnd = node.range[1];
147
+
148
+ return variable => {
149
+ const id = variable.defs[0].name;
150
+ const idStart = id.range[0];
151
+ const defaultValue = (id.parent.type === "AssignmentPattern" ? id.parent.right : null);
152
+ const defaultStart = defaultValue && defaultValue.range[0];
153
+ const defaultEnd = defaultValue && defaultValue.range[1];
154
+
155
+ return variable.references.some(reference => {
156
+ const start = reference.identifier.range[0];
157
+ const end = reference.identifier.range[1];
158
+
159
+ return !reference.init && (
160
+ start < idStart ||
161
+ (defaultValue !== null && start >= defaultStart && end <= defaultEnd) ||
162
+ (start >= initStart && end <= initEnd)
163
+ );
164
+ });
165
+ };
166
+ }
167
+
131
168
  //------------------------------------------------------------------------------
132
169
  // Rule Definition
133
170
  //------------------------------------------------------------------------------
@@ -147,6 +184,21 @@ module.exports = {
147
184
  create(context) {
148
185
  const sourceCode = context.getSourceCode();
149
186
 
187
+ /**
188
+ * Checks whether the variables which are defined by the given declarator node have their references in TDZ.
189
+ *
190
+ * @param {ASTNode} declarator - The VariableDeclarator node to check.
191
+ * @returns {boolean} `true` if one of the variables which are defined by the given declarator node have their references in TDZ.
192
+ */
193
+ function hasSelfReferenceInTDZ(declarator) {
194
+ if (!declarator.init) {
195
+ return false;
196
+ }
197
+ const variables = context.getDeclaredVariables(declarator);
198
+
199
+ return variables.some(hasReferenceInTDZ(declarator.init));
200
+ }
201
+
150
202
  /**
151
203
  * Checks whether it can fix a given variable declaration or not.
152
204
  * It cannot fix if the following cases:
@@ -156,6 +208,8 @@ module.exports = {
156
208
  * - A variable is used from outside the scope.
157
209
  * - A variable is used from a closure within a loop.
158
210
  * - A variable might be used before it is assigned within a loop.
211
+ * - A variable might be used in TDZ.
212
+ * - A variable is declared in statement position (e.g. a single-line `IfStatement`)
159
213
  *
160
214
  * ## A variable is declared on a SwitchCase node.
161
215
  *
@@ -201,8 +255,10 @@ module.exports = {
201
255
  const scopeNode = getScopeNode(node);
202
256
 
203
257
  if (node.parent.type === "SwitchCase" ||
204
- variables.some(isRedeclared) ||
205
- variables.some(isUsedFromOutsideOf(scopeNode))) {
258
+ node.declarations.some(hasSelfReferenceInTDZ) ||
259
+ variables.some(isRedeclared) ||
260
+ variables.some(isUsedFromOutsideOf(scopeNode))
261
+ ) {
206
262
  return false;
207
263
  }
208
264
 
@@ -215,6 +271,16 @@ module.exports = {
215
271
  }
216
272
  }
217
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
+
218
284
  return true;
219
285
  }
220
286
 
@@ -241,7 +307,7 @@ module.exports = {
241
307
  }
242
308
 
243
309
  return {
244
- VariableDeclaration(node) {
310
+ "VariableDeclaration:exit"(node) {
245
311
  if (node.kind === "var") {
246
312
  report(node);
247
313
  }