all files / eslint-plugin-angular/rules/ directive-restrict.js

100% Statements 42/42
100% Branches 26/26
100% Functions 9/9
100% Lines 42/42
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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106                              35× 35× 35× 35×     35× 35× 35× 35×   21×   20× 20× 64× 19× 19×       20×   19×     17×         17×     35×   38× 24×                     20×   19×     35×                                      
/**
 * disallow any other directive restrict than 'A' or 'E'
 *
 * Not all directive restrictions may be desirable.
 * Also it might be desirable to define default restrictions, or explicitly not.
 * The default configuration limits the restrictions `AE` and disallows explicitly specifying a default.
 * ("directive-restrict": [0, {"restrict": "AE", "explicit": "never"}])
 *
 * @styleguideReference {johnpapa} `y074` Restrict to Elements and Attributes
 * @version 0.12.0
 * @category bestPractice
 */
'use strict';
 
var utils = require('./utils/utils');
 
module.exports = function(context) {
    var options = context.options[0] || {};
    var restrictOpt = options.restrict || 'AE';
    var explicitRestrict = options.explicit === 'always';
    var restrictChars = restrictOpt.split('');
 
    // Example RegExp for AE: /^A?E?$/
    var restrictRegExp = new RegExp('^' + restrictChars.join('?') + '?$');
    var foundDirectives = [];
    var checkedDirectives = [];
    var defaultRestrictions = ['AE', 'EA'];
 
    function checkLiteralNode(node) {
        if (node.type !== 'Literal') {
            return;
        }
        var directiveNode;
        context.getAncestors().some(function(ancestor) {
            if (utils.isAngularDirectiveDeclaration(ancestor)) {
                directiveNode = ancestor;
                return true;
            }
        });
        // The restrict property was not defined inside of a directive.
        if (!directiveNode) {
            return;
        }
        if (!explicitRestrict && defaultRestrictions.indexOf(node.value) !== -1) {
            context.report(node, 'No need to explicitly specify a default directive restriction');
            return;
        }
 
        if (!restrictRegExp.test(node.value)) {
            context.report(directiveNode, 'Disallowed directive restriction. It must be one of {{allowed}} in that order', {
                allowed: restrictOpt
            });
        }
 
        checkedDirectives.push(directiveNode);
    }
 
    return {
        CallExpression: function(node) {
            if (utils.isAngularDirectiveDeclaration(node)) {
                foundDirectives.push(node);
            }
        },
        AssignmentExpression: function(node) {
            // Only check for literal member property assignments.
            if (node.left.type !== 'MemberExpression') {
                return;
            }
            // Only check setting properties named 'restrict'.
            if (node.left.property.name !== 'restrict') {
                return;
            }
            checkLiteralNode(node.right);
        },
        Property: function(node) {
            // This only checks for objects which have defined a literal restrict property.
            if (node.key.name !== 'restrict') {
                return;
            }
            checkLiteralNode(node.value);
        },
        'Program:exit': function() {
            if (explicitRestrict) {
                foundDirectives.filter(function(directive) {
                    return checkedDirectives.indexOf(directive) < 0;
                }).forEach(function(directiveNode) {
                    context.report(directiveNode, 'Missing directive restriction');
                });
            }
        }
    };
};
 
module.exports.schema = [{
    type: 'object',
    properties: {
        restrict: {
            type: 'string',
            pattern: '^A|C|E|(AC)|(CA)|(AE)|(EA)|(EC)|(CE)|(AEC)|(ACE)|(EAC)|(CAE)|(ACE)|(AEC)|(CAE)|(ACE)|(AEC)$'
        },
        explicit: {
            enum: ['always', 'never']
        }
    }
}];