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.
- 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-dangle.js +26 -10
- package/lib/rules/comma-spacing.js +72 -36
- package/lib/rules/eol-last.js +10 -4
- package/lib/rules/id-match.js +2 -1
- package/lib/rules/indent.js +10 -8
- package/lib/rules/key-spacing.js +13 -25
- package/lib/rules/no-arrow-condition.js +88 -0
- package/lib/rules/no-extend-native.js +3 -3
- package/lib/rules/no-magic-numbers.js +25 -2
- package/lib/rules/no-mixed-spaces-and-tabs.js +23 -19
- 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 +36 -19
- package/lib/testers/rule-tester.js +62 -7
- package/lib/util/source-code.js +6 -0
- package/package.json +1 -1
@@ -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
|
-
|
19
|
+
ALWAYS = context.options[0] === "always",
|
20
|
+
|
21
|
+
exceptionsArrayOptions = (context.options.length === 2) ? context.options[1].exceptions : [],
|
18
22
|
options = {},
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
options.
|
25
|
-
options.
|
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
|
-
*
|
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} `
|
35
|
+
* @returns {Object} `openers` and `closers` exception values
|
35
36
|
* @private
|
36
37
|
*/
|
37
|
-
function
|
38
|
-
var
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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 (
|
72
|
-
|
73
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
149
|
-
|
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
|
-
*
|
168
|
-
* @param {
|
169
|
-
* @returns {
|
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
|
173
|
-
|
77
|
+
function isOpenerException(token) {
|
78
|
+
return token.type === "Punctuator" && exceptions.openers.indexOf(token.value) >= 0;
|
174
79
|
}
|
175
80
|
|
176
81
|
/**
|
177
|
-
*
|
178
|
-
* @
|
179
|
-
* @
|
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
|
182
|
-
|
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
|
-
*
|
189
|
-
* @param {
|
190
|
-
* @
|
191
|
-
* @
|
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
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
return
|
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
|
-
|
209
|
-
|
210
|
-
|
211
|
-
return {
|
122
|
+
if (sourceCode.isSpaceBetweenTokens(left, right)) {
|
123
|
+
return false;
|
124
|
+
}
|
212
125
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
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
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
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
|
-
|
243
|
-
|
244
|
-
|
144
|
+
if (!sourceCode.isSpaceBetweenTokens(left, right)) {
|
145
|
+
return false;
|
146
|
+
}
|
245
147
|
|
246
|
-
|
148
|
+
if (ALWAYS) {
|
149
|
+
return isOpenerException(right);
|
150
|
+
} else {
|
151
|
+
return !isOpenerException(right);
|
152
|
+
}
|
153
|
+
}
|
247
154
|
|
248
|
-
|
249
|
-
|
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
|
-
|
253
|
-
|
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
|
-
|
181
|
+
//--------------------------------------------------------------------------
|
182
|
+
// Public
|
183
|
+
//--------------------------------------------------------------------------
|
260
184
|
|
261
|
-
|
185
|
+
return {
|
186
|
+
"Program": function checkParenSpaces(node) {
|
187
|
+
var tokens = node.tokens;
|
188
|
+
var prevToken, nextToken;
|
189
|
+
exceptions = getExceptions();
|
262
190
|
|
263
|
-
|
191
|
+
tokens.forEach(function(token, i) {
|
192
|
+
prevToken = tokens[i - 1];
|
193
|
+
nextToken = tokens[i + 1];
|
264
194
|
|
265
|
-
|
195
|
+
if (token.type !== "Punctuator") {
|
196
|
+
return;
|
197
|
+
}
|
266
198
|
|
267
|
-
|
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
|
};
|
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
|
|
@@ -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
|
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
|
|