eslint 4.7.2 → 4.11.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.
Files changed (139) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/README.md +34 -19
  3. package/conf/default-cli-options.js +7 -4
  4. package/conf/eslint-recommended.js +2 -0
  5. package/lib/ast-utils.js +83 -42
  6. package/lib/cli-engine.js +53 -17
  7. package/lib/cli.js +17 -9
  8. package/lib/code-path-analysis/code-path-analyzer.js +8 -4
  9. package/lib/code-path-analysis/code-path-segment.js +43 -41
  10. package/lib/code-path-analysis/code-path-state.js +7 -2
  11. package/lib/config/autoconfig.js +14 -12
  12. package/lib/config/config-file.js +8 -51
  13. package/lib/config/config-initializer.js +10 -6
  14. package/lib/config/config-ops.js +21 -21
  15. package/lib/config/config-rule.js +24 -24
  16. package/lib/config/config-validator.js +38 -36
  17. package/lib/config/plugins.js +8 -35
  18. package/lib/config.js +12 -8
  19. package/lib/formatters/html-template-message.html +1 -1
  20. package/lib/formatters/html-template-page.html +3 -1
  21. package/lib/formatters/html.js +2 -1
  22. package/lib/formatters/junit.js +21 -15
  23. package/lib/formatters/tap.js +5 -3
  24. package/lib/ignored-paths.js +5 -3
  25. package/lib/linter.js +42 -42
  26. package/lib/logging.js +2 -2
  27. package/lib/options.js +12 -0
  28. package/lib/rules/.eslintrc.yml +2 -2
  29. package/lib/rules/array-bracket-newline.js +39 -25
  30. package/lib/rules/array-bracket-spacing.js +28 -28
  31. package/lib/rules/array-callback-return.js +13 -9
  32. package/lib/rules/array-element-newline.js +8 -8
  33. package/lib/rules/arrow-body-style.js +12 -6
  34. package/lib/rules/arrow-parens.js +4 -2
  35. package/lib/rules/block-spacing.js +1 -1
  36. package/lib/rules/brace-style.js +14 -14
  37. package/lib/rules/callback-return.js +2 -1
  38. package/lib/rules/capitalized-comments.js +2 -1
  39. package/lib/rules/comma-style.js +3 -1
  40. package/lib/rules/computed-property-spacing.js +22 -22
  41. package/lib/rules/consistent-return.js +4 -4
  42. package/lib/rules/consistent-this.js +4 -2
  43. package/lib/rules/curly.js +13 -9
  44. package/lib/rules/dot-notation.js +56 -35
  45. package/lib/rules/func-call-spacing.js +4 -2
  46. package/lib/rules/generator-star-spacing.js +3 -3
  47. package/lib/rules/getter-return.js +2 -1
  48. package/lib/rules/indent-legacy.js +25 -14
  49. package/lib/rules/indent.js +101 -91
  50. package/lib/rules/key-spacing.js +5 -3
  51. package/lib/rules/lines-around-comment.js +33 -4
  52. package/lib/rules/lines-around-directive.js +16 -12
  53. package/lib/rules/lines-between-class-members.js +91 -0
  54. package/lib/rules/max-len.js +2 -3
  55. package/lib/rules/max-statements-per-line.js +5 -3
  56. package/lib/rules/multiline-comment-style.js +294 -0
  57. package/lib/rules/new-cap.js +2 -1
  58. package/lib/rules/newline-after-var.js +8 -6
  59. package/lib/rules/newline-before-return.js +13 -9
  60. package/lib/rules/no-alert.js +7 -15
  61. package/lib/rules/no-await-in-loop.js +17 -9
  62. package/lib/rules/no-bitwise.js +5 -3
  63. package/lib/rules/no-catch-shadow.js +4 -2
  64. package/lib/rules/no-console.js +2 -1
  65. package/lib/rules/no-constant-condition.js +2 -2
  66. package/lib/rules/no-control-regex.js +2 -1
  67. package/lib/rules/no-else-return.js +60 -19
  68. package/lib/rules/no-empty-character-class.js +11 -11
  69. package/lib/rules/no-extra-parens.js +22 -11
  70. package/lib/rules/no-extra-semi.js +5 -3
  71. package/lib/rules/no-global-assign.js +4 -2
  72. package/lib/rules/no-implicit-coercion.js +6 -6
  73. package/lib/rules/no-implied-eval.js +2 -1
  74. package/lib/rules/no-label-var.js +4 -2
  75. package/lib/rules/no-lone-blocks.js +3 -3
  76. package/lib/rules/no-lonely-if.js +2 -1
  77. package/lib/rules/no-loop-func.js +10 -7
  78. package/lib/rules/no-mixed-requires.js +8 -4
  79. package/lib/rules/no-native-reassign.js +4 -2
  80. package/lib/rules/no-param-reassign.js +4 -2
  81. package/lib/rules/no-regex-spaces.js +1 -1
  82. package/lib/rules/no-restricted-imports.js +86 -17
  83. package/lib/rules/no-restricted-modules.js +84 -15
  84. package/lib/rules/no-restricted-properties.js +10 -10
  85. package/lib/rules/no-return-await.js +6 -6
  86. package/lib/rules/no-self-assign.js +4 -2
  87. package/lib/rules/no-sequences.js +6 -4
  88. package/lib/rules/no-trailing-spaces.js +14 -8
  89. package/lib/rules/no-unneeded-ternary.js +3 -1
  90. package/lib/rules/no-unreachable.js +4 -2
  91. package/lib/rules/no-unused-labels.js +2 -1
  92. package/lib/rules/no-use-before-define.js +13 -11
  93. package/lib/rules/no-useless-call.js +1 -25
  94. package/lib/rules/no-useless-computed-key.js +2 -1
  95. package/lib/rules/no-useless-escape.js +31 -23
  96. package/lib/rules/no-useless-return.js +14 -8
  97. package/lib/rules/no-var.js +11 -0
  98. package/lib/rules/no-whitespace-before-property.js +4 -2
  99. package/lib/rules/object-curly-newline.js +9 -2
  100. package/lib/rules/object-curly-spacing.js +20 -20
  101. package/lib/rules/object-shorthand.js +47 -35
  102. package/lib/rules/operator-assignment.js +9 -9
  103. package/lib/rules/operator-linebreak.js +15 -11
  104. package/lib/rules/padding-line-between-statements.js +6 -4
  105. package/lib/rules/prefer-arrow-callback.js +12 -10
  106. package/lib/rules/prefer-const.js +18 -10
  107. package/lib/rules/prefer-destructuring.js +4 -2
  108. package/lib/rules/prefer-numeric-literals.js +4 -2
  109. package/lib/rules/prefer-promise-reject-errors.js +16 -16
  110. package/lib/rules/prefer-rest-params.js +4 -2
  111. package/lib/rules/prefer-spread.js +1 -25
  112. package/lib/rules/prefer-template.js +33 -29
  113. package/lib/rules/quote-props.js +8 -8
  114. package/lib/rules/require-jsdoc.js +11 -18
  115. package/lib/rules/semi-style.js +44 -19
  116. package/lib/rules/semi.js +5 -3
  117. package/lib/rules/sort-imports.js +11 -6
  118. package/lib/rules/space-unary-ops.js +67 -69
  119. package/lib/rules/strict.js +8 -8
  120. package/lib/rules/valid-jsdoc.js +39 -33
  121. package/lib/rules/valid-typeof.js +4 -4
  122. package/lib/rules/wrap-iife.js +4 -4
  123. package/lib/rules/yoda.js +9 -7
  124. package/lib/testers/rule-tester.js +63 -40
  125. package/lib/token-store/backward-token-cursor.js +5 -3
  126. package/lib/token-store/forward-token-cursor.js +5 -3
  127. package/lib/token-store/utils.js +8 -4
  128. package/lib/util/apply-disable-directives.js +56 -27
  129. package/lib/util/glob.js +1 -1
  130. package/lib/util/naming.js +112 -0
  131. package/lib/util/node-event-generator.js +13 -27
  132. package/lib/util/safe-emitter.js +54 -0
  133. package/lib/util/source-code-fixer.js +4 -2
  134. package/lib/util/source-code.js +70 -65
  135. package/messages/no-config-found.txt +1 -1
  136. package/package.json +8 -8
  137. package/lib/internal-rules/.eslintrc.yml +0 -3
  138. package/lib/internal-rules/internal-consistent-docs-description.js +0 -130
  139. package/lib/internal-rules/internal-no-invalid-meta.js +0 -188
@@ -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
- * Finds a JSDoc comment node in an array of comment nodes.
48
- * @param {ASTNode[]} comments The array of comment nodes to search.
49
- * @param {int} line Line number to look around
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} The list of comments indexed by their position.
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) {
@@ -248,7 +227,8 @@ class SourceCode extends TokenStore {
248
227
  }
249
228
  } else {
250
229
 
251
- /* Return comments as trailing comments of nodes that only contain
230
+ /*
231
+ * Return comments as trailing comments of nodes that only contain
252
232
  * comments (to mimic the comment attachment behavior present in Espree).
253
233
  */
254
234
  if ((node.type === "BlockStatement" || node.type === "ClassBody") && node.body.length === 0 ||
@@ -297,47 +277,68 @@ class SourceCode extends TokenStore {
297
277
  /**
298
278
  * Retrieves the JSDoc comment for a given node.
299
279
  * @param {ASTNode} node The AST node to get the comment for.
300
- * @returns {ASTNode} The Block comment node containing the JSDoc for the
301
- * given node or null if not found.
280
+ * @returns {Token|null} The Block comment token containing the JSDoc comment
281
+ * for the given node or null if not found.
302
282
  * @public
303
283
  */
304
284
  getJSDocComment(node) {
285
+
286
+ /**
287
+ * Checks for the presence of a JSDoc comment for the given node and returns it.
288
+ * @param {ASTNode} astNode The AST node to get the comment for.
289
+ * @returns {Token|null} The Block comment token containing the JSDoc comment
290
+ * for the given node or null if not found.
291
+ * @private
292
+ */
293
+ const findJSDocComment = astNode => {
294
+ const tokenBefore = this.getTokenBefore(astNode, { includeComments: true });
295
+
296
+ if (
297
+ tokenBefore &&
298
+ astUtils.isCommentToken(tokenBefore) &&
299
+ tokenBefore.type === "Block" &&
300
+ tokenBefore.value.charAt(0) === "*" &&
301
+ astNode.loc.start.line - tokenBefore.loc.end.line <= 1
302
+ ) {
303
+ return tokenBefore;
304
+ }
305
+
306
+ return null;
307
+ };
305
308
  let parent = node.parent;
306
- const leadingComments = this.getCommentsBefore(node);
307
309
 
308
310
  switch (node.type) {
309
311
  case "ClassDeclaration":
310
312
  case "FunctionDeclaration":
311
- if (looksLikeExport(parent)) {
312
- return findJSDocComment(this.getCommentsBefore(parent), parent.loc.start.line);
313
- }
314
- return findJSDocComment(leadingComments, node.loc.start.line);
313
+ return findJSDocComment(looksLikeExport(parent) ? parent : node);
315
314
 
316
315
  case "ClassExpression":
317
- return findJSDocComment(this.getCommentsBefore(parent.parent), parent.parent.loc.start.line);
316
+ return findJSDocComment(parent.parent);
318
317
 
319
318
  case "ArrowFunctionExpression":
320
319
  case "FunctionExpression":
321
320
  if (parent.type !== "CallExpression" && parent.type !== "NewExpression") {
322
- let parentLeadingComments = this.getCommentsBefore(parent);
323
-
324
- while (!parentLeadingComments.length && !/Function/.test(parent.type) && parent.type !== "MethodDefinition" && parent.type !== "Property") {
321
+ while (
322
+ !this.getCommentsBefore(parent).length &&
323
+ !/Function/.test(parent.type) &&
324
+ parent.type !== "MethodDefinition" &&
325
+ parent.type !== "Property"
326
+ ) {
325
327
  parent = parent.parent;
326
328
 
327
329
  if (!parent) {
328
330
  break;
329
331
  }
330
-
331
- parentLeadingComments = this.getCommentsBefore(parent);
332
332
  }
333
333
 
334
- return parent && parent.type !== "FunctionDeclaration" && parent.type !== "Program" ? findJSDocComment(parentLeadingComments, parent.loc.start.line) : null;
335
- } else if (leadingComments.length) {
336
- return findJSDocComment(leadingComments, node.loc.start.line);
334
+ if (parent && parent.type !== "FunctionDeclaration" && parent.type !== "Program") {
335
+ return findJSDocComment(parent);
336
+ }
337
337
  }
338
338
 
339
- // falls through
339
+ return findJSDocComment(node);
340
340
 
341
+ // falls through
341
342
  default:
342
343
  return null;
343
344
  }
@@ -347,6 +348,7 @@ class SourceCode extends TokenStore {
347
348
  * Gets the deepest node containing a range index.
348
349
  * @param {int} index Range index of the desired node.
349
350
  * @returns {ASTNode} The node if found or null if not found.
351
+ * @public
350
352
  */
351
353
  getNodeByRangeIndex(index) {
352
354
  let result = null,
@@ -380,6 +382,7 @@ class SourceCode extends TokenStore {
380
382
  * @param {Token} second The token to check before.
381
383
  * @returns {boolean} True if there is only space between tokens, false
382
384
  * if there is anything other than whitespace between tokens.
385
+ * @public
383
386
  */
384
387
  isSpaceBetweenTokens(first, second) {
385
388
  const text = this.text.slice(first.range[1], second.range[0]);
@@ -388,10 +391,11 @@ class SourceCode extends TokenStore {
388
391
  }
389
392
 
390
393
  /**
391
- * Converts a source text index into a (line, column) pair.
392
- * @param {number} index The index of a character in a file
393
- * @returns {Object} A {line, column} location object with a 0-indexed column
394
- */
394
+ * Converts a source text index into a (line, column) pair.
395
+ * @param {number} index The index of a character in a file
396
+ * @returns {Object} A {line, column} location object with a 0-indexed column
397
+ * @public
398
+ */
395
399
  getLocFromIndex(index) {
396
400
  if (typeof index !== "number") {
397
401
  throw new TypeError("Expected `index` to be a number.");
@@ -422,12 +426,13 @@ class SourceCode extends TokenStore {
422
426
  }
423
427
 
424
428
  /**
425
- * Converts a (line, column) pair into a range index.
426
- * @param {Object} loc A line/column location
427
- * @param {number} loc.line The line number of the location (1-indexed)
428
- * @param {number} loc.column The column number of the location (0-indexed)
429
- * @returns {number} The range index of the location in the file.
430
- */
429
+ * Converts a (line, column) pair into a range index.
430
+ * @param {Object} loc A line/column location
431
+ * @param {number} loc.line The line number of the location (1-indexed)
432
+ * @param {number} loc.column The column number of the location (0-indexed)
433
+ * @returns {number} The range index of the location in the file.
434
+ * @public
435
+ */
431
436
  getIndexFromLoc(loc) {
432
437
  if (typeof loc !== "object" || typeof loc.line !== "number" || typeof loc.column !== "number") {
433
438
  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.7.2",
3
+ "version": "4.11.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,10 +31,10 @@
32
31
  "messages"
33
32
  ],
34
33
  "repository": "eslint/eslint",
35
- "homepage": "http://eslint.org",
34
+ "homepage": "https://eslint.org",
36
35
  "bugs": "https://github.com/eslint/eslint/issues/",
37
36
  "dependencies": {
38
- "ajv": "^5.2.0",
37
+ "ajv": "^5.3.0",
39
38
  "babel-code-frame": "^6.22.0",
40
39
  "chalk": "^2.1.0",
41
40
  "concat-stream": "^1.6.0",
@@ -43,7 +42,7 @@
43
42
  "debug": "^3.0.1",
44
43
  "doctrine": "^2.0.0",
45
44
  "eslint-scope": "^3.7.1",
46
- "espree": "^3.5.1",
45
+ "espree": "^3.5.2",
47
46
  "esquery": "^1.0.0",
48
47
  "estraverse": "^4.2.0",
49
48
  "esutils": "^2.0.2",
@@ -56,7 +55,7 @@
56
55
  "inquirer": "^3.0.6",
57
56
  "is-resolvable": "^1.0.0",
58
57
  "js-yaml": "^3.9.1",
59
- "json-stable-stringify": "^1.0.1",
58
+ "json-stable-stringify-without-jsonify": "^1.0.1",
60
59
  "levn": "^0.3.0",
61
60
  "lodash": "^4.17.4",
62
61
  "minimatch": "^3.0.2",
@@ -87,7 +86,9 @@
87
86
  "ejs": "^2.5.6",
88
87
  "eslint-plugin-eslint-plugin": "^1.2.0",
89
88
  "eslint-plugin-node": "^5.1.0",
89
+ "eslint-plugin-rulesdir": "^0.1.0",
90
90
  "eslint-release": "^0.10.1",
91
+ "eslint-rule-composer": "^0.1.0",
91
92
  "eslump": "1.6.0",
92
93
  "esprima": "^4.0.0",
93
94
  "esprima-fb": "^15001.1001.0-dev-harmony-fb",
@@ -107,7 +108,6 @@
107
108
  "phantomjs-prebuilt": "^2.1.14",
108
109
  "proxyquire": "^1.8.0",
109
110
  "shelljs": "^0.7.7",
110
- "shelljs-nodecli": "~0.1.1",
111
111
  "sinon": "^3.2.1",
112
112
  "temp": "^0.8.3",
113
113
  "through": "^2.3.8"
@@ -1,3 +0,0 @@
1
- rules:
2
- internal-no-invalid-meta: "error"
3
- internal-consistent-docs-description: "error"
@@ -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
- };