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 Jul 9, 2018
1 parent cac2ceb commit a68c854
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 0 deletions.
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);
}
})
});
```
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: false,
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((prop) => {
if (utils.isFunctionExpression(prop.value) && !utils.findNodes(prop.value.body.body, 'ReturnStatement').length) {
report(prop.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 a68c854

Please sign in to comment.