]> git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-warning-comments.js
first commit
[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("lodash");
9 const astUtils = require("./utils/ast-utils");
10
11 //------------------------------------------------------------------------------
12 // Rule Definition
13 //------------------------------------------------------------------------------
14
15 module.exports = {
16 meta: {
17 type: "suggestion",
18
19 docs: {
20 description: "disallow specified warning terms in comments",
21 category: "Best Practices",
22 recommended: false,
23 url: "https://eslint.org/docs/rules/no-warning-comments"
24 },
25
26 schema: [
27 {
28 type: "object",
29 properties: {
30 terms: {
31 type: "array",
32 items: {
33 type: "string"
34 }
35 },
36 location: {
37 enum: ["start", "anywhere"]
38 }
39 },
40 additionalProperties: false
41 }
42 ],
43
44 messages: {
45 unexpectedComment: "Unexpected '{{matchedTerm}}' comment."
46 }
47 },
48
49 create(context) {
50
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 */
110 return new RegExp(prefix + escaped + suffix + eitherOrWordBoundary + term + wordBoundary, "iu");
111 }
112
113 const warningRegExps = warningTerms.map(convertToRegExp);
114
115 /**
116 * Checks the specified comment for matches of the configured warning terms and returns the matches.
117 * @param {string} comment The comment which is checked.
118 * @returns {Array} All matched warning terms for this comment.
119 */
120 function commentContainsWarningTerm(comment) {
121 const matches = [];
122
123 warningRegExps.forEach((regex, index) => {
124 if (regex.test(comment)) {
125 matches.push(warningTerms[index]);
126 }
127 });
128
129 return matches;
130 }
131
132 /**
133 * Checks the specified node for matching warning comments and reports them.
134 * @param {ASTNode} node The AST node being checked.
135 * @returns {void} undefined.
136 */
137 function checkComment(node) {
138 if (astUtils.isDirectiveComment(node) && selfConfigRegEx.test(node.value)) {
139 return;
140 }
141
142 const matches = commentContainsWarningTerm(node.value);
143
144 matches.forEach(matchedTerm => {
145 context.report({
146 node,
147 messageId: "unexpectedComment",
148 data: {
149 matchedTerm
150 }
151 });
152 });
153 }
154
155 return {
156 Program() {
157 const comments = sourceCode.getAllComments();
158
159 comments.filter(token => token.type !== "Shebang").forEach(checkComment);
160 }
161 };
162 }
163 };