UNPKG

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