'use strict'; var MODULE_NAME = 'Controllers', CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/, CATEGORY_CONTROLLER_NAME = 'Name controllers according to best practices', CATEGORY_GLOBAL_CONTROLLER = 'Using global functions as controllers is against Angular best practices and depricated in Angular 1.3 and up', SEVERITY_ERROR = 1, SEVERITY_WARNING = 2; // local state var nameToControllerMap = {}; /** * Decorates $controller with a patching function to * log a message if the controller is instantiated on the window */ angular.module('ngHintControllers', []). config(['$provide', '$controllerProvider', function ($provide, $controllerProvider) { $provide.decorator('$controller', ['$delegate', controllerDecorator]); var originalRegister = $controllerProvider.register; $controllerProvider.register = function(name, constructor) { stringOrObjectRegister(name); originalRegister.apply($controllerProvider, arguments); } }]); function controllerDecorator($delegate) { return function(ctrl) { if (typeof ctrl === 'string') { var match = ctrl.match(CNTRL_REG); var ctrlName = (match && match[1]) || ctrl; if (!nameToControllerMap[ctrlName]) { sendMessageForControllerName(ctrlName); } if (!nameToControllerMap[ctrlName] && typeof window[ctrlName] === 'function') { sendMessageForGlobalController(ctrlName); } } return $delegate.apply(this, arguments); }; } /** * Save details of the controllers as they are instantiated * for use in decoration. * Hint about the best practices for naming controllers. */ var originalModule = angular.module; function stringOrObjectRegister(controllerName) { if ((controllerName !== null) && (typeof controllerName === 'object')) { Object.keys(controllerName).forEach(processController); } else { processController(controllerName); } } function processController(ctrlName) { nameToControllerMap[ctrlName] = true; sendMessageForControllerName(ctrlName); } function sendMessageForGlobalController(name) { angular.hint.emit(MODULE_NAME + ':global', 'add `' + name + '` to a module', angular.version.minor <= 2 ? SEVERITY_WARNING : SEVERITY_ERROR, CATEGORY_GLOBAL_CONTROLLER); } function sendMessageForControllerName(name) { var newName = name; if (!startsWithUpperCase(name)) { newName = title(newName); } if (!endsWithController(name)) { newName = addControllerSuffix(newName); } if (name !== newName) { angular.hint.emit(MODULE_NAME + ':rename', 'Consider renaming `' + name + '` to `' + newName + '`.', SEVERITY_WARNING, CATEGORY_CONTROLLER_NAME); } } function startsWithUpperCase(name) { var firstChar = name.charAt(0); return firstChar === firstChar.toUpperCase() && firstChar !== firstChar.toLowerCase(); } function title (name) { return name[0].toUpperCase() + name.substr(1); } var CONTROLLER_RE = /Controller$/; function endsWithController(name) { return CONTROLLER_RE.test(name); } var RE = /(Ctrl|Kontroller)?$/; function addControllerSuffix(name) { return name.replace(RE, 'Controller'); } /* * decorate angular module API */ angular.module = function() { var module = originalModule.apply(this, arguments), originalController = module.controller; module.controller = function(controllerName, controllerConstructor) { stringOrObjectRegister(controllerName); return originalController.apply(this, arguments); }; return module; };