eslint 4.7.1 → 4.10.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/CHANGELOG.md +113 -0
- package/README.md +34 -19
- package/conf/default-cli-options.js +2 -1
- package/conf/eslint-recommended.js +2 -0
- package/lib/ast-utils.js +2 -1
- package/lib/cli-engine.js +26 -5
- package/lib/cli.js +17 -9
- package/lib/code-path-analysis/code-path-segment.js +39 -39
- package/lib/code-path-analysis/code-path-state.js +3 -0
- package/lib/formatters/html-template-message.html +1 -1
- package/lib/formatters/html-template-page.html +3 -1
- package/lib/formatters/html.js +2 -1
- package/lib/ignored-paths.js +1 -1
- package/lib/linter.js +43 -71
- package/lib/logging.js +2 -2
- package/lib/options.js +12 -0
- package/lib/rules/array-bracket-newline.js +19 -5
- package/lib/rules/block-spacing.js +1 -1
- package/lib/rules/callback-return.js +2 -1
- package/lib/rules/capitalized-comments.js +2 -1
- package/lib/rules/comma-style.js +3 -1
- package/lib/rules/dot-notation.js +56 -35
- package/lib/rules/generator-star-spacing.js +3 -3
- package/lib/rules/indent-legacy.js +5 -2
- package/lib/rules/indent.js +25 -19
- package/lib/rules/lines-around-comment.js +33 -4
- package/lib/rules/lines-between-class-members.js +91 -0
- package/lib/rules/max-len.js +2 -3
- package/lib/rules/multiline-comment-style.js +294 -0
- package/lib/rules/new-cap.js +2 -1
- package/lib/rules/newline-before-return.js +4 -2
- package/lib/rules/no-alert.js +7 -15
- package/lib/rules/no-catch-shadow.js +1 -1
- package/lib/rules/no-constant-condition.js +2 -2
- package/lib/rules/no-control-regex.js +2 -1
- package/lib/rules/no-else-return.js +43 -8
- package/lib/rules/no-extra-parens.js +6 -3
- package/lib/rules/no-lonely-if.js +2 -1
- package/lib/rules/no-loop-func.js +2 -3
- package/lib/rules/no-mixed-requires.js +8 -4
- package/lib/rules/no-restricted-imports.js +86 -17
- package/lib/rules/no-restricted-modules.js +84 -15
- package/lib/rules/no-trailing-spaces.js +1 -1
- package/lib/rules/no-unneeded-ternary.js +3 -1
- package/lib/rules/no-unused-labels.js +2 -1
- package/lib/rules/no-useless-computed-key.js +2 -1
- package/lib/rules/no-useless-escape.js +8 -1
- package/lib/rules/no-var.js +11 -0
- package/lib/rules/object-shorthand.js +6 -2
- package/lib/rules/operator-linebreak.js +3 -1
- package/lib/rules/padding-line-between-statements.js +2 -2
- package/lib/rules/require-jsdoc.js +11 -18
- package/lib/rules/sort-imports.js +6 -3
- package/lib/rules/space-unary-ops.js +6 -8
- package/lib/rules/valid-jsdoc.js +39 -33
- package/lib/testers/rule-tester.js +20 -6
- package/lib/util/apply-disable-directives.js +56 -27
- package/lib/util/node-event-generator.js +6 -20
- package/lib/util/safe-emitter.js +54 -0
- package/lib/util/source-code.js +73 -67
- package/messages/no-config-found.txt +1 -1
- package/package.json +3 -4
- package/lib/internal-rules/.eslintrc.yml +0 -3
- package/lib/internal-rules/internal-consistent-docs-description.js +0 -130
- package/lib/internal-rules/internal-no-invalid-meta.js +0 -188
@@ -0,0 +1,54 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview A variant of EventEmitter which does not give listeners information about each other
|
3
|
+
* @author Teddy Katz
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Typedefs
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
/**
|
13
|
+
* An object describing an AST selector
|
14
|
+
* @typedef {Object} SafeEmitter
|
15
|
+
* @property {function(eventName: string, listenerFunc: Function): void} on Adds a listener for a given event name
|
16
|
+
* @property {function(eventName: string, arg1?: any, arg2?: any, arg3?: any)} emit Emits an event with a given name.
|
17
|
+
* This calls all the listeners that were listening for that name, with `arg1`, `arg2`, and `arg3` as arguments.
|
18
|
+
* @property {function(): string[]} eventNames Gets the list of event names that have registered listeners.
|
19
|
+
*/
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Creates an object which can listen for and emit events.
|
23
|
+
* This is similar to the EventEmitter API in Node's standard library, but it has a few differences.
|
24
|
+
* The goal is to allow multiple modules to attach arbitrary listeners to the same emitter, without
|
25
|
+
* letting the modules know about each other at all.
|
26
|
+
* 1. It has no special keys like `error` and `newListener`, which would allow modules to detect when
|
27
|
+
* another module throws an error or registers a listener.
|
28
|
+
* 2. It calls listener functions without any `this` value. (`EventEmitter` calls listeners with a
|
29
|
+
* `this` value of the emitter instance, which would give listeners access to other listeners.)
|
30
|
+
* 3. Events can be emitted with at most 3 arguments. (For example: when using `emitter.emit('foo', a, b, c)`,
|
31
|
+
* the arguments `a`, `b`, and `c` will be passed to the listener functions.)
|
32
|
+
* @returns {SafeEmitter} An emitter
|
33
|
+
*/
|
34
|
+
module.exports = () => {
|
35
|
+
const listeners = Object.create(null);
|
36
|
+
|
37
|
+
return Object.freeze({
|
38
|
+
on(eventName, listener) {
|
39
|
+
if (eventName in listeners) {
|
40
|
+
listeners[eventName].push(listener);
|
41
|
+
} else {
|
42
|
+
listeners[eventName] = [listener];
|
43
|
+
}
|
44
|
+
},
|
45
|
+
emit(eventName, a, b, c) {
|
46
|
+
if (eventName in listeners) {
|
47
|
+
listeners[eventName].forEach(listener => listener(a, b, c));
|
48
|
+
}
|
49
|
+
},
|
50
|
+
eventNames() {
|
51
|
+
return Object.keys(listeners);
|
52
|
+
}
|
53
|
+
});
|
54
|
+
};
|
package/lib/util/source-code.js
CHANGED
@@ -25,7 +25,6 @@ const TokenStore = require("../token-store"),
|
|
25
25
|
* @private
|
26
26
|
*/
|
27
27
|
function validate(ast) {
|
28
|
-
|
29
28
|
if (!ast.tokens) {
|
30
29
|
throw new Error("AST is missing the tokens array.");
|
31
30
|
}
|
@@ -44,34 +43,9 @@ function validate(ast) {
|
|
44
43
|
}
|
45
44
|
|
46
45
|
/**
|
47
|
-
*
|
48
|
-
* @param {ASTNode
|
49
|
-
* @
|
50
|
-
* @returns {ASTNode} The node if found, null if not.
|
51
|
-
* @private
|
52
|
-
*/
|
53
|
-
function findJSDocComment(comments, line) {
|
54
|
-
|
55
|
-
if (comments) {
|
56
|
-
for (let i = comments.length - 1; i >= 0; i--) {
|
57
|
-
if (comments[i].type === "Block" && comments[i].value.charAt(0) === "*") {
|
58
|
-
|
59
|
-
if (line - comments[i].loc.end.line <= 1) {
|
60
|
-
return comments[i];
|
61
|
-
}
|
62
|
-
break;
|
63
|
-
|
64
|
-
}
|
65
|
-
}
|
66
|
-
}
|
67
|
-
|
68
|
-
return null;
|
69
|
-
}
|
70
|
-
|
71
|
-
/**
|
72
|
-
* Check to see if its a ES6 export declaration
|
73
|
-
* @param {ASTNode} astNode - any node
|
74
|
-
* @returns {boolean} whether the given node represents a export declaration
|
46
|
+
* Check to see if its a ES6 export declaration.
|
47
|
+
* @param {ASTNode} astNode An AST node.
|
48
|
+
* @returns {boolean} whether the given node represents an export declaration.
|
75
49
|
* @private
|
76
50
|
*/
|
77
51
|
function looksLikeExport(astNode) {
|
@@ -80,10 +54,11 @@ function looksLikeExport(astNode) {
|
|
80
54
|
}
|
81
55
|
|
82
56
|
/**
|
83
|
-
* Merges two sorted lists into a larger sorted list in O(n) time
|
84
|
-
* @param {Token[]} tokens The list of tokens
|
85
|
-
* @param {Token[]} comments The list of comments
|
86
|
-
* @returns {Token[]} A sorted list of tokens and comments
|
57
|
+
* Merges two sorted lists into a larger sorted list in O(n) time.
|
58
|
+
* @param {Token[]} tokens The list of tokens.
|
59
|
+
* @param {Token[]} comments The list of comments.
|
60
|
+
* @returns {Token[]} A sorted list of tokens and comments.
|
61
|
+
* @private
|
87
62
|
*/
|
88
63
|
function sortedMerge(tokens, comments) {
|
89
64
|
const result = [];
|
@@ -182,9 +157,9 @@ class SourceCode extends TokenStore {
|
|
182
157
|
}
|
183
158
|
|
184
159
|
/**
|
185
|
-
* Split the source code into multiple lines based on the line delimiters
|
186
|
-
* @param {string} text Source code as a string
|
187
|
-
* @returns {string[]} Array of source code lines
|
160
|
+
* Split the source code into multiple lines based on the line delimiters.
|
161
|
+
* @param {string} text Source code as a string.
|
162
|
+
* @returns {string[]} Array of source code lines.
|
188
163
|
* @public
|
189
164
|
*/
|
190
165
|
static splitLines(text) {
|
@@ -197,6 +172,7 @@ class SourceCode extends TokenStore {
|
|
197
172
|
* @param {int=} beforeCount The number of characters before the node to retrieve.
|
198
173
|
* @param {int=} afterCount The number of characters after the node to retrieve.
|
199
174
|
* @returns {string} The text representing the AST node.
|
175
|
+
* @public
|
200
176
|
*/
|
201
177
|
getText(node, beforeCount, afterCount) {
|
202
178
|
if (node) {
|
@@ -209,6 +185,7 @@ class SourceCode extends TokenStore {
|
|
209
185
|
/**
|
210
186
|
* Gets the entire source text split into an array of lines.
|
211
187
|
* @returns {Array} The source text as an array of lines.
|
188
|
+
* @public
|
212
189
|
*/
|
213
190
|
getLines() {
|
214
191
|
return this.lines;
|
@@ -217,6 +194,7 @@ class SourceCode extends TokenStore {
|
|
217
194
|
/**
|
218
195
|
* Retrieves an array containing all comments in the source code.
|
219
196
|
* @returns {ASTNode[]} An array of comment nodes.
|
197
|
+
* @public
|
220
198
|
*/
|
221
199
|
getAllComments() {
|
222
200
|
return this.ast.comments;
|
@@ -225,7 +203,8 @@ class SourceCode extends TokenStore {
|
|
225
203
|
/**
|
226
204
|
* Gets all comments for the given node.
|
227
205
|
* @param {ASTNode} node The AST node to get the comments for.
|
228
|
-
* @returns {Object}
|
206
|
+
* @returns {Object} An object containing a leading and trailing array
|
207
|
+
* of comments indexed by their position.
|
229
208
|
* @public
|
230
209
|
*/
|
231
210
|
getComments(node) {
|
@@ -297,47 +276,68 @@ class SourceCode extends TokenStore {
|
|
297
276
|
/**
|
298
277
|
* Retrieves the JSDoc comment for a given node.
|
299
278
|
* @param {ASTNode} node The AST node to get the comment for.
|
300
|
-
* @returns {
|
301
|
-
* given node or null if not found.
|
279
|
+
* @returns {Token|null} The Block comment token containing the JSDoc comment
|
280
|
+
* for the given node or null if not found.
|
302
281
|
* @public
|
303
282
|
*/
|
304
283
|
getJSDocComment(node) {
|
284
|
+
|
285
|
+
/**
|
286
|
+
* Checks for the presence of a JSDoc comment for the given node and returns it.
|
287
|
+
* @param {ASTNode} astNode The AST node to get the comment for.
|
288
|
+
* @returns {Token|null} The Block comment token containing the JSDoc comment
|
289
|
+
* for the given node or null if not found.
|
290
|
+
* @private
|
291
|
+
*/
|
292
|
+
const findJSDocComment = astNode => {
|
293
|
+
const tokenBefore = this.getTokenBefore(astNode, { includeComments: true });
|
294
|
+
|
295
|
+
if (
|
296
|
+
tokenBefore &&
|
297
|
+
astUtils.isCommentToken(tokenBefore) &&
|
298
|
+
tokenBefore.type === "Block" &&
|
299
|
+
tokenBefore.value.charAt(0) === "*" &&
|
300
|
+
astNode.loc.start.line - tokenBefore.loc.end.line <= 1
|
301
|
+
) {
|
302
|
+
return tokenBefore;
|
303
|
+
}
|
304
|
+
|
305
|
+
return null;
|
306
|
+
};
|
305
307
|
let parent = node.parent;
|
306
|
-
const leadingComments = this.getCommentsBefore(node);
|
307
308
|
|
308
309
|
switch (node.type) {
|
309
310
|
case "ClassDeclaration":
|
310
311
|
case "FunctionDeclaration":
|
311
|
-
|
312
|
-
return findJSDocComment(this.getCommentsBefore(parent), parent.loc.start.line);
|
313
|
-
}
|
314
|
-
return findJSDocComment(leadingComments, node.loc.start.line);
|
312
|
+
return findJSDocComment(looksLikeExport(parent) ? parent : node);
|
315
313
|
|
316
314
|
case "ClassExpression":
|
317
|
-
return findJSDocComment(
|
315
|
+
return findJSDocComment(parent.parent);
|
318
316
|
|
319
317
|
case "ArrowFunctionExpression":
|
320
318
|
case "FunctionExpression":
|
321
319
|
if (parent.type !== "CallExpression" && parent.type !== "NewExpression") {
|
322
|
-
|
323
|
-
|
324
|
-
|
320
|
+
while (
|
321
|
+
!this.getCommentsBefore(parent).length &&
|
322
|
+
!/Function/.test(parent.type) &&
|
323
|
+
parent.type !== "MethodDefinition" &&
|
324
|
+
parent.type !== "Property"
|
325
|
+
) {
|
325
326
|
parent = parent.parent;
|
326
327
|
|
327
328
|
if (!parent) {
|
328
329
|
break;
|
329
330
|
}
|
330
|
-
|
331
|
-
parentLeadingComments = this.getCommentsBefore(parent);
|
332
331
|
}
|
333
332
|
|
334
|
-
|
335
|
-
|
336
|
-
|
333
|
+
if (parent && parent.type !== "FunctionDeclaration" && parent.type !== "Program") {
|
334
|
+
return findJSDocComment(parent);
|
335
|
+
}
|
337
336
|
}
|
338
337
|
|
339
|
-
|
338
|
+
return findJSDocComment(node);
|
340
339
|
|
340
|
+
// falls through
|
341
341
|
default:
|
342
342
|
return null;
|
343
343
|
}
|
@@ -347,15 +347,18 @@ class SourceCode extends TokenStore {
|
|
347
347
|
* Gets the deepest node containing a range index.
|
348
348
|
* @param {int} index Range index of the desired node.
|
349
349
|
* @returns {ASTNode} The node if found or null if not found.
|
350
|
+
* @public
|
350
351
|
*/
|
351
352
|
getNodeByRangeIndex(index) {
|
352
|
-
let result = null
|
353
|
+
let result = null,
|
354
|
+
resultParent = null;
|
353
355
|
const traverser = new Traverser();
|
354
356
|
|
355
357
|
traverser.traverse(this.ast, {
|
356
|
-
enter(node) {
|
358
|
+
enter(node, parent) {
|
357
359
|
if (node.range[0] <= index && index < node.range[1]) {
|
358
360
|
result = node;
|
361
|
+
resultParent = parent;
|
359
362
|
} else {
|
360
363
|
this.skip();
|
361
364
|
}
|
@@ -367,7 +370,7 @@ class SourceCode extends TokenStore {
|
|
367
370
|
}
|
368
371
|
});
|
369
372
|
|
370
|
-
return result;
|
373
|
+
return result ? Object.assign({ parent: resultParent }, result) : null;
|
371
374
|
}
|
372
375
|
|
373
376
|
/**
|
@@ -378,6 +381,7 @@ class SourceCode extends TokenStore {
|
|
378
381
|
* @param {Token} second The token to check before.
|
379
382
|
* @returns {boolean} True if there is only space between tokens, false
|
380
383
|
* if there is anything other than whitespace between tokens.
|
384
|
+
* @public
|
381
385
|
*/
|
382
386
|
isSpaceBetweenTokens(first, second) {
|
383
387
|
const text = this.text.slice(first.range[1], second.range[0]);
|
@@ -386,10 +390,11 @@ class SourceCode extends TokenStore {
|
|
386
390
|
}
|
387
391
|
|
388
392
|
/**
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
+
* Converts a source text index into a (line, column) pair.
|
394
|
+
* @param {number} index The index of a character in a file
|
395
|
+
* @returns {Object} A {line, column} location object with a 0-indexed column
|
396
|
+
* @public
|
397
|
+
*/
|
393
398
|
getLocFromIndex(index) {
|
394
399
|
if (typeof index !== "number") {
|
395
400
|
throw new TypeError("Expected `index` to be a number.");
|
@@ -420,12 +425,13 @@ class SourceCode extends TokenStore {
|
|
420
425
|
}
|
421
426
|
|
422
427
|
/**
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
428
|
+
* Converts a (line, column) pair into a range index.
|
429
|
+
* @param {Object} loc A line/column location
|
430
|
+
* @param {number} loc.line The line number of the location (1-indexed)
|
431
|
+
* @param {number} loc.column The column number of the location (0-indexed)
|
432
|
+
* @returns {number} The range index of the location in the file.
|
433
|
+
* @public
|
434
|
+
*/
|
429
435
|
getIndexFromLoc(loc) {
|
430
436
|
if (typeof loc !== "object" || typeof loc.line !== "number" || typeof loc.column !== "number") {
|
431
437
|
throw new TypeError("Expected `loc` to be an object with numeric `line` and `column` properties.");
|
@@ -2,6 +2,6 @@ ESLint couldn't find a configuration file. To set up a configuration file for th
|
|
2
2
|
|
3
3
|
eslint --init
|
4
4
|
|
5
|
-
ESLint looked for configuration files in <%= directory %> and its ancestors.
|
5
|
+
ESLint looked for configuration files in <%= directory %> and its ancestors. If it found none, it then looked in your home directory.
|
6
6
|
|
7
7
|
If you think you already have a configuration file or if you need more help, please stop by the ESLint chat room: https://gitter.im/eslint/eslint
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "4.
|
3
|
+
"version": "4.10.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -20,8 +20,7 @@
|
|
20
20
|
"browserify": "node Makefile.js browserify",
|
21
21
|
"perf": "node Makefile.js perf",
|
22
22
|
"profile": "beefy tests/bench/bench.js --open -- -t brfs -t ./tests/bench/xform-rules.js -r espree",
|
23
|
-
"coveralls": "cat ./coverage/lcov.info | coveralls"
|
24
|
-
"check-commit": "node Makefile.js checkGitCommit"
|
23
|
+
"coveralls": "cat ./coverage/lcov.info | coveralls"
|
25
24
|
},
|
26
25
|
"files": [
|
27
26
|
"LICENSE",
|
@@ -32,7 +31,7 @@
|
|
32
31
|
"messages"
|
33
32
|
],
|
34
33
|
"repository": "eslint/eslint",
|
35
|
-
"homepage": "
|
34
|
+
"homepage": "https://eslint.org",
|
36
35
|
"bugs": "https://github.com/eslint/eslint/issues/",
|
37
36
|
"dependencies": {
|
38
37
|
"ajv": "^5.2.0",
|
@@ -1,130 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @fileoverview Internal rule to enforce meta.docs.description conventions.
|
3
|
-
* @author Vitor Balocco
|
4
|
-
*/
|
5
|
-
|
6
|
-
"use strict";
|
7
|
-
|
8
|
-
const ALLOWED_FIRST_WORDS = [
|
9
|
-
"enforce",
|
10
|
-
"require",
|
11
|
-
"disallow"
|
12
|
-
];
|
13
|
-
|
14
|
-
//------------------------------------------------------------------------------
|
15
|
-
// Helpers
|
16
|
-
//------------------------------------------------------------------------------
|
17
|
-
|
18
|
-
/**
|
19
|
-
* Gets the property of the Object node passed in that has the name specified.
|
20
|
-
*
|
21
|
-
* @param {string} property Name of the property to return.
|
22
|
-
* @param {ASTNode} node The ObjectExpression node.
|
23
|
-
* @returns {ASTNode} The Property node or null if not found.
|
24
|
-
*/
|
25
|
-
function getPropertyFromObject(property, node) {
|
26
|
-
const properties = node.properties;
|
27
|
-
|
28
|
-
for (let i = 0; i < properties.length; i++) {
|
29
|
-
if (properties[i].key.name === property) {
|
30
|
-
return properties[i];
|
31
|
-
}
|
32
|
-
}
|
33
|
-
|
34
|
-
return null;
|
35
|
-
}
|
36
|
-
|
37
|
-
/**
|
38
|
-
* Verifies that the meta.docs.description property follows our internal conventions.
|
39
|
-
*
|
40
|
-
* @param {RuleContext} context The ESLint rule context.
|
41
|
-
* @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
|
42
|
-
* @returns {void}
|
43
|
-
*/
|
44
|
-
function checkMetaDocsDescription(context, exportsNode) {
|
45
|
-
if (exportsNode.type !== "ObjectExpression") {
|
46
|
-
|
47
|
-
// if the exported node is not the correct format, "internal-no-invalid-meta" will already report this.
|
48
|
-
return;
|
49
|
-
}
|
50
|
-
|
51
|
-
const metaProperty = getPropertyFromObject("meta", exportsNode);
|
52
|
-
const metaDocs = metaProperty && getPropertyFromObject("docs", metaProperty.value);
|
53
|
-
const metaDocsDescription = metaDocs && getPropertyFromObject("description", metaDocs.value);
|
54
|
-
|
55
|
-
if (!metaDocsDescription) {
|
56
|
-
|
57
|
-
// if there is no `meta.docs.description` property, "internal-no-invalid-meta" will already report this.
|
58
|
-
return;
|
59
|
-
}
|
60
|
-
|
61
|
-
const description = metaDocsDescription.value.value;
|
62
|
-
|
63
|
-
if (typeof description !== "string") {
|
64
|
-
context.report({
|
65
|
-
node: metaDocsDescription.value,
|
66
|
-
message: "`meta.docs.description` should be a string."
|
67
|
-
});
|
68
|
-
return;
|
69
|
-
}
|
70
|
-
|
71
|
-
if (description === "") {
|
72
|
-
context.report({
|
73
|
-
node: metaDocsDescription.value,
|
74
|
-
message: "`meta.docs.description` should not be empty."
|
75
|
-
});
|
76
|
-
return;
|
77
|
-
}
|
78
|
-
|
79
|
-
if (description.indexOf(" ") === 0) {
|
80
|
-
context.report({
|
81
|
-
node: metaDocsDescription.value,
|
82
|
-
message: "`meta.docs.description` should not start with whitespace."
|
83
|
-
});
|
84
|
-
return;
|
85
|
-
}
|
86
|
-
|
87
|
-
const firstWord = description.split(" ")[0];
|
88
|
-
|
89
|
-
if (ALLOWED_FIRST_WORDS.indexOf(firstWord) === -1) {
|
90
|
-
context.report({
|
91
|
-
node: metaDocsDescription.value,
|
92
|
-
message: "`meta.docs.description` should start with one of the following words: {{ allowedWords }}. Started with \"{{ firstWord }}\" instead.",
|
93
|
-
data: {
|
94
|
-
allowedWords: ALLOWED_FIRST_WORDS.join(", "),
|
95
|
-
firstWord
|
96
|
-
}
|
97
|
-
});
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
//------------------------------------------------------------------------------
|
102
|
-
// Rule Definition
|
103
|
-
//------------------------------------------------------------------------------
|
104
|
-
|
105
|
-
module.exports = {
|
106
|
-
meta: {
|
107
|
-
docs: {
|
108
|
-
description: "enforce correct conventions of `meta.docs.description` property in core rules",
|
109
|
-
category: "Internal",
|
110
|
-
recommended: false
|
111
|
-
},
|
112
|
-
|
113
|
-
schema: []
|
114
|
-
},
|
115
|
-
|
116
|
-
create(context) {
|
117
|
-
return {
|
118
|
-
AssignmentExpression(node) {
|
119
|
-
if (node.left &&
|
120
|
-
node.right &&
|
121
|
-
node.left.type === "MemberExpression" &&
|
122
|
-
node.left.object.name === "module" &&
|
123
|
-
node.left.property.name === "exports") {
|
124
|
-
|
125
|
-
checkMetaDocsDescription(context, node.right);
|
126
|
-
}
|
127
|
-
}
|
128
|
-
};
|
129
|
-
}
|
130
|
-
};
|
@@ -1,188 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @fileoverview Internal rule to prevent missing or invalid meta property in core rules.
|
3
|
-
* @author Vitor Balocco
|
4
|
-
*/
|
5
|
-
|
6
|
-
"use strict";
|
7
|
-
|
8
|
-
//------------------------------------------------------------------------------
|
9
|
-
// Helpers
|
10
|
-
//------------------------------------------------------------------------------
|
11
|
-
|
12
|
-
/**
|
13
|
-
* Gets the property of the Object node passed in that has the name specified.
|
14
|
-
*
|
15
|
-
* @param {string} property Name of the property to return.
|
16
|
-
* @param {ASTNode} node The ObjectExpression node.
|
17
|
-
* @returns {ASTNode} The Property node or null if not found.
|
18
|
-
*/
|
19
|
-
function getPropertyFromObject(property, node) {
|
20
|
-
const properties = node.properties;
|
21
|
-
|
22
|
-
for (let i = 0; i < properties.length; i++) {
|
23
|
-
if (properties[i].key.name === property) {
|
24
|
-
return properties[i];
|
25
|
-
}
|
26
|
-
}
|
27
|
-
|
28
|
-
return null;
|
29
|
-
}
|
30
|
-
|
31
|
-
/**
|
32
|
-
* Extracts the `meta` property from the ObjectExpression that all rules export.
|
33
|
-
*
|
34
|
-
* @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
|
35
|
-
* @returns {ASTNode} The `meta` Property node or null if not found.
|
36
|
-
*/
|
37
|
-
function getMetaPropertyFromExportsNode(exportsNode) {
|
38
|
-
return getPropertyFromObject("meta", exportsNode);
|
39
|
-
}
|
40
|
-
|
41
|
-
/**
|
42
|
-
* Whether this `meta` ObjectExpression has a `docs` property defined or not.
|
43
|
-
*
|
44
|
-
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
|
45
|
-
* @returns {boolean} `true` if a `docs` property exists.
|
46
|
-
*/
|
47
|
-
function hasMetaDocs(metaPropertyNode) {
|
48
|
-
return Boolean(getPropertyFromObject("docs", metaPropertyNode.value));
|
49
|
-
}
|
50
|
-
|
51
|
-
/**
|
52
|
-
* Whether this `meta` ObjectExpression has a `docs.description` property defined or not.
|
53
|
-
*
|
54
|
-
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
|
55
|
-
* @returns {boolean} `true` if a `docs.description` property exists.
|
56
|
-
*/
|
57
|
-
function hasMetaDocsDescription(metaPropertyNode) {
|
58
|
-
const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
|
59
|
-
|
60
|
-
return metaDocs && getPropertyFromObject("description", metaDocs.value);
|
61
|
-
}
|
62
|
-
|
63
|
-
/**
|
64
|
-
* Whether this `meta` ObjectExpression has a `docs.category` property defined or not.
|
65
|
-
*
|
66
|
-
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
|
67
|
-
* @returns {boolean} `true` if a `docs.category` property exists.
|
68
|
-
*/
|
69
|
-
function hasMetaDocsCategory(metaPropertyNode) {
|
70
|
-
const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
|
71
|
-
|
72
|
-
return metaDocs && getPropertyFromObject("category", metaDocs.value);
|
73
|
-
}
|
74
|
-
|
75
|
-
/**
|
76
|
-
* Whether this `meta` ObjectExpression has a `docs.recommended` property defined or not.
|
77
|
-
*
|
78
|
-
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
|
79
|
-
* @returns {boolean} `true` if a `docs.recommended` property exists.
|
80
|
-
*/
|
81
|
-
function hasMetaDocsRecommended(metaPropertyNode) {
|
82
|
-
const metaDocs = getPropertyFromObject("docs", metaPropertyNode.value);
|
83
|
-
|
84
|
-
return metaDocs && getPropertyFromObject("recommended", metaDocs.value);
|
85
|
-
}
|
86
|
-
|
87
|
-
/**
|
88
|
-
* Whether this `meta` ObjectExpression has a `schema` property defined or not.
|
89
|
-
*
|
90
|
-
* @param {ASTNode} metaPropertyNode The `meta` ObjectExpression for this rule.
|
91
|
-
* @returns {boolean} `true` if a `schema` property exists.
|
92
|
-
*/
|
93
|
-
function hasMetaSchema(metaPropertyNode) {
|
94
|
-
return getPropertyFromObject("schema", metaPropertyNode.value);
|
95
|
-
}
|
96
|
-
|
97
|
-
/**
|
98
|
-
* Checks the validity of the meta definition of this rule and reports any errors found.
|
99
|
-
*
|
100
|
-
* @param {RuleContext} context The ESLint rule context.
|
101
|
-
* @param {ASTNode} exportsNode ObjectExpression node that the rule exports.
|
102
|
-
* @param {boolean} ruleIsFixable whether the rule is fixable or not.
|
103
|
-
* @returns {void}
|
104
|
-
*/
|
105
|
-
function checkMetaValidity(context, exportsNode) {
|
106
|
-
const metaProperty = getMetaPropertyFromExportsNode(exportsNode);
|
107
|
-
|
108
|
-
if (!metaProperty) {
|
109
|
-
context.report(exportsNode, "Rule is missing a meta property.");
|
110
|
-
return;
|
111
|
-
}
|
112
|
-
|
113
|
-
if (!hasMetaDocs(metaProperty)) {
|
114
|
-
context.report(metaProperty, "Rule is missing a meta.docs property.");
|
115
|
-
return;
|
116
|
-
}
|
117
|
-
|
118
|
-
if (!hasMetaDocsDescription(metaProperty)) {
|
119
|
-
context.report(metaProperty, "Rule is missing a meta.docs.description property.");
|
120
|
-
return;
|
121
|
-
}
|
122
|
-
|
123
|
-
if (!hasMetaDocsCategory(metaProperty)) {
|
124
|
-
context.report(metaProperty, "Rule is missing a meta.docs.category property.");
|
125
|
-
return;
|
126
|
-
}
|
127
|
-
|
128
|
-
if (!hasMetaDocsRecommended(metaProperty)) {
|
129
|
-
context.report(metaProperty, "Rule is missing a meta.docs.recommended property.");
|
130
|
-
return;
|
131
|
-
}
|
132
|
-
|
133
|
-
if (!hasMetaSchema(metaProperty)) {
|
134
|
-
context.report(metaProperty, "Rule is missing a meta.schema property.");
|
135
|
-
}
|
136
|
-
}
|
137
|
-
|
138
|
-
/**
|
139
|
-
* Whether this node is the correct format for a rule definition or not.
|
140
|
-
*
|
141
|
-
* @param {ASTNode} node node that the rule exports.
|
142
|
-
* @returns {boolean} `true` if the exported node is the correct format for a rule definition
|
143
|
-
*/
|
144
|
-
function isCorrectExportsFormat(node) {
|
145
|
-
return node.type === "ObjectExpression";
|
146
|
-
}
|
147
|
-
|
148
|
-
//------------------------------------------------------------------------------
|
149
|
-
// Rule Definition
|
150
|
-
//------------------------------------------------------------------------------
|
151
|
-
|
152
|
-
module.exports = {
|
153
|
-
meta: {
|
154
|
-
docs: {
|
155
|
-
description: "enforce correct use of `meta` property in core rules",
|
156
|
-
category: "Internal",
|
157
|
-
recommended: false
|
158
|
-
},
|
159
|
-
|
160
|
-
schema: []
|
161
|
-
},
|
162
|
-
|
163
|
-
create(context) {
|
164
|
-
let exportsNode;
|
165
|
-
|
166
|
-
return {
|
167
|
-
AssignmentExpression(node) {
|
168
|
-
if (node.left &&
|
169
|
-
node.right &&
|
170
|
-
node.left.type === "MemberExpression" &&
|
171
|
-
node.left.object.name === "module" &&
|
172
|
-
node.left.property.name === "exports") {
|
173
|
-
|
174
|
-
exportsNode = node.right;
|
175
|
-
}
|
176
|
-
},
|
177
|
-
|
178
|
-
"Program:exit"() {
|
179
|
-
if (!isCorrectExportsFormat(exportsNode)) {
|
180
|
-
context.report({ node: exportsNode, message: "Rule does not export an Object. Make sure the rule follows the new rule format." });
|
181
|
-
return;
|
182
|
-
}
|
183
|
-
|
184
|
-
checkMetaValidity(context, exportsNode);
|
185
|
-
}
|
186
|
-
};
|
187
|
-
}
|
188
|
-
};
|