Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
60 changes: 60 additions & 0 deletions docs/no-run-logic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!-- WARNING: Generated documentation. Edit docs and examples in the rule and examples file ('rules/no-run-logic.js', 'examples/no-run-logic.js'). -->

# 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)
23 changes: 23 additions & 0 deletions examples/no-run-logic.js
Original file line number Diff line number Diff line change
@@ -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);
});
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
58 changes: 58 additions & 0 deletions rules/no-run-logic.js
Original file line number Diff line number Diff line change
@@ -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'
}
}
}];
119 changes: 119 additions & 0 deletions test/no-run-logic.js
Original file line number Diff line number Diff line change
@@ -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'}]
}
]
});