Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW RULE] Adding no-async-actions rule [DO NOT MERGE] #424

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -103,6 +103,7 @@ The `--fix` option on the command line automatically fixes problems reported by
| :wrench: | [no-unnecessary-service-injection-argument](./docs/rules/no-unnecessary-service-injection-argument.md) | Disallow unnecessary argument when injecting service |
| | [route-path-style](./docs/rules/route-path-style.md) | Enforces usage of kebab-case (instead of snake_case or camelCase) in route paths |
| :wrench: | [use-ember-get-and-set](./docs/rules/use-ember-get-and-set.md) | Enforces usage of Ember.get and Ember.set |
| :white_check_mark: | [no-async-actions](./docs/rules/no-async-actions.md) | Prevents usage of async actions |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember to run yarn update and add the rule to index.js too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done



### Possible Errors
Expand Down
50 changes: 50 additions & 0 deletions docs/rules/no-async-actions.md
@@ -0,0 +1,50 @@
# Do not use async actions
## Rule `no-async-actions`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead, could we throw a lint error only after an await AND access of this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead, could we throw a lint error only after an await AND access of this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example?: #1421



Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit unclear on why we should not use async actions. Can you explain that more and include in the doc here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me too not very clear about the requirement, i am exploring , will add more info once ready

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add at least a brief explanation of the reasoning in this doc.


Examples of **incorrect** code for this rule:
```js
actions: {
async handleClick() {
// ...
}
}
```

```js
actions: {
handleClick() {
return something.then(() => { /* ... */ });
}
}
```

```js
@action
async handleClick() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why forbid this? This is totally valid

// ...
}
```

```js
@action
handleClick() {
return something.then(() => { /* ... */ });
}
```


Examples of **correct** code for this rule:
```js
actions: {
handleClick() {
return nothingOrSomethingThatIsNotAPromise;
}
}
```


## Further Reading

See http://ember-concurrency.com/docs/tutorial (scroll down to Version 4)
61 changes: 61 additions & 0 deletions lib/rules/no-async-actions.js
@@ -0,0 +1,61 @@
'use strict';

const utils = require('../utils/utils');


//------------------------------------------------------------------------------
// General rule - Don't use async actions
//------------------------------------------------------------------------------


const message = 'Do not use async actions';

module.exports = {
meta: {
docs: {
description: 'Disallow usage of async actions in components',
category: 'Possible Errors',
recommended: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rules should never be added as recommended since that would be a breaking change. We can consider making it recommended in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the recommended flag

url: 'http://ember-concurrency.com/docs/tutorial'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rebase on the latest master to pickup the prettier changes.

},
fixable: null,
},

create(context) {
return {
Property(node) {
if (node.key.name === 'actions') {
const props = node.value.properties;

props.forEach((p) => {
const body = p.value.body.body;
if (p.value.async) {
context.report({
node: p,
message,
});
} else if (body.length === 1 && utils.isReturnStatement(body[0])) {
const retSt = body[0];
if (retSt.argument.type === 'CallExpression' &&
retSt.argument.callee.property.name === 'then') {
context.report({
node: retSt,
message,
});
}
}
});
} else if (node.decorators) {
if (node.decorators.find(d => d.expression.name === 'action')) {
if (node.value.async) {
context.report({
node,
message: 'Do not use async actions'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use the message variable defined at the top of the file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

});
}
}
}
}
};
}
};
96 changes: 96 additions & 0 deletions tests/lib/rules/no-async-actions.js
@@ -0,0 +1,96 @@
'use strict';

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const rule = require('../../../lib/rules/no-async-actions.js');
const RuleTester = require('eslint').RuleTester;


//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

const message = 'Do not use async actions';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

message should be imported from the rule file. See the no-test-and-then rule for an example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
}
});

ruleTester.run('no-async-actions', rule, {
valid: [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a valid example to ensure this rule does not trigger on actions in an irrelevant object/class. It should only trigger on the actions key inside an Ember controller/component/route.

Copy link
Member

@bmish bmish Jun 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ still waiting on this rule to be limited to actions inside components only.

{
code: `
Component.extend({
actions: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unreadable without proper indentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indented propertly

handleClick() {
// ...
}
}
});`,
}

],

invalid: [
{
code: `Component.extend({
actions: {
async handleClick() {
// ...
}
}
});`,
output: null,
errors: [{
message,
}]
},
{
code: `Component.extend({
actions: {
handleClick() {
return something.then(() => {
let hello = "world";
});
}
}
});`,
output: null,
errors: [{
message,
}]
},
{
code: `Component.extend({
@action
async handleClick() {
// ...
}
});`,
output: null,
errors: [{
message,
}]
},
{
code: `Component.extend({
@action
handleClick() {
return something.then(() => {
let hello = "world";
});
}
});`,
output: null,
errors: [{
message,
}]
},

]
});