massive update, probably broken
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-warning-comments.js
1 /**
2  * @fileoverview Rule that warns about used warning comments
3  * @author Alexander Schmidt <https://github.com/lxanders>
4  */
5
6 "use strict";
7
8 const escapeRegExp = require("escape-string-regexp");
9 const astUtils = require("./utils/ast-utils");
10
11 const CHAR_LIMIT = 40;
12
13 //------------------------------------------------------------------------------
14 // Rule Definition
15 //------------------------------------------------------------------------------
16
17 module.exports = {
18     meta: {
19         type: "suggestion",
20
21         docs: {
22             description: "disallow specified warning terms in comments",
23             category: "Best Practices",
24             recommended: false,
25             url: "https://eslint.org/docs/rules/no-warning-comments"
26         },
27
28         schema: [
29             {
30                 type: "object",
31                 properties: {
32                     terms: {
33                         type: "array",
34                         items: {
35                             type: "string"
36                         }
37                     },
38                     location: {
39                         enum: ["start", "anywhere"]
40                     }
41                 },
42                 additionalProperties: false
43             }
44         ],
45
46         messages: {
47             unexpectedComment: "Unexpected '{{matchedTerm}}' comment: '{{comment}}'."
48         }
49     },
50
51     create(context) {
52         const sourceCode = context.getSourceCode(),
53             configuration = context.options[0] || {},
54             warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
55             location = configuration.location || "start",
56             selfConfigRegEx = /\bno-warning-comments\b/u;
57
58         /**
59          * Convert a warning term into a RegExp which will match a comment containing that whole word in the specified
60          * location ("start" or "anywhere"). If the term starts or ends with non word characters, then the match will not
61          * require word boundaries on that side.
62          * @param {string} term A term to convert to a RegExp
63          * @returns {RegExp} The term converted to a RegExp
64          */
65         function convertToRegExp(term) {
66             const escaped = escapeRegExp(term);
67             const wordBoundary = "\\b";
68             const eitherOrWordBoundary = `|${wordBoundary}`;
69             let prefix;
70
71             /*
72              * If the term ends in a word character (a-z0-9_), ensure a word
73              * boundary at the end, so that substrings do not get falsely
74              * matched. eg "todo" in a string such as "mastodon".
75              * If the term ends in a non-word character, then \b won't match on
76              * the boundary to the next non-word character, which would likely
77              * be a space. For example `/\bFIX!\b/.test('FIX! blah') === false`.
78              * In these cases, use no bounding match. Same applies for the
79              * prefix, handled below.
80              */
81             const suffix = /\w$/u.test(term) ? "\\b" : "";
82
83             if (location === "start") {
84
85                 /*
86                  * When matching at the start, ignore leading whitespace, and
87                  * there's no need to worry about word boundaries.
88                  */
89                 prefix = "^\\s*";
90             } else if (/^\w/u.test(term)) {
91                 prefix = wordBoundary;
92             } else {
93                 prefix = "";
94             }
95
96             if (location === "start") {
97
98                 /*
99                  * For location "start" the regex should be
100                  * ^\s*TERM\b.  This checks the word boundary
101                  * at the beginning of the comment.
102                  */
103                 return new RegExp(prefix + escaped + suffix, "iu");
104             }
105
106             /*
107              * For location "anywhere" the regex should be
108              * \bTERM\b|\bTERM\b, this checks the entire comment
109              * for the term.
110              */
111             return new RegExp(
112                 prefix +
113                     escaped +
114                     suffix +
115                     eitherOrWordBoundary +
116                     term +
117                     wordBoundary,
118                 "iu"
119             );
120         }
121
122         const warningRegExps = warningTerms.map(convertToRegExp);
123
124         /**
125          * Checks the specified comment for matches of the configured warning terms and returns the matches.
126          * @param {string} comment The comment which is checked.
127          * @returns {Array} All matched warning terms for this comment.
128          */
129         function commentContainsWarningTerm(comment) {
130             const matches = [];
131
132             warningRegExps.forEach((regex, index) => {
133                 if (regex.test(comment)) {
134                     matches.push(warningTerms[index]);
135                 }
136             });
137
138             return matches;
139         }
140
141         /**
142          * Checks the specified node for matching warning comments and reports them.
143          * @param {ASTNode} node The AST node being checked.
144          * @returns {void} undefined.
145          */
146         function checkComment(node) {
147             const comment = node.value;
148
149             if (
150                 astUtils.isDirectiveComment(node) &&
151                 selfConfigRegEx.test(comment)
152             ) {
153                 return;
154             }
155
156             const matches = commentContainsWarningTerm(comment);
157
158             matches.forEach(matchedTerm => {
159                 let commentToDisplay = "";
160                 let truncated = false;
161
162                 for (const c of comment.trim().split(/\s+/u)) {
163                     const tmp = commentToDisplay ? `${commentToDisplay} ${c}` : c;
164
165                     if (tmp.length <= CHAR_LIMIT) {
166                         commentToDisplay = tmp;
167                     } else {
168                         truncated = true;
169                         break;
170                     }
171                 }
172
173                 context.report({
174                     node,
175                     messageId: "unexpectedComment",
176                     data: {
177                         matchedTerm,
178                         comment: `${commentToDisplay}${
179                             truncated ? "..." : ""
180                         }`
181                     }
182                 });
183             });
184         }
185
186         return {
187             Program() {
188                 const comments = sourceCode.getAllComments();
189
190                 comments
191                     .filter(token => token.type !== "Shebang")
192                     .forEach(checkComment);
193             }
194         };
195     }
196 };