1 | /**
|
2 | * @fileoverview Rule that warns about used warning comments
|
3 | * @author Alexander Schmidt <https://github.com/lxanders>
|
4 | */
|
5 |
|
6 | ;
|
7 |
|
8 | const astUtils = require("../ast-utils");
|
9 |
|
10 | //------------------------------------------------------------------------------
|
11 | // Rule Definition
|
12 | //------------------------------------------------------------------------------
|
13 |
|
14 | module.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 | };
|