UNPKG

4.8 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule that warns about used warning comments
3 * @author Alexander Schmidt <https://github.com/lxanders>
4 */
5
6"use strict";
7
8const astUtils = require("../ast-utils");
9
10//------------------------------------------------------------------------------
11// Rule Definition
12//------------------------------------------------------------------------------
13
14module.exports = {
15 meta: {
16 docs: {
17 description: "disallow specified warning terms in comments",
18 category: "Best Practices",
19 recommended: false,
20 url: "https://eslint.org/docs/rules/no-warning-comments"
21 },
22
23 schema: [
24 {
25 type: "object",
26 properties: {
27 terms: {
28 type: "array",
29 items: {
30 type: "string"
31 }
32 },
33 location: {
34 enum: ["start", "anywhere"]
35 }
36 },
37 additionalProperties: false
38 }
39 ]
40 },
41
42 create(context) {
43
44 const sourceCode = context.getSourceCode(),
45 configuration = context.options[0] || {},
46 warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
47 location = configuration.location || "start",
48 selfConfigRegEx = /\bno-warning-comments\b/;
49
50 /**
51 * Convert a warning term into a RegExp which will match a comment containing that whole word in the specified
52 * location ("start" or "anywhere"). If the term starts or ends with non word characters, then the match will not
53 * require word boundaries on that side.
54 *
55 * @param {string} term A term to convert to a RegExp
56 * @returns {RegExp} The term converted to a RegExp
57 */
58 function convertToRegExp(term) {
59 const escaped = term.replace(/[-/\\$^*+?.()|[\]{}]/g, "\\$&");
60 let prefix;
61
62 /*
63 * If the term ends in a word character (a-z0-9_), ensure a word
64 * boundary at the end, so that substrings do not get falsely
65 * matched. eg "todo" in a string such as "mastodon".
66 * If the term ends in a non-word character, then \b won't match on
67 * the boundary to the next non-word character, which would likely
68 * be a space. For example `/\bFIX!\b/.test('FIX! blah') === false`.
69 * In these cases, use no bounding match. Same applies for the
70 * prefix, handled below.
71 */
72 const suffix = /\w$/.test(term) ? "\\b" : "";
73
74 if (location === "start") {
75
76 /*
77 * When matching at the start, ignore leading whitespace, and
78 * there's no need to worry about word boundaries.
79 */
80 prefix = "^\\s*";
81 } else if (/^\w/.test(term)) {
82 prefix = "\\b";
83 } else {
84 prefix = "";
85 }
86
87 return new RegExp(prefix + escaped + suffix, "i");
88 }
89
90 const warningRegExps = warningTerms.map(convertToRegExp);
91
92 /**
93 * Checks the specified comment for matches of the configured warning terms and returns the matches.
94 * @param {string} comment The comment which is checked.
95 * @returns {Array} All matched warning terms for this comment.
96 */
97 function commentContainsWarningTerm(comment) {
98 const matches = [];
99
100 warningRegExps.forEach((regex, index) => {
101 if (regex.test(comment)) {
102 matches.push(warningTerms[index]);
103 }
104 });
105
106 return matches;
107 }
108
109 /**
110 * Checks the specified node for matching warning comments and reports them.
111 * @param {ASTNode} node The AST node being checked.
112 * @returns {void} undefined.
113 */
114 function checkComment(node) {
115 if (astUtils.isDirectiveComment(node) && selfConfigRegEx.test(node.value)) {
116 return;
117 }
118
119 const matches = commentContainsWarningTerm(node.value);
120
121 matches.forEach(matchedTerm => {
122 context.report({
123 node,
124 message: "Unexpected '{{matchedTerm}}' comment.",
125 data: {
126 matchedTerm
127 }
128 });
129 });
130 }
131
132 return {
133 Program() {
134 const comments = sourceCode.getAllComments();
135
136 comments.filter(token => token.type !== "Shebang").forEach(checkComment);
137 }
138 };
139 }
140};