Skip to content

Commit

Permalink
Adding return-from-computed recommended rule. Fixes ember-cli#247.
Browse files Browse the repository at this point in the history
  • Loading branch information
Garrett Murphey committed Jun 11, 2018
1 parent 04c7696 commit d29eb8b
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ The `--fix` option on the command line automatically fixes problems reported by
| :white_check_mark: | [no-capital-letters-in-routes](./docs/rules/no-capital-letters-in-routes.md) | Raise an error when there is a route with uppercased letters in router.js |
| :white_check_mark: | [no-duplicate-dependent-keys](./docs/rules/no-duplicate-dependent-keys.md) | Disallow repeating dependent keys |
| :white_check_mark: | [no-side-effects](./docs/rules/no-side-effects.md) | Warns about unexpected side effects in computed properties |
| :white_check_mark: | [return-from-computed](./docs/rules/require-super-in-init.md) | Enforces return statements in computed properties |
| :white_check_mark: | [require-super-in-init](./docs/rules/require-super-in-init.md) | Enforces super calls in init hooks |
| :white_check_mark: | [routes-segments-snake-case](./docs/rules/routes-segments-snake-case.md) | Enforces usage of snake_cased dynamic segments in routes |

Expand Down
44 changes: 44 additions & 0 deletions docs/rules/return-from-computed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## Always return a value from a computed property function

### Rule name: `return-from-computed`

When using computed properties always return a value.

```javascript
import Ember from 'ember';

const {
Component,
computed
} = Ember;

export default Component.extend({
firstName: null,
lastName: null,

// GOOD
fullName: computed('firstName', 'lastName', {
get(key) {
return `${this.get('firstName')} ${this.get('lastName')}`;
},
set(key, value) {
let [firstName, lastName] = value.split(/\s+/);
this.set('firstName', firstName);
this.set('lastName', lastName);
return value;
}
}),

// BAD
fullName: computed('firstName', 'lastName', {
get(key) {
return `${this.get('firstName')} ${this.get('lastName')}`;
},
set(key, value) {
let [firstName, lastName] = value.split(/\s+/);
this.set('firstName', firstName);
this.set('lastName', lastName);
}
})
});
```
3 changes: 2 additions & 1 deletion lib/recommended-rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ module.exports = {
"ember/order-in-controllers": "off",
"ember/order-in-models": "off",
"ember/order-in-routes": "off",
"ember/return-from-computed": "error",
"ember/require-super-in-init": "error",
"ember/routes-segments-snake-case": "error",
"ember/use-brace-expansion": "error",
"ember/use-ember-get-and-set": "off"
}
}
53 changes: 53 additions & 0 deletions lib/rules/return-from-computed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use strict';

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

//------------------------------------------------------------------------------
// General rule - Don't introduce side-effects in computed properties
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: 'Warns about missing return statements in computed properties',
category: 'Possible Errors',
recommended: true,
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/no-side-effects.md'
},
fixable: null, // or "code" or "whitespace"
},

create(context) {
const message = 'Return a value from computed property';

const report = function (node) {
context.report(node, message);
};

return {
'CallExpression[callee.name="computed"]': function (node) {
const objExps = utils.findNodes(node.arguments, 'ObjectExpression');
const fnExps = utils.findNodes(node.arguments, 'FunctionExpression');

if (objExps.length) {
const computedObj = objExps[0];
const props = computedObj.properties;

props.forEach(({ value }) => {
if (utils.isFunctionExpression(value) && !utils.findNodes(value.body.body, 'ReturnStatement').length) {
report(value);
}
});
}

if (fnExps.length) {
const computedFn = fnExps[0];

if (!utils.findNodes(computedFn.body.body, 'ReturnStatement').length) {
report(computedFn);
}
}
}
};
}
};
59 changes: 59 additions & 0 deletions tests/lib/rules/return-from-computed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const rule = require('../../../lib/rules/return-from-computed');
const RuleTester = require('eslint').RuleTester;

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

const eslintTester = new RuleTester();
eslintTester.run('return-from-computed', rule, {
valid: [
{
code: 'let foo = computed("test.length", { get() { return ""; }, set() { return ""; }})',
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
},
{
code: 'let foo = computed("test", function() { return ""; })',
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
}
],
invalid: [
{
code: 'let foo = computed("test", function() { })',
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
errors: [{
message: 'Return a value from computed property'
}]
},
{
code: 'let foo = computed("test", function() { if (true) { return ""; } })',
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
errors: [{
message: 'Return a value from computed property'
}]
},
{
code: 'let foo = computed("test", { get() {}, set() {} })',
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
errors: [
{
message: 'Return a value from computed property'
},
{
message: 'Return a value from computed property'
}
]
},
{
code: 'let foo = computed("test", function() { })',
parserOptions: { ecmaVersion: 6, sourceType: 'module' },
errors: [{
message: 'Return a value from computed property'
}]
}
],
});

0 comments on commit d29eb8b

Please sign in to comment.