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