diff --git a/README.md b/README.md index 47421fbe..1d5a9339 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ Users may use the shareable [eslint-config-angular](https://github.com/dustinspe | controller-name | All your controllers should have a name starting with the parameter you can define in your config object. The second parameter can be a Regexp wrapped in quotes. ("controller-name": [2, "ng"]) [Y123](https://github.com/johnpapa/angular-styleguide#style-y123), [Y124](https://github.com/johnpapa/angular-styleguide#style-y124)| | deferred | When you want to create a new promise, you should not use the $q.deferred anymore. Prefer the new syntax : $q(function(resolve, reject){}) | | definedundefined | You should use the angular.isUndefined or angular.isDefined methods instead of using the keyword undefined. We also check the use of !angular.isUndefined and !angular.isDefined (should prefer the reverse function)| -| di | All your DI should use the same syntax : the Array or function syntaxes ("di": [2, "function or array"])| +| di | All your DI should use the same syntax : the Array, function, or $inject syntaxes ("di": [2, "array, function, or $inject"])| | di-order | Injected dependencies should be sorted alphabetically. If the second parameter is set to false, values which start and end with an underscore those underscores are stripped. This means for example that `_$httpBackend_` goes before `_$http_`. | | directive-name | All your directives should have a name starting with the parameter you can define in your config object. The second parameter can be a Regexp wrapped in quotes. You can not prefix your directives by "ng" (reserved keyword for AngularJS directives) ("directive-name": [2, "ng"]) [Y073](https://github.com/johnpapa/angular-styleguide#style-y073), [Y126](https://github.com/johnpapa/angular-styleguide#style-y126) | | directive-restrict | 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` [Y074](https://github.com/johnpapa/angular-styleguide#style-y074) and disallows explicitly specifying a default. ("directive-restrict": [0, {"restrict": "AE", "explicit": "never"}]) | diff --git a/rules/di.js b/rules/di.js index dfa06c04..042313cf 100644 --- a/rules/di.js +++ b/rules/di.js @@ -11,18 +11,46 @@ module.exports = function(context) { }); } + var noninjectedFunctions = {}; + var injectedFunctions = []; + function checkDi(syntax, node, param) { if (syntax === 'function' && (!utils.isFunctionType(param) && !utils.isIdentifierType(param))) { report(node, syntax); - } - if (syntax === 'array') { - if (!utils.isArrayType(param)) { - report(node, syntax); - } else { + } else if (syntax === 'array') { + if (utils.isArrayType(param)) { var fn = param.elements[param.elements.length - 1]; if (utils.isFunctionType(fn) && fn.params.length !== param.elements.length - 1) { context.report(fn, 'The signature of the method is incorrect', {}); } + } else { + report(node, syntax); + } + } else if (syntax === '$inject') { + if (utils.isIdentifierType(param)) { + noninjectedFunctions[param.name] = node; + } else { + report(node, syntax); + } + } + } + + function maybeNoteInjection(syntax, node) { + if (syntax === '$inject' && node.left && node.left.property && + ((utils.isLiteralType(node.left.property) && node.left.property.value === '$inject') || + (utils.isIdentifierType(node.left.property) && node.left.property.name === '$inject'))) { + injectedFunctions.push(node.left.object.name); + } + } + + function verifyInjections(syntax) { + if (syntax === '$inject') { + injectedFunctions.forEach(function(f) { + delete noninjectedFunctions[f]; + }); + + for (var func in noninjectedFunctions) { + report(noninjectedFunctions[func], syntax); } } } @@ -41,6 +69,12 @@ module.exports = function(context) { */ checkDi(context.options[0], node, node.arguments[0]); } + }, + AssignmentExpression: function(node) { + maybeNoteInjection(context.options[0], node); + }, + 'Program:exit': function() { + verifyInjections(context.options[0]); } }; }; diff --git a/test/di.js b/test/di.js index 823858b0..2683019b 100644 --- a/test/di.js +++ b/test/di.js @@ -28,6 +28,21 @@ angularObjectList.forEach(function(object) { }, { code: 'angular.' + object + '(myFunction);function MyFunction() {}', options: ['function'] + }, { + code: 'angular.' + object + '(myFunction);myFunction.$inject=[];function myFunction() {}', + options: ['$inject'] + }, { + code: 'angular.' + object + '(myFunction);myFunction["$inject"]=[];function myFunction() {}', + options: ['$inject'] + }, { + code: 'myFunction.$inject=[];function myFunction() {} angular.' + object + '(myFunction);', + options: ['$inject'] + }, { + code: 'function myFunction() {} myFunction.$inject=[];angular.' + object + '(myFunction);', + options: ['$inject'] + }, { + code: 'var myFunction = function() {}; myFunction.$inject=[];angular.' + object + '(myFunction);', + options: ['$inject'] }); invalid.push({ @@ -46,6 +61,22 @@ angularObjectList.forEach(function(object) { code: 'angular.' + object + '([function(Service1) {}]);', options: ['array'], errors: [{message: 'The signature of the method is incorrect'}] + }, { + code: 'angular.' + object + '(myFunction); function myFunction() {}', + options: ['$inject'], + errors: [{message: 'You should use the $inject syntax for DI'}] + }, { + code: 'function myFunction() {} angular.' + object + '(myFunction);', + options: ['$inject'], + errors: [{message: 'You should use the $inject syntax for DI'}] + }, { + code: 'var myFunction = function() {};angular.' + object + '(myFunction);', + options: ['$inject'], + errors: [{message: 'You should use the $inject syntax for DI'}] + }, { + code: 'angular.' + object + '(function() {});', + options: ['$inject'], + errors: [{message: 'You should use the $inject syntax for DI'}] }); }); @@ -62,6 +93,18 @@ angularNamedObjectList.forEach(function(object) { }, { code: 'angular.' + object + '("name", myFunction);function MyFunction() {}', options: ['function'] + }, { + code: 'angular.' + object + '("name", myFunction);myFunction.$inject=[];function myFunction() {}', + options: ['$inject'] + }, { + code: 'myFunction.$inject=[];function myFunction() {} angular.' + object + '("name", myFunction);', + options: ['$inject'] + }, { + code: 'function myFunction() {} myFunction.$inject=[];angular.' + object + '("name", myFunction);', + options: ['$inject'] + }, { + code: 'var myFunction = function() {}; myFunction.$inject=[];angular.' + object + '("name", myFunction);', + options: ['$inject'] }); invalid.push({ @@ -80,6 +123,22 @@ angularNamedObjectList.forEach(function(object) { code: 'angular.' + object + '("name", [function(Service1) {}]);', options: ['array'], errors: [{message: 'The signature of the method is incorrect'}] + }, { + code: 'angular.' + object + '("name", myFunction); function myFunction() {}', + options: ['$inject'], + errors: [{message: 'You should use the $inject syntax for DI'}] + }, { + code: 'function myFunction() {} angular.' + object + '("name", myFunction);', + options: ['$inject'], + errors: [{message: 'You should use the $inject syntax for DI'}] + }, { + code: 'var myFunction = function () {};angular.' + object + '("name", myFunction);', + options: ['$inject'], + errors: [{message: 'You should use the $inject syntax for DI'}] + }, { + code: 'angular.' + object + '("name", function() {});', + options: ['$inject'], + errors: [{message: 'You should use the $inject syntax for DI'}] }); });