all files / eslint-plugin-angular/rules/ on-watch.js

100% Statements 24/24
100% Branches 25/25
100% Functions 5/5
100% Lines 24/24
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                                    39×     37× 37×         31× 31×       31×       29× 29×   29×               11×   11×         28×     35×                        
/**
 * require `$on` and `$watch` deregistration callbacks to be saved in a variable
 *
 * Watch and On methods on the scope object should be assigned to a variable, in order to be deleted in a $destroy event handler
 * @version 0.1.0
 * @category bestPractice
 */
'use strict';
 
module.exports = function(context) {
    function report(node, method) {
        context.report(node, 'The "{{method}}" call should be assigned to a variable, in order to be destroyed during the $destroy event', {
            method: method
        });
    }
 
    /**
     * Return true if the given node is a call expression calling a function
     * named '$on' or '$watch' on an object named '$scope', '$rootScope' or
     * 'scope'.
     */
    function isScopeOnOrWatch(node, scopes) {
        if (node.type !== 'CallExpression') {
            return false;
        }
 
        var calledFunction = node.callee;
        if (calledFunction.type !== 'MemberExpression') {
            return false;
        }
 
        // can only easily tell what name was used if a simple
        // identifiers were used to access it.
        var parentObject = calledFunction.object;
        var accessedFunction = calledFunction.property;
 
        // cannot check name of the parent object if it is returned from a
        // complex expression.
        if (parentObject.type !== 'Identifier' ||
            accessedFunction.type !== 'Identifier') {
            return false;
        }
 
        var objectName = parentObject.name;
        var functionName = accessedFunction.name;
 
        return scopes.indexOf(objectName) >= 0 && (functionName === '$on' ||
                                            functionName === '$watch');
    }
 
    /**
     * Return true if the given node is a call expression that has a first
     * argument of the string '$destroy'.
     */
    function isFirstArgDestroy(node) {
        var args = node.arguments;
 
        return (args.length >= 1 &&
                args[0].type === 'Literal' &&
                args[0].value === '$destroy');
    }
 
    return {
 
        CallExpression: function(node) {
            if (isScopeOnOrWatch(node, ['$rootScope']) && !isFirstArgDestroy(node)) {
                if (node.parent.type !== 'VariableDeclarator' &&
                    node.parent.type !== 'AssignmentExpression' &&
                    !(isScopeOnOrWatch(node.parent, ['$rootScope', '$scope', 'scope']) &&
                     isFirstArgDestroy(node.parent))) {
                    report(node, node.callee.property.name);
                }
            }
        }
    };
};
 
module.exports.schema = [
    // JSON Schema for rule options goes here
];