summaryrefslogtreecommitdiff
path: root/tools/eslint/lib/rules/no-warning-comments.js
blob: 58b6764ff1f676398ab94151a017cb1ffe149bcc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/**
 * @fileoverview Rule that warns about used warning comments
 * @author Alexander Schmidt <https://github.com/lxanders>
 */

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function (context) {

    var configuration = context.options[0] || {},
        warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
        location = configuration.location || "start",
        warningRegExps;

    /**
     * Convert a warning term into a RegExp which will match a comment containing that whole word in the specified
     * location ("start" or "anywhere"). If the term starts or ends with non word characters, then the match will not
     * require word boundaries on that side.
     *
     * @param {String} term A term to convert to a RegExp
     * @returns {RegExp} The term converted to a RegExp
     */
    function convertToRegExp(term) {
        var escaped = term.replace(/[-\/\\$\^*+?.()|\[\]{}]/g, "\\$&"),
            // If the term ends in a word character (a-z0-9_), ensure a word boundary at the end, so that substrings do
            // not get falsely matched. eg "todo" in a string such as "mastodon".
            // If the term ends in a non-word character, then \b won't match on the boundary to the next non-word
            // character, which would likely be a space. For example `/\bFIX!\b/.test('FIX! blah') === false`.
            // In these cases, use no bounding match. Same applies for the prefix, handled below.
            suffix = /\w$/.test(term) ? "\\b" : "",
            prefix;

        if (location === "start") {
            // When matching at the start, ignore leading whitespace, and there's no need to worry about word boundaries
            prefix = "^\\s*";
        } else if (/^\w/.test(term)) {
            prefix = "\\b";
        } else {
            prefix = "";
        }

        return new RegExp(prefix + escaped + suffix, "i");
    }

    /**
     * Checks the specified comment for matches of the configured warning terms and returns the matches.
     * @param {String} comment The comment which is checked.
     * @returns {Array} All matched warning terms for this comment.
     */
    function commentContainsWarningTerm(comment) {
        var matches = [];

        warningRegExps.forEach(function (regex, index) {
            if (regex.test(comment)) {
                matches.push(warningTerms[index]);
            }
        });

        return matches;
    }

    /**
     * Checks the specified node for matching warning comments and reports them.
     * @param {ASTNode} node The AST node being checked.
     * @returns {void} undefined.
     */
    function checkComment(node) {
        var matches = commentContainsWarningTerm(node.value);

        matches.forEach(function (matchedTerm) {
            context.report(node, "Unexpected " + matchedTerm + " comment.");
        });
    }

    warningRegExps = warningTerms.map(convertToRegExp);
    return {
        "BlockComment": checkComment,
        "LineComment": checkComment
    };
};