eslint 3.16.0 → 3.18.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 (59) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/conf/eslint-recommended.js +2 -0
  3. package/lib/ast-utils.js +3 -67
  4. package/lib/code-path-analysis/code-path-analyzer.js +2 -7
  5. package/lib/code-path-analysis/debug-helpers.js +17 -16
  6. package/lib/config/config-file.js +68 -38
  7. package/lib/eslint.js +5 -5
  8. package/lib/formatters/stylish.js +5 -4
  9. package/lib/ignored-paths.js +6 -0
  10. package/lib/internal-rules/internal-no-invalid-meta.js +2 -40
  11. package/lib/rules/array-callback-return.js +15 -5
  12. package/lib/rules/capitalized-comments.js +2 -1
  13. package/lib/rules/complexity.js +14 -8
  14. package/lib/rules/consistent-return.js +17 -10
  15. package/lib/rules/func-name-matching.js +18 -7
  16. package/lib/rules/func-names.js +20 -5
  17. package/lib/rules/keyword-spacing.js +19 -4
  18. package/lib/rules/line-comment-position.js +15 -5
  19. package/lib/rules/lines-around-comment.js +19 -0
  20. package/lib/rules/max-params.js +17 -4
  21. package/lib/rules/max-statements.js +11 -10
  22. package/lib/rules/no-compare-neg-zero.js +53 -0
  23. package/lib/rules/no-else-return.js +13 -1
  24. package/lib/rules/no-empty-function.js +9 -16
  25. package/lib/rules/no-extra-parens.js +64 -19
  26. package/lib/rules/no-extra-semi.js +13 -1
  27. package/lib/rules/no-global-assign.js +1 -1
  28. package/lib/rules/no-invalid-regexp.js +2 -1
  29. package/lib/rules/no-multiple-empty-lines.js +2 -4
  30. package/lib/rules/no-new-func.js +6 -8
  31. package/lib/rules/no-new.js +2 -6
  32. package/lib/rules/no-param-reassign.js +29 -6
  33. package/lib/rules/no-process-exit.js +2 -10
  34. package/lib/rules/no-restricted-properties.js +2 -0
  35. package/lib/rules/no-restricted-syntax.js +6 -22
  36. package/lib/rules/no-return-await.js +1 -1
  37. package/lib/rules/no-sync.js +8 -13
  38. package/lib/rules/no-unused-expressions.js +10 -1
  39. package/lib/rules/no-unused-vars.js +12 -12
  40. package/lib/rules/no-use-before-define.js +1 -1
  41. package/lib/rules/no-useless-escape.js +8 -2
  42. package/lib/rules/no-useless-return.js +13 -2
  43. package/lib/rules/nonblock-statement-body-position.js +114 -0
  44. package/lib/rules/object-shorthand.js +2 -1
  45. package/lib/rules/operator-assignment.js +1 -1
  46. package/lib/rules/padded-blocks.js +37 -28
  47. package/lib/rules/prefer-destructuring.js +1 -1
  48. package/lib/rules/semi.js +13 -1
  49. package/lib/rules/sort-vars.js +3 -5
  50. package/lib/rules/space-unary-ops.js +19 -1
  51. package/lib/rules/strict.js +8 -2
  52. package/lib/rules/yoda.js +2 -2
  53. package/lib/testers/rule-tester.js +44 -13
  54. package/lib/util/fix-tracker.js +121 -0
  55. package/lib/util/node-event-generator.js +274 -4
  56. package/lib/util/source-code-fixer.js +2 -2
  57. package/lib/util/source-code.js +99 -2
  58. package/lib/util/traverser.js +16 -25
  59. package/package.json +8 -8
@@ -6,6 +6,14 @@
6
6
 
7
7
  "use strict";
8
8
 
9
+ //------------------------------------------------------------------------------
10
+ // Requirements
11
+ //------------------------------------------------------------------------------
12
+
13
+ const lodash = require("lodash");
14
+
15
+ const astUtils = require("../ast-utils");
16
+
9
17
  //------------------------------------------------------------------------------
10
18
  // Rule Definition
11
19
  //------------------------------------------------------------------------------
@@ -81,17 +89,15 @@ module.exports = {
81
89
  * @private
82
90
  */
83
91
  function endFunction(node) {
92
+ const name = lodash.upperFirst(astUtils.getFunctionNameWithKind(node));
84
93
  const complexity = fns.pop();
85
- let name = "anonymous";
86
-
87
- if (node.id) {
88
- name = node.id.name;
89
- } else if (node.parent.type === "MethodDefinition" || node.parent.type === "Property") {
90
- name = node.parent.key.name;
91
- }
92
94
 
93
95
  if (complexity > THRESHOLD) {
94
- context.report({ node, message: "Function '{{name}}' has a complexity of {{complexity}}.", data: { name, complexity } });
96
+ context.report({
97
+ node,
98
+ message: "{{name}} has a complexity of {{complexity}}.",
99
+ data: { name, complexity }
100
+ });
95
101
  }
96
102
  }
97
103
 
@@ -8,6 +8,8 @@
8
8
  // Requirements
9
9
  //------------------------------------------------------------------------------
10
10
 
11
+ const lodash = require("lodash");
12
+
11
13
  const astUtils = require("../ast-utils");
12
14
 
13
15
  //------------------------------------------------------------------------------
@@ -81,7 +83,7 @@ module.exports = {
81
83
  * @returns {void}
82
84
  */
83
85
  function checkLastSegment(node) {
84
- let loc, type;
86
+ let loc, name;
85
87
 
86
88
  /*
87
89
  * Skip if it expected no return value or unreachable.
@@ -100,12 +102,11 @@ module.exports = {
100
102
 
101
103
  // The head of program.
102
104
  loc = { line: 1, column: 0 };
103
- type = "program";
105
+ name = "program";
104
106
  } else if (node.type === "ArrowFunctionExpression") {
105
107
 
106
108
  // `=>` token
107
109
  loc = context.getSourceCode().getTokenBefore(node.body, astUtils.isArrowToken).loc.start;
108
- type = "function";
109
110
  } else if (
110
111
  node.parent.type === "MethodDefinition" ||
111
112
  (node.parent.type === "Property" && node.parent.method)
@@ -113,33 +114,36 @@ module.exports = {
113
114
 
114
115
  // Method name.
115
116
  loc = node.parent.key.loc.start;
116
- type = "method";
117
117
  } else {
118
118
 
119
119
  // Function name or `function` keyword.
120
120
  loc = (node.id || node).loc.start;
121
- type = "function";
121
+ }
122
+
123
+ if (!name) {
124
+ name = astUtils.getFunctionNameWithKind(node);
122
125
  }
123
126
 
124
127
  // Reports.
125
128
  context.report({
126
129
  node,
127
130
  loc,
128
- message: "Expected to return a value at the end of this {{type}}.",
129
- data: { type }
131
+ message: "Expected to return a value at the end of {{name}}.",
132
+ data: { name }
130
133
  });
131
134
  }
132
135
 
133
136
  return {
134
137
 
135
138
  // Initializes/Disposes state of each code path.
136
- onCodePathStart(codePath) {
139
+ onCodePathStart(codePath, node) {
137
140
  funcInfo = {
138
141
  upper: funcInfo,
139
142
  codePath,
140
143
  hasReturn: false,
141
144
  hasReturnValue: false,
142
- message: ""
145
+ message: "",
146
+ node
143
147
  };
144
148
  },
145
149
  onCodePathEnd() {
@@ -158,8 +162,11 @@ module.exports = {
158
162
  if (!funcInfo.hasReturn) {
159
163
  funcInfo.hasReturn = true;
160
164
  funcInfo.hasReturnValue = hasReturnValue;
161
- funcInfo.message = "Expected {{which}} return value.";
165
+ funcInfo.message = "{{name}} expected {{which}} return value.";
162
166
  funcInfo.data = {
167
+ name: funcInfo.node.type === "Program"
168
+ ? "Program"
169
+ : lodash.upperFirst(astUtils.getFunctionNameWithKind(funcInfo.node)),
163
170
  which: hasReturnValue ? "a" : "no"
164
171
  };
165
172
  } else if (funcInfo.hasReturnValue !== hasReturnValue) {
@@ -132,6 +132,15 @@ module.exports = {
132
132
  });
133
133
  }
134
134
 
135
+ /**
136
+ * Determines whether a given node is a string literal
137
+ * @param {ASTNode} node The node to check
138
+ * @returns {boolean} `true` if the node is a string literal
139
+ */
140
+ function isStringLiteral(node) {
141
+ return node.type === "Literal" && typeof node.value === "string";
142
+ }
143
+
135
144
  //--------------------------------------------------------------------------
136
145
  // Public
137
146
  //--------------------------------------------------------------------------
@@ -139,7 +148,7 @@ module.exports = {
139
148
  return {
140
149
 
141
150
  VariableDeclarator(node) {
142
- if (!node.init || node.init.type !== "FunctionExpression") {
151
+ if (!node.init || node.init.type !== "FunctionExpression" || node.id.type !== "Identifier") {
143
152
  return;
144
153
  }
145
154
  if (node.init.id && shouldWarn(node.id.name, node.init.id.name)) {
@@ -148,10 +157,12 @@ module.exports = {
148
157
  },
149
158
 
150
159
  AssignmentExpression(node) {
151
- if (node.right.type !== "FunctionExpression" ||
152
- (node.left.computed && node.left.property.type !== "Literal") ||
153
- (!includeModuleExports && isModuleExports(node.left))
154
- ) {
160
+ if (
161
+ node.right.type !== "FunctionExpression" ||
162
+ (node.left.computed && node.left.property.type !== "Literal") ||
163
+ (!includeModuleExports && isModuleExports(node.left)) ||
164
+ (node.left.type !== "Identifier" && node.left.type !== "MemberExpression")
165
+ ) {
155
166
  return;
156
167
  }
157
168
 
@@ -164,13 +175,13 @@ module.exports = {
164
175
  },
165
176
 
166
177
  Property(node) {
167
- if (node.value.type !== "FunctionExpression" || !node.value.id || node.computed && node.key.type !== "Literal") {
178
+ if (node.value.type !== "FunctionExpression" || !node.value.id || node.computed && !isStringLiteral(node.key)) {
168
179
  return;
169
180
  }
170
181
  if (node.key.type === "Identifier" && shouldWarn(node.key.name, node.value.id.name)) {
171
182
  report(node, node.key.name, node.value.id.name, true);
172
183
  } else if (
173
- node.key.type === "Literal" &&
184
+ isStringLiteral(node.key) &&
174
185
  isIdentifier(node.key.value, ecmaVersion) &&
175
186
  shouldWarn(node.key.value, node.value.id.name)
176
187
  ) {
@@ -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
  * Checks whether or not a given variable is a function name.
10
16
  * @param {escope.Variable} variable - A variable to check.
@@ -82,15 +88,24 @@ module.exports = {
82
88
  return;
83
89
  }
84
90
 
85
- const name = node.id && node.id.name;
91
+ const hasName = Boolean(node.id && node.id.name);
92
+ const name = astUtils.getFunctionNameWithKind(node);
86
93
 
87
94
  if (never) {
88
- if (name) {
89
- context.report({ node, message: "Unexpected function expression name." });
95
+ if (hasName) {
96
+ context.report({
97
+ node,
98
+ message: "Unexpected named {{name}}.",
99
+ data: { name }
100
+ });
90
101
  }
91
102
  } else {
92
- if (!name && (asNeeded ? !hasInferredName(node) : !isObjectOrClassMethod(node))) {
93
- context.report({ node, message: "Missing function expression name." });
103
+ if (!hasName && (asNeeded ? !hasInferredName(node) : !isObjectOrClassMethod(node))) {
104
+ context.report({
105
+ node,
106
+ message: "Unexpected unnamed {{name}}.",
107
+ data: { name }
108
+ });
94
109
  }
95
110
  }
96
111
  }
@@ -359,7 +359,8 @@ module.exports = {
359
359
  const firstToken = node && sourceCode.getFirstToken(node);
360
360
 
361
361
  if (firstToken &&
362
- (firstToken.type === "Keyword" || firstToken.value === "async")
362
+ ((firstToken.type === "Keyword" && firstToken.value === "function") ||
363
+ firstToken.value === "async")
363
364
  ) {
364
365
  checkSpacingBefore(firstToken);
365
366
  }
@@ -495,11 +496,25 @@ module.exports = {
495
496
  node.value.async
496
497
  )
497
498
  ) {
498
- const token = sourceCode.getFirstToken(
499
- node,
500
- node.static ? 1 : 0
499
+ const token = sourceCode.getTokenBefore(
500
+ node.key,
501
+ tok => {
502
+ switch (tok.value) {
503
+ case "get":
504
+ case "set":
505
+ case "async":
506
+ return true;
507
+ default:
508
+ return false;
509
+ }
510
+ }
501
511
  );
502
512
 
513
+ if (!token) {
514
+ throw new Error("Failed to find token get, set, or async beside method name");
515
+ }
516
+
517
+
503
518
  checkSpacingAround(token);
504
519
  }
505
520
  }
@@ -4,6 +4,8 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ const astUtils = require("../ast-utils");
8
+
7
9
  //------------------------------------------------------------------------------
8
10
  // Rule Definition
9
11
  //------------------------------------------------------------------------------
@@ -33,6 +35,9 @@ module.exports = {
33
35
  },
34
36
  applyDefaultPatterns: {
35
37
  type: "boolean"
38
+ },
39
+ applyDefaultIgnorePatterns: {
40
+ type: "boolean"
36
41
  }
37
42
  },
38
43
  additionalProperties: false
@@ -43,12 +48,11 @@ module.exports = {
43
48
  },
44
49
 
45
50
  create(context) {
46
- const DEFAULT_IGNORE_PATTERN = "^\\s*(?:eslint|jshint\\s+|jslint\\s+|istanbul\\s+|globals?\\s+|exported\\s+|jscs|falls?\\s?through)";
47
51
  const options = context.options[0];
48
52
 
49
53
  let above,
50
54
  ignorePattern,
51
- applyDefaultPatterns = true;
55
+ applyDefaultIgnorePatterns = true;
52
56
 
53
57
  if (!options || typeof options === "string") {
54
58
  above = !options || options === "above";
@@ -56,10 +60,16 @@ module.exports = {
56
60
  } else {
57
61
  above = options.position === "above";
58
62
  ignorePattern = options.ignorePattern;
59
- applyDefaultPatterns = options.applyDefaultPatterns !== false;
63
+
64
+ if (options.hasOwnProperty("applyDefaultIgnorePatterns")) {
65
+ applyDefaultIgnorePatterns = options.applyDefaultIgnorePatterns !== false;
66
+ } else {
67
+ applyDefaultIgnorePatterns = options.applyDefaultPatterns !== false;
68
+ }
60
69
  }
61
70
 
62
- const defaultIgnoreRegExp = new RegExp(DEFAULT_IGNORE_PATTERN);
71
+ const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
72
+ const fallThroughRegExp = /^\s*falls?\s?through/;
63
73
  const customIgnoreRegExp = new RegExp(ignorePattern);
64
74
  const sourceCode = context.getSourceCode();
65
75
 
@@ -69,7 +79,7 @@ module.exports = {
69
79
 
70
80
  return {
71
81
  LineComment(node) {
72
- if (applyDefaultPatterns && defaultIgnoreRegExp.test(node.value)) {
82
+ if (applyDefaultIgnorePatterns && (defaultIgnoreRegExp.test(node.value) || fallThroughRegExp.test(node.value))) {
73
83
  return;
74
84
  }
75
85
 
@@ -93,6 +93,12 @@ module.exports = {
93
93
  },
94
94
  allowArrayEnd: {
95
95
  type: "boolean"
96
+ },
97
+ ignorePattern: {
98
+ type: "string"
99
+ },
100
+ applyDefaultIgnorePatterns: {
101
+ type: "boolean"
96
102
  }
97
103
  },
98
104
  additionalProperties: false
@@ -103,6 +109,11 @@ module.exports = {
103
109
  create(context) {
104
110
 
105
111
  const options = context.options[0] ? Object.assign({}, context.options[0]) : {};
112
+ const ignorePattern = options.ignorePattern;
113
+ const defaultIgnoreRegExp = astUtils.COMMENTS_IGNORE_PATTERN;
114
+ const customIgnoreRegExp = new RegExp(ignorePattern);
115
+ const applyDefaultIgnorePatterns = options.applyDefaultIgnorePatterns !== false;
116
+
106
117
 
107
118
  options.beforeLineComment = options.beforeLineComment || false;
108
119
  options.afterLineComment = options.afterLineComment || false;
@@ -270,6 +281,14 @@ module.exports = {
270
281
  * @returns {void}
271
282
  */
272
283
  function checkForEmptyLine(node, opts) {
284
+ if (applyDefaultIgnorePatterns && defaultIgnoreRegExp.test(node.value)) {
285
+ return;
286
+ }
287
+
288
+ if (ignorePattern && customIgnoreRegExp.test(node.value)) {
289
+ return;
290
+ }
291
+
273
292
  let after = opts.after,
274
293
  before = opts.before;
275
294
 
@@ -5,6 +5,14 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const lodash = require("lodash");
13
+
14
+ const astUtils = require("../ast-utils");
15
+
8
16
  //------------------------------------------------------------------------------
9
17
  // Rule Definition
10
18
  //------------------------------------------------------------------------------
@@ -66,10 +74,15 @@ module.exports = {
66
74
  */
67
75
  function checkFunction(node) {
68
76
  if (node.params.length > numParams) {
69
- context.report({ node, message: "This function has too many parameters ({{count}}). Maximum allowed is {{max}}.", data: {
70
- count: node.params.length,
71
- max: numParams
72
- } });
77
+ context.report({
78
+ node,
79
+ message: "{{name}} has too many parameters ({{count}}). Maximum allowed is {{max}}.",
80
+ data: {
81
+ name: lodash.upperFirst(astUtils.getFunctionNameWithKind(node)),
82
+ count: node.params.length,
83
+ max: numParams
84
+ }
85
+ });
73
86
  }
74
87
  }
75
88
 
@@ -5,6 +5,14 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const lodash = require("lodash");
13
+
14
+ const astUtils = require("../ast-utils");
15
+
8
16
  //------------------------------------------------------------------------------
9
17
  // Rule Definition
10
18
  //------------------------------------------------------------------------------
@@ -84,19 +92,12 @@ module.exports = {
84
92
  */
85
93
  function reportIfTooManyStatements(node, count, max) {
86
94
  if (count > max) {
87
- const messageEnd = " has too many statements ({{count}}). Maximum allowed is {{max}}.";
88
- let name = "This function";
89
-
90
- if (node.id) {
91
- name = `Function '${node.id.name}'`;
92
- } else if (node.parent.type === "MethodDefinition" || node.parent.type === "Property") {
93
- name = `Function '${context.getSource(node.parent.key)}'`;
94
- }
95
+ const name = lodash.upperFirst(astUtils.getFunctionNameWithKind(node));
95
96
 
96
97
  context.report({
97
98
  node,
98
- message: name + messageEnd,
99
- data: { count, max }
99
+ message: "{{name}} has too many statements ({{count}}). Maximum allowed is {{max}}.",
100
+ data: { name, count, max }
100
101
  });
101
102
  }
102
103
  }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @fileoverview The rule should warn against code that tries to compare against -0.
3
+ * @author Aladdin-ADD <hh_2013@foxmail.com>
4
+ */
5
+ "use strict";
6
+
7
+ //------------------------------------------------------------------------------
8
+ // Rule Definition
9
+ //------------------------------------------------------------------------------
10
+
11
+ module.exports = {
12
+ meta: {
13
+ docs: {
14
+ description: "disallow comparing against -0",
15
+ category: "Possible Errors",
16
+ recommended: false
17
+ },
18
+ fixable: null,
19
+ schema: []
20
+ },
21
+
22
+ create(context) {
23
+
24
+ //--------------------------------------------------------------------------
25
+ // Helpers
26
+ //--------------------------------------------------------------------------
27
+
28
+ /**
29
+ * Checks a given node is -0
30
+ *
31
+ * @param {ASTNode} node - A node to check.
32
+ * @returns {boolean} `true` if the node is -0.
33
+ */
34
+ function isNegZero(node) {
35
+ return node.type === "UnaryExpression" && node.operator === "-" && node.argument.type === "Literal" && node.argument.value === 0;
36
+ }
37
+ const OPERATORS_TO_CHECK = new Set([">", ">=", "<", "<=", "==", "===", "!=", "!=="]);
38
+
39
+ return {
40
+ BinaryExpression(node) {
41
+ if (OPERATORS_TO_CHECK.has(node.operator)) {
42
+ if (isNegZero(node.left) || isNegZero(node.right)) {
43
+ context.report({
44
+ node,
45
+ message: "Do not use the '{{operator}}' operator to compare against -0.",
46
+ data: { operator: node.operator }
47
+ });
48
+ }
49
+ }
50
+ }
51
+ };
52
+ }
53
+ };
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const FixTracker = require("../util/fix-tracker");
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Rule Definition
10
16
  //------------------------------------------------------------------------------
@@ -86,7 +92,13 @@ module.exports = {
86
92
  } else {
87
93
  fixedSource = source;
88
94
  }
89
- return fixer.replaceTextRange([elseToken.start, node.end], fixedSource);
95
+
96
+ // Extend the replacement range to include the entire
97
+ // function to avoid conflicting with no-useless-return.
98
+ // https://github.com/eslint/eslint/issues/8026
99
+ return new FixTracker(fixer, sourceCode)
100
+ .retainEnclosingFunction(node)
101
+ .replaceTextRange([elseToken.start, node.end], fixedSource);
90
102
  }
91
103
  });
92
104
  }
@@ -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
  // Helpers
10
16
  //------------------------------------------------------------------------------
@@ -19,18 +25,6 @@ const ALLOW_OPTIONS = Object.freeze([
19
25
  "setters",
20
26
  "constructors"
21
27
  ]);
22
- const SHOW_KIND = Object.freeze({
23
- functions: "function",
24
- arrowFunctions: "arrow function",
25
- generatorFunctions: "generator function",
26
- asyncFunctions: "async function",
27
- methods: "method",
28
- generatorMethods: "generator method",
29
- asyncMethods: "async method",
30
- getters: "getter",
31
- setters: "setter",
32
- constructors: "constructor"
33
- });
34
28
 
35
29
  /**
36
30
  * Gets the kind of a given function node.
@@ -137,6 +131,7 @@ module.exports = {
137
131
  */
138
132
  function reportIfEmpty(node) {
139
133
  const kind = getKind(node);
134
+ const name = astUtils.getFunctionNameWithKind(node);
140
135
 
141
136
  if (allowed.indexOf(kind) === -1 &&
142
137
  node.body.type === "BlockStatement" &&
@@ -146,10 +141,8 @@ module.exports = {
146
141
  context.report({
147
142
  node,
148
143
  loc: node.body.loc.start,
149
- message: "Unexpected empty {{kind}}.",
150
- data: {
151
- kind: SHOW_KIND[kind]
152
- }
144
+ message: "Unexpected empty {{name}}.",
145
+ data: { name }
153
146
  });
154
147
  }
155
148
  }