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
@@ -0,0 +1,212 @@
1
+ /**
2
+ * @fileoverview Rule to disallow mixed binary operators.
3
+ * @author Toru Nagashima
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ var astUtils = require("../ast-utils.js");
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Helpers
16
+ //------------------------------------------------------------------------------
17
+
18
+ var ARITHMETIC_OPERATORS = ["+", "-", "*", "/", "%", "**"];
19
+ var BITWISE_OPERATORS = ["&", "|", "^", "~", "<<", ">>", ">>>"];
20
+ var COMPARISON_OPERATORS = ["==", "!=", "===", "!==", ">", ">=", "<", "<="];
21
+ var LOGICAL_OPERATORS = ["&&", "||"];
22
+ var RELATIONAL_OPERATORS = ["in", "instanceof"];
23
+ var ALL_OPERATORS = [].concat(
24
+ ARITHMETIC_OPERATORS,
25
+ BITWISE_OPERATORS,
26
+ COMPARISON_OPERATORS,
27
+ LOGICAL_OPERATORS,
28
+ RELATIONAL_OPERATORS
29
+ );
30
+ var DEFAULT_GROUPS = [
31
+ ARITHMETIC_OPERATORS,
32
+ BITWISE_OPERATORS,
33
+ COMPARISON_OPERATORS,
34
+ LOGICAL_OPERATORS,
35
+ RELATIONAL_OPERATORS
36
+ ];
37
+ var TARGET_NODE_TYPE = /^(?:Binary|Logical)Expression$/;
38
+
39
+ /**
40
+ * Normalizes options.
41
+ *
42
+ * @param {object|undefined} options - A options object to normalize.
43
+ * @returns {object} Normalized option object.
44
+ */
45
+ function normalizeOptions(options) {
46
+ var hasGroups = (options && options.groups && options.groups.length > 0);
47
+ var groups = hasGroups ? options.groups : DEFAULT_GROUPS;
48
+ var allowSamePrecedence = (options && options.allowSamePrecedence) !== false;
49
+
50
+ return {
51
+ groups: groups,
52
+ allowSamePrecedence: allowSamePrecedence
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Checks whether any group which includes both given operator exists or not.
58
+ *
59
+ * @param {Array.<string[]>} groups - A list of groups to check.
60
+ * @param {string} left - An operator.
61
+ * @param {string} right - Another operator.
62
+ * @returns {boolean} `true` if such group existed.
63
+ */
64
+ function includesBothInAGroup(groups, left, right) {
65
+ return groups.some(function(group) {
66
+ return group.indexOf(left) !== -1 && group.indexOf(right) !== -1;
67
+ });
68
+ }
69
+
70
+ //------------------------------------------------------------------------------
71
+ // Rule Definition
72
+ //------------------------------------------------------------------------------
73
+
74
+ module.exports = {
75
+ meta: {
76
+ docs: {
77
+ description: "disallow mixed binary operators",
78
+ category: "Stylistic Issues",
79
+ recommended: false
80
+ },
81
+ schema: [
82
+ {
83
+ type: "object",
84
+ properties: {
85
+ groups: {
86
+ type: "array",
87
+ items: {
88
+ type: "array",
89
+ items: {enum: ALL_OPERATORS},
90
+ minItems: 2,
91
+ uniqueItems: true
92
+ },
93
+ uniqueItems: true
94
+ },
95
+ allowSamePrecedence: {
96
+ type: "boolean"
97
+ }
98
+ },
99
+ additionalProperties: false
100
+ }
101
+ ]
102
+ },
103
+
104
+ create: function(context) {
105
+ var sourceCode = context.getSourceCode();
106
+ var options = normalizeOptions(context.options[0]);
107
+
108
+ /**
109
+ * Checks whether a given node should be ignored by options or not.
110
+ *
111
+ * @param {ASTNode} node - A node to check. This is a BinaryExpression
112
+ * node or a LogicalExpression node. This parent node is one of
113
+ * them, too.
114
+ * @returns {boolean} `true` if the node should be ignored.
115
+ */
116
+ function shouldIgnore(node) {
117
+ var a = node;
118
+ var b = node.parent;
119
+
120
+ return (
121
+ !includesBothInAGroup(options.groups, a.operator, b.operator) ||
122
+ (
123
+ options.allowSamePrecedence &&
124
+ astUtils.getPrecedence(a) === astUtils.getPrecedence(b)
125
+ )
126
+ );
127
+ }
128
+
129
+ /**
130
+ * Checks whether the operator of a given node is mixed with parent
131
+ * node's operator or not.
132
+ *
133
+ * @param {ASTNode} node - A node to check. This is a BinaryExpression
134
+ * node or a LogicalExpression node. This parent node is one of
135
+ * them, too.
136
+ * @returns {boolean} `true` if the node was mixed.
137
+ */
138
+ function isMixedWithParent(node) {
139
+ return (
140
+ node.operator !== node.parent.operator &&
141
+ !astUtils.isParenthesised(sourceCode, node)
142
+ );
143
+ }
144
+
145
+ /**
146
+ * Gets the operator token of a given node.
147
+ *
148
+ * @param {ASTNode} node - A node to check. This is a BinaryExpression
149
+ * node or a LogicalExpression node.
150
+ * @returns {Token} The operator token of the node.
151
+ */
152
+ function getOperatorToken(node) {
153
+ var token = sourceCode.getTokenAfter(node.left);
154
+
155
+ while (token.value === ")") {
156
+ token = sourceCode.getTokenAfter(token);
157
+ }
158
+
159
+ return token;
160
+ }
161
+
162
+ /**
163
+ * Reports both the operator of a given node and the operator of the
164
+ * parent node.
165
+ *
166
+ * @param {ASTNode} node - A node to check. This is a BinaryExpression
167
+ * node or a LogicalExpression node. This parent node is one of
168
+ * them, too.
169
+ * @returns {void}
170
+ */
171
+ function reportBothOperators(node) {
172
+ var parent = node.parent;
173
+ var left = (parent.left === node) ? node : parent;
174
+ var right = (parent.left !== node) ? node : parent;
175
+ var message =
176
+ "Unexpected mix of '" + left.operator + "' and '" +
177
+ right.operator + "'.";
178
+
179
+ context.report({
180
+ node: left,
181
+ loc: getOperatorToken(left).loc.start,
182
+ message: message
183
+ });
184
+ context.report({
185
+ node: right,
186
+ loc: getOperatorToken(right).loc.start,
187
+ message: message
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Checks between the operator of this node and the operator of the
193
+ * parent node.
194
+ *
195
+ * @param {ASTNode} node - A node to check.
196
+ * @returns {void}
197
+ */
198
+ function check(node) {
199
+ if (TARGET_NODE_TYPE.test(node.parent.type) &&
200
+ isMixedWithParent(node) &&
201
+ !shouldIgnore(node)
202
+ ) {
203
+ reportBothOperators(node);
204
+ }
205
+ }
206
+
207
+ return {
208
+ BinaryExpression: check,
209
+ LogicalExpression: check
210
+ };
211
+ }
212
+ };
@@ -17,6 +17,8 @@ module.exports = {
17
17
  recommended: false
18
18
  },
19
19
 
20
+ fixable: "whitespace",
21
+
20
22
  schema: [
21
23
  {
22
24
  type: "object",
@@ -76,21 +78,34 @@ module.exports = {
76
78
 
77
79
  "Program:exit": function checkBlankLines(node) {
78
80
  var lines = sourceCode.lines,
79
- currentLocation = -1,
80
- lastLocation,
81
+ fullLines = sourceCode.text.match(/.*(\r\n|\r|\n|\u2028|\u2029)/g) || [],
82
+ firstNonBlankLine = -1,
83
+ trimmedLines = [],
84
+ linesRangeStart = [],
81
85
  blankCounter = 0,
86
+ currentLocation,
87
+ lastLocation,
82
88
  location,
83
89
  firstOfEndingBlankLines,
84
- firstNonBlankLine = -1,
85
- trimmedLines = [];
90
+ diff,
91
+ fix,
92
+ rangeStart,
93
+ rangeEnd;
94
+
95
+ fix = function(fixer) {
96
+ return fixer.removeRange([rangeStart, rangeEnd]);
97
+ };
86
98
 
99
+ linesRangeStart.push(0);
87
100
  lines.forEach(function(str, i) {
88
- var trimmed = str.trim();
101
+ var length = i < fullLines.length ? fullLines[i].length : 0,
102
+ trimmed = str.trim();
89
103
 
90
104
  if ((firstNonBlankLine === -1) && (trimmed !== "")) {
91
105
  firstNonBlankLine = i;
92
106
  }
93
107
 
108
+ linesRangeStart.push(linesRangeStart[linesRangeStart.length - 1] + length);
94
109
  trimmedLines.push(trimmed);
95
110
  });
96
111
 
@@ -122,8 +137,15 @@ module.exports = {
122
137
 
123
138
  // Aggregate and count blank lines
124
139
  if (firstNonBlankLine > maxBOF) {
125
- context.report(node, 0,
126
- "Too many blank lines at the beginning of file. Max of " + maxBOF + " allowed.");
140
+ diff = firstNonBlankLine - maxBOF;
141
+ rangeStart = linesRangeStart[firstNonBlankLine - diff];
142
+ rangeEnd = linesRangeStart[firstNonBlankLine];
143
+ context.report({
144
+ node: node,
145
+ loc: node.loc.start,
146
+ message: "Too many blank lines at the beginning of file. Max of " + maxBOF + " allowed.",
147
+ fix: fix
148
+ });
127
149
  }
128
150
  currentLocation = firstNonBlankLine - 1;
129
151
 
@@ -144,20 +166,29 @@ module.exports = {
144
166
 
145
167
  // within the file, not at the end
146
168
  if (blankCounter >= max) {
169
+ diff = blankCounter - max + 1;
170
+ rangeStart = linesRangeStart[location.line - diff];
171
+ rangeEnd = linesRangeStart[location.line];
172
+
147
173
  context.report({
148
174
  node: node,
149
175
  loc: location,
150
- message: "More than " + max + " blank " + (max === 1 ? "line" : "lines") + " not allowed."
176
+ message: "More than " + max + " blank " + (max === 1 ? "line" : "lines") + " not allowed.",
177
+ fix: fix
151
178
  });
152
179
  }
153
180
  } else {
154
181
 
155
182
  // inside the last blank lines
156
183
  if (blankCounter > maxEOF) {
184
+ diff = blankCounter - maxEOF + 1;
185
+ rangeStart = linesRangeStart[location.line - diff];
186
+ rangeEnd = linesRangeStart[location.line - 1];
157
187
  context.report({
158
188
  node: node,
159
189
  loc: location,
160
- message: "Too many blank lines at the end of file. Max of " + maxEOF + " allowed."
190
+ message: "Too many blank lines at the end of file. Max of " + maxEOF + " allowed.",
191
+ fix: fix
161
192
  });
162
193
  }
163
194
  }
@@ -11,7 +11,7 @@
11
11
  module.exports = {
12
12
  meta: {
13
13
  docs: {
14
- description: "disallow calling some Object.prototype methods directly on objects",
14
+ description: "disallow calling some `Object.prototype` methods directly on objects",
15
15
  category: "Possible Errors",
16
16
  recommended: false
17
17
  }
@@ -14,7 +14,7 @@
14
14
  module.exports = {
15
15
  meta: {
16
16
  docs: {
17
- description: "disallow `javascript",
17
+ description: "disallow `javascript:` urls",
18
18
  category: "Best Practices",
19
19
  recommended: false
20
20
  },
@@ -21,7 +21,7 @@ var SENTINEL_NODE_TYPE_CONTINUE = /^(?:Program|(?:Function|Class)(?:Declaration|
21
21
  module.exports = {
22
22
  meta: {
23
23
  docs: {
24
- description: "disallow control flow statements in finally blocks",
24
+ description: "disallow control flow statements in `finally` blocks",
25
25
  category: "Possible Errors",
26
26
  recommended: false
27
27
  }
@@ -87,7 +87,13 @@ module.exports = {
87
87
  return;
88
88
  }
89
89
 
90
- if (properties[i].computed) {
90
+ /**
91
+ * If an ObjectPattern property is computed, we have no idea
92
+ * if a rename is useless or not. If an ObjectPattern property
93
+ * lacks a key, it is likely an ExperimentalRestProperty and
94
+ * so there is no "renaming" occurring here.
95
+ */
96
+ if (properties[i].computed || !properties[i].key) {
91
97
  return;
92
98
  }
93
99
 
@@ -108,7 +114,8 @@ module.exports = {
108
114
  return;
109
115
  }
110
116
 
111
- if (node.imported.name === node.local.name) {
117
+ if (node.imported.name === node.local.name &&
118
+ node.imported.range[0] !== node.local.range[0]) {
112
119
  reportError(node, node.imported, node.local, "Import");
113
120
  }
114
121
  }
@@ -123,7 +130,8 @@ module.exports = {
123
130
  return;
124
131
  }
125
132
 
126
- if (node.local.name === node.exported.name) {
133
+ if (node.local.name === node.exported.name &&
134
+ node.local.range[0] !== node.exported.range[0]) {
127
135
  reportError(node, node.local, node.exported, "Export");
128
136
  }
129
137
 
@@ -0,0 +1,209 @@
1
+ /**
2
+ * @fileoverview Rule to require or disallow line breaks inside braces.
3
+ * @author Toru Nagashima
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ var astUtils = require("../ast-utils");
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Helpers
16
+ //------------------------------------------------------------------------------
17
+
18
+ // Schema objects.
19
+ var OPTION_VALUE = {
20
+ oneOf: [
21
+ {
22
+ enum: ["always", "never"]
23
+ },
24
+ {
25
+ type: "object",
26
+ properties: {
27
+ multiline: {
28
+ type: "boolean"
29
+ },
30
+ minProperties: {
31
+ type: "integer",
32
+ minimum: 0
33
+ }
34
+ },
35
+ additionalProperties: false,
36
+ minProperties: 1
37
+ }
38
+ ]
39
+ };
40
+
41
+ /**
42
+ * Normalizes a given option value.
43
+ *
44
+ * @param {string|object|undefined} value - An option value to parse.
45
+ * @returns {{multiline: boolean, minProperties: number}} Normalized option object.
46
+ */
47
+ function normalizeOptionValue(value) {
48
+ var multiline = false;
49
+ var minProperties = Number.POSITIVE_INFINITY;
50
+
51
+ if (value) {
52
+ if (value === "always") {
53
+ minProperties = 0;
54
+ } else if (value === "never") {
55
+ minProperties = Number.POSITIVE_INFINITY;
56
+ } else {
57
+ multiline = Boolean(value.multiline);
58
+ minProperties = value.minProperties || Number.POSITIVE_INFINITY;
59
+ }
60
+ } else {
61
+ multiline = true;
62
+ }
63
+
64
+ return {multiline: multiline, minProperties: minProperties};
65
+ }
66
+
67
+ /**
68
+ * Normalizes a given option value.
69
+ *
70
+ * @param {string|object|undefined} options - An option value to parse.
71
+ * @returns {{ObjectExpression: {multiline: boolean, minProperties: number}, ObjectPattern: {multiline: boolean, minProperties: number}}} Normalized option object.
72
+ */
73
+ function normalizeOptions(options) {
74
+ if (options && (options.ObjectExpression || options.ObjectPattern)) {
75
+ return {
76
+ ObjectExpression: normalizeOptionValue(options.ObjectExpression),
77
+ ObjectPattern: normalizeOptionValue(options.ObjectPattern)
78
+ };
79
+ }
80
+
81
+ var value = normalizeOptionValue(options);
82
+
83
+ return {ObjectExpression: value, ObjectPattern: value};
84
+ }
85
+
86
+ //------------------------------------------------------------------------------
87
+ // Rule Definition
88
+ //------------------------------------------------------------------------------
89
+
90
+ module.exports = {
91
+ meta: {
92
+ docs: {
93
+ description: "enforce consistent line breaks inside braces",
94
+ category: "Stylistic Issues",
95
+ recommended: false
96
+ },
97
+ fixable: "whitespace",
98
+ schema: [
99
+ {
100
+ oneOf: [
101
+ OPTION_VALUE,
102
+ {
103
+ type: "object",
104
+ properties: {
105
+ ObjectExpression: OPTION_VALUE,
106
+ ObjectPattern: OPTION_VALUE
107
+ },
108
+ additionalProperties: false,
109
+ minProperties: 1
110
+ }
111
+ ]
112
+ }
113
+ ]
114
+ },
115
+
116
+ create: function(context) {
117
+ var sourceCode = context.getSourceCode();
118
+ var normalizedOptions = normalizeOptions(context.options[0]);
119
+
120
+ /**
121
+ * Reports a given node if it violated this rule.
122
+ *
123
+ * @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node.
124
+ * @param {{multiline: boolean, minProperties: number}} options - An option object.
125
+ * @returns {void}
126
+ */
127
+ function check(node) {
128
+ var options = normalizedOptions[node.type];
129
+ var openBrace = sourceCode.getFirstToken(node);
130
+ var closeBrace = sourceCode.getLastToken(node);
131
+ var first = sourceCode.getTokenOrCommentAfter(openBrace);
132
+ var last = sourceCode.getTokenOrCommentBefore(closeBrace);
133
+ var needsLinebreaks = (
134
+ node.properties.length >= options.minProperties ||
135
+ (
136
+ options.multiline &&
137
+ node.properties.length > 0 &&
138
+ first.loc.start.line !== last.loc.end.line
139
+ )
140
+ );
141
+
142
+ /*
143
+ * Use tokens or comments to check multiline or not.
144
+ * But use only tokens to check whether line breaks are needed.
145
+ * This allows:
146
+ * var obj = { // eslint-disable-line foo
147
+ * a: 1
148
+ * }
149
+ */
150
+ first = sourceCode.getTokenAfter(openBrace);
151
+ last = sourceCode.getTokenBefore(closeBrace);
152
+
153
+ if (needsLinebreaks) {
154
+ if (astUtils.isTokenOnSameLine(openBrace, first)) {
155
+ context.report({
156
+ message: "Expected a line break after this open brace.",
157
+ node: node,
158
+ loc: openBrace.loc.start,
159
+ fix: function(fixer) {
160
+ return fixer.insertTextAfter(openBrace, "\n");
161
+ }
162
+ });
163
+ }
164
+ if (astUtils.isTokenOnSameLine(last, closeBrace)) {
165
+ context.report({
166
+ message: "Expected a line break before this close brace.",
167
+ node: node,
168
+ loc: closeBrace.loc.start,
169
+ fix: function(fixer) {
170
+ return fixer.insertTextBefore(closeBrace, "\n");
171
+ }
172
+ });
173
+ }
174
+ } else {
175
+ if (!astUtils.isTokenOnSameLine(openBrace, first)) {
176
+ context.report({
177
+ message: "Unexpected a line break after this open brace.",
178
+ node: node,
179
+ loc: openBrace.loc.start,
180
+ fix: function(fixer) {
181
+ return fixer.removeRange([
182
+ openBrace.range[1],
183
+ first.range[0]
184
+ ]);
185
+ }
186
+ });
187
+ }
188
+ if (!astUtils.isTokenOnSameLine(last, closeBrace)) {
189
+ context.report({
190
+ message: "Unexpected a line break before this close brace.",
191
+ node: node,
192
+ loc: closeBrace.loc.start,
193
+ fix: function(fixer) {
194
+ return fixer.removeRange([
195
+ last.range[1],
196
+ closeBrace.range[0]
197
+ ]);
198
+ }
199
+ });
200
+ }
201
+ }
202
+ }
203
+
204
+ return {
205
+ ObjectExpression: check,
206
+ ObjectPattern: check
207
+ };
208
+ }
209
+ };