]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-warning-comments.js
5f3ea21d7d0e867e285a3d0e7fc5cf77b9fa9098
[pve-eslint.git] / 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 /** @type {import('../shared/types').Rule} */
18 module.exports = {
19 meta: {
20 type: "suggestion",
21
22 docs: {
23 description: "disallow specified warning terms in comments",
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 };