UNPKG

3.89 kBJavaScriptView Raw
1/**
2 * @fileoverview Internal rule to enforce meta.docs.description conventions.
3 * @author Vitor Balocco
4 */
5
6"use strict";
7
8const 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 */
25function 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 */
44function 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
105module.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};