eslint 1.7.3 → 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.
- package/README.md +1 -0
- package/conf/eslint.json +2 -0
- package/lib/cli-engine.js +80 -13
- package/lib/cli.js +12 -10
- package/lib/eslint.js +14 -14
- package/lib/logging.js +25 -0
- package/lib/options.js +7 -2
- package/lib/rules/array-bracket-spacing.js +2 -2
- package/lib/rules/arrow-body-style.js +71 -0
- package/lib/rules/comma-spacing.js +72 -36
- package/lib/rules/eol-last.js +10 -4
- package/lib/rules/no-arrow-condition.js +88 -0
- package/lib/rules/no-multiple-empty-lines.js +39 -13
- package/lib/rules/no-plusplus.js +22 -1
- package/lib/rules/no-shadow.js +21 -3
- package/lib/rules/space-in-parens.js +145 -200
- package/lib/rules/valid-jsdoc.js +35 -18
- package/lib/testers/rule-tester.js +62 -7
- package/lib/util/source-code.js +6 -0
- package/package.json +1 -1
package/lib/rules/valid-jsdoc.js
CHANGED
@@ -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
|
|
@@ -165,7 +176,7 @@ module.exports = function(context) {
|
|
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
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
if (
|
183
|
-
|
184
|
-
name
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
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
|
-
|
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
|
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
|
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
|
265
|
-
assert.equal(
|
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
|
package/lib/util/source-code.js
CHANGED
@@ -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
|
|