all files / eslint-plugin-angular/rules/ controller-as-vm.js

100% Statements 33/33
96.15% Branches 25/26
100% Functions 12/12
100% Lines 33/33
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                                    19× 19× 19×   19×     19× 19×       33×           19×                               11× 84×       19×     36×         11× 11× 11×             19×                  
/**
 * require and specify a capture variable for `this` in controllers
 *
 * You should use a capture variable for 'this' when using the controllerAs syntax.
 * The second parameter specifies the capture variable you want to use in your application.
 * The third parameter can be a Regexp for identifying controller functions (when using something like Browserify)
 *
 * ### Options
 *
 * - The name that should be used for the view model.
 *
 * @styleguideReference {johnpapa} `y032` controllerAs with vm
 * @version 0.1.0
 * @category bestPractice
 */
'use strict';
 
var utils = require('./utils/utils');
 
module.exports = function(context) {
    var badStatements = [];
    var badCaptureStatements = [];
    var controllerFunctions = [];
 
    var viewModelName = context.options[0] || 'vm';
    // If your Angular code is written so that controller functions are in
    // separate files from your .controller() calls, you can specify a regex for your controller function names
    var controllerNameMatcher = context.options[1];
    if (controllerNameMatcher && utils.isStringRegexp(controllerNameMatcher)) {
        controllerNameMatcher = utils.convertStringToRegex(controllerNameMatcher);
    }
 
    // check node against known controller functions or pattern if specified
    function isControllerFunction(node) {
        return controllerFunctions.indexOf(node) >= 0 ||
            (controllerNameMatcher && (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration') &&
            node.id && controllerNameMatcher.test(node.id.name));
    }
 
    // for each of the bad uses, find any parent nodes that are controller functions
    function reportBadUses() {
        if (controllerFunctions.length > 0 || controllerNameMatcher) {
            badCaptureStatements.forEach(function(item) {
                item.parents.filter(isControllerFunction).forEach(function() {
                    context.report(item.stmt, 'You should assign "this" to a consistent variable across your project: {{capture}}',
                        {
                            capture: viewModelName
                        }
                    );
                });
            });
            badStatements.forEach(function(item) {
                item.parents.filter(isControllerFunction).forEach(function() {
                    context.report(item.stmt, 'You should not use "this" directly. Instead, assign it to a variable called "{{capture}}"',
                        {
                            capture: viewModelName
                        }
                    );
                });
            });
        }
    }
 
    function isClassDeclaration(ancestors) {
        return ancestors.findIndex(function(ancestor) {
            return ancestor.type === 'ClassDeclaration';
        }) > -1;
    }
 
    return {
        // Looking for .controller() calls here and getting the associated controller function
        'CallExpression:exit': function(node) {
            if (utils.isAngularControllerDeclaration(node)) {
                controllerFunctions.push(utils.getControllerDefinition(context, node));
            }
        },
        // statements are checked here for bad uses of $scope
        ThisExpression: function(stmt) {
            var parents = context.getAncestors();
            Eif (!isClassDeclaration(parents)) {
                if (stmt.parent.type === 'VariableDeclarator') {
                    if (!stmt.parent.id || stmt.parent.id.name !== viewModelName) {
                        badCaptureStatements.push({parents: parents, stmt: stmt});
                    }
                } else {
                    badStatements.push({parents: parents, stmt: stmt});
                }
            }
        },
        'Program:exit': function() {
            reportBadUses();
        }
    };
};
 
module.exports.schema = [{
    type: 'string'
}, {
    type: 'string'
}];