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 Add vue/no-computed-in-data Rule #1075

Closed
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| [vue/max-attributes-per-line](./max-attributes-per-line.md) | enforce the maximum number of attributes per line | :wrench: |
| [vue/multiline-html-element-content-newline](./multiline-html-element-content-newline.md) | require a line break before and after the contents of a multiline element | :wrench: |
| [vue/mustache-interpolation-spacing](./mustache-interpolation-spacing.md) | enforce unified spacing in mustache interpolations | :wrench: |
| [vue/no-computed-in-data](./no-computed-in-data.md) | require computed properties are not used in the data() | |
| [vue/no-multi-spaces](./no-multi-spaces.md) | disallow multiple spaces | :wrench: |
| [vue/no-spaces-around-equal-signs-in-attribute](./no-spaces-around-equal-signs-in-attribute.md) | disallow spaces around equal signs in attribute | :wrench: |
| [vue/no-template-shadow](./no-template-shadow.md) | disallow variable declarations from shadowing variables declared in the outer scope | |
Expand Down
81 changes: 81 additions & 0 deletions docs/rules/no-computed-in-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-computed-in-data
description: require computed properties are not used in the data()
---
# vue/no-computed-in-data
> require computed properties are not used in the data()

- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.

> require computed properties are not used in the data()
IWANABETHATGUY marked this conversation as resolved.
Show resolved Hide resolved

- :gear: This rule is included in `"plugin:vue/strongly-recommended"` and `"plugin:vue/recommended"`.

Please describe the origin of the rule here.

## :book: Rule Details

This rule report computed properties are used in data property

Examples of **incorrect** code for this rule:
IWANABETHATGUY marked this conversation as resolved.
Show resolved Hide resolved

<eslint-code-block :rules="{'vue/no-computed-in-data': ['error']}">

```vue
<script>
export default {
data() {
return {
value: 'hello ' + this.world,
a: this.world,
b: this.world,
};
},
computed: {
world() {
return 'world';
},
},
};
</script>
```

</eslint-code-block>

Examples of **correct** code for this rule:

<eslint-code-block :rules="{'vue/no-computed-in-data': ['error']}">

```vue
<script>
export default {
data() {
return {
value: 'hello ' + 'world',
};
},
computed: {
world() {
return 'world';
},
},
};
</script>
```

</eslint-code-block>

## :wrench: Options

nothing

## Further Reading

[Computed Properties](https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-computed-in-data.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-computed-in-data.js)
1 change: 1 addition & 0 deletions lib/configs/strongly-recommended.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module.exports = {
'vue/max-attributes-per-line': 'warn',
'vue/multiline-html-element-content-newline': 'warn',
'vue/mustache-interpolation-spacing': 'warn',
'vue/no-computed-in-data': 'warn',
'vue/no-multi-spaces': 'warn',
'vue/no-spaces-around-equal-signs-in-attribute': 'warn',
'vue/no-template-shadow': 'warn',
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ module.exports = {
'name-property-casing': require('./rules/name-property-casing'),
'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'),
'no-boolean-default': require('./rules/no-boolean-default'),
'no-computed-in-data': require('./rules/no-computed-in-data'),
'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),
'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'),
'no-deprecated-filter': require('./rules/no-deprecated-filter'),
Expand Down
81 changes: 81 additions & 0 deletions lib/rules/no-computed-in-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @fileoverview Ensure computed properties are not used in the data()
* @author IWANABETHATGUY
*/
'use strict'

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
const utils = require('../utils')
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'require computed properties are not used in the data()',
categories: ['strongly-recommended'],
IWANABETHATGUY marked this conversation as resolved.
Show resolved Hide resolved
recommended: false,
url: 'https://eslint.vuejs.org/rules/no-computed-in-data.html'
},
fixable: null, // or "code" or "whitespace"
schema: [
// fill in your schema
]
},

create: function (context) {
let dataAstNode
const memberExpressionMap = Object.create(null)
return {
ObjectExpression (node) {
if (!dataAstNode) {
dataAstNode = node.properties.find(
p =>
p.type === 'Property' &&
p.key.type === 'Identifier' &&
p.key.name === 'data'
)
}
},
'Property:exit' (node) {
if (node === dataAstNode) {
dataAstNode = undefined
}
},
MemberExpression (node) {
if (dataAstNode) {
// a memberExpression `like this.a.c.d` -> when traverse to c.d we can got the the full name -> this.a.c.d
const fullName = utils.parseMemberExpression(node).join('.')
IWANABETHATGUY marked this conversation as resolved.
Show resolved Hide resolved
if (memberExpressionMap[fullName]) {
memberExpressionMap[fullName] = [...memberExpressionMap[fullName], node]
IWANABETHATGUY marked this conversation as resolved.
Show resolved Hide resolved
} else {
memberExpressionMap[fullName] = [node]
}
}
},
...utils.executeOnVue(context, obj => {
const computedPropertyNameList = utils.getComputedProperties(obj).map(item => `this.${item.key}`)
Object.keys(memberExpressionMap).forEach(fullName => {
IWANABETHATGUY marked this conversation as resolved.
Show resolved Hide resolved
let index = -1
for (let i = 0, len = computedPropertyNameList.length; i < len; i++) {
IWANABETHATGUY marked this conversation as resolved.
Show resolved Hide resolved
if (fullName.startsWith(computedPropertyNameList[i])) {
index = i
break
}
}
if (index !== -1) {
memberExpressionMap[fullName].forEach(memberExpressionNode => {
context.report({
node: memberExpressionNode,
message: `Computed property '{{name}}' can't use in data property.`,
data: {
name: computedPropertyNameList[index]
}
})
})
}
})
})
}
}
}
2 changes: 1 addition & 1 deletion lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ module.exports = {
return computedPropertiesNode.value.properties
.filter(cp => cp.type === 'Property')
.map(cp => {
const key = cp.key.name
const key = cp.key.type === 'Identifier' ? cp.key.name : cp.key.type === 'Literal' ? cp.key.value : undefined
let value
IWANABETHATGUY marked this conversation as resolved.
Show resolved Hide resolved

if (cp.value.type === 'FunctionExpression') {
Expand Down
131 changes: 131 additions & 0 deletions tests/lib/rules/no-computed-in-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/**
* @fileoverview Ensure computed properties are not used in the data()
* @author IWANABETHATGUY
*/
'use strict'

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

var rule = require('../../../lib/rules/no-computed-in-data')

var RuleTester = require('eslint').RuleTester

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

var ruleTester = new RuleTester({
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
ecmaVersion: 2019,
sourceType: 'module'
}
})
ruleTester.run('no-computed-in-data', rule, {

valid: [
{
filename: 'test.vue',
code: `
<script>
export default {
data() {
return {
a: this.b
}
},
computed: {
test() {},
'foo': {

}
}
}
</script>
`
},
// should not warn when use computed in methods
{
filename: 'test.vue',
code: `
<script>
export default {
data() {
return {
a: this.b
}
},
computed: {
test() {},
},
methods: {
foo() {
this.test
}
}
}
</script>
`
}
],
invalid: [
// should warn when prop key is an String literal
{
filename: 'test.vue',
code: `
<script>
export default {
data() {
return {
a: this.test + this.foo
}
},
computed: {
test() {},
'foo'() {

}
}
}
</script>
`,
errors: [
`Computed property 'this.test' can't use in data property.`,
`Computed property 'this.foo' can't use in data property.`
]
},
// same computed data referenced by multi data property
{
filename: 'test.vue',
code: `
<script>
export default {
data() {
return {
a: this.test,
b: this.test
}
},
computed: {
test() {},
}
}
</script>
`,
errors: [
{
message: `Computed property 'this.test' can't use in data property.`,
line: 6,
column: 16
},
{
message: `Computed property 'this.test' can't use in data property.`,
line: 7,
column: 16
}
]
}
]
})