eslint 2.11.0 → 2.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +24 -0
  3. package/conf/eslint-all.js +29 -0
  4. package/conf/eslint.json +10 -6
  5. package/lib/ast-utils.js +91 -0
  6. package/lib/config/config-file.js +9 -4
  7. package/lib/config/config-ops.js +27 -2
  8. package/lib/eslint.js +34 -15
  9. package/lib/file-finder.js +3 -59
  10. package/lib/ignored-paths.js +38 -4
  11. package/lib/options.js +1 -1
  12. package/lib/rules/accessor-pairs.js +1 -1
  13. package/lib/rules/array-bracket-spacing.js +1 -1
  14. package/lib/rules/arrow-body-style.js +57 -15
  15. package/lib/rules/callback-return.js +25 -3
  16. package/lib/rules/default-case.js +1 -1
  17. package/lib/rules/eqeqeq.js +1 -1
  18. package/lib/rules/func-names.js +15 -4
  19. package/lib/rules/max-len.js +3 -2
  20. package/lib/rules/max-lines.js +148 -0
  21. package/lib/rules/max-statements-per-line.js +1 -1
  22. package/lib/rules/newline-per-chained-call.js +16 -1
  23. package/lib/rules/no-extra-parens.js +1 -92
  24. package/lib/rules/no-extra-semi.js +10 -1
  25. package/lib/rules/no-mixed-operators.js +212 -0
  26. package/lib/rules/no-multiple-empty-lines.js +40 -9
  27. package/lib/rules/no-prototype-builtins.js +1 -1
  28. package/lib/rules/no-script-url.js +1 -1
  29. package/lib/rules/no-unsafe-finally.js +1 -1
  30. package/lib/rules/no-useless-rename.js +11 -3
  31. package/lib/rules/object-curly-newline.js +209 -0
  32. package/lib/rules/object-shorthand.js +75 -5
  33. package/lib/rules/one-var.js +3 -0
  34. package/lib/rules/padded-blocks.js +19 -1
  35. package/lib/rules/rest-spread-spacing.js +107 -0
  36. package/lib/rules/unicode-bom.js +1 -1
  37. package/lib/util/glob-util.js +2 -1
  38. package/package.json +4 -2
@@ -16,16 +16,45 @@ module.exports = {
16
16
  recommended: false
17
17
  },
18
18
 
19
- schema: [
20
- {
21
- enum: ["always", "as-needed"]
22
- }
23
- ]
19
+ schema: {
20
+ anyOf: [
21
+ {
22
+ type: "array",
23
+ items: [
24
+ {
25
+ enum: ["always", "never"]
26
+ }
27
+ ],
28
+ minItems: 0,
29
+ maxItems: 1
30
+ },
31
+ {
32
+ type: "array",
33
+ items: [
34
+ {
35
+ enum: ["as-needed"]
36
+ },
37
+ {
38
+ type: "object",
39
+ properties: {
40
+ requireReturnForObjectLiteral: {type: "boolean"}
41
+ },
42
+ additionalProperties: false
43
+ }
44
+ ],
45
+ minItems: 0,
46
+ maxItems: 2
47
+ }
48
+ ]
49
+ }
24
50
  },
25
51
 
26
52
  create: function(context) {
27
- var always = context.options[0] === "always";
28
- var asNeeded = !context.options[0] || context.options[0] === "as-needed";
53
+ var options = context.options;
54
+ var always = options[0] === "always";
55
+ var asNeeded = !options[0] || options[0] === "as-needed";
56
+ var never = options[0] === "never";
57
+ var requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral;
29
58
 
30
59
  /**
31
60
  * Determines whether a arrow function body needs braces
@@ -36,21 +65,34 @@ module.exports = {
36
65
  var arrowBody = node.body;
37
66
 
38
67
  if (arrowBody.type === "BlockStatement") {
39
- var blockBody = arrowBody.body;
40
-
41
- if (blockBody.length !== 1) {
42
- return;
43
- }
44
-
45
- if (asNeeded && blockBody[0].type === "ReturnStatement") {
68
+ if (never) {
46
69
  context.report({
47
70
  node: node,
48
71
  loc: arrowBody.loc.start,
49
72
  message: "Unexpected block statement surrounding arrow body."
50
73
  });
74
+ } else {
75
+ var blockBody = arrowBody.body;
76
+
77
+ if (blockBody.length !== 1) {
78
+ return;
79
+ }
80
+
81
+ if (asNeeded && requireReturnForObjectLiteral && blockBody[0].type === "ReturnStatement" &&
82
+ blockBody[0].argument.type === "ObjectExpression") {
83
+ return;
84
+ }
85
+
86
+ if (asNeeded && blockBody[0].type === "ReturnStatement") {
87
+ context.report({
88
+ node: node,
89
+ loc: arrowBody.loc.start,
90
+ message: "Unexpected block statement surrounding arrow body."
91
+ });
92
+ }
51
93
  }
52
94
  } else {
53
- if (always) {
95
+ if (always || (asNeeded && requireReturnForObjectLiteral && arrowBody.type === "ObjectExpression")) {
54
96
  context.report({
55
97
  node: node,
56
98
  loc: arrowBody.loc.start,
@@ -24,7 +24,8 @@ module.exports = {
24
24
 
25
25
  create: function(context) {
26
26
 
27
- var callbacks = context.options[0] || ["callback", "cb", "next"];
27
+ var callbacks = context.options[0] || ["callback", "cb", "next"],
28
+ sourceCode = context.getSourceCode();
28
29
 
29
30
  //--------------------------------------------------------------------------
30
31
  // Helpers
@@ -46,13 +47,34 @@ module.exports = {
46
47
  return node.parent;
47
48
  }
48
49
 
50
+ /**
51
+ * Check to see if a node contains only identifers
52
+ * @param {ASTNode} node The node to check
53
+ * @returns {Boolean} Whether or not the node contains only identifers
54
+ */
55
+ function containsOnlyIdentifiers(node) {
56
+ if (node.type === "Identifier") {
57
+ return true;
58
+ }
59
+
60
+ if (node.type === "MemberExpression") {
61
+ if (node.object.type === "Identifier") {
62
+ return true;
63
+ } else if (node.object.type === "MemberExpression") {
64
+ return containsOnlyIdentifiers(node.object);
65
+ }
66
+ }
67
+
68
+ return false;
69
+ }
70
+
49
71
  /**
50
72
  * Check to see if a CallExpression is in our callback list.
51
73
  * @param {ASTNode} node The node to check against our callback names list.
52
74
  * @returns {Boolean} Whether or not this function matches our callback name.
53
75
  */
54
76
  function isCallback(node) {
55
- return node.callee.type === "Identifier" && callbacks.indexOf(node.callee.name) > -1;
77
+ return containsOnlyIdentifiers(node.callee) && callbacks.indexOf(sourceCode.getText(node.callee)) > -1;
56
78
  }
57
79
 
58
80
  /**
@@ -90,7 +112,7 @@ module.exports = {
90
112
  return {
91
113
  CallExpression: function(node) {
92
114
 
93
- // if we"re not a callback we can return
115
+ // if we're not a callback we can return
94
116
  if (!isCallback(node)) {
95
117
  return;
96
118
  }
@@ -13,7 +13,7 @@ var DEFAULT_COMMENT_PATTERN = /^no default$/;
13
13
  module.exports = {
14
14
  meta: {
15
15
  docs: {
16
- description: "require `default` cases in <code>switch</code> statements",
16
+ description: "require `default` cases in `switch` statements",
17
17
  category: "Best Practices",
18
18
  recommended: false
19
19
  },
@@ -19,7 +19,7 @@ module.exports = {
19
19
 
20
20
  schema: [
21
21
  {
22
- enum: ["smart", "allow-null"]
22
+ enum: ["always", "smart", "allow-null"]
23
23
  }
24
24
  ]
25
25
  },
@@ -12,15 +12,20 @@
12
12
  module.exports = {
13
13
  meta: {
14
14
  docs: {
15
- description: "enforce named `function` expressions",
15
+ description: "require or disallow named `function` expressions",
16
16
  category: "Stylistic Issues",
17
17
  recommended: false
18
18
  },
19
19
 
20
- schema: []
20
+ schema: [
21
+ {
22
+ enum: ["always", "never"]
23
+ }
24
+ ]
21
25
  },
22
26
 
23
27
  create: function(context) {
28
+ var never = context.options[0] === "never";
24
29
 
25
30
  /**
26
31
  * Determines whether the current FunctionExpression node is a get, set, or
@@ -44,8 +49,14 @@ module.exports = {
44
49
 
45
50
  var name = node.id && node.id.name;
46
51
 
47
- if (!name && !isObjectOrClassMethod()) {
48
- context.report(node, "Missing function expression name.");
52
+ if (never) {
53
+ if (name) {
54
+ context.report(node, "Unexpected function expression name.");
55
+ }
56
+ } else {
57
+ if (!name && !isObjectOrClassMethod()) {
58
+ context.report(node, "Missing function expression name.");
59
+ }
49
60
  }
50
61
  }
51
62
  };
@@ -157,10 +157,11 @@ module.exports = {
157
157
  */
158
158
  function isFullLineComment(line, lineNumber, comment) {
159
159
  var start = comment.loc.start,
160
- end = comment.loc.end;
160
+ end = comment.loc.end,
161
+ isFirstTokenOnLine = !line.slice(0, comment.loc.start.column).trim();
161
162
 
162
163
  return comment &&
163
- (start.line < lineNumber || (start.line === lineNumber && start.column === 0)) &&
164
+ (start.line < lineNumber || (start.line === lineNumber && isFirstTokenOnLine)) &&
164
165
  (end.line > lineNumber || end.column === line.length);
165
166
  }
166
167
 
@@ -0,0 +1,148 @@
1
+ /**
2
+ * @fileoverview enforce a maximum file length
3
+ * @author Alberto Rodríguez
4
+ */
5
+ "use strict";
6
+
7
+ //------------------------------------------------------------------------------
8
+ // Requirements
9
+ //------------------------------------------------------------------------------
10
+
11
+ var lodash = require("lodash");
12
+ var astUtils = require("../ast-utils");
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Rule Definition
16
+ //------------------------------------------------------------------------------
17
+
18
+ module.exports = {
19
+ meta: {
20
+ docs: {
21
+ description: "enforce a maximum number of lines per file",
22
+ category: "Stylistic Issues",
23
+ recommended: false
24
+ },
25
+
26
+ schema: [
27
+ {
28
+ oneOf: [
29
+ {
30
+ type: "integer",
31
+ minimum: 0
32
+ },
33
+ {
34
+ type: "object",
35
+ properties: {
36
+ max: {
37
+ type: "integer",
38
+ minimum: 0
39
+ },
40
+ skipComments: {
41
+ type: "boolean"
42
+ },
43
+ skipBlankLines: {
44
+ type: "boolean"
45
+ }
46
+ },
47
+ additionalProperties: false
48
+ }
49
+ ]
50
+ }
51
+ ]
52
+ },
53
+
54
+ create: function(context) {
55
+ var option = context.options[0],
56
+ max = 300;
57
+
58
+ if (typeof option === "object" && option.hasOwnProperty("max") && typeof option.max === "number") {
59
+ max = option.max;
60
+ }
61
+
62
+ if (typeof option === "number") {
63
+ max = option;
64
+ }
65
+
66
+ var skipComments = option && option.skipComments;
67
+ var skipBlankLines = option && option.skipBlankLines;
68
+
69
+ var sourceCode = context.getSourceCode();
70
+
71
+ /**
72
+ * Returns whether or not a token is a comment node type
73
+ * @param {Token} token The token to check
74
+ * @returns {boolean} True if the token is a comment node
75
+ */
76
+ function isCommentNodeType(token) {
77
+ return token && (token.type === "Block" || token.type === "Line");
78
+ }
79
+
80
+ /**
81
+ * Returns the line numbers of a comment that don't have any code on the same line
82
+ * @param {Node} comment The comment node to check
83
+ * @returns {int[]} The line numbers
84
+ */
85
+ function getLinesWithoutCode(comment) {
86
+ var start = comment.loc.start.line;
87
+ var end = comment.loc.end.line;
88
+
89
+ var token;
90
+
91
+ token = comment;
92
+ do {
93
+ token = sourceCode.getTokenOrCommentBefore(token);
94
+ } while (isCommentNodeType(token));
95
+
96
+ if (token && astUtils.isTokenOnSameLine(token, comment)) {
97
+ start += 1;
98
+ }
99
+
100
+ token = comment;
101
+ do {
102
+ token = sourceCode.getTokenOrCommentAfter(token);
103
+ } while (isCommentNodeType(token));
104
+
105
+ if (token && astUtils.isTokenOnSameLine(comment, token)) {
106
+ end -= 1;
107
+ }
108
+
109
+ if (start <= end) {
110
+ return lodash.range(start, end + 1);
111
+ }
112
+ return [];
113
+ }
114
+
115
+ return {
116
+ "Program:exit": function() {
117
+ var lines = sourceCode.lines.map(function(text, i) {
118
+ return { lineNumber: i + 1, text: text };
119
+ });
120
+
121
+ if (skipBlankLines) {
122
+ lines = lines.filter(function(l) {
123
+ return l.text.trim() !== "";
124
+ });
125
+ }
126
+
127
+ if (skipComments) {
128
+ var comments = sourceCode.getAllComments();
129
+
130
+ var commentLines = lodash.flatten(comments.map(function(comment) {
131
+ return getLinesWithoutCode(comment);
132
+ }));
133
+
134
+ lines = lines.filter(function(l) {
135
+ return !lodash.includes(commentLines, l.lineNumber);
136
+ });
137
+ }
138
+
139
+ if (lines.length > max) {
140
+ context.report({
141
+ loc: { line: 1, column: 0 },
142
+ message: "File must be at most " + max + " lines long"
143
+ });
144
+ }
145
+ }
146
+ };
147
+ }
148
+ };
@@ -43,7 +43,7 @@ module.exports = {
43
43
  // Helpers
44
44
  //--------------------------------------------------------------------------
45
45
 
46
- var SINGLE_CHILD_ALLOWED = /^(?:DoWhile|For|ForIn|ForOf|If|Labeled|While)Statement$/;
46
+ var SINGLE_CHILD_ALLOWED = /^(?:(?:DoWhile|For|ForIn|ForOf|If|Labeled|While)Statement|Export(?:Default|Named)Declaration)$/;
47
47
 
48
48
  /**
49
49
  * Gets the actual last token of a given node.
@@ -38,6 +38,21 @@ module.exports = {
38
38
 
39
39
  var sourceCode = context.getSourceCode();
40
40
 
41
+ /**
42
+ * Gets the property text of a given MemberExpression node.
43
+ * If the text is multiline, this returns only the first line.
44
+ *
45
+ * @param {ASTNode} node - A MemberExpression node to get.
46
+ * @returns {string} The property text of the node.
47
+ */
48
+ function getPropertyText(node) {
49
+ var prefix = node.computed ? "[" : ".";
50
+ var lines = sourceCode.getText(node.property).split(/\r\n|\r|\n/g);
51
+ var suffix = node.computed && lines.length === 1 ? "]" : "";
52
+
53
+ return prefix + lines[0] + suffix;
54
+ }
55
+
41
56
  return {
42
57
  "CallExpression:exit": function(node) {
43
58
  if (!node.callee || node.callee.type !== "MemberExpression") {
@@ -57,7 +72,7 @@ module.exports = {
57
72
  context.report(
58
73
  callee.property,
59
74
  callee.property.loc.start,
60
- "Expected line break after `" + sourceCode.getText(callee.object).replace(/\r\n|\r|\n/g, "\\n") + "`."
75
+ "Expected line break before `" + getPropertyText(callee) + "`."
61
76
  );
62
77
  }
63
78
  }
@@ -57,6 +57,7 @@ module.exports = {
57
57
  var sourceCode = context.getSourceCode();
58
58
 
59
59
  var isParenthesised = astUtils.isParenthesised.bind(astUtils, sourceCode);
60
+ var precedence = astUtils.getPrecedence;
60
61
  var ALL_NODES = context.options[0] !== "functions";
61
62
  var EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false;
62
63
  var NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false;
@@ -255,98 +256,6 @@ module.exports = {
255
256
  throw new Error("unreachable");
256
257
  }
257
258
 
258
- /**
259
- * Get the precedence level based on the node type
260
- * @param {ASTNode} node node to evaluate
261
- * @returns {int} precedence level
262
- * @private
263
- */
264
- function precedence(node) {
265
-
266
- switch (node.type) {
267
- case "SequenceExpression":
268
- return 0;
269
-
270
- case "AssignmentExpression":
271
- case "ArrowFunctionExpression":
272
- case "YieldExpression":
273
- return 1;
274
-
275
- case "ConditionalExpression":
276
- return 3;
277
-
278
- case "LogicalExpression":
279
- switch (node.operator) {
280
- case "||":
281
- return 4;
282
- case "&&":
283
- return 5;
284
-
285
- // no default
286
- }
287
-
288
- /* falls through */
289
-
290
- case "BinaryExpression":
291
-
292
- switch (node.operator) {
293
- case "|":
294
- return 6;
295
- case "^":
296
- return 7;
297
- case "&":
298
- return 8;
299
- case "==":
300
- case "!=":
301
- case "===":
302
- case "!==":
303
- return 9;
304
- case "<":
305
- case "<=":
306
- case ">":
307
- case ">=":
308
- case "in":
309
- case "instanceof":
310
- return 10;
311
- case "<<":
312
- case ">>":
313
- case ">>>":
314
- return 11;
315
- case "+":
316
- case "-":
317
- return 12;
318
- case "*":
319
- case "/":
320
- case "%":
321
- return 13;
322
-
323
- // no default
324
- }
325
-
326
- /* falls through */
327
-
328
- case "UnaryExpression":
329
- return 14;
330
-
331
- case "UpdateExpression":
332
- return 15;
333
-
334
- case "CallExpression":
335
-
336
- // IIFE is allowed to have parens in any position (#655)
337
- if (node.callee.type === "FunctionExpression") {
338
- return -1;
339
- }
340
- return 16;
341
-
342
- case "NewExpression":
343
- return 17;
344
-
345
- // no default
346
- }
347
- return 18;
348
- }
349
-
350
259
  /**
351
260
  * Report the node
352
261
  * @param {ASTNode} node node to evaluate
@@ -66,7 +66,16 @@ module.exports = {
66
66
  */
67
67
  EmptyStatement: function(node) {
68
68
  var parent = node.parent,
69
- allowedParentTypes = ["ForStatement", "ForInStatement", "ForOfStatement", "WhileStatement", "DoWhileStatement"];
69
+ allowedParentTypes = [
70
+ "ForStatement",
71
+ "ForInStatement",
72
+ "ForOfStatement",
73
+ "WhileStatement",
74
+ "DoWhileStatement",
75
+ "IfStatement",
76
+ "LabeledStatement",
77
+ "WithStatement"
78
+ ];
70
79
 
71
80
  if (allowedParentTypes.indexOf(parent.type) === -1) {
72
81
  report(node);