eslint 1.7.0 → 1.8.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.
@@ -6,6 +6,8 @@
6
6
  */
7
7
  "use strict";
8
8
 
9
+ var astUtils = require("../ast-utils");
10
+
9
11
  //------------------------------------------------------------------------------
10
12
  // Rule Definition
11
13
  //------------------------------------------------------------------------------
@@ -14,258 +16,201 @@ module.exports = function(context) {
14
16
 
15
17
  var MISSING_SPACE_MESSAGE = "There must be a space inside this paren.",
16
18
  REJECTED_SPACE_MESSAGE = "There should be no spaces inside this paren.",
17
- exceptionsArray = (context.options.length === 2) ? context.options[1].exceptions : [],
19
+ ALWAYS = context.options[0] === "always",
20
+
21
+ exceptionsArrayOptions = (context.options.length === 2) ? context.options[1].exceptions : [],
18
22
  options = {},
19
- rejectedSpaceRegExp,
20
- missingSpaceRegExp,
21
- spaceChecks;
22
-
23
- if (exceptionsArray && exceptionsArray.length) {
24
- options.braceException = exceptionsArray.indexOf("{}") !== -1 || false;
25
- options.bracketException = exceptionsArray.indexOf("[]") !== -1 || false;
26
- options.parenException = exceptionsArray.indexOf("()") !== -1 || false;
27
- options.empty = exceptionsArray.indexOf("empty") !== -1 || false;
23
+ exceptions;
24
+
25
+ if (exceptionsArrayOptions.length) {
26
+ options.braceException = exceptionsArrayOptions.indexOf("{}") !== -1;
27
+ options.bracketException = exceptionsArrayOptions.indexOf("[]") !== -1;
28
+ options.parenException = exceptionsArrayOptions.indexOf("()") !== -1;
29
+ options.empty = exceptionsArrayOptions.indexOf("empty") !== -1;
28
30
  }
29
31
 
30
32
  /**
31
- * Used with the `never` option to produce, given the exception options,
32
- * two regular expressions to check for missing and rejected spaces.
33
+ * Produces an object with the opener and closer exception values
33
34
  * @param {Object} opts The exception options
34
- * @returns {Object} `missingSpace` and `rejectedSpace` regular expressions
35
+ * @returns {Object} `openers` and `closers` exception values
35
36
  * @private
36
37
  */
37
- function getNeverChecks(opts) {
38
- var missingSpaceOpeners = [],
39
- missingSpaceClosers = [],
40
- rejectedSpaceOpeners = ["\\s"],
41
- rejectedSpaceClosers = ["\\s"],
42
- missingSpaceCheck,
43
- rejectedSpaceCheck;
44
-
45
- // Populate openers and closers
46
- if (opts.braceException) {
47
- missingSpaceOpeners.push("\\{");
48
- missingSpaceClosers.push("\\}");
49
- rejectedSpaceOpeners.push("\\{");
50
- rejectedSpaceClosers.push("\\}");
51
- }
52
- if (opts.bracketException) {
53
- missingSpaceOpeners.push("\\[");
54
- missingSpaceClosers.push("\\]");
55
- rejectedSpaceOpeners.push("\\[");
56
- rejectedSpaceClosers.push("\\]");
57
- }
58
- if (opts.parenException) {
59
- missingSpaceOpeners.push("\\(");
60
- missingSpaceClosers.push("\\)");
61
- rejectedSpaceOpeners.push("\\(");
62
- rejectedSpaceClosers.push("\\)");
63
- }
64
- if (opts.empty) {
65
- missingSpaceOpeners.push("\\)");
66
- missingSpaceClosers.push("\\(");
67
- rejectedSpaceOpeners.push("\\)");
68
- rejectedSpaceClosers.push("\\(");
38
+ function getExceptions() {
39
+ var openers = [],
40
+ closers = [];
41
+ if (options.braceException) {
42
+ openers.push("{");
43
+ closers.push("}");
69
44
  }
70
45
 
71
- if (missingSpaceOpeners.length) {
72
- missingSpaceCheck = "\\((" + missingSpaceOpeners.join("|") + ")";
73
- if (missingSpaceClosers.length) {
74
- missingSpaceCheck += "|";
75
- }
46
+ if (options.bracketException) {
47
+ openers.push("[");
48
+ closers.push("]");
76
49
  }
77
- if (missingSpaceClosers.length) {
78
- missingSpaceCheck += "(" + missingSpaceClosers.join("|") + ")\\)";
79
- }
80
-
81
- // compose the rejected regexp
82
- rejectedSpaceCheck = "\\( +[^" + rejectedSpaceOpeners.join("") + "]";
83
- rejectedSpaceCheck += "|[^" + rejectedSpaceClosers.join("") + "] +\\)";
84
50
 
85
- return {
86
- // e.g. \((\{)|(\})\) --- where {} is an exception
87
- missingSpace: missingSpaceCheck || ".^",
88
- // e.g. \( +[^ \n\r\{]|[^ \n\r\}] +\) --- where {} is an exception
89
- rejectedSpace: rejectedSpaceCheck
90
- };
91
- }
92
-
93
- /**
94
- * Used with the `always` option to produce, given the exception options,
95
- * two regular expressions to check for missing and rejected spaces.
96
- * @param {Object} opts The exception options
97
- * @returns {Object} `missingSpace` and `rejectedSpace` regular expressions
98
- * @private
99
- */
100
- function getAlwaysChecks(opts) {
101
- var missingSpaceOpeners = ["\\s", "\\)"],
102
- missingSpaceClosers = ["\\s", "\\("],
103
- rejectedSpaceOpeners = [],
104
- rejectedSpaceClosers = [],
105
- missingSpaceCheck,
106
- rejectedSpaceCheck;
107
-
108
- // Populate openers and closers
109
- if (opts.braceException) {
110
- missingSpaceOpeners.push("\\{");
111
- missingSpaceClosers.push("\\}");
112
- rejectedSpaceOpeners.push(" \\{");
113
- rejectedSpaceClosers.push("\\} ");
114
- }
115
- if (opts.bracketException) {
116
- missingSpaceOpeners.push("\\[");
117
- missingSpaceClosers.push("\\]");
118
- rejectedSpaceOpeners.push(" \\[");
119
- rejectedSpaceClosers.push("\\] ");
51
+ if (options.parenException) {
52
+ openers.push("(");
53
+ closers.push(")");
120
54
  }
121
- if (opts.parenException) {
122
- missingSpaceOpeners.push("\\(");
123
- missingSpaceClosers.push("\\)");
124
- rejectedSpaceOpeners.push(" \\(");
125
- rejectedSpaceClosers.push("\\) ");
126
- }
127
- if (opts.empty) {
128
- rejectedSpaceOpeners.push(" \\)");
129
- rejectedSpaceClosers.push("\\( ");
130
- }
131
-
132
- // compose the allowed regexp
133
- missingSpaceCheck = "\\([^" + missingSpaceOpeners.join("") + "]";
134
- missingSpaceCheck += "|[^" + missingSpaceClosers.join("") + "]\\)";
135
55
 
136
- // compose the rejected regexp
137
- if (rejectedSpaceOpeners.length) {
138
- rejectedSpaceCheck = "\\((" + rejectedSpaceOpeners.join("|") + ")";
139
- if (rejectedSpaceClosers.length) {
140
- rejectedSpaceCheck += "|";
141
- }
142
- }
143
- if (rejectedSpaceClosers.length) {
144
- rejectedSpaceCheck += "(" + rejectedSpaceClosers.join("|") + ")\\)";
56
+ if (options.empty) {
57
+ openers.push(")");
58
+ closers.push("(");
145
59
  }
146
60
 
147
61
  return {
148
- // e.g. \([^ \)\r\n\{]|[^ \(\r\n\}]\) --- where {} is an exception
149
- missingSpace: missingSpaceCheck,
150
- // e.g. \(( \{})|(\} )\) --- where {} is an excpetion
151
- rejectedSpace: rejectedSpaceCheck || ".^"
62
+ openers: openers,
63
+ closers: closers
152
64
  };
153
65
  }
154
66
 
155
- spaceChecks = (context.options[0] === "always") ? getAlwaysChecks(options) : getNeverChecks(options);
156
- missingSpaceRegExp = new RegExp(spaceChecks.missingSpace, "mg");
157
- rejectedSpaceRegExp = new RegExp(spaceChecks.rejectedSpace, "mg");
158
-
159
-
160
67
  //--------------------------------------------------------------------------
161
68
  // Helpers
162
69
  //--------------------------------------------------------------------------
163
-
164
- var skipRanges = [];
70
+ var sourceCode = context.getSourceCode();
165
71
 
166
72
  /**
167
- * Adds the range of a node to the set to be skipped when checking parens
168
- * @param {ASTNode} node The node to skip
169
- * @returns {void}
170
- * @private
73
+ * Determines if a token is one of the exceptions for the opener paren
74
+ * @param {Object} token The token to check
75
+ * @returns {boolean} True if the token is one of the exceptions for the opener paren
171
76
  */
172
- function addSkipRange(node) {
173
- skipRanges.push(node.range);
77
+ function isOpenerException(token) {
78
+ return token.type === "Punctuator" && exceptions.openers.indexOf(token.value) >= 0;
174
79
  }
175
80
 
176
81
  /**
177
- * Sorts the skipRanges array. Must be called before shouldSkip
178
- * @returns {void}
179
- * @private
82
+ * Determines if a token is one of the exceptions for the closer paren
83
+ * @param {Object} token The token to check
84
+ * @returns {boolean} True if the token is one of the exceptions for the closer paren
180
85
  */
181
- function sortSkipRanges() {
182
- skipRanges.sort(function(a, b) {
183
- return a[0] - b[0];
184
- });
86
+ function isCloserException(token) {
87
+ return token.type === "Punctuator" && exceptions.closers.indexOf(token.value) >= 0;
185
88
  }
186
89
 
187
90
  /**
188
- * Checks if a certain position in the source should be skipped
189
- * @param {Number} pos The 0-based index in the source
190
- * @returns {boolean} whether the position should be skipped
191
- * @private
91
+ * Determines if an opener paren should have a missing space after it
92
+ * @param {Object} left The paren token
93
+ * @param {Object} right The token after it
94
+ * @returns {boolean} True if the paren should have a space
192
95
  */
193
- function shouldSkip(pos) {
194
- var i, len, range;
195
- for (i = 0, len = skipRanges.length; i < len; i += 1) {
196
- range = skipRanges[i];
197
- if (pos < range[0]) {
198
- break;
199
- } else if (pos < range[1]) {
200
- return true;
96
+ function shouldOpenerHaveSpace(left, right) {
97
+ if (sourceCode.isSpaceBetweenTokens(left, right)) {
98
+ return false;
99
+ }
100
+
101
+ if (ALWAYS) {
102
+ if (right.type === "Punctuator" && right.value === ")") {
103
+ return false;
201
104
  }
105
+ return !isOpenerException(right);
106
+ } else {
107
+ return isOpenerException(right);
202
108
  }
203
- return false;
204
109
  }
205
110
 
111
+ /**
112
+ * Determines if an closer paren should have a missing space after it
113
+ * @param {Object} left The token before the paren
114
+ * @param {Object} right The paren token
115
+ * @returns {boolean} True if the paren should have a space
116
+ */
117
+ function shouldCloserHaveSpace(left, right) {
118
+ if (left.type === "Punctuator" && left.value === "(") {
119
+ return false;
120
+ }
206
121
 
207
- //--------------------------------------------------------------------------
208
- // Public
209
- //--------------------------------------------------------------------------
210
-
211
- return {
122
+ if (sourceCode.isSpaceBetweenTokens(left, right)) {
123
+ return false;
124
+ }
212
125
 
213
- "Program:exit": function checkParenSpaces(node) {
214
-
215
- var nextMatch,
216
- nextLine,
217
- column,
218
- line = 1,
219
- source = context.getSource(),
220
- pos = 0;
221
-
222
- /**
223
- * Check the match
224
- * @param {object} match Object to match
225
- * @param {string} message Message to report
226
- * @returns {void}
227
- * @private
228
- */
229
- function checkMatch(match, message) {
230
- if (source.charAt(match.index) !== "(") {
231
- // Matched a closing paren pattern
232
- match.index += 1;
233
- }
126
+ if (ALWAYS) {
127
+ return !isCloserException(left);
128
+ } else {
129
+ return isCloserException(left);
130
+ }
131
+ }
234
132
 
235
- if (!shouldSkip(match.index)) {
236
- while ((nextLine = source.indexOf("\n", pos)) !== -1 && nextLine < match.index) {
237
- pos = nextLine + 1;
238
- line += 1;
239
- }
240
- column = match.index - pos;
133
+ /**
134
+ * Determines if an opener paren should not have an existing space after it
135
+ * @param {Object} left The paren token
136
+ * @param {Object} right The token after it
137
+ * @returns {boolean} True if the paren should reject the space
138
+ */
139
+ function shouldOpenerRejectSpace(left, right) {
140
+ if (!astUtils.isTokenOnSameLine(left, right)) {
141
+ return false;
142
+ }
241
143
 
242
- context.report(node, { line: line, column: column }, message);
243
- }
244
- }
144
+ if (!sourceCode.isSpaceBetweenTokens(left, right)) {
145
+ return false;
146
+ }
245
147
 
246
- sortSkipRanges();
148
+ if (ALWAYS) {
149
+ return isOpenerException(right);
150
+ } else {
151
+ return !isOpenerException(right);
152
+ }
153
+ }
247
154
 
248
- while ((nextMatch = rejectedSpaceRegExp.exec(source)) !== null) {
249
- checkMatch(nextMatch, REJECTED_SPACE_MESSAGE);
250
- }
155
+ /**
156
+ * Determines if an closer paren should not have an existing space after it
157
+ * @param {Object} left The token before the paren
158
+ * @param {Object} right The paren token
159
+ * @returns {boolean} True if the paren should reject the space
160
+ */
161
+ function shouldCloserRejectSpace(left, right) {
162
+ if (left.type === "Punctuator" && left.value === "(") {
163
+ return false;
164
+ }
251
165
 
252
- while ((nextMatch = missingSpaceRegExp.exec(source)) !== null) {
253
- checkMatch(nextMatch, MISSING_SPACE_MESSAGE);
254
- }
166
+ if (!astUtils.isTokenOnSameLine(left, right)) {
167
+ return false;
168
+ }
255
169
 
256
- },
170
+ if (!sourceCode.isSpaceBetweenTokens(left, right)) {
171
+ return false;
172
+ }
257
173
 
174
+ if (ALWAYS) {
175
+ return isCloserException(left);
176
+ } else {
177
+ return !isCloserException(left);
178
+ }
179
+ }
258
180
 
259
- // These nodes can contain parentheses that this rule doesn't care about
181
+ //--------------------------------------------------------------------------
182
+ // Public
183
+ //--------------------------------------------------------------------------
260
184
 
261
- LineComment: addSkipRange,
185
+ return {
186
+ "Program": function checkParenSpaces(node) {
187
+ var tokens = node.tokens;
188
+ var prevToken, nextToken;
189
+ exceptions = getExceptions();
262
190
 
263
- BlockComment: addSkipRange,
191
+ tokens.forEach(function(token, i) {
192
+ prevToken = tokens[i - 1];
193
+ nextToken = tokens[i + 1];
264
194
 
265
- Literal: addSkipRange,
195
+ if (token.type !== "Punctuator") {
196
+ return;
197
+ }
266
198
 
267
- TemplateElement: addSkipRange
199
+ if (token.value !== "(" && token.value !== ")") {
200
+ return;
201
+ }
268
202
 
203
+ if (token.value === "(" && shouldOpenerHaveSpace(token, nextToken)) {
204
+ context.report(node, token.loc.end, MISSING_SPACE_MESSAGE);
205
+ } else if (token.value === "(" && shouldOpenerRejectSpace(token, nextToken)) {
206
+ context.report(node, token.loc.end, REJECTED_SPACE_MESSAGE);
207
+ } else if (token.value === ")" && shouldCloserHaveSpace(prevToken, token)) {
208
+ context.report(node, token.loc.end, MISSING_SPACE_MESSAGE);
209
+ } else if (token.value === ")" && shouldCloserRejectSpace(prevToken, token)) {
210
+ context.report(node, token.loc.end, REJECTED_SPACE_MESSAGE);
211
+ }
212
+ });
213
+ }
269
214
  };
270
215
 
271
216
  };
@@ -33,6 +33,16 @@ module.exports = function(context) {
33
33
  // Using a stack to store if a function returns or not (handling nested functions)
34
34
  var fns = [];
35
35
 
36
+ /**
37
+ * Check if node type is a Class
38
+ * @param {ASTNode} node node to check.
39
+ * @returns {boolean} True is its a class
40
+ * @private
41
+ */
42
+ function isTypeClass(node) {
43
+ return node.type === "ClassExpression" || node.type === "ClassDeclaration";
44
+ }
45
+
36
46
  /**
37
47
  * When parsing a new function, store it in our function stack.
38
48
  * @param {ASTNode} node A function node to check.
@@ -41,7 +51,8 @@ module.exports = function(context) {
41
51
  */
42
52
  function startFunction(node) {
43
53
  fns.push({
44
- returnPresent: (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement")
54
+ returnPresent: (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") ||
55
+ isTypeClass(node)
45
56
  });
46
57
  }
47
58
 
@@ -158,14 +169,14 @@ module.exports = function(context) {
158
169
  }
159
170
 
160
171
  // check tag preferences
161
- if (prefer.hasOwnProperty(tag.title)) {
172
+ if (prefer.hasOwnProperty(tag.title) && tag.title !== prefer[tag.title]) {
162
173
  context.report(jsdocNode, "Use @{{name}} instead.", { name: prefer[tag.title] });
163
174
  }
164
175
 
165
176
  });
166
177
 
167
178
  // check for functions missing @returns
168
- if (!isOverride && !hasReturns && !hasConstructor && node.parent.kind !== "get") {
179
+ if (!isOverride && !hasReturns && !hasConstructor && node.parent.kind !== "get" && !isTypeClass(node)) {
169
180
  if (requireReturn || functionData.returnPresent) {
170
181
  context.report(jsdocNode, "Missing JSDoc @" + (prefer.returns || "returns") + " for function.");
171
182
  }
@@ -174,23 +185,25 @@ module.exports = function(context) {
174
185
  // check the parameters
175
186
  var jsdocParams = Object.keys(params);
176
187
 
177
- node.params.forEach(function(param, i) {
178
- var name = param.name;
179
-
180
- // TODO(nzakas): Figure out logical things to do with destructured, default, rest params
181
- if (param.type === "Identifier") {
182
- if (jsdocParams[i] && (name !== jsdocParams[i])) {
183
- context.report(jsdocNode, "Expected JSDoc for '{{name}}' but found '{{jsdocName}}'.", {
184
- name: name,
185
- jsdocName: jsdocParams[i]
186
- });
187
- } else if (!params[name] && !isOverride) {
188
- context.report(jsdocNode, "Missing JSDoc for parameter '{{name}}'.", {
189
- name: name
190
- });
188
+ if (node.params) {
189
+ node.params.forEach(function(param, i) {
190
+ var name = param.name;
191
+
192
+ // TODO(nzakas): Figure out logical things to do with destructured, default, rest params
193
+ if (param.type === "Identifier") {
194
+ if (jsdocParams[i] && (name !== jsdocParams[i])) {
195
+ context.report(jsdocNode, "Expected JSDoc for '{{name}}' but found '{{jsdocName}}'.", {
196
+ name: name,
197
+ jsdocName: jsdocParams[i]
198
+ });
199
+ } else if (!params[name] && !isOverride) {
200
+ context.report(jsdocNode, "Missing JSDoc for parameter '{{name}}'.", {
201
+ name: name
202
+ });
203
+ }
191
204
  }
192
- }
193
- });
205
+ });
206
+ }
194
207
 
195
208
  if (options.matchDescription) {
196
209
  var regex = new RegExp(options.matchDescription);
@@ -212,9 +225,13 @@ module.exports = function(context) {
212
225
  "ArrowFunctionExpression": startFunction,
213
226
  "FunctionExpression": startFunction,
214
227
  "FunctionDeclaration": startFunction,
228
+ "ClassExpression": startFunction,
229
+ "ClassDeclaration": startFunction,
215
230
  "ArrowFunctionExpression:exit": checkJSDoc,
216
231
  "FunctionExpression:exit": checkJSDoc,
217
232
  "FunctionDeclaration:exit": checkJSDoc,
233
+ "ClassExpression:exit": checkJSDoc,
234
+ "ClassDeclaration:exit": checkJSDoc,
218
235
  "ReturnStatement": addReturn
219
236
  };
220
237
 
@@ -78,6 +78,34 @@ var RuleTesterParameters = [
78
78
 
79
79
  var validateSchema = validate(metaSchema, { verbose: true });
80
80
 
81
+ var hasOwnProperty = Function.call.bind(Object.hasOwnProperty);
82
+
83
+ /**
84
+ * Clones a given value deeply.
85
+ * Note: This ignores `parent` property.
86
+ *
87
+ * @param {any} x - A value to clone.
88
+ * @returns {any} A cloned value.
89
+ */
90
+ function cloneDeeplyExcludesParent(x) {
91
+ if (typeof x === "object" && x !== null) {
92
+ if (Array.isArray(x)) {
93
+ return x.map(cloneDeeplyExcludesParent);
94
+ }
95
+
96
+ var retv = {};
97
+ for (var key in x) {
98
+ if (key !== "parent" && hasOwnProperty(x, key)) {
99
+ retv[key] = cloneDeeplyExcludesParent(x[key]);
100
+ }
101
+ }
102
+
103
+ return retv;
104
+ }
105
+
106
+ return x;
107
+ }
108
+
81
109
  //------------------------------------------------------------------------------
82
110
  // Public Interface
83
111
  //------------------------------------------------------------------------------
@@ -178,7 +206,7 @@ RuleTester.prototype = {
178
206
  */
179
207
  function runRuleForItem(ruleName, item) {
180
208
  var config = clone(testerConfig),
181
- code, filename, schema;
209
+ code, filename, schema, beforeAST, afterAST;
182
210
 
183
211
  if (typeof item === "string") {
184
212
  code = item;
@@ -223,7 +251,21 @@ RuleTester.prototype = {
223
251
 
224
252
  validator.validate(config, "rule-tester");
225
253
 
226
- return eslint.verify(code, config, filename);
254
+ // To cache AST.
255
+ eslint.reset();
256
+ eslint.on("Program", function(node) {
257
+ beforeAST = cloneDeeplyExcludesParent(node);
258
+
259
+ eslint.on("Program:exit", function(node) {
260
+ afterAST = cloneDeeplyExcludesParent(node);
261
+ });
262
+ });
263
+
264
+ return {
265
+ messages: eslint.verify(code, config, filename, true),
266
+ beforeAST: beforeAST,
267
+ afterAST: afterAST
268
+ };
227
269
  }
228
270
 
229
271
  /**
@@ -235,10 +277,17 @@ RuleTester.prototype = {
235
277
  * @private
236
278
  */
237
279
  function testValidTemplate(ruleName, item) {
238
- var messages = runRuleForItem(ruleName, item);
280
+ var result = runRuleForItem(ruleName, item);
281
+ var messages = result.messages;
239
282
 
240
283
  assert.equal(messages.length, 0, util.format("Should have no errors but had %d: %s",
241
284
  messages.length, util.inspect(messages)));
285
+
286
+ assert.deepEqual(
287
+ result.beforeAST,
288
+ result.afterAST,
289
+ "Rule should not modify AST."
290
+ );
242
291
  }
243
292
 
244
293
  /**
@@ -250,7 +299,8 @@ RuleTester.prototype = {
250
299
  * @private
251
300
  */
252
301
  function testInvalidTemplate(ruleName, item) {
253
- var messages = runRuleForItem(ruleName, item);
302
+ var result = runRuleForItem(ruleName, item);
303
+ var messages = result.messages;
254
304
 
255
305
  if (typeof item.errors === "number") {
256
306
  assert.equal(messages.length, item.errors, util.format("Should have %d errors but had %d: %s",
@@ -261,11 +311,10 @@ RuleTester.prototype = {
261
311
  item.errors.length, messages.length, util.inspect(messages)));
262
312
 
263
313
  if (item.hasOwnProperty("output")) {
264
- var result = SourceCodeFixer.applyFixes(eslint.getSourceCode(), messages);
265
- assert.equal(result.output, item.output, "Output is incorrect.");
314
+ var fixResult = SourceCodeFixer.applyFixes(eslint.getSourceCode(), messages);
315
+ assert.equal(fixResult.output, item.output, "Output is incorrect.");
266
316
  }
267
317
 
268
-
269
318
  for (var i = 0, l = item.errors.length; i < l; i++) {
270
319
  assert.ok(!("fatal" in messages[i]), "A fatal parsing error occurred: " + messages[i].message);
271
320
  assert.equal(messages[i].ruleId, ruleName, "Error rule name should be the same as the name of the rule being tested");
@@ -299,6 +348,12 @@ RuleTester.prototype = {
299
348
  }
300
349
  }
301
350
  }
351
+
352
+ assert.deepEqual(
353
+ result.beforeAST,
354
+ result.afterAST,
355
+ "Rule should not modify AST."
356
+ );
302
357
  }
303
358
 
304
359
  // this creates a mocha test suite and pipes all supplied info
@@ -211,6 +211,12 @@ SourceCode.prototype = {
211
211
  }
212
212
  break;
213
213
 
214
+ case "ClassDeclaration":
215
+ return findJSDocComment(node.leadingComments, line);
216
+
217
+ case "ClassExpression":
218
+ return findJSDocComment(parent.parent.leadingComments, line);
219
+
214
220
  case "ArrowFunctionExpression":
215
221
  case "FunctionExpression":
216
222
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {