From 79c28ba345bd19336685aac91a2dcfdf569cc9a5 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Thu, 26 Nov 2015 17:16:31 +0100 Subject: [PATCH 01/35] add short link to styleguide in rule section (#288) --- README.md | 37 +++++++++---------- scripts/templates.js | 28 ++++++++++---- .../readmeRuleSectionContent.template.md | 3 +- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 17d8f408..278e2e68 100644 --- a/README.md +++ b/README.md @@ -156,31 +156,31 @@ Users may use the shareable [eslint-config-angular](https://github.com/dustinspe * [angularelement](docs/angularelement.md) - use `angular.element` instead of `$` or `jQuery` - * [component-limit](docs/component-limit.md) - limit the number of angular components per file - * [controller-as](docs/controller-as.md) - disallow assignments to `$scope` in controllers - * [controller-as-route](docs/controller-as-route.md) - require the use of controllerAs in routes or states - * [controller-as-vm](docs/controller-as-vm.md) - require and specify a capture variable for `this` in controllers - * [controller-name](docs/controller-name.md) - require and specify a prefix for all controller names + * [component-limit](docs/component-limit.md) - limit the number of angular components per file ([y001](https://github.com/johnpapa/angular-styleguide#style-y001)) + * [controller-as](docs/controller-as.md) - disallow assignments to `$scope` in controllers ([y031](https://github.com/johnpapa/angular-styleguide#style-y031)) + * [controller-as-route](docs/controller-as-route.md) - require the use of controllerAs in routes or states ([y031](https://github.com/johnpapa/angular-styleguide#style-y031)) + * [controller-as-vm](docs/controller-as-vm.md) - require and specify a capture variable for `this` in controllers ([y032](https://github.com/johnpapa/angular-styleguide#style-y032)) + * [controller-name](docs/controller-name.md) - require and specify a prefix for all controller names ([y123](https://github.com/johnpapa/angular-styleguide#style-y123), [y124](https://github.com/johnpapa/angular-styleguide#style-y124)) * [deferred](docs/deferred.md) - use `$q(function(resolve, reject){})` instead of `$q.deferred` * [definedundefined](docs/definedundefined.md) - use `angular.isDefined` and `angular.isUndefined` instead of other undefined checks * [di](docs/di.md) - require a consistent DI syntax * [di-order](docs/di-order.md) - require DI parameters to be sorted alphabetically * [di-unused](docs/di-unused.md) - disallow unused DI parameters - * [directive-name](docs/directive-name.md) - require and specify a prefix for all directive names - * [directive-restrict](docs/directive-restrict.md) - disallow any other directive restrict than 'A' or 'E' - * [document-service](docs/document-service.md) - use `$document` instead of `document` + * [directive-name](docs/directive-name.md) - require and specify a prefix for all directive names ([y073](https://github.com/johnpapa/angular-styleguide#style-y073), [y126](https://github.com/johnpapa/angular-styleguide#style-y126)) + * [directive-restrict](docs/directive-restrict.md) - disallow any other directive restrict than 'A' or 'E' ([y074](https://github.com/johnpapa/angular-styleguide#style-y074)) + * [document-service](docs/document-service.md) - use `$document` instead of `document` ([y180](https://github.com/johnpapa/angular-styleguide#style-y180)) * [empty-controller](docs/empty-controller.md) - disallow empty controllers - * [file-name](docs/file-name.md) - require and specify a consistent component name pattern + * [file-name](docs/file-name.md) - require and specify a consistent component name pattern ([y120](https://github.com/johnpapa/angular-styleguide#style-y120), [y121](https://github.com/johnpapa/angular-styleguide#style-y121)) * [filter-name](docs/filter-name.md) - require and specify a prefix for all filter names * [foreach](docs/foreach.md) - use `angular.forEach` instead of native `Array.prototype.forEach` - * [function-type](docs/function-type.md) - require and specify a consistent function style for components ('named' or 'anonymous') - * [interval-service](docs/interval-service.md) - use `$interval` instead of `setInterval` + * [function-type](docs/function-type.md) - require and specify a consistent function style for components ('named' or 'anonymous') ([y024](https://github.com/johnpapa/angular-styleguide#style-y024)) + * [interval-service](docs/interval-service.md) - use `$interval` instead of `setInterval` ([y181](https://github.com/johnpapa/angular-styleguide#style-y181)) * [json-functions](docs/json-functions.md) - use `angular.fromJson` and 'angular.toJson' instead of `JSON.parse` and `JSON.stringify` * [log](docs/log.md) - use the `$log` service instead of the `console` methods * [module-dependency-order](docs/module-dependency-order.md) - require a consistent order of module dependencies - * [module-getter](docs/module-getter.md) - disallow to reference modules with variables and require to use the getter syntax instead `angular.module('myModule')` - * [module-name](docs/module-name.md) - require and specify a prefix for all module names - * [module-setter](docs/module-setter.md) - disallow to assign modules to variables (linked to [module-getter](docs/module-getter.md) + * [module-getter](docs/module-getter.md) - disallow to reference modules with variables and require to use the getter syntax instead `angular.module('myModule')` ([y022](https://github.com/johnpapa/angular-styleguide#style-y022)) + * [module-name](docs/module-name.md) - require and specify a prefix for all module names ([y127](https://github.com/johnpapa/angular-styleguide#style-y127)) + * [module-setter](docs/module-setter.md) - disallow to assign modules to variables (linked to [module-getter](docs/module-getter.md) ([y021](https://github.com/johnpapa/angular-styleguide#style-y021)) * [no-angular-mock](docs/no-angular-mock.md) - require to use `angular.mock` methods directly * [no-controller](docs/no-controller.md) - disallow use of controllers (according to the component first pattern) * [no-cookiestore](docs/no-cookiestore.md) - use `$cookies` instead of `$cookieStore` @@ -190,12 +190,12 @@ Users may use the shareable [eslint-config-angular](https://github.com/dustinspe * [no-jquery-angularelement](docs/no-jquery-angularelement.md) - disallow to wrap `angular.element` objects with `jQuery` or `$` * [no-private-call](docs/no-private-call.md) - disallow use of internal angular properties prefixed with $$ * [no-services](docs/no-services.md) - disallow DI of specified services for other angular components (`$http` for controllers, filters and directives) - * [no-service-method](docs/no-service-method.md) - use `factory()` instead of `service()` + * [no-service-method](docs/no-service-method.md) - use `factory()` instead of `service()` ([y040](https://github.com/johnpapa/angular-styleguide#style-y040)) * [on-watch](docs/on-watch.md) - require `$on` and `$watch` deregistration callbacks to be saved in a variable * [one-dependency-per-line](docs/one-dependency-per-line.md) - require all DI parameters to be located in their own line * [rest-service](docs/rest-service.md) - disallow different rest service and specify one of '$http', '$resource', 'Restangular' - * [service-name](docs/service-name.md) - require and specify a prefix for all service names - * [timeout-service](docs/timeout-service.md) - use `$timeout` instead of `setTimeout` + * [service-name](docs/service-name.md) - require and specify a prefix for all service names ([y125](https://github.com/johnpapa/angular-styleguide#style-y125)) + * [timeout-service](docs/timeout-service.md) - use `$timeout` instead of `setTimeout` ([y181](https://github.com/johnpapa/angular-styleguide#style-y181)) * [typecheck-array](docs/typecheck-array.md) - use `angular.isArray` instead of `typeof` comparisons * [typecheck-date](docs/typecheck-date.md) - use `angular.isDate` instead of `typeof` comparisons * [typecheck-function](docs/typecheck-function.md) - use `angular.isFunction` instead of `typeof` comparisons @@ -204,8 +204,7 @@ Users may use the shareable [eslint-config-angular](https://github.com/dustinspe * [typecheck-regexp](docs/typecheck-regexp.md) - DEPRECATED! use `angular.isRegexp` instead of other comparisons (no native angular method) * [typecheck-string](docs/typecheck-string.md) - use `angular.isString` instead of `typeof` comparisons * [watchers-execution](docs/watchers-execution.md) - require and specify consistent use `$scope.digest()` or `$scope.apply()` - * [window-service](docs/window-service.md) - use `$window` instead of `window` - + * [window-service](docs/window-service.md) - use `$window` instead of `window` ([y180](https://github.com/johnpapa/angular-styleguide#style-y180)) ---- diff --git a/scripts/templates.js b/scripts/templates.js index a6e2138a..efbbc17c 100644 --- a/scripts/templates.js +++ b/scripts/templates.js @@ -8,6 +8,7 @@ var templates = { ruleDocumentationPath: _.template('docs/<%= ruleName %>.md'), ruleExamplesPath: _.template('examples/<%= ruleName %>.js'), styleguide: _.template('[<%= name %> by <%= type %> - <%= description %>](<%= link %>)'), + styleguideShort: _.template('[<%= name %>](<%= link %>)'), styleguideLinks: { johnpapa: _.template('https://github.com/johnpapa/angular-styleguide#style-<%= name %>') } @@ -17,15 +18,16 @@ var templatesDir = './scripts/templates/'; var templateSettings = { imports: { formatStyleguideReference: function(styleRef) { - var linkTemplate = templates.styleguideLinks[styleRef.type]; - if (!linkTemplate) { - throw new Error('No styleguide link template for styleguide type: "' + styleRef.type); + return templates.styleguide(styleguideReferenceTemplateContext(styleRef)); + }, + formatStyleguideReferenceListShort: function(rule) { + if (!rule.styleguideReferences || rule.styleguideReferences.length === 0) { + return ''; } - var templateContext = _.extend({ - link: linkTemplate(styleRef) - }, styleRef); - - return templates.styleguide(templateContext); + return ' (' + rule.styleguideReferences + .map(styleguideReferenceTemplateContext) + .map(templates.styleguideShort).join(', ') + + ')'; }, formatConfigAsJson: function(examples) { var config = examples[0].displayOptions; @@ -59,3 +61,13 @@ fs.readdirSync(templatesDir).forEach(function(templateFilename) { }); module.exports = templates; + +function styleguideReferenceTemplateContext(styleRef) { + var linkTemplate = templates.styleguideLinks[styleRef.type]; + if (!linkTemplate) { + throw new Error('No styleguide link template for styleguide type: "' + styleRef.type); + } + return _.extend({ + link: linkTemplate(styleRef) + }, styleRef); +} diff --git a/scripts/templates/readmeRuleSectionContent.template.md b/scripts/templates/readmeRuleSectionContent.template.md index 4565f477..3cf54a26 100644 --- a/scripts/templates/readmeRuleSectionContent.template.md +++ b/scripts/templates/readmeRuleSectionContent.template.md @@ -1,6 +1,5 @@ ## Rules <% _.each(rules, function (rule) { %> - * [<%= rule.ruleName %>](<%= rule.documentationPath %>) - <%= rule.linkDescription %><% }) %> - + * [<%= rule.ruleName %>](<%= rule.documentationPath %>) - <%= rule.linkDescription %><%= formatStyleguideReferenceListShort(rule) %><% }) %> ---- From c11460b9a970642c82c522273b9341b9dd28b4aa Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Thu, 3 Dec 2015 22:16:28 +0100 Subject: [PATCH 02/35] Add new rule for disallowing run logic --- README.md | 2 + docs/no-run-logic.md | 60 ++++++++++++++++++++ examples/no-run-logic.js | 23 ++++++++ index.js | 1 + rules/no-run-logic.js | 58 +++++++++++++++++++ test/no-run-logic.js | 119 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 263 insertions(+) create mode 100644 docs/no-run-logic.md create mode 100644 examples/no-run-logic.js create mode 100644 rules/no-run-logic.js create mode 100644 test/no-run-logic.js diff --git a/README.md b/README.md index 278e2e68..4e724c78 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ Users may use the shareable [eslint-config-angular](https://github.com/dustinspe "angular/no-inline-template": [0, {"allowSimple": true}], "angular/no-jquery-angularelement": 2, "angular/no-private-call": 2, + "angular/no-run-logic": [0, {"allowParams": true}], "angular/no-service-method": 2, "angular/no-services": [2, ["$http", "$resource", "Restangular"]], "angular/on-watch": 2, @@ -189,6 +190,7 @@ Users may use the shareable [eslint-config-angular](https://github.com/dustinspe * [no-inline-template](docs/no-inline-template.md) - disallow the use of inline templates * [no-jquery-angularelement](docs/no-jquery-angularelement.md) - disallow to wrap `angular.element` objects with `jQuery` or `$` * [no-private-call](docs/no-private-call.md) - disallow use of internal angular properties prefixed with $$ + * [no-run-logic](docs/no-run-logic.md) - keep run functions clean and simple ([y171](https://github.com/johnpapa/angular-styleguide#style-y171)) * [no-services](docs/no-services.md) - disallow DI of specified services for other angular components (`$http` for controllers, filters and directives) * [no-service-method](docs/no-service-method.md) - use `factory()` instead of `service()` ([y040](https://github.com/johnpapa/angular-styleguide#style-y040)) * [on-watch](docs/on-watch.md) - require `$on` and `$watch` deregistration callbacks to be saved in a variable diff --git a/docs/no-run-logic.md b/docs/no-run-logic.md new file mode 100644 index 00000000..907341aa --- /dev/null +++ b/docs/no-run-logic.md @@ -0,0 +1,60 @@ + + +# no-run-logic - keep run functions clean and simple + +Initialization logic should be moved into a factory or service. This improves testability. + +**Styleguide Reference** + +* [y171 by johnpapa - Run Blocks](https://github.com/johnpapa/angular-styleguide#style-y171) + +## Examples + +The following patterns are considered problems with default config; + + /*eslint angular/no-run-logic: 2*/ + + // invalid + angular.module('app').run(function($window) { + $window.addEventListener('deviceready', deviceready); + + function deviceready() {} + }); // error: The run function may only contain call expressions + +The following patterns are **not** considered problems with default config; + + /*eslint angular/no-run-logic: 2*/ + + // valid + angular.module('app').run(function(KITTENS, kittenService, startup) { + kittenService.prefetchData(KITTENS); + startup('foo', true, 1); + }); + +The following patterns are considered problems when configured `{"allowParams":false}`: + + /*eslint angular/no-run-logic: [2,{"allowParams":false}]*/ + + // invalid + angular.module('app').run(function(kittenService, startup) { + startup('foo', true, 1); + }); // error: Run function call expressions may not take any arguments + +The following patterns are **not** considered problems when configured `{"allowParams":false}`: + + /*eslint angular/no-run-logic: [2,{"allowParams":false}]*/ + + // valid + angular.module('app').run(function(kittenService, startup) { + kittenService.prefetchData(); + startup(); + }); + +## Version + +This rule was introduced in eslint-plugin-angular 0.15.0 + +## Links + +* [Rule source](../rules/no-run-logic.js) +* [Example source](../examples/no-run-logic.js) diff --git a/examples/no-run-logic.js b/examples/no-run-logic.js new file mode 100644 index 00000000..dbf88f0e --- /dev/null +++ b/examples/no-run-logic.js @@ -0,0 +1,23 @@ +// example - valid: true +angular.module('app').run(function(KITTENS, kittenService, startup) { + kittenService.prefetchData(KITTENS); + startup('foo', true, 1); +}); + +// example - valid: true, options: [{allowParams: false}] +angular.module('app').run(function(kittenService, startup) { + kittenService.prefetchData(); + startup(); +}); + +// example - valid: false, errorMessage: "The run function may only contain call expressions" +angular.module('app').run(function($window) { + $window.addEventListener('deviceready', deviceready); + + function deviceready() {} +}); + +// example - valid: false, options: [{allowParams: false}], errorMessage: "Run function call expressions may not take any arguments" +angular.module('app').run(function(startup) { + startup('foo', true, 1); +}); diff --git a/index.js b/index.js index b62ea10a..dcfd851f 100644 --- a/index.js +++ b/index.js @@ -36,6 +36,7 @@ rulesConfiguration.addRule('no-http-callback', 0); rulesConfiguration.addRule('no-inline-template', [0, {'allow-simple': true}]); rulesConfiguration.addRule('no-jquery-angularelement', 2); rulesConfiguration.addRule('no-private-call', 2); +rulesConfiguration.addRule('no-run-logic', 0); rulesConfiguration.addRule('no-services', [2, ['$http', '$resource', 'Restangular', '$q']]); rulesConfiguration.addRule('no-service-method', 2); rulesConfiguration.addRule('on-watch', 2); diff --git a/rules/no-run-logic.js b/rules/no-run-logic.js new file mode 100644 index 00000000..782e0ef8 --- /dev/null +++ b/rules/no-run-logic.js @@ -0,0 +1,58 @@ +/** + * keep run functions clean and simple + * + * Initialization logic should be moved into a factory or service. This improves testability. + * + * @styleguideReference {johnpapa} `y171` Run Blocks + * @version 0.15.0 + */ +'use strict'; + +var angularRule = require('./utils/angular-rule'); + + +module.exports = angularRule(function(context) { + var options = context.options[0] || {}; + var allowParams = options.allowParams !== false; + + function report(node) { + context.report(node, 'The run function may only contain call expressions'); + } + + return { + 'angular:run': function(callExpression, fn) { + if (!fn) { + return; + } + fn.body.body.forEach(function(statement) { + if (statement.type !== 'ExpressionStatement') { + return report(statement); + } + var expression = statement.expression; + if (expression.type !== 'CallExpression') { + return report(statement); + } + if (expression.callee.type === 'MemberExpression' && expression.callee.object.type !== 'Identifier') { + return report(statement); + } + if (!allowParams && expression.arguments.length) { + return context.report(expression, 'Run function call expressions may not take any arguments'); + } + expression.arguments.forEach(function(argument) { + if (argument.type !== 'Literal' && argument.type !== 'Identifier') { + context.report(argument, 'Run function call expressions may only take simple arguments'); + } + }); + }); + } + }; +}); + +module.exports.schema = [{ + type: 'object', + properties: { + allowParams: { + type: 'boolean' + } + } +}]; diff --git a/test/no-run-logic.js b/test/no-run-logic.js new file mode 100644 index 00000000..321a9a98 --- /dev/null +++ b/test/no-run-logic.js @@ -0,0 +1,119 @@ +'use strict'; + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +var rule = require('../rules/no-run-logic'); +var RuleTester = require('eslint').RuleTester; + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +var eslintTester = new RuleTester(); +eslintTester.run('no-run-logic', rule, { + valid: [ + 'angular.module("").run();', + 'angular.module("").run(function() {});', + 'angular.module("").run(function() {foo()});', + 'angular.module("").run(function() {foo.bar()});', + // valid arguments if params are allowed + 'angular.module("").run(function() {foo(0)});', + 'angular.module("").run(function() {foo(true)});', + 'angular.module("").run(function() {foo(null)});', + 'angular.module("").run(function() {foo(undefined)});', + 'angular.module("").run(function() {foo("bar")});', + 'angular.module("").run(function() {foo(bar)});' + ], + invalid: [ + // Nested function declarations + { + code: 'angular.module().run(function() {function foo() {}})', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'angular.module().run([function() {function foo() {}}])', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'var app = angular.module(); app.run(function() {function foo() {}})', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'angular.module().run(fn); function fn() {function foo() {}}', + errors: [{message: 'The run function may only contain call expressions'}] + }, + // Non call expression statements + { + code: 'angular.module().run(function() {foo = bar;})', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'angular.module().run([function() {foo = bar;}])', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'var app = angular.module(); app.run(function() {foo = bar;})', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'angular.module().run(fn); function fn() {foo = bar;}', + errors: [{message: 'The run function may only contain call expressions'}] + }, + // Nested member expressions + { + code: 'angular.module().run(function() {foo.bar.baz();})', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'angular.module().run([function() {foo.bar.baz();}])', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'var app = angular.module(); app.run(function() {foo.bar.baz();})', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'angular.module().run(fn); function fn() {foo.bar.baz();}', + errors: [{message: 'The run function may only contain call expressions'}] + }, + // Nested member expressions + { + code: 'angular.module().run(function() {foo().bar.baz();})', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'angular.module().run([function() {foo().bar.baz();}])', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'var app = angular.module(); app.run(function() {foo().bar.baz();})', + errors: [{message: 'The run function may only contain call expressions'}] + }, { + code: 'angular.module().run(fn); function fn() {foo().bar.baz();}', + errors: [{message: 'The run function may only contain call expressions'}] + }, + // Disallow any parameters + { + code: 'angular.module().run(function() {foo(1);})', + options: [{allowParams: false}], + errors: [{message: 'Run function call expressions may not take any arguments'}] + }, { + code: 'angular.module().run([function() {foo(1);}])', + options: [{allowParams: false}], + errors: [{message: 'Run function call expressions may not take any arguments'}] + }, { + code: 'var app = angular.module(); app.run(function() {foo(1);})', + options: [{allowParams: false}], + errors: [{message: 'Run function call expressions may not take any arguments'}] + }, { + code: 'angular.module().run(fn); function fn() {foo(1);}', + options: [{allowParams: false}], + errors: [{message: 'Run function call expressions may not take any arguments'}] + }, + // Allow simple parameters + { + code: 'angular.module().run(function() {foo(bar());})', + errors: [{message: 'Run function call expressions may only take simple arguments'}] + }, { + code: 'angular.module().run([function() {foo(bar());}])', + errors: [{message: 'Run function call expressions may only take simple arguments'}] + }, { + code: 'var app = angular.module(); app.run(function() {foo(bar());})', + errors: [{message: 'Run function call expressions may only take simple arguments'}] + }, { + code: 'angular.module().run(fn); function fn() {foo(bar());}', + errors: [{message: 'Run function call expressions may only take simple arguments'}] + } + ] +}); From 7d9a989ce466bf84a21eea661f8c4da431e4d43b Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Fri, 4 Dec 2015 12:44:30 +0100 Subject: [PATCH 03/35] add missing example for typecheck-array (fixes #291) --- docs/typecheck-array.md | 3 +++ examples/typecheck-array.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/docs/typecheck-array.md b/docs/typecheck-array.md index 0dc1a1d7..b5032410 100644 --- a/docs/typecheck-array.md +++ b/docs/typecheck-array.md @@ -13,6 +13,9 @@ The following patterns are considered problems; // invalid Object.prototype.toString.call(someArray) === '[object Array]'; // error: You should use the angular.isArray method + // invalid + Array.isArray(someArray) // error: You should use the angular.isArray method + The following patterns are **not** considered problems; /*eslint angular/typecheck-array: 2*/ diff --git a/examples/typecheck-array.js b/examples/typecheck-array.js index 164034af..b364c843 100644 --- a/examples/typecheck-array.js +++ b/examples/typecheck-array.js @@ -3,3 +3,6 @@ angular.isArray(someArray); // example - valid: false, errorMessage: "You should use the angular.isArray method" Object.prototype.toString.call(someArray) === '[object Array]'; + +// example - valid: false, errorMessage: "You should use the angular.isArray method" +Array.isArray(someArray) From 57230374fea0f3101eae71d9e9793f5d010a61be Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Fri, 4 Dec 2015 14:37:13 +0100 Subject: [PATCH 04/35] add categories for rule documentation (fixes #297) --- README.md | 90 +++++++++++++------ rules/angularelement.js | 1 + rules/component-limit.js | 1 + rules/controller-as-route.js | 1 + rules/controller-as-vm.js | 1 + rules/controller-as.js | 1 + rules/controller-name.js | 1 + rules/deferred.js | 1 + rules/definedundefined.js | 1 + rules/di-order.js | 1 + rules/di-unused.js | 1 + rules/di.js | 1 + rules/directive-name.js | 1 + rules/directive-restrict.js | 1 + rules/document-service.js | 1 + rules/empty-controller.js | 1 + rules/file-name.js | 1 + rules/filter-name.js | 1 + rules/foreach.js | 1 + rules/function-type.js | 1 + rules/interval-service.js | 1 + rules/json-functions.js | 1 + rules/log.js | 1 + rules/module-dependency-order.js | 1 + rules/module-getter.js | 1 + rules/module-name.js | 1 + rules/module-setter.js | 1 + rules/no-angular-mock.js | 1 + rules/no-controller.js | 1 + rules/no-cookiestore.js | 1 + rules/no-digest.js | 1 + rules/no-http-callback.js | 1 + rules/no-inline-template.js | 1 + rules/no-jquery-angularelement.js | 1 + rules/no-private-call.js | 1 + rules/no-service-method.js | 1 + rules/no-services.js | 1 + rules/on-watch.js | 1 + rules/one-dependency-per-line.js | 1 + rules/rest-service.js | 1 + rules/service-name.js | 1 + rules/timeout-service.js | 1 + rules/typecheck-array.js | 1 + rules/typecheck-date.js | 1 + rules/typecheck-function.js | 1 + rules/typecheck-number.js | 1 + rules/typecheck-object.js | 1 + rules/typecheck-regexp.js | 1 + rules/typecheck-string.js | 1 + rules/watchers-execution.js | 1 + rules/window-service.js | 1 + scripts/docs.js | 13 ++- scripts/ruleCategories.json | 51 +++++++++++ .../readmeRuleSectionContent.template.md | 10 ++- 54 files changed, 186 insertions(+), 28 deletions(-) create mode 100644 scripts/ruleCategories.json diff --git a/README.md b/README.md index 278e2e68..7a43d468 100644 --- a/README.md +++ b/README.md @@ -154,57 +154,95 @@ Users may use the shareable [eslint-config-angular](https://github.com/dustinspe ## Rules +Rules in eslint-plugin-angular are divided into several categories to help you better understand their value. + + +## Possible Errors + +The following rules detect patterns that can lead to errors. + + * [module-getter](docs/module-getter.md) - disallow to reference modules with variables and require to use the getter syntax instead `angular.module('myModule')` ([y022](https://github.com/johnpapa/angular-styleguide#style-y022)) + * [module-setter](docs/module-setter.md) - disallow to assign modules to variables (linked to [module-getter](docs/module-getter.md) ([y021](https://github.com/johnpapa/angular-styleguide#style-y021)) + * [no-private-call](docs/no-private-call.md) - disallow use of internal angular properties prefixed with $$ + +## Best Practices + +These are rules designed to prevent you from making mistakes. They either prescribe a better way of doing something or help you avoid footguns.. - * [angularelement](docs/angularelement.md) - use `angular.element` instead of `$` or `jQuery` * [component-limit](docs/component-limit.md) - limit the number of angular components per file ([y001](https://github.com/johnpapa/angular-styleguide#style-y001)) * [controller-as](docs/controller-as.md) - disallow assignments to `$scope` in controllers ([y031](https://github.com/johnpapa/angular-styleguide#style-y031)) * [controller-as-route](docs/controller-as-route.md) - require the use of controllerAs in routes or states ([y031](https://github.com/johnpapa/angular-styleguide#style-y031)) * [controller-as-vm](docs/controller-as-vm.md) - require and specify a capture variable for `this` in controllers ([y032](https://github.com/johnpapa/angular-styleguide#style-y032)) - * [controller-name](docs/controller-name.md) - require and specify a prefix for all controller names ([y123](https://github.com/johnpapa/angular-styleguide#style-y123), [y124](https://github.com/johnpapa/angular-styleguide#style-y124)) * [deferred](docs/deferred.md) - use `$q(function(resolve, reject){})` instead of `$q.deferred` - * [definedundefined](docs/definedundefined.md) - use `angular.isDefined` and `angular.isUndefined` instead of other undefined checks - * [di](docs/di.md) - require a consistent DI syntax - * [di-order](docs/di-order.md) - require DI parameters to be sorted alphabetically * [di-unused](docs/di-unused.md) - disallow unused DI parameters - * [directive-name](docs/directive-name.md) - require and specify a prefix for all directive names ([y073](https://github.com/johnpapa/angular-styleguide#style-y073), [y126](https://github.com/johnpapa/angular-styleguide#style-y126)) * [directive-restrict](docs/directive-restrict.md) - disallow any other directive restrict than 'A' or 'E' ([y074](https://github.com/johnpapa/angular-styleguide#style-y074)) - * [document-service](docs/document-service.md) - use `$document` instead of `document` ([y180](https://github.com/johnpapa/angular-styleguide#style-y180)) * [empty-controller](docs/empty-controller.md) - disallow empty controllers + * [no-controller](docs/no-controller.md) - disallow use of controllers (according to the component first pattern) + * [no-inline-template](docs/no-inline-template.md) - disallow the use of inline templates + * [no-services](docs/no-services.md) - disallow DI of specified services for other angular components (`$http` for controllers, filters and directives) + * [on-watch](docs/on-watch.md) - require `$on` and `$watch` deregistration callbacks to be saved in a variable + +## Deprecated Angular Features + +There rules prevent you from using deprecated angular features. + + * [no-cookiestore](docs/no-cookiestore.md) - use `$cookies` instead of `$cookieStore` + * [no-http-callback](docs/no-http-callback.md) - disallow the `$http` methods `success()` and `error()` + +## Naming + +These rules help you to specify several naming conventions. + + * [controller-name](docs/controller-name.md) - require and specify a prefix for all controller names ([y123](https://github.com/johnpapa/angular-styleguide#style-y123), [y124](https://github.com/johnpapa/angular-styleguide#style-y124)) + * [directive-name](docs/directive-name.md) - require and specify a prefix for all directive names ([y073](https://github.com/johnpapa/angular-styleguide#style-y073), [y126](https://github.com/johnpapa/angular-styleguide#style-y126)) * [file-name](docs/file-name.md) - require and specify a consistent component name pattern ([y120](https://github.com/johnpapa/angular-styleguide#style-y120), [y121](https://github.com/johnpapa/angular-styleguide#style-y121)) * [filter-name](docs/filter-name.md) - require and specify a prefix for all filter names - * [foreach](docs/foreach.md) - use `angular.forEach` instead of native `Array.prototype.forEach` + * [module-name](docs/module-name.md) - require and specify a prefix for all module names ([y127](https://github.com/johnpapa/angular-styleguide#style-y127)) + * [service-name](docs/service-name.md) - require and specify a prefix for all service names ([y125](https://github.com/johnpapa/angular-styleguide#style-y125)) + +## Conventions + +Angular often provide multi ways to to something. These rules help you to define convention for your project. + + * [di](docs/di.md) - require a consistent DI syntax + * [di-order](docs/di-order.md) - require DI parameters to be sorted alphabetically * [function-type](docs/function-type.md) - require and specify a consistent function style for components ('named' or 'anonymous') ([y024](https://github.com/johnpapa/angular-styleguide#style-y024)) + * [module-dependency-order](docs/module-dependency-order.md) - require a consistent order of module dependencies + * [no-service-method](docs/no-service-method.md) - use `factory()` instead of `service()` ([y040](https://github.com/johnpapa/angular-styleguide#style-y040)) + * [one-dependency-per-line](docs/one-dependency-per-line.md) - require all DI parameters to be located in their own line + * [rest-service](docs/rest-service.md) - disallow different rest service and specify one of '$http', '$resource', 'Restangular' + * [watchers-execution](docs/watchers-execution.md) - require and specify consistent use `$scope.digest()` or `$scope.apply()` + +## Angular Wrappers + +These rules help you to enforce the usage of angular wrappers. + + * [angularelement](docs/angularelement.md) - use `angular.element` instead of `$` or `jQuery` + * [definedundefined](docs/definedundefined.md) - use `angular.isDefined` and `angular.isUndefined` instead of other undefined checks + * [document-service](docs/document-service.md) - use `$document` instead of `document` ([y180](https://github.com/johnpapa/angular-styleguide#style-y180)) + * [foreach](docs/foreach.md) - use `angular.forEach` instead of native `Array.prototype.forEach` * [interval-service](docs/interval-service.md) - use `$interval` instead of `setInterval` ([y181](https://github.com/johnpapa/angular-styleguide#style-y181)) * [json-functions](docs/json-functions.md) - use `angular.fromJson` and 'angular.toJson' instead of `JSON.parse` and `JSON.stringify` * [log](docs/log.md) - use the `$log` service instead of the `console` methods - * [module-dependency-order](docs/module-dependency-order.md) - require a consistent order of module dependencies - * [module-getter](docs/module-getter.md) - disallow to reference modules with variables and require to use the getter syntax instead `angular.module('myModule')` ([y022](https://github.com/johnpapa/angular-styleguide#style-y022)) - * [module-name](docs/module-name.md) - require and specify a prefix for all module names ([y127](https://github.com/johnpapa/angular-styleguide#style-y127)) - * [module-setter](docs/module-setter.md) - disallow to assign modules to variables (linked to [module-getter](docs/module-getter.md) ([y021](https://github.com/johnpapa/angular-styleguide#style-y021)) * [no-angular-mock](docs/no-angular-mock.md) - require to use `angular.mock` methods directly - * [no-controller](docs/no-controller.md) - disallow use of controllers (according to the component first pattern) - * [no-cookiestore](docs/no-cookiestore.md) - use `$cookies` instead of `$cookieStore` - * [no-digest](docs/no-digest.md) - DEPRECATED! use `$apply()` instead of `$digest()` (replaced by [watchers-execution](docs/watchers-execution.md)) - * [no-http-callback](docs/no-http-callback.md) - disallow the `$http` methods `success()` and `error()` - * [no-inline-template](docs/no-inline-template.md) - disallow the use of inline templates * [no-jquery-angularelement](docs/no-jquery-angularelement.md) - disallow to wrap `angular.element` objects with `jQuery` or `$` - * [no-private-call](docs/no-private-call.md) - disallow use of internal angular properties prefixed with $$ - * [no-services](docs/no-services.md) - disallow DI of specified services for other angular components (`$http` for controllers, filters and directives) - * [no-service-method](docs/no-service-method.md) - use `factory()` instead of `service()` ([y040](https://github.com/johnpapa/angular-styleguide#style-y040)) - * [on-watch](docs/on-watch.md) - require `$on` and `$watch` deregistration callbacks to be saved in a variable - * [one-dependency-per-line](docs/one-dependency-per-line.md) - require all DI parameters to be located in their own line - * [rest-service](docs/rest-service.md) - disallow different rest service and specify one of '$http', '$resource', 'Restangular' - * [service-name](docs/service-name.md) - require and specify a prefix for all service names ([y125](https://github.com/johnpapa/angular-styleguide#style-y125)) * [timeout-service](docs/timeout-service.md) - use `$timeout` instead of `setTimeout` ([y181](https://github.com/johnpapa/angular-styleguide#style-y181)) * [typecheck-array](docs/typecheck-array.md) - use `angular.isArray` instead of `typeof` comparisons * [typecheck-date](docs/typecheck-date.md) - use `angular.isDate` instead of `typeof` comparisons * [typecheck-function](docs/typecheck-function.md) - use `angular.isFunction` instead of `typeof` comparisons * [typecheck-number](docs/typecheck-number.md) - use `angular.isNumber` instead of `typeof` comparisons * [typecheck-object](docs/typecheck-object.md) - use `angular.isObject` instead of `typeof` comparisons - * [typecheck-regexp](docs/typecheck-regexp.md) - DEPRECATED! use `angular.isRegexp` instead of other comparisons (no native angular method) * [typecheck-string](docs/typecheck-string.md) - use `angular.isString` instead of `typeof` comparisons - * [watchers-execution](docs/watchers-execution.md) - require and specify consistent use `$scope.digest()` or `$scope.apply()` * [window-service](docs/window-service.md) - use `$window` instead of `window` ([y180](https://github.com/johnpapa/angular-styleguide#style-y180)) + +## Deprecated rules + +These rules will be removed in version 1.0.0 + + * [no-digest](docs/no-digest.md) - DEPRECATED! use `$apply()` instead of `$digest()` (replaced by [watchers-execution](docs/watchers-execution.md)) + * [typecheck-regexp](docs/typecheck-regexp.md) - DEPRECATED! use `angular.isRegexp` instead of other comparisons (no native angular method) + + ---- diff --git a/rules/angularelement.js b/rules/angularelement.js index a9ccfa3d..f8a28d31 100644 --- a/rules/angularelement.js +++ b/rules/angularelement.js @@ -5,6 +5,7 @@ * If the jQuery library is imported, angular.element will be a wrapper around the jQuery object. * * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/component-limit.js b/rules/component-limit.js index d7d5409d..edf61069 100644 --- a/rules/component-limit.js +++ b/rules/component-limit.js @@ -6,6 +6,7 @@ * * @styleguideReference {johnpapa} `y001` Define 1 component per file * @version 0.11.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/controller-as-route.js b/rules/controller-as-route.js index 2c14b794..fa13104e 100644 --- a/rules/controller-as-route.js +++ b/rules/controller-as-route.js @@ -5,6 +5,7 @@ * * @styleguideReference {johnpapa} `y031` controllerAs Controller Syntax * @version 0.1.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/controller-as-vm.js b/rules/controller-as-vm.js index 1081ead6..970aa82b 100644 --- a/rules/controller-as-vm.js +++ b/rules/controller-as-vm.js @@ -7,6 +7,7 @@ * * @styleguideReference {johnpapa} `y032` controllerAs with vm * @version 0.1.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/controller-as.js b/rules/controller-as.js index 531ae4c4..1cfbff9c 100644 --- a/rules/controller-as.js +++ b/rules/controller-as.js @@ -7,6 +7,7 @@ * * @styleguideReference {johnpapa} `y031` controllerAs Controller Syntax * @version 0.1.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/controller-name.js b/rules/controller-name.js index 4c021415..89705187 100644 --- a/rules/controller-name.js +++ b/rules/controller-name.js @@ -8,6 +8,7 @@ * @styleguideReference {johnpapa} `y123` Controller Names * @styleguideReference {johnpapa} `y124` Controller Name Suffix * @version 0.1.0 + * @category naming */ 'use strict'; diff --git a/rules/deferred.js b/rules/deferred.js index 4ed7a577..68f4de83 100644 --- a/rules/deferred.js +++ b/rules/deferred.js @@ -4,6 +4,7 @@ * When you want to create a new promise, you should not use the $q.deferred anymore. * Prefer the new syntax : $q(function(resolve, reject){}) * @version 0.1.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/definedundefined.js b/rules/definedundefined.js index 3b663bcb..e747a9ed 100644 --- a/rules/definedundefined.js +++ b/rules/definedundefined.js @@ -5,6 +5,7 @@ * We also check the use of !angular.isUndefined and !angular.isDefined (should prefer the reverse function) * * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/di-order.js b/rules/di-order.js index 47a3c778..501758a7 100644 --- a/rules/di-order.js +++ b/rules/di-order.js @@ -6,6 +6,7 @@ * This means for example that `_$httpBackend_` goes before `_$http_`. * * @version 0.6.0 + * @category conventions */ 'use strict'; diff --git a/rules/di-unused.js b/rules/di-unused.js index 61577875..264a0c2b 100644 --- a/rules/di-unused.js +++ b/rules/di-unused.js @@ -4,6 +4,7 @@ * Unused dependencies should not be injected. * * @version 0.8.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/di.js b/rules/di.js index 284ecdaf..3c92416b 100644 --- a/rules/di.js +++ b/rules/di.js @@ -4,6 +4,7 @@ * All your DI should use the same syntax : the Array, function, or $inject syntaxes ("di": [2, "array, function, or $inject"]) * * @version 0.1.0 + * @category conventions */ 'use strict'; diff --git a/rules/directive-name.js b/rules/directive-name.js index 08f21999..bdead682 100644 --- a/rules/directive-name.js +++ b/rules/directive-name.js @@ -8,6 +8,7 @@ * @styleguideReference {johnpapa} `y073` Provide a Unique Directive Prefix * @styleguideReference {johnpapa} `y126` Directive Component Names * @version 0.1.0 + * @category naming */ 'use strict'; diff --git a/rules/directive-restrict.js b/rules/directive-restrict.js index e081992d..46747098 100644 --- a/rules/directive-restrict.js +++ b/rules/directive-restrict.js @@ -8,6 +8,7 @@ * * @styleguideReference {johnpapa} `y074` Restrict to Elements and Attributes * @version 0.12.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/document-service.js b/rules/document-service.js index b08c26a0..70ed2b60 100644 --- a/rules/document-service.js +++ b/rules/document-service.js @@ -5,6 +5,7 @@ * * @styleguideReference {johnpapa} `y180` Angular $ Wrapper Services - $document and $window * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/empty-controller.js b/rules/empty-controller.js index 6d80ef3c..b3174699 100644 --- a/rules/empty-controller.js +++ b/rules/empty-controller.js @@ -5,6 +5,7 @@ * You can remove this declaration because this controller is useless * * @version 0.1.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/file-name.js b/rules/file-name.js index 32ffaff7..d5ef4ac0 100644 --- a/rules/file-name.js +++ b/rules/file-name.js @@ -9,6 +9,7 @@ * @styleguideReference {johnpapa} `y120` Naming - Naming Guidelines * @styleguideReference {johnpapa} `y121` Naming - Feature File Names * @version 0.7.0 + * @category naming */ 'use strict'; diff --git a/rules/filter-name.js b/rules/filter-name.js index cd4374bf..7fb7983e 100644 --- a/rules/filter-name.js +++ b/rules/filter-name.js @@ -6,6 +6,7 @@ * ("filter-name": [2, "ng"]) * * @version 0.1.0 + * @category naming */ 'use strict'; diff --git a/rules/foreach.js b/rules/foreach.js index ffc2eca8..5e5bedf8 100644 --- a/rules/foreach.js +++ b/rules/foreach.js @@ -4,6 +4,7 @@ * You should use the angular.forEach method instead of the default JavaScript implementation [].forEach. * * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/function-type.js b/rules/function-type.js index d83fa632..7566c9b9 100644 --- a/rules/function-type.js +++ b/rules/function-type.js @@ -8,6 +8,7 @@ * @linkDescription require and specify a consistent function style for components ('named' or 'anonymous') * @styleguideReference {johnpapa} `y024` Named vs Anonymous Functions * @version 0.1.0 + * @category conventions */ 'use strict'; diff --git a/rules/interval-service.js b/rules/interval-service.js index 313976d5..b89370c1 100644 --- a/rules/interval-service.js +++ b/rules/interval-service.js @@ -5,6 +5,7 @@ * * @styleguideReference {johnpapa} `y181` Angular $ Wrapper Services - $timeout and $interval * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/json-functions.js b/rules/json-functions.js index b73acae2..8f310d78 100644 --- a/rules/json-functions.js +++ b/rules/json-functions.js @@ -5,6 +5,7 @@ * * @linkDescription use `angular.fromJson` and 'angular.toJson' instead of `JSON.parse` and `JSON.stringify` * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/log.js b/rules/log.js index a36e3da8..df8c3faf 100644 --- a/rules/log.js +++ b/rules/log.js @@ -3,6 +3,7 @@ * * You should use $log service instead of console for the methods 'log', 'debug', 'error', 'info', 'warn' * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/module-dependency-order.js b/rules/module-dependency-order.js index 70b6cdac..ed459eaa 100644 --- a/rules/module-dependency-order.js +++ b/rules/module-dependency-order.js @@ -10,6 +10,7 @@ * ('module-dependency-order', [2, {grouped: true, prefix: "app"}]) * * @version 0.12.0 + * @category conventions */ 'use strict'; diff --git a/rules/module-getter.js b/rules/module-getter.js index 6c014018..72e3a389 100644 --- a/rules/module-getter.js +++ b/rules/module-getter.js @@ -6,6 +6,7 @@ * @linkDescription disallow to reference modules with variables and require to use the getter syntax instead `angular.module('myModule')` * @styleguideReference {johnpapa} `y022` Module - Getters * @version 0.1.0 + * @category possibleError */ 'use strict'; diff --git a/rules/module-name.js b/rules/module-name.js index 4c08ab09..be61a1b9 100644 --- a/rules/module-name.js +++ b/rules/module-name.js @@ -7,6 +7,7 @@ * * @styleguideReference {johnpapa} `y127` Naming - Modules * @version 0.1.0 + * @category naming */ 'use strict'; diff --git a/rules/module-setter.js b/rules/module-setter.js index 163d55f6..06b90d8b 100644 --- a/rules/module-setter.js +++ b/rules/module-setter.js @@ -6,6 +6,7 @@ * @linkDescription disallow to assign modules to variables (linked to [module-getter](docs/module-getter.md) * @styleguideReference {johnpapa} `y021` Module - Definitions (aka Setters) * @version 0.1.0 + * @category possibleError */ 'use strict'; diff --git a/rules/no-angular-mock.js b/rules/no-angular-mock.js index 8eca50cf..a3ddc280 100644 --- a/rules/no-angular-mock.js +++ b/rules/no-angular-mock.js @@ -5,6 +5,7 @@ * So you can remove angular.mock from your code * * @version 0.2.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/no-controller.js b/rules/no-controller.js index a50eb185..b1d5b531 100644 --- a/rules/no-controller.js +++ b/rules/no-controller.js @@ -4,6 +4,7 @@ * According to the Component-First pattern, we should avoid the use of AngularJS controller. * * @version 0.9.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/no-cookiestore.js b/rules/no-cookiestore.js index cc72fab3..d1d7ef20 100644 --- a/rules/no-cookiestore.js +++ b/rules/no-cookiestore.js @@ -5,6 +5,7 @@ * Please use the $cookies service instead * * @version 0.3.0 + * @category deprecatedAngularFeature */ 'use strict'; diff --git a/rules/no-digest.js b/rules/no-digest.js index 3e56e725..4d67da12 100644 --- a/rules/no-digest.js +++ b/rules/no-digest.js @@ -6,6 +6,7 @@ * * @linkDescription DEPRECATED! use `$apply()` instead of `$digest()` (replaced by [watchers-execution](docs/watchers-execution.md)) * @version 0.1.0 + * @category deprecatedRule */ 'use strict'; diff --git a/rules/no-http-callback.js b/rules/no-http-callback.js index 39c5ef77..cef62501 100644 --- a/rules/no-http-callback.js +++ b/rules/no-http-callback.js @@ -5,6 +5,7 @@ * Instead the standard promise API should be used. * * @version 0.12.0 + * @category deprecatedAngularFeature */ 'use strict'; diff --git a/rules/no-inline-template.js b/rules/no-inline-template.js index 63cfa1c2..4c990843 100644 --- a/rules/no-inline-template.js +++ b/rules/no-inline-template.js @@ -6,6 +6,7 @@ * ('no-inline-template': [0, {allowSimple: true}]) * * @version 0.12.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/no-jquery-angularelement.js b/rules/no-jquery-angularelement.js index ea3f74c6..66c64cfb 100644 --- a/rules/no-jquery-angularelement.js +++ b/rules/no-jquery-angularelement.js @@ -3,6 +3,7 @@ * * You should not wrap angular.element object into jQuery(), because angular.element already return jQLite element * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/no-private-call.js b/rules/no-private-call.js index bb637202..9d1051b9 100644 --- a/rules/no-private-call.js +++ b/rules/no-private-call.js @@ -6,6 +6,7 @@ * Exception can be allowed with this option: {allow:['$$watchers']} * * @version 0.1.0 + * @category possibleError */ 'use strict'; diff --git a/rules/no-service-method.js b/rules/no-service-method.js index a84674e0..6767045c 100644 --- a/rules/no-service-method.js +++ b/rules/no-service-method.js @@ -5,6 +5,7 @@ * * @styleguideReference {johnpapa} `y040` Services - Singletons * @version 0.1.0 + * @category conventions */ 'use strict'; diff --git a/rules/no-services.js b/rules/no-services.js index 63aec6a7..66925848 100644 --- a/rules/no-services.js +++ b/rules/no-services.js @@ -8,6 +8,7 @@ * * @linkDescription disallow DI of specified services for other angular components (`$http` for controllers, filters and directives) * @version 0.1.0 + * @category bestPractice */ 'use strict'; diff --git a/rules/on-watch.js b/rules/on-watch.js index 6ad723ae..23a3274b 100644 --- a/rules/on-watch.js +++ b/rules/on-watch.js @@ -3,6 +3,7 @@ * * 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'; diff --git a/rules/one-dependency-per-line.js b/rules/one-dependency-per-line.js index 89ed39ec..dc895299 100644 --- a/rules/one-dependency-per-line.js +++ b/rules/one-dependency-per-line.js @@ -4,6 +4,7 @@ * Injected dependencies should be written one per line. * * @version 0.14.0 + * @category conventions */ 'use strict'; diff --git a/rules/rest-service.js b/rules/rest-service.js index e771be1c..9656f17c 100644 --- a/rules/rest-service.js +++ b/rules/rest-service.js @@ -5,6 +5,7 @@ * This rule can have one parameter, with one of the following values: $http, $resource or Restangular ('rest-service': [0, '$http']). * * @version 0.5.0 + * @category conventions */ 'use strict'; diff --git a/rules/service-name.js b/rules/service-name.js index 7993655c..774b934f 100644 --- a/rules/service-name.js +++ b/rules/service-name.js @@ -7,6 +7,7 @@ ** * @styleguideReference {johnpapa} `y125` Naming - Factory and Service Names * @version 0.1.0 + * @category naming */ 'use strict'; diff --git a/rules/timeout-service.js b/rules/timeout-service.js index 932c4d29..492609bf 100644 --- a/rules/timeout-service.js +++ b/rules/timeout-service.js @@ -5,6 +5,7 @@ ** * @styleguideReference {johnpapa} `y181` Angular $ Wrapper Services - $timeout and $interval * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/typecheck-array.js b/rules/typecheck-array.js index af262cc3..32af27a6 100644 --- a/rules/typecheck-array.js +++ b/rules/typecheck-array.js @@ -4,6 +4,7 @@ * You should use the angular.isArray method instead of the default JavaScript implementation (typeof [] === "[object Array]"). * * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/typecheck-date.js b/rules/typecheck-date.js index 6e53d7a6..95b0b26d 100644 --- a/rules/typecheck-date.js +++ b/rules/typecheck-date.js @@ -4,6 +4,7 @@ * You should use the angular.isDate method instead of the default JavaScript implementation (typeof new Date() === "[object Date]"). * * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/typecheck-function.js b/rules/typecheck-function.js index 8735f538..2587ad20 100644 --- a/rules/typecheck-function.js +++ b/rules/typecheck-function.js @@ -4,6 +4,7 @@ * You should use the angular.isFunction method instead of the default JavaScript implementation (typeof function(){} ==="[object Function]"). * * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/typecheck-number.js b/rules/typecheck-number.js index 5b632622..cf12b890 100644 --- a/rules/typecheck-number.js +++ b/rules/typecheck-number.js @@ -4,6 +4,7 @@ * You should use the angular.isNumber method instead of the default JavaScript implementation (typeof 3 === "[object Number]"). * * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/typecheck-object.js b/rules/typecheck-object.js index 762347c6..760f85a1 100644 --- a/rules/typecheck-object.js +++ b/rules/typecheck-object.js @@ -4,6 +4,7 @@ * You should use the angular.isObject method instead of the default JavaScript implementation (typeof {} === "[object Object]"). * * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/typecheck-regexp.js b/rules/typecheck-regexp.js index 38de545d..c5677bdc 100644 --- a/rules/typecheck-regexp.js +++ b/rules/typecheck-regexp.js @@ -5,6 +5,7 @@ * * @linkDescription DEPRECATED! use `angular.isRegexp` instead of other comparisons (no native angular method) * @version 0.1.0 + * @category deprecatedRule */ 'use strict'; diff --git a/rules/typecheck-string.js b/rules/typecheck-string.js index 0a984302..9bdcd0f1 100644 --- a/rules/typecheck-string.js +++ b/rules/typecheck-string.js @@ -4,6 +4,7 @@ * You should use the angular.isString method instead of the default JavaScript implementation (typeof "" === "[object String]"). * * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/rules/watchers-execution.js b/rules/watchers-execution.js index 7d7ec718..9d4c0e1e 100644 --- a/rules/watchers-execution.js +++ b/rules/watchers-execution.js @@ -5,6 +5,7 @@ * This will cause an performance improvement comparing to the $apply method, who start from the $rootScope * * @version 0.4.0 + * @category conventions */ 'use strict'; diff --git a/rules/window-service.js b/rules/window-service.js index 814faefe..28ee09ef 100644 --- a/rules/window-service.js +++ b/rules/window-service.js @@ -5,6 +5,7 @@ * * @styleguideReference {johnpapa} `y180` Angular $ Wrapper Services - $document and $window * @version 0.1.0 + * @category angularWrapper */ 'use strict'; diff --git a/scripts/docs.js b/scripts/docs.js index da695b6e..85f1fa47 100644 --- a/scripts/docs.js +++ b/scripts/docs.js @@ -41,7 +41,17 @@ function createDocFiles(cb) { * @param cb callback */ function updateReadme(readmePath, cb) { - var readmeRuleSection = templates.readmeRuleSectionContent(this); + var ruleCategories = require('./ruleCategories.json'); + + ruleCategories.rulesByCategory = _.groupBy(this.rules, 'category'); + + // filter categories without rules + ruleCategories.categoryOrder = ruleCategories.categoryOrder.filter(function(categoryName) { + var rules = ruleCategories.rulesByCategory[categoryName] + return rules && rules.length > 0; + }); + + var readmeRuleSection = templates.readmeRuleSectionContent(ruleCategories); var readmeContent = fs.readFileSync(readmePath).toString(); // use split and join to prevent the replace() and dollar sign problem (http://stackoverflow.com/questions/9423722) @@ -175,6 +185,7 @@ function _createRule(ruleName) { rule.linkDescription = mainRuleComment.linkDescription ? mainRuleComment.linkDescription : rule.lead; rule.styleguideReferences = mainRuleComment.styleguideReferences || []; rule.version = mainRuleComment.version; + rule.category = mainRuleComment.category || 'uncategorizedRule'; if (!rule.version) { throw new Error('No @version found for ' + ruleName); diff --git a/scripts/ruleCategories.json b/scripts/ruleCategories.json new file mode 100644 index 00000000..dcd0b26c --- /dev/null +++ b/scripts/ruleCategories.json @@ -0,0 +1,51 @@ +{ + "categoryOrder": [ + "possibleError", + "bestPractice", + "deprecatedAngularFeature", + "naming", + "conventions", + "angularWrapper", + "deprecatedRule", + "removedRule", + "uncategorizedRule" + ], + "categories": { + "possibleError": { + "headline": "Possible Errors", + "description": "The following rules detect patterns that can lead to errors." + }, + "bestPractice": { + "headline": "Best Practices", + "description": "These are rules designed to prevent you from making mistakes. They either prescribe a better way of doing something or help you avoid footguns.." + }, + "deprecatedAngularFeature": { + "headline": "Deprecated Angular Features", + "description": "There rules prevent you from using deprecated angular features." + }, + "naming": { + "headline": "Naming", + "description": "These rules help you to specify several naming conventions." + }, + "conventions": { + "headline": "Conventions", + "description": "Angular often provide multi ways to to something. These rules help you to define convention for your project." + }, + "angularWrapper": { + "headline": "Angular Wrappers", + "description": "These rules help you to enforce the usage of angular wrappers." + }, + "deprecatedRule": { + "headline": "Deprecated rules", + "description": "These rules will be removed in version 1.0.0" + }, + "removedRule": { + "headline": "Removed", + "description": "These rules were removed and only exist to inform users about their removal." + }, + "uncategorizedRule": { + "headline": "Uncategorized rule (only for development)", + "description": "Add a @category tag to your rule." + } + } +} diff --git a/scripts/templates/readmeRuleSectionContent.template.md b/scripts/templates/readmeRuleSectionContent.template.md index 3cf54a26..bdeac6fc 100644 --- a/scripts/templates/readmeRuleSectionContent.template.md +++ b/scripts/templates/readmeRuleSectionContent.template.md @@ -1,5 +1,13 @@ ## Rules -<% _.each(rules, function (rule) { %> +Rules in eslint-plugin-angular are divided into several categories to help you better understand their value. + +<% _.each(categoryOrder, function (categoryName) { %> +## <%= categories[categoryName].headline %> + +<%= categories[categoryName].description %> +<% _.each(rulesByCategory[categoryName], function (rule) { %> * [<%= rule.ruleName %>](<%= rule.documentationPath %>) - <%= rule.linkDescription %><%= formatStyleguideReferenceListShort(rule) %><% }) %> +<% }) %> + ---- From 15fd03f1b91210d3c1a8c5f646320da6a2bfc01b Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Fri, 4 Dec 2015 22:18:15 +0100 Subject: [PATCH 05/35] fixed eslint issues (#297) --- scripts/docs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/docs.js b/scripts/docs.js index 85f1fa47..59f39764 100644 --- a/scripts/docs.js +++ b/scripts/docs.js @@ -47,8 +47,8 @@ function updateReadme(readmePath, cb) { // filter categories without rules ruleCategories.categoryOrder = ruleCategories.categoryOrder.filter(function(categoryName) { - var rules = ruleCategories.rulesByCategory[categoryName] - return rules && rules.length > 0; + var rulesForCategory = ruleCategories.rulesByCategory[categoryName]; + return rulesForCategory && rulesForCategory.length > 0; }); var readmeRuleSection = templates.readmeRuleSectionContent(ruleCategories); From 69dc0b7171ee91a22e3f9fb8ea51294cf58a78c7 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Fri, 4 Dec 2015 23:42:13 +0100 Subject: [PATCH 06/35] add category to no-run-logic rule (#297) --- README.md | 7 +------ rules/no-run-logic.js | 1 + 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e4e89c86..0405727a 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ These are rules designed to prevent you from making mistakes. They either prescr * [empty-controller](docs/empty-controller.md) - disallow empty controllers * [no-controller](docs/no-controller.md) - disallow use of controllers (according to the component first pattern) * [no-inline-template](docs/no-inline-template.md) - disallow the use of inline templates + * [no-run-logic](docs/no-run-logic.md) - keep run functions clean and simple ([y171](https://github.com/johnpapa/angular-styleguide#style-y171)) * [no-services](docs/no-services.md) - disallow DI of specified services for other angular components (`$http` for controllers, filters and directives) * [on-watch](docs/on-watch.md) - require `$on` and `$watch` deregistration callbacks to be saved in a variable @@ -243,12 +244,6 @@ These rules will be removed in version 1.0.0 * [no-digest](docs/no-digest.md) - DEPRECATED! use `$apply()` instead of `$digest()` (replaced by [watchers-execution](docs/watchers-execution.md)) * [typecheck-regexp](docs/typecheck-regexp.md) - DEPRECATED! use `angular.isRegexp` instead of other comparisons (no native angular method) -## Uncategorized rule (only for development) - -Add a @category tag to your rule. - - * [no-run-logic](docs/no-run-logic.md) - keep run functions clean and simple ([y171](https://github.com/johnpapa/angular-styleguide#style-y171)) - ---- diff --git a/rules/no-run-logic.js b/rules/no-run-logic.js index 782e0ef8..088d6e8d 100644 --- a/rules/no-run-logic.js +++ b/rules/no-run-logic.js @@ -5,6 +5,7 @@ * * @styleguideReference {johnpapa} `y171` Run Blocks * @version 0.15.0 + * @category bestPractice */ 'use strict'; From 1eaec14dab650ee69443fc6bdc427cf778d1f113 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 15:03:10 +0100 Subject: [PATCH 07/35] add deprecated annotation to documentation (fixes #295) - interpret deprecated annotation in the docs task - add deprecated section to rule documentation template - add deprecated annotation to no-digest and typecheck-regexp rules --- README.md | 4 ++-- docs/no-digest.md | 6 +++++- docs/typecheck-regexp.md | 4 +++- rules/no-digest.js | 8 +++++--- rules/typecheck-regexp.js | 6 +++--- scripts/docs.js | 7 +++++++ scripts/templates/ruleDocumentationContent.template.md | 4 ++++ 7 files changed, 29 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0405727a..7418d73a 100644 --- a/README.md +++ b/README.md @@ -241,8 +241,8 @@ These rules help you to enforce the usage of angular wrappers. These rules will be removed in version 1.0.0 - * [no-digest](docs/no-digest.md) - DEPRECATED! use `$apply()` instead of `$digest()` (replaced by [watchers-execution](docs/watchers-execution.md)) - * [typecheck-regexp](docs/typecheck-regexp.md) - DEPRECATED! use `angular.isRegexp` instead of other comparisons (no native angular method) + * [no-digest](docs/no-digest.md) - use `$apply()` instead of `$digest()` (replaced by [watchers-execution](docs/watchers-execution.md)) + * [typecheck-regexp](docs/typecheck-regexp.md) - use `angular.isRegexp` instead of other comparisons (no native angular method) ---- diff --git a/docs/no-digest.md b/docs/no-digest.md index d3f0ba15..07bacbef 100644 --- a/docs/no-digest.md +++ b/docs/no-digest.md @@ -2,9 +2,13 @@ # no-digest - use `$apply()` instead of `$digest()` -DEPRECATED! The scope's $digest() method shouldn't be used. +**This rule is deprecated and will be removed in future versions. Explanation: There is no reason to forbid the use of `$digest()` in general.** + +The scope's $digest() method shouldn't be used. You should prefer the $apply method. +The `watchers-execution` rule can be configured to enforce the use of `$apply()` or `$digest()`. + ## Version This rule was introduced in eslint-plugin-angular 0.1.0 diff --git a/docs/typecheck-regexp.md b/docs/typecheck-regexp.md index be9eb046..ed44ee70 100644 --- a/docs/typecheck-regexp.md +++ b/docs/typecheck-regexp.md @@ -2,7 +2,9 @@ # typecheck-regexp - use `angular.isRegexp` instead of other comparisons -DEPRECATED! You should use the angular.isRegexp method instead of the default JavaScript implementation (toString.call(/^A/) === "[object RegExp]"). +**This rule is deprecated and will be removed in future versions. Explanation: `angular.isRegexp` is no built-in angular method.** + +You should use the angular.isRegexp method instead of the default JavaScript implementation (toString.call(/^A/) === "[object RegExp]"). ## Version diff --git a/rules/no-digest.js b/rules/no-digest.js index 4d67da12..cb6097ca 100644 --- a/rules/no-digest.js +++ b/rules/no-digest.js @@ -1,12 +1,14 @@ /** * use `$apply()` instead of `$digest()` * - * DEPRECATED! The scope's $digest() method shouldn't be used. + * The scope's $digest() method shouldn't be used. * You should prefer the $apply method. * - * @linkDescription DEPRECATED! use `$apply()` instead of `$digest()` (replaced by [watchers-execution](docs/watchers-execution.md)) + * The `watchers-execution` rule can be configured to enforce the use of `$apply()` or `$digest()`. + * + * @linkDescription use `$apply()` instead of `$digest()` (replaced by [watchers-execution](docs/watchers-execution.md)) * @version 0.1.0 - * @category deprecatedRule + * @deprecated There is no reason to forbid the use of `$digest()` in general. */ 'use strict'; diff --git a/rules/typecheck-regexp.js b/rules/typecheck-regexp.js index c5677bdc..d352e1f0 100644 --- a/rules/typecheck-regexp.js +++ b/rules/typecheck-regexp.js @@ -1,11 +1,11 @@ /** * use `angular.isRegexp` instead of other comparisons * - * DEPRECATED! You should use the angular.isRegexp method instead of the default JavaScript implementation (toString.call(/^A/) === "[object RegExp]"). + * You should use the angular.isRegexp method instead of the default JavaScript implementation (toString.call(/^A/) === "[object RegExp]"). * - * @linkDescription DEPRECATED! use `angular.isRegexp` instead of other comparisons (no native angular method) + * @linkDescription use `angular.isRegexp` instead of other comparisons (no native angular method) * @version 0.1.0 - * @category deprecatedRule + * @deprecated `angular.isRegexp` is no built-in angular method. */ 'use strict'; diff --git a/scripts/docs.js b/scripts/docs.js index 59f39764..e885e6a3 100644 --- a/scripts/docs.js +++ b/scripts/docs.js @@ -187,6 +187,13 @@ function _createRule(ruleName) { rule.version = mainRuleComment.version; rule.category = mainRuleComment.category || 'uncategorizedRule'; + rule.deprecated = !!mainRuleComment.deprecated; + + if (rule.deprecated) { + rule.deprecationReason = mainRuleComment.deprecated; + rule.category = 'deprecatedRule'; + } + if (!rule.version) { throw new Error('No @version found for ' + ruleName); } diff --git a/scripts/templates/ruleDocumentationContent.template.md b/scripts/templates/ruleDocumentationContent.template.md index f8627a5f..97e9d8bc 100644 --- a/scripts/templates/ruleDocumentationContent.template.md +++ b/scripts/templates/ruleDocumentationContent.template.md @@ -2,6 +2,10 @@ # <%= ruleName %> - <%= lead %> +<% if(deprecated) { %> +**This rule is deprecated and will be removed in future versions. Explanation: <%= deprecationReason %>** +<% } %> + <%= description %> <% if(styleguideReferences.length > 0) { %> From 18151ec2789dd7ae690f75850ab37a6d26598c6b Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sun, 6 Dec 2015 16:17:06 +0100 Subject: [PATCH 08/35] Add new dumb-inject rule --- README.md | 1 + docs/dumb-inject.md | 64 +++++++++++++++++++++++++++++++++++++ examples/dumb-inject.js | 38 ++++++++++++++++++++++ index.js | 1 + rules/dumb-inject.js | 71 +++++++++++++++++++++++++++++++++++++++++ test/dumb-inject.js | 61 +++++++++++++++++++++++++++++++++++ 6 files changed, 236 insertions(+) create mode 100644 docs/dumb-inject.md create mode 100644 examples/dumb-inject.js create mode 100644 rules/dumb-inject.js create mode 100644 test/dumb-inject.js diff --git a/README.md b/README.md index 0405727a..b7716a44 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,7 @@ Angular often provide multi ways to to something. These rules help you to define * [di](docs/di.md) - require a consistent DI syntax * [di-order](docs/di-order.md) - require DI parameters to be sorted alphabetically + * [dumb-inject](docs/dumb-inject.md) - unittest `inject` functions should only consist of assignments from injected values to describe block variables * [function-type](docs/function-type.md) - require and specify a consistent function style for components ('named' or 'anonymous') ([y024](https://github.com/johnpapa/angular-styleguide#style-y024)) * [module-dependency-order](docs/module-dependency-order.md) - require a consistent order of module dependencies * [no-service-method](docs/no-service-method.md) - use `factory()` instead of `service()` ([y040](https://github.com/johnpapa/angular-styleguide#style-y040)) diff --git a/docs/dumb-inject.md b/docs/dumb-inject.md new file mode 100644 index 00000000..e4d50319 --- /dev/null +++ b/docs/dumb-inject.md @@ -0,0 +1,64 @@ + + +# dumb-inject - unittest `inject` functions should only consist of assignments from injected values to describe block variables + +`inject` functions in unittests should only contain a sorted mapping of injected values to values in the `describe` block with matching names. +This way the dependency injection setup is separated from the other setup logic, improving readability of the test. + +## Examples + +The following patterns are considered problems; + + /*eslint angular/dumb-inject: 2*/ + + // invalid + describe(function() { + var $httpBackend; + var $rootScope; + + beforeEach(inject(function(_$httpBackend_, _$rootScope_) { + $httpBackend = _$httpBackend_; + $rootScope = _$rootScope_; + + $httpBackend.whenGET('/data').respond([]); + })); + }); // error: inject functions may only consist of assignments in the form myService = _myService_ + + // invalid + describe(function() { + var $httpBackend; + var $rootScope; + + beforeEach(inject(function(_$httpBackend_, _$rootScope_) { + $rootScope = _$rootScope_; + $httpBackend = _$httpBackend_; + })); + }); // error: '$httpBackend' must be sorted before '$rootScope' + +The following patterns are **not** considered problems; + + /*eslint angular/dumb-inject: 2*/ + + // valid + describe(function() { + var $httpBackend; + var $rootScope; + + beforeEach(inject(function(_$httpBackend_, _$rootScope_) { + $httpBackend = _$httpBackend_; + $rootScope = _$rootScope_; + })); + + beforeEach(function() { + $httpBackend.whenGET('/data').respond([]); + }); + }); + +## Version + +This rule was introduced in eslint-plugin-angular 0.15.0 + +## Links + +* [Rule source](../rules/dumb-inject.js) +* [Example source](../examples/dumb-inject.js) diff --git a/examples/dumb-inject.js b/examples/dumb-inject.js new file mode 100644 index 00000000..4e5152ec --- /dev/null +++ b/examples/dumb-inject.js @@ -0,0 +1,38 @@ +// example - valid: true +describe(function() { + var $httpBackend; + var $rootScope; + + beforeEach(inject(function(_$httpBackend_, _$rootScope_) { + $httpBackend = _$httpBackend_; + $rootScope = _$rootScope_; + })); + + beforeEach(function() { + $httpBackend.whenGET('/data').respond([]); + }); +}); + +// example - valid: false, errorMessage: "inject functions may only consist of assignments in the form myService = _myService_" +describe(function() { + var $httpBackend; + var $rootScope; + + beforeEach(inject(function(_$httpBackend_, _$rootScope_) { + $httpBackend = _$httpBackend_; + $rootScope = _$rootScope_; + + $httpBackend.whenGET('/data').respond([]); + })); +}); + +// example - valid: false, errorMessage: "'$httpBackend' must be sorted before '$rootScope'" +describe(function() { + var $httpBackend; + var $rootScope; + + beforeEach(inject(function(_$httpBackend_, _$rootScope_) { + $rootScope = _$rootScope_; + $httpBackend = _$httpBackend_; + })); +}); diff --git a/index.js b/index.js index dcfd851f..8ffd23f5 100644 --- a/index.js +++ b/index.js @@ -16,6 +16,7 @@ rulesConfiguration.addRule('di-unused', 0); rulesConfiguration.addRule('directive-name', 0); rulesConfiguration.addRule('directive-restrict', 0); rulesConfiguration.addRule('document-service', 2); +rulesConfiguration.addRule('dumb-inject', 2); rulesConfiguration.addRule('empty-controller', 0); rulesConfiguration.addRule('file-name', 0); rulesConfiguration.addRule('filter-name', 0); diff --git a/rules/dumb-inject.js b/rules/dumb-inject.js new file mode 100644 index 00000000..91f647ff --- /dev/null +++ b/rules/dumb-inject.js @@ -0,0 +1,71 @@ +/** + * unittest `inject` functions should only consist of assignments from injected values to describe block variables + * + * `inject` functions in unittests should only contain a sorted mapping of injected values to values in the `describe` block with matching names. + * This way the dependency injection setup is separated from the other setup logic, improving readability of the test. + * + * @version 0.15.0 + * @category conventions + */ +'use strict'; + +var angularRule = require('./utils/angular-rule'); + + +module.exports = angularRule(function(context) { + function report(node, name) { + context.report(node, 'inject functions may only consist of assignments in the form {{name}} = _{{name}}_', { + name: name || 'myService' + }); + } + + return { + 'angular:inject': function(callExpression, fn) { + if (!fn) { + return; + } + var valid = []; + // Report bad statement types + fn.body.body.forEach(function(statement) { + if (statement.type !== 'ExpressionStatement') { + return report(statement); + } + if (statement.expression.type !== 'AssignmentExpression') { + return report(statement); + } + if (statement.expression.right.type !== 'Identifier') { + return report(statement); + } + // From this point there is more context on what to report. + var name = statement.expression.right.name.replace(/^_(.+)_$/, '$1'); + if (statement.expression.left.type !== 'Identifier') { + return report(statement, name); + } + if (statement.expression.right.name !== '_' + name + '_') { + return report(statement, name); + } + if (statement.expression.left.name !== name) { + return report(statement, name); + } + // Register valid statements for sort order validation + valid.push(statement); + }); + // Validate the sorting order + var lastValid; + valid.forEach(function(statement) { + if (!lastValid) { + lastValid = statement.expression.left.name; + return; + } + if (statement.expression.left.name.localeCompare(lastValid) !== -1) { + lastValid = statement.expression.left.name; + return; + } + context.report(statement, "'{{current}}' must be sorted before '{{previous}}'", { + current: statement.expression.left.name, + previous: lastValid + }); + }); + } + }; +}); diff --git a/test/dumb-inject.js b/test/dumb-inject.js new file mode 100644 index 00000000..488a8556 --- /dev/null +++ b/test/dumb-inject.js @@ -0,0 +1,61 @@ +'use strict'; + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +var rule = require('../rules/dumb-inject'); +var RuleTester = require('eslint').RuleTester; + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +var eslintTester = new RuleTester(); +eslintTester.run('log', rule, { + valid: [ + // Don't crash if no function is passed. + 'inject()', + // Some valid examples + 'inject(function() {$httpBackend = _$httpBackend_})', + 'inject(function() {$controller = _$controller_; $rootScope = _$rootScope_})', + 'inject(function() {$httpBackend = _$httpBackend_; myService = _myService_})' + ], + invalid: [ + // Not an expression statement + { + code: 'inject(function() {function foo() {}})', + errors: [{message: 'inject functions may only consist of assignments in the form myService = _myService_'}] + }, + // Not an assignment expression + { + code: 'inject(function() {$httpBackend.whenGET()})', + errors: [{message: 'inject functions may only consist of assignments in the form myService = _myService_'}] + }, + // Right is not a simple identifier + { + code: 'inject(function() {navigator = $window.navigator})', + errors: [{message: 'inject functions may only consist of assignments in the form myService = _myService_'}] + }, + // Left is not a simple identifier + { + code: 'inject(function() {foo.bar = _bar_})', + errors: [{message: 'inject functions may only consist of assignments in the form bar = _bar_'}] + }, + // Right is not wrapped using underscores + { + code: 'inject(function() {bar = bar})', + errors: [{message: 'inject functions may only consist of assignments in the form bar = _bar_'}] + }, + // Left does not match right + { + code: 'inject(function() {foo = _bar_})', + errors: [{message: 'inject functions may only consist of assignments in the form bar = _bar_'}] + }, + // Bad sorting of statements + { + code: 'inject(function() {foo = _foo_; bar = _bar_})', + errors: [{message: "'bar' must be sorted before 'foo'"}] + } + ] +}); From 107ace332b1a46cc717497220851559e881c0126 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 16:53:27 +0100 Subject: [PATCH 09/35] fixed typo in no-cookiestore --- docs/no-cookiestore.md | 4 ++-- examples/no-cookiestore.js | 4 ++-- rules/no-cookiestore.js | 2 +- test/no-cookiestore.js | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/no-cookiestore.md b/docs/no-cookiestore.md index a735e30a..440d4797 100644 --- a/docs/no-cookiestore.md +++ b/docs/no-cookiestore.md @@ -12,10 +12,10 @@ The following patterns are considered problems; /*eslint angular/no-cookiestore: 2*/ // invalid - $cookieStore.put('favoriteMeal', 'pizza'); // error: Since Angular 1.4, the $cookieStore service is depreacted. Please use now the $cookies service. + $cookieStore.put('favoriteMeal', 'pizza'); // error: Since Angular 1.4, the $cookieStore service is deprecated. Please use now the $cookies service. // invalid - $cookieStore.get('favoriteMeal'); // error: Since Angular 1.4, the $cookieStore service is depreacted. Please use now the $cookies service. + $cookieStore.get('favoriteMeal'); // error: Since Angular 1.4, the $cookieStore service is deprecated. Please use now the $cookies service. The following patterns are **not** considered problems; diff --git a/examples/no-cookiestore.js b/examples/no-cookiestore.js index 17990802..081060e9 100644 --- a/examples/no-cookiestore.js +++ b/examples/no-cookiestore.js @@ -4,8 +4,8 @@ $cookies.put('favoriteMeal', 'pizza'); // example - valid: true $cookies.get('favoriteMeal'); -// example - valid: false, errorMessage: "Since Angular 1.4, the $cookieStore service is depreacted. Please use now the $cookies service." +// example - valid: false, errorMessage: "Since Angular 1.4, the $cookieStore service is deprecated. Please use now the $cookies service." $cookieStore.put('favoriteMeal', 'pizza'); -// example - valid: false, errorMessage: "Since Angular 1.4, the $cookieStore service is depreacted. Please use now the $cookies service." +// example - valid: false, errorMessage: "Since Angular 1.4, the $cookieStore service is deprecated. Please use now the $cookies service." $cookieStore.get('favoriteMeal'); diff --git a/rules/no-cookiestore.js b/rules/no-cookiestore.js index d1d7ef20..dfe974e4 100644 --- a/rules/no-cookiestore.js +++ b/rules/no-cookiestore.js @@ -14,7 +14,7 @@ module.exports = function(context) { MemberExpression: function(node) { if (node.object && node.object.name === '$cookieStore') { - context.report(node, 'Since Angular 1.4, the $cookieStore service is depreacted. Please use now the $cookies service.', {}); + context.report(node, 'Since Angular 1.4, the $cookieStore service is deprecated. Please use now the $cookies service.', {}); } } }; diff --git a/test/no-cookiestore.js b/test/no-cookiestore.js index c20bbe65..638098c2 100644 --- a/test/no-cookiestore.js +++ b/test/no-cookiestore.js @@ -19,10 +19,10 @@ eslintTester.run('no-cookiestore', rule, { ].concat(commonFalsePositives), invalid: [{ code: '$cookieStore.get("");', - errors: [{message: 'Since Angular 1.4, the $cookieStore service is depreacted. Please use now the $cookies service.'}] + errors: [{message: 'Since Angular 1.4, the $cookieStore service is deprecated. Please use now the $cookies service.'}] }, { code: '$cookieStore.put("", "");', - errors: [{message: 'Since Angular 1.4, the $cookieStore service is depreacted. Please use now the $cookies service.'}] + errors: [{message: 'Since Angular 1.4, the $cookieStore service is deprecated. Please use now the $cookies service.'}] } ] }); From ed0b507d7ad731e401a8eba20df8d7086ed50a77 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 17:39:40 +0100 Subject: [PATCH 10/35] add rule no-directive-replace to detect the deprecated replace property (fixes #130) --- README.md | 1 + docs/no-directive-replace.md | 74 ++++++++++++++++++++++ examples/no-directive-replace.js | 40 ++++++++++++ index.js | 1 + rules/no-directive-replace.js | 103 +++++++++++++++++++++++++++++++ rules/utils/utils.js | 15 +++++ test/no-directive-replace.js | 60 ++++++++++++++++++ 7 files changed, 294 insertions(+) create mode 100644 docs/no-directive-replace.md create mode 100644 examples/no-directive-replace.js create mode 100644 rules/no-directive-replace.js create mode 100644 test/no-directive-replace.js diff --git a/README.md b/README.md index 0405727a..45c2cab1 100644 --- a/README.md +++ b/README.md @@ -189,6 +189,7 @@ These are rules designed to prevent you from making mistakes. They either prescr There rules prevent you from using deprecated angular features. * [no-cookiestore](docs/no-cookiestore.md) - use `$cookies` instead of `$cookieStore` + * [no-directive-replace](docs/no-directive-replace.md) - disallow the deprecated directive replace property * [no-http-callback](docs/no-http-callback.md) - disallow the `$http` methods `success()` and `error()` ## Naming diff --git a/docs/no-directive-replace.md b/docs/no-directive-replace.md new file mode 100644 index 00000000..e8d9fad3 --- /dev/null +++ b/docs/no-directive-replace.md @@ -0,0 +1,74 @@ + + +# no-directive-replace - disallow the deprecated directive replace property + +This rule disallows the replace attribute in a directive definition object. +The replace property of a directive definition object is deprecated since angular 1.3 ([latest angular docs](https://docs.angularjs.org/api/ng/service/$compile). + +The option `ignoreReplaceFalse` let you ignore directive definitions with replace set to false. + +## Examples + +The following patterns are considered problems with default config; + + /*eslint angular/no-directive-replace: 2*/ + + // invalid + angular.module('myModule').directive('helloWorld', function() { + return { + template: '

Hello World!

', + replace: true + }; + }); // error: Directive definition property replace is deprecated. + + // invalid + angular.module('myModule').directive('helloWorld', function() { + var directiveDefinition = {}; + directiveDefinition.templateUrl = 'helloWorld.html'; + directiveDefinition.replace = true; + return directiveDefinition; + }); // error: Directive definition property replace is deprecated. + +The following patterns are **not** considered problems with default config; + + /*eslint angular/no-directive-replace: 2*/ + + // valid + angular.module('myModule').directive('helloWorld', function() { + return { + template: '

Hello World!

' + }; + }); + +The following patterns are **not** considered problems when configured `{"ignoreReplaceFalse":true}`: + + /*eslint angular/no-directive-replace: [2,{"ignoreReplaceFalse":true}]*/ + + // valid + angular.module('myModule').directive('helloWorld', function() { + return { + template: '

Hello World!

', + replace: false + }; + }); + +The following patterns are considered problems when configured `{"ignoreReplaceFalse":false}`: + + /*eslint angular/no-directive-replace: [2,{"ignoreReplaceFalse":false}]*/ + + // invalid + angular.module('myModule').directive('helloWorld', function() { + return { + template: '

Hello World!

', + replace: true + }; + }); // error: Directive definition property replace is deprecated. + +## Version + +This rule was introduced in eslint-plugin-angular 0.15.0 + +## Links + +* [Rule source](../rules/no-directive-replace.js) +* [Example source](../examples/no-directive-replace.js) diff --git a/examples/no-directive-replace.js b/examples/no-directive-replace.js new file mode 100644 index 00000000..f813242e --- /dev/null +++ b/examples/no-directive-replace.js @@ -0,0 +1,40 @@ +// example - valid: true +angular.module('myModule').directive('helloWorld', function() { + return { + template: '

Hello World!

' + }; +}); + +// example - valid: true, options: [{"ignoreReplaceFalse": true}] +angular.module('myModule').directive('helloWorld', function() { + return { + template: '

Hello World!

', + replace: false + }; +}); + +// example - valid: false, errorMessage: "Directive definition property replace is deprecated." +angular.module('myModule').directive('helloWorld', function() { + return { + template: '

Hello World!

', + replace: true + }; +}); + +// example - valid: false, errorMessage: "Directive definition property replace is deprecated." +angular.module('myModule').directive('helloWorld', function() { + var directiveDefinition = {}; + directiveDefinition.templateUrl = 'helloWorld.html'; + directiveDefinition.replace = true; + return directiveDefinition; +}); + +// example - valid: false, options: [{"ignoreReplaceFalse": false}], errorMessage: "Directive definition property replace is deprecated." +angular.module('myModule').directive('helloWorld', function() { + return { + template: '

Hello World!

', + replace: true + }; +}); + + diff --git a/index.js b/index.js index dcfd851f..0ce35b99 100644 --- a/index.js +++ b/index.js @@ -32,6 +32,7 @@ rulesConfiguration.addRule('no-angular-mock', 0); rulesConfiguration.addRule('no-controller', 0); rulesConfiguration.addRule('no-cookiestore', 2); rulesConfiguration.addRule('no-digest', 0); +rulesConfiguration.addRule('no-directive-replace', 0); rulesConfiguration.addRule('no-http-callback', 0); rulesConfiguration.addRule('no-inline-template', [0, {'allow-simple': true}]); rulesConfiguration.addRule('no-jquery-angularelement', 2); diff --git a/rules/no-directive-replace.js b/rules/no-directive-replace.js new file mode 100644 index 00000000..9cc33244 --- /dev/null +++ b/rules/no-directive-replace.js @@ -0,0 +1,103 @@ +/** + * disallow the deprecated directive replace property + * + * This rule disallows the replace attribute in a directive definition object. + * The replace property of a directive definition object is deprecated since angular 1.3 ([latest angular docs](https://docs.angularjs.org/api/ng/service/$compile). + * + * The option `ignoreReplaceFalse` let you ignore directive definitions with replace set to false. + * + * @version 0.15.0 + * @category deprecatedAngularFeature + */ +'use strict'; + +module.exports = function(context) { + var utils = require('./utils/utils'); + + var options = context.options[0] || {}; + var ignoreReplaceFalse = !!options.ignoreReplaceFalse; + + function isDirectiveDefinitionFunction(fnExpression) { + return fnExpression.parent.type === 'CallExpression' && utils.isAngularDirectiveDeclaration(fnExpression.parent); + } + + function inDirectiveBody(node) { + var block = utils.findNodeTypeInParents(node, 'BlockStatement'); + return block && isDirectiveDefinitionFunction(block.parent); + } + + var reportedNodesByName = {}; + + function addPotentialReplaceNode(variableName, node) { + var nodeList = reportedNodesByName[variableName] || []; + + var report = { + name: variableName, + node: node, + block: utils.findNodeTypeInParents(node, 'BlockStatement') + }; + + nodeList.push(report); + + reportedNodesByName[variableName] = nodeList; + } + + return { + ReturnStatement: function(node) { + if (node.argument.type === 'Identifier') { + var reportedNodes = reportedNodesByName[node.argument.name]; + if (!reportedNodes) { + return; + } + reportedNodes.forEach(function(report) { + if (report.block === node.parent) { + context.report(node, 'Directive definition property replace is deprecated.'); + } + }); + } + }, + AssignmentExpression: function(node) { + // Only check for literal member property assignments. + if (node.left.type !== 'MemberExpression') { + return; + } + // Only check setting properties named 'replace'. + if (node.left.property.name !== 'replace') { + return; + } + if (ignoreReplaceFalse && node.right.value === false) { + return; + } + addPotentialReplaceNode(node.left.object.name, node); + }, + Property: function(node) { + // This only checks for objects which have defined a literal restrict property. + if (node.key.name !== 'replace') { + return; + } + if (ignoreReplaceFalse === true && node.value.value === false) { + return; + } + + // assumption: Property always belongs to a ObjectExpression + var objectExpression = node.parent; + + if (objectExpression.parent.type === 'VariableDeclarator') { + addPotentialReplaceNode(objectExpression.parent.id.name, node); + } + + if (objectExpression.parent.type === 'ReturnStatement' && inDirectiveBody(objectExpression.parent)) { + context.report(node, 'Directive definition property replace is deprecated.'); + } + } + }; +}; + +module.exports.schema = [{ + type: 'object', + properties: { + ignoreReplaceFalse: { + type: 'boolean' + } + } +}]; diff --git a/rules/utils/utils.js b/rules/utils/utils.js index f5d74deb..96ea18fd 100644 --- a/rules/utils/utils.js +++ b/rules/utils/utils.js @@ -50,6 +50,7 @@ module.exports = { isRouteDefinition: isRouteDefinition, isUIRouterStateDefinition: isUIRouterStateDefinition, findIdentiferInScope: findIdentiferInScope, + findNodeTypeInParents: findNodeTypeInParents, getControllerDefinition: getControllerDefinition }; @@ -467,6 +468,20 @@ function findIdentiferInScope(context, identifier) { return identifierNode; } +/** + * Find the closest parent node with the specified nodeType. + * + * @param {Object} node The node to start the search. + * @param {String} nodeType The node type (e.g. 'BlockStatement') + * @returns {Object} The first parent node with the specified nodeType or undefined; + */ +function findNodeTypeInParents(node, nodeType) { + if (!node || node.type === nodeType) { + return node; + } + return findNodeTypeInParents(node.parent, nodeType); +} + /** * Find the function definition of a controller in the current context. * diff --git a/test/no-directive-replace.js b/test/no-directive-replace.js new file mode 100644 index 00000000..baf41548 --- /dev/null +++ b/test/no-directive-replace.js @@ -0,0 +1,60 @@ +'use strict'; + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +var rule = require('../rules/no-directive-replace'); +var RuleTester = require('eslint').RuleTester; +var commonFalsePositives = require('./utils/commonFalsePositives'); + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +var eslintTester = new RuleTester(); +eslintTester.run('no-directive-replace', rule, { + valid: [ + 'angular.module("").factory("", function() {return {replace: false}})', + 'angular.module("").directive("")', + 'angular.module("").directive()', + 'angular.module("").directive("", function() {})', + 'angular.module("").directive("", function() {return {anotherProperty:"anotherValue"}})', + 'angular.module("").directive("", function() {return {restrict:"A"}})', + 'angular.module("").directive("", function() { var def = {}; return def; })', + 'angular.module("").directive("", function() { function x() { return {replace: true} }; x(); return {}; })', + + 'angular.module("").directive("", function() { var def = {}; def.otherProperty = true; return def; })', + 'angular.module("").directive("", function() { var nonDef = {replace: true}; return {}; })', + 'angular.module("").directive("", function() { var nonDef = {}; function x() { var nonDef = {replace: true} }; x(); return nonDef; })', + { + code: 'angular.module("").directive("", function() {return {replace:false}})', + options: [{ignoreReplaceFalse: true}] + }, + { + code: 'angular.module("").directive("", function() { var def = {}; def.replace = false; return def; })', + options: [{ignoreReplaceFalse: true}] + } + ].concat(commonFalsePositives), + invalid: [ + // Disallowed with default configuration + { + code: 'angular.module("").directive("", function() {return {replace:true}})', + errors: [{message: 'Directive definition property replace is deprecated.'}] + }, + { + code: 'angular.module("").directive("", function() { var def = {replace: true}; return def; })', + errors: [{message: 'Directive definition property replace is deprecated.'}] + }, + { + code: 'angular.module("").directive("", function() { var def = {}; def.replace = true; return def; })', + errors: [{message: 'Directive definition property replace is deprecated.'}] + }, + // Disallow replace false with default configuration + { + code: 'angular.module("").directive("", function() {return {replace:0}})', + options: [{ignoreReplaceFalse: true}], + errors: [{message: 'Directive definition property replace is deprecated.'}] + } + ] +}); From 466a295fcb4c7026ce795917b1c11e3dbd17a81f Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 18:13:19 +0100 Subject: [PATCH 11/35] fixed typo in ruleCategories.json --- scripts/ruleCategories.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ruleCategories.json b/scripts/ruleCategories.json index dcd0b26c..c0b4a90d 100644 --- a/scripts/ruleCategories.json +++ b/scripts/ruleCategories.json @@ -21,7 +21,7 @@ }, "deprecatedAngularFeature": { "headline": "Deprecated Angular Features", - "description": "There rules prevent you from using deprecated angular features." + "description": "These rules prevent you from using deprecated angular features." }, "naming": { "headline": "Naming", From 6fd63464245127e2eca2bbe262bfe10cf570818c Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 18:28:02 +0100 Subject: [PATCH 12/35] add some documentation and refactoring (#130) --- rules/no-directive-replace.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/rules/no-directive-replace.js b/rules/no-directive-replace.js index 9cc33244..83305668 100644 --- a/rules/no-directive-replace.js +++ b/rules/no-directive-replace.js @@ -44,14 +44,16 @@ module.exports = function(context) { return { ReturnStatement: function(node) { + // only check identifiers, because expression in return statements are already checked by the Property callback if (node.argument.type === 'Identifier') { var reportedNodes = reportedNodesByName[node.argument.name]; if (!reportedNodes) { return; } reportedNodes.forEach(function(report) { + // only reports nodes that belong to the same expression if (report.block === node.parent) { - context.report(node, 'Directive definition property replace is deprecated.'); + context.report(report.node, 'Directive definition property replace is deprecated.'); } }); } @@ -80,13 +82,15 @@ module.exports = function(context) { } // assumption: Property always belongs to a ObjectExpression - var objectExpression = node.parent; + var objectExpressionParent = node.parent.parent; - if (objectExpression.parent.type === 'VariableDeclarator') { - addPotentialReplaceNode(objectExpression.parent.id.name, node); + // add to potential replace nodes if the object is defined in a variable + if (objectExpressionParent.type === 'VariableDeclarator') { + addPotentialReplaceNode(objectExpressionParent.id.name, node); } - if (objectExpression.parent.type === 'ReturnStatement' && inDirectiveBody(objectExpression.parent)) { + // report directly if object is part of a return statement and inside a directive body + if (objectExpressionParent.type === 'ReturnStatement' && inDirectiveBody(objectExpressionParent)) { context.report(node, 'Directive definition property replace is deprecated.'); } } From 050fb45f16042f6dc0748ec15fcc4e30ed728643 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 18:48:44 +0100 Subject: [PATCH 13/35] test case to prove that reported examples in #287 work (fixes #287) --- test/di-unused.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/di-unused.js b/test/di-unused.js index cdd8e248..4e337ac1 100644 --- a/test/di-unused.js +++ b/test/di-unused.js @@ -106,6 +106,11 @@ eslintTester.run('di-unused', rule, { errors: [ {message: 'Unused injected value $http'} ] + }, { + code: 'angular.module("").factory("", ["$http", "$q", function($http, $q) {return $q.resolve()}]);', + errors: [ + {message: 'Unused injected value $http'} + ] }, // filter { @@ -192,6 +197,11 @@ eslintTester.run('di-unused', rule, { }, { code: 'angular.module("").provider("", function() {this.$get = ["q", function($q) {}];});', errors: [{message: 'Unused injected value $q'}] + }, + // examples from issue #287 + { + code: 'angular.module("myapp").filter("myfilter", [ "$translate", "$filter", function ($translate, $filter) { return function (value) { return $filter(value, 4) * 100; } } ]);', + errors: [{message: 'Unused injected value $translate'}] } ] }); From dcfff4a45824fe8c12827b17901605a81731766a Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 18:49:32 +0100 Subject: [PATCH 14/35] Fixes another typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0405727a..d069bf75 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ These are rules designed to prevent you from making mistakes. They either prescr ## Deprecated Angular Features -There rules prevent you from using deprecated angular features. +These rules prevent you from using deprecated angular features. * [no-cookiestore](docs/no-cookiestore.md) - use `$cookies` instead of `$cookieStore` * [no-http-callback](docs/no-http-callback.md) - disallow the `$http` methods `success()` and `error()` From 0ca4dfaf632b243728af604fbf0451cfced15ecf Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Tue, 13 Oct 2015 00:44:51 +0200 Subject: [PATCH 15/35] Add failing test for typeof undefined check (#232) --- test/definedundefined.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/definedundefined.js b/test/definedundefined.js index 4a6710fd..1c58cd65 100644 --- a/test/definedundefined.js +++ b/test/definedundefined.js @@ -23,6 +23,8 @@ eslintTester.run('definedundefined', rule, { {code: 'undefined === variable', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, {code: 'undefined !== variable', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, {code: 'variable !== undefined', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, + {code: 'typeof variable == "undefined"', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, + {code: 'typeof variable !== "undefined"', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, {code: '!angular.isUndefined(variable)', errors: [{message: 'Instead of !angular.isUndefined, you can use the out-of-box angular.isDefined method'}]}, {code: '!angular.isDefined(variable)', errors: [{message: 'Instead of !angular.isDefined, you can use the out-of-box angular.isUndefined method'}]} ] From 2aa54003f418f5ce5b81484d7053518bbf0dc51e Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 19:39:02 +0100 Subject: [PATCH 16/35] extract require utils in rules --- rules/controller-as-route.js | 3 ++- rules/controller-as-vm.js | 3 ++- rules/controller-as.js | 3 ++- rules/controller-name.js | 3 ++- rules/di.js | 3 ++- rules/directive-name.js | 3 ++- rules/directive-restrict.js | 3 ++- rules/empty-controller.js | 3 ++- rules/file-name.js | 3 ++- rules/filter-name.js | 3 ++- rules/function-type.js | 3 ++- rules/module-dependency-order.js | 3 ++- rules/module-getter.js | 3 ++- rules/module-name.js | 3 ++- rules/module-setter.js | 2 +- rules/no-controller.js | 3 ++- rules/no-service-method.js | 3 ++- rules/no-services.js | 3 ++- rules/rest-service.js | 3 ++- rules/service-name.js | 3 ++- rules/typecheck-array.js | 3 ++- rules/typecheck-date.js | 3 ++- rules/typecheck-function.js | 3 ++- rules/typecheck-number.js | 3 ++- rules/typecheck-object.js | 3 ++- rules/typecheck-regexp.js | 3 ++- rules/typecheck-string.js | 4 ++-- 27 files changed, 53 insertions(+), 28 deletions(-) diff --git a/rules/controller-as-route.js b/rules/controller-as-route.js index fa13104e..48181c92 100644 --- a/rules/controller-as-route.js +++ b/rules/controller-as-route.js @@ -9,8 +9,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); return { CallExpression: function(node) { diff --git a/rules/controller-as-vm.js b/rules/controller-as-vm.js index 970aa82b..eec549a0 100644 --- a/rules/controller-as-vm.js +++ b/rules/controller-as-vm.js @@ -11,8 +11,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); var badStatements = []; var badCaptureStatements = []; var controllerFunctions = []; diff --git a/rules/controller-as.js b/rules/controller-as.js index 1cfbff9c..a67147d9 100644 --- a/rules/controller-as.js +++ b/rules/controller-as.js @@ -11,8 +11,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); var badStatements = []; var controllerFunctions = []; diff --git a/rules/controller-name.js b/rules/controller-name.js index 89705187..b9bd4d75 100644 --- a/rules/controller-name.js +++ b/rules/controller-name.js @@ -12,8 +12,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); return { diff --git a/rules/di.js b/rules/di.js index 3c92416b..1ca21e2f 100644 --- a/rules/di.js +++ b/rules/di.js @@ -8,8 +8,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); var angularNamedObjectList = ['value', 'factory', 'service', 'provider', 'controller', 'filter', 'directive']; diff --git a/rules/directive-name.js b/rules/directive-name.js index bdead682..f10039b0 100644 --- a/rules/directive-name.js +++ b/rules/directive-name.js @@ -12,8 +12,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); if (context.settings.angular === 2) { return {}; } diff --git a/rules/directive-restrict.js b/rules/directive-restrict.js index 46747098..192a6e24 100644 --- a/rules/directive-restrict.js +++ b/rules/directive-restrict.js @@ -12,8 +12,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); var options = context.options[0] || {}; var restrictOpt = options.restrict || 'AE'; diff --git a/rules/empty-controller.js b/rules/empty-controller.js index b3174699..44449035 100644 --- a/rules/empty-controller.js +++ b/rules/empty-controller.js @@ -9,8 +9,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); function report(node, name) { context.report(node, 'The {{ctrl}} controller is useless because empty. You can remove it from your Router configuration or in one of your view', { diff --git a/rules/file-name.js b/rules/file-name.js index d5ef4ac0..5192ac90 100644 --- a/rules/file-name.js +++ b/rules/file-name.js @@ -13,8 +13,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = (function() { - var utils = require('./utils/utils'); var path = require('path'); var fileEnding = '.js'; diff --git a/rules/filter-name.js b/rules/filter-name.js index 7fb7983e..fdcca71d 100644 --- a/rules/filter-name.js +++ b/rules/filter-name.js @@ -10,8 +10,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); return { diff --git a/rules/function-type.js b/rules/function-type.js index 7566c9b9..8a84381c 100644 --- a/rules/function-type.js +++ b/rules/function-type.js @@ -12,8 +12,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); var angularObjectList = ['animation', 'config', 'constant', 'controller', 'directive', 'factory', 'filter', 'provider', 'service', 'value', 'decorator']; var configType = context.options[0] || 'anonymous'; var messageByConfigType = { diff --git a/rules/module-dependency-order.js b/rules/module-dependency-order.js index ed459eaa..13b894d0 100644 --- a/rules/module-dependency-order.js +++ b/rules/module-dependency-order.js @@ -14,8 +14,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); var options = context.options[0] || {}; var groupedMode = options.grouped !== false; diff --git a/rules/module-getter.js b/rules/module-getter.js index 72e3a389..e7a26945 100644 --- a/rules/module-getter.js +++ b/rules/module-getter.js @@ -10,8 +10,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); return { diff --git a/rules/module-name.js b/rules/module-name.js index be61a1b9..cd9132ea 100644 --- a/rules/module-name.js +++ b/rules/module-name.js @@ -11,8 +11,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); return { diff --git a/rules/module-setter.js b/rules/module-setter.js index 06b90d8b..d989fbd3 100644 --- a/rules/module-setter.js +++ b/rules/module-setter.js @@ -10,9 +10,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); module.exports = function(context) { - var utils = require('./utils/utils'); return { diff --git a/rules/no-controller.js b/rules/no-controller.js index b1d5b531..0e1afbbb 100644 --- a/rules/no-controller.js +++ b/rules/no-controller.js @@ -8,8 +8,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); return { diff --git a/rules/no-service-method.js b/rules/no-service-method.js index 6767045c..7eddb315 100644 --- a/rules/no-service-method.js +++ b/rules/no-service-method.js @@ -9,8 +9,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); return { diff --git a/rules/no-services.js b/rules/no-services.js index 66925848..9b4cecca 100644 --- a/rules/no-services.js +++ b/rules/no-services.js @@ -12,8 +12,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); var angularObjectList = ['controller', 'filter', 'directive']; var badServices; diff --git a/rules/rest-service.js b/rules/rest-service.js index 9656f17c..506b3fb7 100644 --- a/rules/rest-service.js +++ b/rules/rest-service.js @@ -9,8 +9,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); var angularObjectList = ['controller', 'filter', 'directive', 'service', 'factory', 'provider']; var services = ['$http', '$resource', 'Restangular']; diff --git a/rules/service-name.js b/rules/service-name.js index 774b934f..17886cdb 100644 --- a/rules/service-name.js +++ b/rules/service-name.js @@ -11,8 +11,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); return { diff --git a/rules/typecheck-array.js b/rules/typecheck-array.js index 32af27a6..84af52be 100644 --- a/rules/typecheck-array.js +++ b/rules/typecheck-array.js @@ -8,8 +8,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); function recordError(node, origin) { if (node.type === 'Literal' && node.value === '[object Array]') { diff --git a/rules/typecheck-date.js b/rules/typecheck-date.js index 95b0b26d..c43f552e 100644 --- a/rules/typecheck-date.js +++ b/rules/typecheck-date.js @@ -8,8 +8,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); function recordError(node, origin) { if (node.type === 'Literal' && node.value === '[object Date]') { diff --git a/rules/typecheck-function.js b/rules/typecheck-function.js index 2587ad20..98baf684 100644 --- a/rules/typecheck-function.js +++ b/rules/typecheck-function.js @@ -8,8 +8,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); function recordError(node, origin) { if (node.type === 'Literal' && (node.value === 'function' || node.value === '[object Function]')) { diff --git a/rules/typecheck-number.js b/rules/typecheck-number.js index cf12b890..141214fd 100644 --- a/rules/typecheck-number.js +++ b/rules/typecheck-number.js @@ -8,8 +8,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); function recordError(node, origin) { if (node.type === 'Literal' && (node.value === 'number' || node.value === '[object Number]')) { diff --git a/rules/typecheck-object.js b/rules/typecheck-object.js index 760f85a1..390d2946 100644 --- a/rules/typecheck-object.js +++ b/rules/typecheck-object.js @@ -8,8 +8,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); function recordError(node, origin) { if (node.type === 'Literal' && (node.value === 'object' || node.value === '[object Object]')) { diff --git a/rules/typecheck-regexp.js b/rules/typecheck-regexp.js index c5677bdc..13429048 100644 --- a/rules/typecheck-regexp.js +++ b/rules/typecheck-regexp.js @@ -9,8 +9,9 @@ */ 'use strict'; +var utils = require('./utils/utils'); + module.exports = function(context) { - var utils = require('./utils/utils'); function recordError(node, origin) { if (node.type === 'Literal' && node.value === '[object RegExp]') { diff --git a/rules/typecheck-string.js b/rules/typecheck-string.js index 9bdcd0f1..e3a2e147 100644 --- a/rules/typecheck-string.js +++ b/rules/typecheck-string.js @@ -8,9 +8,9 @@ */ 'use strict'; -module.exports = function(context) { - var utils = require('./utils/utils'); +var utils = require('./utils/utils'); +module.exports = function(context) { function recordError(node, origin) { if (node.type === 'Literal' && (node.value === 'string' || node.value === '[object String]')) { context.report(origin, 'You should use the angular.isString method', {}); From e4bbf3974c862eb432b713f23507b3974feb1e82 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 19:46:53 +0100 Subject: [PATCH 17/35] fixed eslint issues --- rules/controller-as-route.js | 1 - rules/controller-name.js | 1 - rules/di.js | 1 - rules/directive-restrict.js | 1 - rules/empty-controller.js | 1 - rules/filter-name.js | 1 - rules/module-dependency-order.js | 1 - rules/module-getter.js | 1 - rules/module-name.js | 1 - rules/module-setter.js | 1 - rules/no-controller.js | 1 - rules/no-service-method.js | 1 - rules/no-services.js | 1 - rules/rest-service.js | 1 - rules/service-name.js | 1 - rules/typecheck-array.js | 1 - rules/typecheck-date.js | 1 - rules/typecheck-function.js | 1 - rules/typecheck-number.js | 1 - rules/typecheck-object.js | 1 - rules/typecheck-regexp.js | 1 - 21 files changed, 21 deletions(-) diff --git a/rules/controller-as-route.js b/rules/controller-as-route.js index 48181c92..1797330d 100644 --- a/rules/controller-as-route.js +++ b/rules/controller-as-route.js @@ -12,7 +12,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - return { CallExpression: function(node) { var routeObject = null; diff --git a/rules/controller-name.js b/rules/controller-name.js index b9bd4d75..dd8bdcce 100644 --- a/rules/controller-name.js +++ b/rules/controller-name.js @@ -15,7 +15,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - return { CallExpression: function(node) { diff --git a/rules/di.js b/rules/di.js index 1ca21e2f..30b79b48 100644 --- a/rules/di.js +++ b/rules/di.js @@ -11,7 +11,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - var angularNamedObjectList = ['value', 'factory', 'service', 'provider', 'controller', 'filter', 'directive']; function report(node, syntax) { diff --git a/rules/directive-restrict.js b/rules/directive-restrict.js index 192a6e24..221f4242 100644 --- a/rules/directive-restrict.js +++ b/rules/directive-restrict.js @@ -15,7 +15,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - var options = context.options[0] || {}; var restrictOpt = options.restrict || 'AE'; var explicitRestrict = options.explicit === 'always'; diff --git a/rules/empty-controller.js b/rules/empty-controller.js index 44449035..26d1187f 100644 --- a/rules/empty-controller.js +++ b/rules/empty-controller.js @@ -12,7 +12,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - function report(node, name) { context.report(node, 'The {{ctrl}} controller is useless because empty. You can remove it from your Router configuration or in one of your view', { ctrl: name diff --git a/rules/filter-name.js b/rules/filter-name.js index fdcca71d..0e1d9fcd 100644 --- a/rules/filter-name.js +++ b/rules/filter-name.js @@ -13,7 +13,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - return { CallExpression: function(node) { diff --git a/rules/module-dependency-order.js b/rules/module-dependency-order.js index 13b894d0..c63ce015 100644 --- a/rules/module-dependency-order.js +++ b/rules/module-dependency-order.js @@ -17,7 +17,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - var options = context.options[0] || {}; var groupedMode = options.grouped !== false; var moduleRegex; diff --git a/rules/module-getter.js b/rules/module-getter.js index e7a26945..02e93670 100644 --- a/rules/module-getter.js +++ b/rules/module-getter.js @@ -13,7 +13,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - return { ExpressionStatement: function(node) { diff --git a/rules/module-name.js b/rules/module-name.js index cd9132ea..8c95ed2e 100644 --- a/rules/module-name.js +++ b/rules/module-name.js @@ -14,7 +14,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - return { CallExpression: function(node) { diff --git a/rules/module-setter.js b/rules/module-setter.js index d989fbd3..42518c1e 100644 --- a/rules/module-setter.js +++ b/rules/module-setter.js @@ -13,7 +13,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - return { VariableDeclaration: function(node) { diff --git a/rules/no-controller.js b/rules/no-controller.js index 0e1afbbb..4d3214d4 100644 --- a/rules/no-controller.js +++ b/rules/no-controller.js @@ -11,7 +11,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - return { CallExpression: function(node) { diff --git a/rules/no-service-method.js b/rules/no-service-method.js index 7eddb315..f210c009 100644 --- a/rules/no-service-method.js +++ b/rules/no-service-method.js @@ -12,7 +12,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - return { CallExpression: function(node) { diff --git a/rules/no-services.js b/rules/no-services.js index 9b4cecca..e15a68b7 100644 --- a/rules/no-services.js +++ b/rules/no-services.js @@ -15,7 +15,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - var angularObjectList = ['controller', 'filter', 'directive']; var badServices; var map; diff --git a/rules/rest-service.js b/rules/rest-service.js index 506b3fb7..90e2627e 100644 --- a/rules/rest-service.js +++ b/rules/rest-service.js @@ -12,7 +12,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - var angularObjectList = ['controller', 'filter', 'directive', 'service', 'factory', 'provider']; var services = ['$http', '$resource', 'Restangular']; var message = 'You should use the same service ({{method}}) for REST API calls'; diff --git a/rules/service-name.js b/rules/service-name.js index 17886cdb..b214f5af 100644 --- a/rules/service-name.js +++ b/rules/service-name.js @@ -14,7 +14,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - return { CallExpression: function(node) { diff --git a/rules/typecheck-array.js b/rules/typecheck-array.js index 84af52be..fcbbfd42 100644 --- a/rules/typecheck-array.js +++ b/rules/typecheck-array.js @@ -11,7 +11,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - function recordError(node, origin) { if (node.type === 'Literal' && node.value === '[object Array]') { context.report(origin, 'You should use the angular.isArray method', {}); diff --git a/rules/typecheck-date.js b/rules/typecheck-date.js index c43f552e..7df5c01d 100644 --- a/rules/typecheck-date.js +++ b/rules/typecheck-date.js @@ -11,7 +11,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - function recordError(node, origin) { if (node.type === 'Literal' && node.value === '[object Date]') { context.report(origin, 'You should use the angular.isDate method', {}); diff --git a/rules/typecheck-function.js b/rules/typecheck-function.js index 98baf684..6632cb9f 100644 --- a/rules/typecheck-function.js +++ b/rules/typecheck-function.js @@ -11,7 +11,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - function recordError(node, origin) { if (node.type === 'Literal' && (node.value === 'function' || node.value === '[object Function]')) { context.report(origin, 'You should use the angular.isFunction method', {}); diff --git a/rules/typecheck-number.js b/rules/typecheck-number.js index 141214fd..b1fc34fc 100644 --- a/rules/typecheck-number.js +++ b/rules/typecheck-number.js @@ -11,7 +11,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - function recordError(node, origin) { if (node.type === 'Literal' && (node.value === 'number' || node.value === '[object Number]')) { context.report(origin, 'You should use the angular.isNumber method', {}); diff --git a/rules/typecheck-object.js b/rules/typecheck-object.js index 390d2946..8ac0ade0 100644 --- a/rules/typecheck-object.js +++ b/rules/typecheck-object.js @@ -11,7 +11,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - function recordError(node, origin) { if (node.type === 'Literal' && (node.value === 'object' || node.value === '[object Object]')) { context.report(origin, 'You should use the angular.isObject method', {}); diff --git a/rules/typecheck-regexp.js b/rules/typecheck-regexp.js index 13429048..c6e3c9ab 100644 --- a/rules/typecheck-regexp.js +++ b/rules/typecheck-regexp.js @@ -12,7 +12,6 @@ var utils = require('./utils/utils'); module.exports = function(context) { - function recordError(node, origin) { if (node.type === 'Literal' && node.value === '[object RegExp]') { context.report(origin, 'You should use the angular.isRegexp method', {}); From 6fe716270b8a3b48000fe0c7c74d2d2d5510a4b4 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sun, 6 Dec 2015 20:20:55 +0100 Subject: [PATCH 18/35] Enfore eslint rule global-require --- .eslintrc | 1 + rules/file-name.js | 3 ++- rules/utils/rulesConfiguration.js | 2 +- scripts/docs.js | 7 +++---- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.eslintrc b/.eslintrc index 6773f25a..8beec8ac 100644 --- a/.eslintrc +++ b/.eslintrc @@ -118,6 +118,7 @@ rules: # http://eslint.org/docs/rules/#nodejs callback-return: 2 + global-require: 2 handle-callback-err: - 2 - ^(e|err|error)$ diff --git a/rules/file-name.js b/rules/file-name.js index 5192ac90..6ae8a410 100644 --- a/rules/file-name.js +++ b/rules/file-name.js @@ -13,10 +13,11 @@ */ 'use strict'; +var path = require('path'); + var utils = require('./utils/utils'); module.exports = (function() { - var path = require('path'); var fileEnding = '.js'; var separators = { diff --git a/rules/utils/rulesConfiguration.js b/rules/utils/rulesConfiguration.js index d069ec6b..7adc2370 100644 --- a/rules/utils/rulesConfiguration.js +++ b/rules/utils/rulesConfiguration.js @@ -3,7 +3,7 @@ function Rule(name, config) { this.name = name; this.config = config; - this._requireRule = require('../' + this.name); + this._requireRule = require('../' + this.name); // eslint-disable-line global-require } Rule.prototype = { diff --git a/scripts/docs.js b/scripts/docs.js index 59f39764..ab440295 100644 --- a/scripts/docs.js +++ b/scripts/docs.js @@ -3,7 +3,8 @@ var fs = require('fs'); var parseComments = require('parse-comments'); var _ = require('lodash'); -var eslintAngularIndex = require('../index.js'); +var eslintAngularIndex = require('../index'); +var ruleCategories = require('./ruleCategories.json'); var RuleTester = require('eslint').RuleTester; var templates = require('./templates.js'); @@ -41,8 +42,6 @@ function createDocFiles(cb) { * @param cb callback */ function updateReadme(readmePath, cb) { - var ruleCategories = require('./ruleCategories.json'); - ruleCategories.rulesByCategory = _.groupBy(this.rules, 'category'); // filter categories without rules @@ -192,7 +191,7 @@ function _createRule(ruleName) { } // load rule module for tests - rule.module = require('../rules/' + rule.ruleName); + rule.module = require('../rules/' + rule.ruleName); // eslint-disable-line global-require // load examples, prepare them for the tests and group the for the template rule.allExamples = _loadExamples(rule); From 22caa4e0a0f36d8c069276a2ccf568c89679c6e5 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 21:35:16 +0100 Subject: [PATCH 19/35] add tests for di false positives (#99) --- test/di.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/di.js b/test/di.js index 6adcb922..24883425 100644 --- a/test/di.js +++ b/test/di.js @@ -9,7 +9,7 @@ var RuleTester = require('eslint').RuleTester; var commonFalsePositives = require('./utils/commonFalsePositives'); -var angularNamedObjectList = ['value', 'factory', 'service', 'provider', 'controller', 'filter', 'directive']; +var angularNamedObjectList = ['factory', 'service', 'provider', 'controller', 'filter', 'directive']; var angularObjectList = ['run', 'config']; @@ -153,7 +153,19 @@ valid.push({ }, { code: 'mocha.run();', options: ['array'] +}, { + code: 'mocha.run();', + options: ['array'] +}, { + // value false positive with function + code: 'angular.module("") .value("", function () {});', + options: ['array'] +}, { + // value false positive with array (example from issue #99) + code: 'angular.module("") .value("", [{ }, { }]);', + options: ['function'] }); + // ------------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------------ From 217e3891d8ef4568e8d41339547777e8e7444eda Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 21:35:47 +0100 Subject: [PATCH 20/35] remove value from di checks (fixes #99) --- rules/di.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/di.js b/rules/di.js index 3c92416b..669437a6 100644 --- a/rules/di.js +++ b/rules/di.js @@ -11,7 +11,7 @@ module.exports = function(context) { var utils = require('./utils/utils'); - var angularNamedObjectList = ['value', 'factory', 'service', 'provider', 'controller', 'filter', 'directive']; + var angularNamedObjectList = ['factory', 'service', 'provider', 'controller', 'filter', 'directive']; function report(node, syntax) { context.report(node, 'You should use the {{syntax}} syntax for DI', { From adf9f0c95f07e3e15e1ae2eaf5d461abd497ba54 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sun, 6 Dec 2015 21:50:36 +0100 Subject: [PATCH 21/35] Disable dumb-inject rule by default New rules may not be enabled by default, as this would be a breaking change for existing setups. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 8ffd23f5..0d029540 100644 --- a/index.js +++ b/index.js @@ -16,7 +16,7 @@ rulesConfiguration.addRule('di-unused', 0); rulesConfiguration.addRule('directive-name', 0); rulesConfiguration.addRule('directive-restrict', 0); rulesConfiguration.addRule('document-service', 2); -rulesConfiguration.addRule('dumb-inject', 2); +rulesConfiguration.addRule('dumb-inject', 0); rulesConfiguration.addRule('empty-controller', 0); rulesConfiguration.addRule('file-name', 0); rulesConfiguration.addRule('filter-name', 0); From 8129487081493dd1ebd6bf2ff176a26dda23dbcb Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 21:53:52 +0100 Subject: [PATCH 22/35] change legacy node in travis.yml from 0.10 to 0.12 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 71812dc1..d807dd0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,5 @@ language: node_js node_js: - "5.0" - "4.2" - - "0.10" + - "0.12" after_script: "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" From 6398d120e5a7c8f2aba28326ab169c7c3f8b397a Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sun, 6 Dec 2015 21:56:57 +0100 Subject: [PATCH 23/35] Add Tilman Potthof and Remco Haszing to the team --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d069bf75..edfe5f00 100644 --- a/README.md +++ b/README.md @@ -361,6 +361,6 @@ Here is the basic configuration for the rules defined in the ESLint plugin, in o ## Team -[![Emmanuel DEMEY](https://avatars.githubusercontent.com/u/555768?s=117)](http://gillespie59.github.io/) | -:---:| -[Emmanuel DEMEY](http://gillespie59.github.io/) +[![Emmanuel Demey](https://avatars.githubusercontent.com/u/555768?s=117)](http://gillespie59.github.io/) | [![Tilman Potthof](https://avatars.githubusercontent.com/u/157532?s=117)](https://github.com/tilmanpotthof) | [![Remco Haszing](https://avatars.githubusercontent.com/u/779047?s=117)](https://github.com/remcohaszing) | +:---:|:---:|:---:| +[Emmanuel Demey](http://gillespie59.github.io/) | [Tilman Potthof](https://github.com/tilmanpotthof) | [Remco Haszing](https://github.com/remcohaszing) | From 7e70866494ca8f2a22e422d34f24f4b8528c3b33 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 22:30:05 +0100 Subject: [PATCH 24/35] cover typeof checks in definedundefined rule (fixes #232) --- rules/definedundefined.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/rules/definedundefined.js b/rules/definedundefined.js index e747a9ed..4f9265e4 100644 --- a/rules/definedundefined.js +++ b/rules/definedundefined.js @@ -9,7 +9,17 @@ */ 'use strict'; +var utils = require('./utils/utils'); + + module.exports = function(context) { + function isCompareOperator(operator) { + return operator === '===' || operator === '!==' || operator === '==' || operator === '!='; + } + function reportError(node) { + context.report(node, 'You should not use directly the "undefined" keyword. Prefer ' + + 'angular.isUndefined or angular.isDefined', {}); + } /** * Rule that check if we use angular.is(Un)defined() instead of the undefined keyword */ @@ -27,15 +37,15 @@ module.exports = function(context) { } }, BinaryExpression: function(node) { - if (node.operator === '===' || node.operator === '!==') { - if (node.left.type === 'Identifier' && node.left.name === 'undefined') { - context.report(node, 'You should not use directly the "undefined" keyword. Prefer ' + - 'angular.isUndefined or angular.isDefined', {}); - } - - if (node.right.type === 'Identifier' && node.right.name === 'undefined') { - context.report(node, 'You should not use directly the "undefined" keyword. Prefer ' + - 'angular.isUndefined or angular.isDefined', {}); + if (isCompareOperator(node.operator)) { + if (utils.isTypeOfStatement(node.left) || utils.isToStringStatement(node.left)) { + reportError(node); + } else if (utils.isTypeOfStatement(node.right) || utils.isToStringStatement(node.right)) { + reportError(node); + } else if (node.left.type === 'Identifier' && node.left.name === 'undefined') { + reportError(node); + } else if (node.right.type === 'Identifier' && node.right.name === 'undefined') { + reportError(node); } } } From 69649834a4d7ed909e4be31001ebeec04ecc88e1 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 22:44:14 +0100 Subject: [PATCH 25/35] find directive block with context.getScope (#130) --- rules/no-directive-replace.js | 9 ++------- rules/utils/utils.js | 15 --------------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/rules/no-directive-replace.js b/rules/no-directive-replace.js index 83305668..16e8e5e4 100644 --- a/rules/no-directive-replace.js +++ b/rules/no-directive-replace.js @@ -21,11 +21,6 @@ module.exports = function(context) { return fnExpression.parent.type === 'CallExpression' && utils.isAngularDirectiveDeclaration(fnExpression.parent); } - function inDirectiveBody(node) { - var block = utils.findNodeTypeInParents(node, 'BlockStatement'); - return block && isDirectiveDefinitionFunction(block.parent); - } - var reportedNodesByName = {}; function addPotentialReplaceNode(variableName, node) { @@ -34,7 +29,7 @@ module.exports = function(context) { var report = { name: variableName, node: node, - block: utils.findNodeTypeInParents(node, 'BlockStatement') + block: context.getScope().block.body }; nodeList.push(report); @@ -90,7 +85,7 @@ module.exports = function(context) { } // report directly if object is part of a return statement and inside a directive body - if (objectExpressionParent.type === 'ReturnStatement' && inDirectiveBody(objectExpressionParent)) { + if (objectExpressionParent.type === 'ReturnStatement' && isDirectiveDefinitionFunction(context.getScope().block)) { context.report(node, 'Directive definition property replace is deprecated.'); } } diff --git a/rules/utils/utils.js b/rules/utils/utils.js index 96ea18fd..f5d74deb 100644 --- a/rules/utils/utils.js +++ b/rules/utils/utils.js @@ -50,7 +50,6 @@ module.exports = { isRouteDefinition: isRouteDefinition, isUIRouterStateDefinition: isUIRouterStateDefinition, findIdentiferInScope: findIdentiferInScope, - findNodeTypeInParents: findNodeTypeInParents, getControllerDefinition: getControllerDefinition }; @@ -468,20 +467,6 @@ function findIdentiferInScope(context, identifier) { return identifierNode; } -/** - * Find the closest parent node with the specified nodeType. - * - * @param {Object} node The node to start the search. - * @param {String} nodeType The node type (e.g. 'BlockStatement') - * @returns {Object} The first parent node with the specified nodeType or undefined; - */ -function findNodeTypeInParents(node, nodeType) { - if (!node || node.type === nodeType) { - return node; - } - return findNodeTypeInParents(node.parent, nodeType); -} - /** * Find the function definition of a controller in the current context. * From 752cd89ab31a5060586c517b22c7a399e2abfea4 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sun, 6 Dec 2015 22:57:29 +0100 Subject: [PATCH 26/35] Remove duplicate check from angular-rule This takes up the coverage. --- rules/utils/angular-rule.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/rules/utils/angular-rule.js b/rules/utils/angular-rule.js index 5309e308..6e6ba489 100644 --- a/rules/utils/angular-rule.js +++ b/rules/utils/angular-rule.js @@ -184,9 +184,6 @@ function angularRule(ruleDefinition) { function findFunctionByNode(callExpressionNode, scope) { var node; if (callExpressionNode.callee.type === 'Identifier') { - if (callExpressionNode.callee.name !== 'inject') { - return; - } node = callExpressionNode.arguments[0]; } else if (callExpressionNode.callee.property.name === 'run' || callExpressionNode.callee.property.name === 'config') { node = callExpressionNode.arguments[0]; From 590ed2edef404abaa761bb1cace1e0665fdcfc49 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sun, 6 Dec 2015 23:02:05 +0100 Subject: [PATCH 27/35] Add potential failing crashes These crashes are caught by angular-rule, but this was untested. This took down coverage. --- test/component-limit.js | 10 ++++++++++ test/di-order.js | 10 ++++++++++ test/di-unused.js | 12 +++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/test/component-limit.js b/test/component-limit.js index 92683555..a539b225 100644 --- a/test/component-limit.js +++ b/test/component-limit.js @@ -22,7 +22,17 @@ eslintTester.run('component-limit', rule, { 'angular.module("").factory();', 'angular.module("").filter();', 'angular.module("").provider();', + 'angular.module("").run();', 'angular.module("").service();', + 'angular.module("").animation("", "");', + 'angular.module("").config("");', + 'angular.module("").controller("", "");', + 'angular.module("").directive("", "");', + 'angular.module("").factory("", "");', + 'angular.module("").filter("", "");', + 'angular.module("").provider("", "");', + 'angular.module("").run("");', + 'angular.module("").service("", "");', // Identified potential false positives '$scope.$on("", function() {});$scope.$on("", function() {});', 'app.service("", function(myService) { var data = {}; myService.someMethod("", data); });', diff --git a/test/di-order.js b/test/di-order.js index 3cca5d61..f76b7a4d 100644 --- a/test/di-order.js +++ b/test/di-order.js @@ -28,6 +28,16 @@ eslintTester.run('di-order', rule, { 'inject(function($http, $q) {});', 'it(inject(function($http, $q) {}));', 'it(inject(function(_$http_, _$httpBackend_) {}));', + // Potential crashes + 'angular.module("").animation("", "");', + 'angular.module("").config("");', + 'angular.module("").controller("", "");', + 'angular.module("").directive("", "");', + 'angular.module("").factory("", "");', + 'angular.module("").filter("", "");', + 'angular.module("").provider("", "");', + 'angular.module("").run("");', + 'angular.module("").service("", "");', { code: 'it(inject(function(_$httpBackend_, _$http_) {}));', options: [false] diff --git a/test/di-unused.js b/test/di-unused.js index 4e337ac1..fa662398 100644 --- a/test/di-unused.js +++ b/test/di-unused.js @@ -37,7 +37,17 @@ eslintTester.run('di-unused', rule, { 'angular.module("").run(["$q", function($q) {return $q;}]);', 'inject(function($q) {_$q_ = $q;});', 'angular.module("").provider("", function() {this.$get = function($q) {return $q};});', - 'angular.module("").provider("", function() {this.$get = ["$q", function($q) {return $q}];});' + 'angular.module("").provider("", function() {this.$get = ["$q", function($q) {return $q}];});', + // Potential crashes + 'angular.module("").animation("", "");', + 'angular.module("").config("");', + 'angular.module("").controller("", "");', + 'angular.module("").directive("", "");', + 'angular.module("").factory("", "");', + 'angular.module("").filter("", "");', + 'angular.module("").provider("", "");', + 'angular.module("").run("");', + 'angular.module("").service("", "");' ].concat(commonFalsePositives), invalid: [ // animation From 396267ae880daeb57c6f9386d2f21c151176cfc7 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sun, 6 Dec 2015 23:05:17 +0100 Subject: [PATCH 28/35] Add potential failures for component definitions This takes up the coverage for angular-rule too 100%. --- test/no-run-logic.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/no-run-logic.js b/test/no-run-logic.js index 321a9a98..58dae054 100644 --- a/test/no-run-logic.js +++ b/test/no-run-logic.js @@ -24,7 +24,17 @@ eslintTester.run('no-run-logic', rule, { 'angular.module("").run(function() {foo(null)});', 'angular.module("").run(function() {foo(undefined)});', 'angular.module("").run(function() {foo("bar")});', - 'angular.module("").run(function() {foo(bar)});' + 'angular.module("").run(function() {foo(bar)});', + // don't crash on component definitions + 'angular.module("").animation();', + 'angular.module("").config();', + 'angular.module("").controller();', + 'angular.module("").directive();', + 'angular.module("").factory();', + 'angular.module("").filter();', + 'angular.module("").provider();', + 'angular.module("").run();', + 'angular.module("").service();' ], invalid: [ // Nested function declarations From 8e3640553dd6a20da38a646d65958db765f7aeb3 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Sun, 6 Dec 2015 23:35:03 +0100 Subject: [PATCH 29/35] fixed typeof check and add possible positives (#232) --- rules/definedundefined.js | 4 ++-- test/definedundefined.js | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/rules/definedundefined.js b/rules/definedundefined.js index 4f9265e4..8005cf27 100644 --- a/rules/definedundefined.js +++ b/rules/definedundefined.js @@ -38,9 +38,9 @@ module.exports = function(context) { }, BinaryExpression: function(node) { if (isCompareOperator(node.operator)) { - if (utils.isTypeOfStatement(node.left) || utils.isToStringStatement(node.left)) { + if (utils.isTypeOfStatement(node.left) && node.right.value === 'undefined') { reportError(node); - } else if (utils.isTypeOfStatement(node.right) || utils.isToStringStatement(node.right)) { + } else if (utils.isTypeOfStatement(node.right) && node.left.value === 'undefined') { reportError(node); } else if (node.left.type === 'Identifier' && node.left.name === 'undefined') { reportError(node); diff --git a/test/definedundefined.js b/test/definedundefined.js index 1c58cd65..f3e1cc4a 100644 --- a/test/definedundefined.js +++ b/test/definedundefined.js @@ -16,15 +16,26 @@ var eslintTester = new RuleTester(); eslintTester.run('definedundefined', rule, { valid: [ 'angular.isUndefined(toto)', - 'angular.isDefined(toto)' + 'angular.isDefined(toto)', + // possible false positives + 'variable === otherValue', + 'variable === null', + 'variable > undefined', + 'angular.isString(null)' ].concat(commonFalsePositives), invalid: [ {code: 'variable === undefined', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, {code: 'undefined === variable', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, {code: 'undefined !== variable', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, {code: 'variable !== undefined', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, - {code: 'typeof variable == "undefined"', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, + {code: 'variable == undefined', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, + {code: 'undefined == variable', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, + {code: 'undefined != variable', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, + {code: 'variable != undefined', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, + {code: 'typeof variable === "undefined"', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, {code: 'typeof variable !== "undefined"', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, + {code: '"undefined" == typeof variable', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, + {code: '"undefined" != typeof variable', errors: [{message: 'You should not use directly the "undefined" keyword. Prefer angular.isUndefined or angular.isDefined'}]}, {code: '!angular.isUndefined(variable)', errors: [{message: 'Instead of !angular.isUndefined, you can use the out-of-box angular.isDefined method'}]}, {code: '!angular.isDefined(variable)', errors: [{message: 'Instead of !angular.isDefined, you can use the out-of-box angular.isUndefined method'}]} ] From 7f7cbfc8c8636eb26ac2499ea2a9c00aea58187c Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Mon, 7 Dec 2015 00:22:52 +0100 Subject: [PATCH 30/35] cover directive definitions with named function (#130) --- rules/no-directive-replace.js | 57 +++++++++++++++++------------------ test/no-directive-replace.js | 9 ++++++ 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/rules/no-directive-replace.js b/rules/no-directive-replace.js index 16e8e5e4..83355d08 100644 --- a/rules/no-directive-replace.js +++ b/rules/no-directive-replace.js @@ -11,47 +11,46 @@ */ 'use strict'; -module.exports = function(context) { - var utils = require('./utils/utils'); +var angularRule = require('./utils/angular-rule'); +module.exports = angularRule(function(context) { var options = context.options[0] || {}; var ignoreReplaceFalse = !!options.ignoreReplaceFalse; - function isDirectiveDefinitionFunction(fnExpression) { - return fnExpression.parent.type === 'CallExpression' && utils.isAngularDirectiveDeclaration(fnExpression.parent); - } - - var reportedNodesByName = {}; + var potentialReplaceNodes = {}; function addPotentialReplaceNode(variableName, node) { - var nodeList = reportedNodesByName[variableName] || []; + var nodeList = potentialReplaceNodes[variableName] || []; - var report = { + nodeList.push({ name: variableName, node: node, block: context.getScope().block.body - }; + }); - nodeList.push(report); - - reportedNodesByName[variableName] = nodeList; + potentialReplaceNodes[variableName] = nodeList; } return { - ReturnStatement: function(node) { - // only check identifiers, because expression in return statements are already checked by the Property callback - if (node.argument.type === 'Identifier') { - var reportedNodes = reportedNodesByName[node.argument.name]; - if (!reportedNodes) { - return; - } - reportedNodes.forEach(function(report) { - // only reports nodes that belong to the same expression - if (report.block === node.parent) { - context.report(report.node, 'Directive definition property replace is deprecated.'); - } - }); + 'angular:directive': function(callExpressionNode, fnNode) { + if (!fnNode || !fnNode.body) { + return; } + fnNode.body.body.forEach(function(statement) { + if (statement.type === 'ReturnStatement') { + // get potential replace node by argument name of empty string for object expressions + var potentialNodes = potentialReplaceNodes[statement.argument.name || '']; + if (!potentialNodes) { + return; + } + potentialNodes.forEach(function(report) { + // only reports nodes that belong to the same expression + if (report.block === statement.parent) { + context.report(report.node, 'Directive definition property replace is deprecated.'); + } + }); + } + }); }, AssignmentExpression: function(node) { // Only check for literal member property assignments. @@ -85,12 +84,12 @@ module.exports = function(context) { } // report directly if object is part of a return statement and inside a directive body - if (objectExpressionParent.type === 'ReturnStatement' && isDirectiveDefinitionFunction(context.getScope().block)) { - context.report(node, 'Directive definition property replace is deprecated.'); + if (objectExpressionParent.type === 'ReturnStatement') { + addPotentialReplaceNode('', node); } } }; -}; +}); module.exports.schema = [{ type: 'object', diff --git a/test/no-directive-replace.js b/test/no-directive-replace.js index baf41548..58e35728 100644 --- a/test/no-directive-replace.js +++ b/test/no-directive-replace.js @@ -55,6 +55,15 @@ eslintTester.run('no-directive-replace', rule, { code: 'angular.module("").directive("", function() {return {replace:0}})', options: [{ignoreReplaceFalse: true}], errors: [{message: 'Directive definition property replace is deprecated.'}] + }, + // named functions + { + code: 'angular.module("").directive("", directive); function directive() { return {replace:true} };', + errors: [{message: 'Directive definition property replace is deprecated.'}] + }, + { + code: 'angular.module("").directive("", directive); function directive() { var def = {}; def.replace = true; return def; };', + errors: [{message: 'Directive definition property replace is deprecated.'}] } ] }); From 52f8ab9edaa2f10de77afe50f1a5d0025adda523 Mon Sep 17 00:00:00 2001 From: Tilman Potthof Date: Mon, 7 Dec 2015 01:00:31 +0100 Subject: [PATCH 31/35] improve readme - add headline, description, npm badges and pixel-perfect shield badge svgs --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d069bf75..b4d917fc 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -[![Build Status](https://travis-ci.org/Gillespie59/eslint-plugin-angular.svg?branch=master)](https://travis-ci.org/Gillespie59/eslint-plugin-angular) -[![Npm dependencies](https://david-dm.org/Gillespie59/eslint-plugin-angular.svg)](https://david-dm.org/Gillespie59/eslint-plugin-angular) -[![devDependency Status](https://david-dm.org/Gillespie59/eslint-plugin-angular/dev-status.png)](https://david-dm.org/Gillespie59/eslint-plugin-angular#info=devDependencies) -[![Join the chat at https://gitter.im/Gillespie59/eslint-plugin-angular](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Gillespie59/eslint-plugin-angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Coverage Status](https://coveralls.io/repos/Gillespie59/eslint-plugin-angular/badge.svg?branch=master)](https://coveralls.io/r/Gillespie59/eslint-plugin-angular?branch=master) +# eslint plugin angular [![Npm version](https://img.shields.io/npm/v/eslint-plugin-angular.svg)](https://www.npmjs.com/package/eslint-plugin-angular) [![Npm downloads per month](https://img.shields.io/npm/dm/eslint-plugin-angular.svg)](https://www.npmjs.com/package/eslint-plugin-angular) +> ESLint rules for your angular project with checks for best-practices, conventions or potential errors. +[![Build Status](https://img.shields.io/travis/Gillespie59/eslint-plugin-angular/master.svg)](https://travis-ci.org/Gillespie59/eslint-plugin-angular) +[![Npm dependencies](https://img.shields.io/david/Gillespie59/eslint-plugin-angular.svg)](https://david-dm.org/Gillespie59/eslint-plugin-angular) +[![devDependency Status](https://img.shields.io/david/dev/Gillespie59/eslint-plugin-angular.svg)](https://david-dm.org/Gillespie59/eslint-plugin-angular#info=devDependencies) +[![Join the chat at https://gitter.im/Gillespie59/eslint-plugin-angular](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/Gillespie59/eslint-plugin-angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Coverage Status](https://img.shields.io/coveralls/Gillespie59/eslint-plugin-angular/master.svg)](https://coveralls.io/r/Gillespie59/eslint-plugin-angular?branch=master) ## Summary From 637b8e90b3ab62d75e8e060b8aba3f3a145fe913 Mon Sep 17 00:00:00 2001 From: palortoff Date: Mon, 7 Dec 2015 17:17:22 +0100 Subject: [PATCH 32/35] Fix typos --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0405727a..07d787b4 100644 --- a/README.md +++ b/README.md @@ -286,18 +286,18 @@ Have a look at the existing issues. There may exist similar issues with useful i * `examples/.js` * Add some examples for the documentation * Run the `gulp docs` task to test the examples and update the markdown documentation -* `docs/.js` +* `docs/.md` * Generated by the `gulp docs` task ### Files you have to touch * `index.js` - * Add your rule `rulesConfiguration.addRule('', [0, {someConfig: 'someValue'])` + * Add your rule `rulesConfiguration.addRule('', [0, {someConfig: 'someValue'}])` ### Before you open your PR * Check that the `gulp` task is working -* Commit generated changes in `README.md` and `docs/.js` +* Commit generated changes in `README.md` and `docs/.md` * Open your PR to the `development` branch NOT `master` ### Rules specific for Angular 1 or 2 From 81818371b177d2a14926012a6f11d2970baad777 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Tue, 8 Dec 2015 20:00:55 +0100 Subject: [PATCH 33/35] Add some useful references to the readme --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index a585fd7d..d822e922 100644 --- a/README.md +++ b/README.md @@ -276,6 +276,14 @@ We appreciate contributions and the following notes will help you before you ope Have a look at the existing issues. There may exist similar issues with useful information. +### Read the documentation + +There are some useful references for creating new rules. Specificly useful are: + +* [The Context Object](http://eslint.org/docs/developer-guide/working-with-rules#the-context-object) - This is the most basic understanding needed for adding or modifying a rule. +* [Options Schemas](http://eslint.org/docs/developer-guide/working-with-rules#options-schemas) - This is the preferred way for validating configuration options. +* [Scope](http://estools.github.io/escope/Scope.html) - This is the scope object returned by `context.getScope()`. + ### Files you have to create * `rules/.js` From b1f4d0c59b064e08353e063bfac2427f097029fe Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Tue, 8 Dec 2015 20:06:43 +0100 Subject: [PATCH 34/35] Add contributors to package.json --- package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7d5b7253..0382735d 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,11 @@ "type": "git", "url": "https://github.com/Gillespie59/eslint-plugin-angularjs.git" }, - "author": "Emmanuel DEMEY", + "contributors": [ + "Emmanuel Demey (http://gillespie59.github.io/)", + "Tilman Potthof (https://github.com/tilmanpotthof)", + "Remco Haszing (https://github.com/remcohaszing)" + ], "license": "MIT", "bugs": { "url": "https://github.com/Gillespie59/eslint-plugin-angularjs/issues" From 7acc2e0c849b0ca93215dbb9b8d138d2cc2eb7a3 Mon Sep 17 00:00:00 2001 From: Emmanuel DEMEY Date: Wed, 9 Dec 2015 15:15:06 +0100 Subject: [PATCH 35/35] Issue #319 controller-as-route error on empty state call --- rules/controller-as-route.js | 2 +- test/controller-as-route.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rules/controller-as-route.js b/rules/controller-as-route.js index 1797330d..dfbe2d0c 100644 --- a/rules/controller-as-route.js +++ b/rules/controller-as-route.js @@ -46,7 +46,7 @@ module.exports = function(context) { var isObjectState = node.arguments.length === 1; stateObject = isObjectState ? node.arguments[0] : node.arguments[1]; - if (stateObject.properties) { + if (stateObject && stateObject.properties) { stateObject.properties.forEach(function(prop) { if (prop.key.name === 'controller') { controllerProp = prop; diff --git a/test/controller-as-route.js b/test/controller-as-route.js index 3c21e3c9..c095d736 100644 --- a/test/controller-as-route.js +++ b/test/controller-as-route.js @@ -26,7 +26,8 @@ eslintTester.run('controller-as-route', rule, { 'var state = "mystate2"', 'something[type][changeType][state](test)', 'var when = "mystate2"', - 'something[type][changeType][when](test)' + 'something[type][changeType][when](test)', + '$stateProvider.state();' ].concat(commonFalsePositives), invalid: [ {code: '$routeProvider.when("/myroute", {controller: "MyController"})',