Skip to content

Commit

Permalink
Merge pull request #397 from jrjohnson/344-computed-style
Browse files Browse the repository at this point in the history
Add rule to enforce computed property style
  • Loading branch information
Turbo87 committed May 8, 2019
2 parents 3e47908 + 1a77d94 commit cce30db
Show file tree
Hide file tree
Showing 4 changed files with 438 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ The `--fix` option on the command line automatically fixes problems reported by
| | Rule ID | Description |
|:---|:--------|:------------|
| :white_check_mark: | [avoid-leaking-state-in-ember-objects](./docs/rules/avoid-leaking-state-in-ember-objects.md) | Avoids state leakage |
| | [computed-property-getters](./docs/rules/computed-property-getters.md) | Enforce the consistent use of getters in computed properties |


### Testing
Expand Down
102 changes: 102 additions & 0 deletions docs/rules/computed-property-getters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Rule name: `computed-property-getters`

## Enforce the consistent use of getters in computed properties

Computed properties may be created with or without a `get` method. This rule ensures that the choice
is consistent.

## Options

This rule takes a single string option

String option:

* `"always-with-setter"` (default) getters are *required* when computed property has a setter
* `"always"` getters are *required* in computed properties
* `"never"` getters are *never allowed* in computed properties


### always-with-setter

```javascript
/// BAD
Ember.Object.extend({
fullName: computed('firstName', 'lastName', {
get() {
//...
}
})
});

// GOOD
Ember.Object.extend({
fullName: computed('firstName', 'lastName', function() {
//...
})
});

// GOOD
Ember.Object.extend({
fullName: computed('firstName', 'lastName', {
set() {
//...
},
get() {
//...
}
})
});
```


### always

```javascript
/// GOOD
Ember.Object.extend({
fullName: computed('firstName', 'lastName', {
get() {
//...
}
})
});

// BAD
Ember.Object.extend({
fullName: computed('firstName', 'lastName', function() {
//...
})
});
```

### never

```javascript
/// GOOD
Ember.Object.extend({
fullName: computed('firstName', 'lastName', function() {
//...
})
});

// BAD
Ember.Object.extend({
fullName: computed('firstName', 'lastName', {
get() {
//...
}
})
});

// BAD
Ember.Object.extend({
fullName: computed('firstName', 'lastName', {
get() {
//...
},
set() {
//...
}
})
});
```
99 changes: 99 additions & 0 deletions lib/rules/computed-property-getters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
'use strict';

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

//------------------------------------------------------------------------------
// General rule - Prevent using a getter inside computed properties.
//------------------------------------------------------------------------------

module.exports = {
meta: {
docs: {
description: 'Warns about getters in computed properties',
category: 'Possible Errors',
recommended: false,
url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/computed-property-getters.md'
},
fixable: null, // or "code" or "whitespace"
schema: [
{
enum: ['always-with-setter', 'always', 'never']
},
],
},

create(context) {
const requireGetters = context.options[0] || 'always-with-setter';
const message = 'Do not use a getter inside computed properties.';

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

const requireGetterOnlyWithASetterInComputedProperty = function (node) {
const objectExpressions = node.arguments.filter(arg => utils.isObjectExpression(arg));
if (
objectExpressions.length
) {
const { properties } = objectExpressions[0];
const getters = properties.filter(prop => prop.key && prop.key.name && prop.key.name === 'get');
const setters = properties.filter(prop => prop.key && prop.key.name && prop.key.name === 'set');
if (
(setters.length > 0 && getters.length === 0) ||
(getters.length > 0 && setters.length === 0)
) {
report(node);
}
}
};

const preventGetterInComputedProperty = function (node) {
const objectExpressions = node.arguments.filter(arg => utils.isObjectExpression(arg));
if (
objectExpressions.length
) {
const { properties } = objectExpressions[0];
const getters = properties.filter(prop => prop.key && prop.key.name && prop.key.name === 'get');
const setters = properties.filter(prop => prop.key && prop.key.name && prop.key.name === 'set');
if (getters.length > 0 || setters.length > 0) {
report(node);
}
}
};

const requireGetterInComputedProperty = function (node) {
const objectExpressions = node.arguments.filter(arg => utils.isObjectExpression(arg));
if (
objectExpressions.length
) {
const { properties } = objectExpressions[0];
const getters = properties.filter(prop => prop.key && prop.key.name && prop.key.name === 'get');
if (getters.length === 0) {
report(node);
}
} else {
report(node);
}
};

return {
CallExpression(node) {
if (
ember.isComputedProp(node) &&
node.arguments.length
) {
if (requireGetters === 'always-with-setter') {
requireGetterOnlyWithASetterInComputedProperty(node);
}
if (requireGetters === 'always') {
requireGetterInComputedProperty(node);
}
if (requireGetters === 'never') {
preventGetterInComputedProperty(node);
}
}
}
};
}
};

0 comments on commit cce30db

Please sign in to comment.