Skip to content

Commit dc48d1c

Browse files
authored
Add vue/no-computed-properties-in-data rule (#1653)
1 parent a70175e commit dc48d1c

File tree

6 files changed

+286
-1
lines changed

6 files changed

+286
-1
lines changed

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ For example:
300300
| [vue/next-tick-style](./next-tick-style.md) | enforce Promise or callback style in `nextTick` | :wrench: |
301301
| [vue/no-bare-strings-in-template](./no-bare-strings-in-template.md) | disallow the use of bare strings in `<template>` | |
302302
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
303+
| [vue/no-computed-properties-in-data](./no-computed-properties-in-data.md) | disallow accessing computed properties in `data`. | |
303304
| [vue/no-deprecated-v-is](./no-deprecated-v-is.md) | disallow deprecated `v-is` directive (in Vue.js 3.1.0+) | :wrench: |
304305
| [vue/no-duplicate-attr-inheritance](./no-duplicate-attr-inheritance.md) | enforce `inheritAttrs` to be set to `false` when using `v-bind="$attrs"` | |
305306
| [vue/no-empty-component-block](./no-empty-component-block.md) | disallow the `<template>` `<script>` `<style>` block to be empty | |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-computed-properties-in-data
5+
description: disallow accessing computed properties in `data`.
6+
---
7+
# vue/no-computed-properties-in-data
8+
9+
> disallow accessing computed properties in `data`.
10+
11+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12+
13+
## :book: Rule Details
14+
15+
This rule disallow accessing computed properties in `data()`.
16+
The computed property cannot be accessed in `data()` because is before initialization.
17+
18+
<eslint-code-block :rules="{'vue/no-computed-properties-in-data': ['error']}">
19+
20+
```vue
21+
<script>
22+
export default {
23+
data() {
24+
return {
25+
/* ✗ BAD */
26+
bar: this.foo
27+
}
28+
},
29+
computed: {
30+
foo () {}
31+
}
32+
}
33+
</script>
34+
```
35+
36+
</eslint-code-block>
37+
38+
## :wrench: Options
39+
40+
Nothing.
41+
42+
## :mag: Implementation
43+
44+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-computed-properties-in-data.js)
45+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-computed-properties-in-data.js)

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ module.exports = {
5656
'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'),
5757
'no-bare-strings-in-template': require('./rules/no-bare-strings-in-template'),
5858
'no-boolean-default': require('./rules/no-boolean-default'),
59+
'no-computed-properties-in-data': require('./rules/no-computed-properties-in-data'),
5960
'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),
6061
'no-constant-condition': require('./rules/no-constant-condition'),
6162
'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'),
+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
13+
/**
14+
* @typedef {import('../utils').VueObjectData} VueObjectData
15+
*/
16+
17+
// ------------------------------------------------------------------------------
18+
// Rule Definition
19+
// ------------------------------------------------------------------------------
20+
21+
module.exports = {
22+
meta: {
23+
type: 'problem',
24+
docs: {
25+
description: 'disallow accessing computed properties in `data`.',
26+
categories: undefined,
27+
url: 'https://eslint.vuejs.org/rules/no-computed-properties-in-data.html'
28+
},
29+
fixable: null,
30+
schema: [],
31+
messages: {
32+
cannotBeUsed:
33+
'The computed property cannot be used in `data()` because it is before initialization.'
34+
}
35+
},
36+
/** @param {RuleContext} context */
37+
create(context) {
38+
/** @type {Map<ObjectExpression, {data: FunctionExpression | ArrowFunctionExpression, computedNames:Set<string>}>} */
39+
const contextMap = new Map()
40+
41+
/**
42+
* @typedef {object} ScopeStack
43+
* @property {ScopeStack | null} upper
44+
* @property {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
45+
*/
46+
/** @type {ScopeStack | null} */
47+
let scopeStack = null
48+
49+
return utils.compositingVisitors(
50+
{
51+
/**
52+
* @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
53+
*/
54+
':function'(node) {
55+
scopeStack = {
56+
upper: scopeStack,
57+
node
58+
}
59+
},
60+
':function:exit'() {
61+
scopeStack = scopeStack && scopeStack.upper
62+
}
63+
},
64+
utils.defineVueVisitor(context, {
65+
onVueObjectEnter(node) {
66+
const dataProperty = utils.findProperty(node, 'data')
67+
if (
68+
!dataProperty ||
69+
(dataProperty.value.type !== 'FunctionExpression' &&
70+
dataProperty.value.type !== 'ArrowFunctionExpression')
71+
) {
72+
return
73+
}
74+
const computedNames = new Set()
75+
for (const computed of utils.iterateProperties(
76+
node,
77+
new Set(['computed'])
78+
)) {
79+
computedNames.add(computed.name)
80+
}
81+
82+
contextMap.set(node, { data: dataProperty.value, computedNames })
83+
},
84+
/**
85+
* @param {MemberExpression} node
86+
* @param {VueObjectData} vueData
87+
*/
88+
MemberExpression(node, vueData) {
89+
if (!scopeStack || !utils.isThis(node.object, context)) {
90+
return
91+
}
92+
const ctx = contextMap.get(vueData.node)
93+
if (!ctx || ctx.data !== scopeStack.node) {
94+
return
95+
}
96+
const name = utils.getStaticPropertyName(node)
97+
if (!name || !ctx.computedNames.has(name)) {
98+
return
99+
}
100+
context.report({
101+
node,
102+
messageId: 'cannotBeUsed'
103+
})
104+
}
105+
})
106+
)
107+
}
108+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const RuleTester = require('eslint').RuleTester
8+
const rule = require('../../../lib/rules/no-computed-properties-in-data')
9+
10+
const tester = new RuleTester({
11+
parser: require.resolve('vue-eslint-parser'),
12+
parserOptions: {
13+
ecmaVersion: 2020,
14+
sourceType: 'module'
15+
}
16+
})
17+
18+
tester.run('no-computed-properties-in-data', rule, {
19+
valid: [
20+
{
21+
filename: 'test.vue',
22+
code: `
23+
<script>
24+
export default {
25+
data() {
26+
const foo = this.foo
27+
return {}
28+
}
29+
}
30+
</script>
31+
`
32+
},
33+
{
34+
filename: 'test.vue',
35+
code: `
36+
<script>
37+
export default {
38+
data() {
39+
const foo = this.foo()
40+
return {}
41+
},
42+
methods: {
43+
foo() {}
44+
}
45+
}
46+
</script>
47+
`
48+
},
49+
{
50+
filename: 'test.vue',
51+
code: `
52+
<script>
53+
export default {
54+
props: ['foo'],
55+
data() {
56+
const foo = this.foo
57+
return {}
58+
},
59+
}
60+
</script>
61+
`
62+
},
63+
{
64+
filename: 'test.vue',
65+
code: `
66+
<script>
67+
export default {
68+
data: {
69+
foo: this.foo
70+
},
71+
computed: {
72+
foo () {}
73+
}
74+
}
75+
</script>
76+
`
77+
}
78+
],
79+
invalid: [
80+
{
81+
filename: 'test.vue',
82+
code: `
83+
<script>
84+
export default {
85+
data() {
86+
const foo = this.foo
87+
return {}
88+
},
89+
computed: {
90+
foo () {}
91+
}
92+
}
93+
</script>
94+
`,
95+
errors: [
96+
{
97+
message:
98+
'The computed property cannot be used in `data()` because it is before initialization.',
99+
line: 5,
100+
column: 23
101+
}
102+
]
103+
},
104+
{
105+
filename: 'test.vue',
106+
code: `
107+
<script>
108+
export default {
109+
data() {
110+
const vm = this
111+
const foo = vm.foo
112+
return {}
113+
},
114+
computed: {
115+
foo () {}
116+
}
117+
}
118+
</script>
119+
`,
120+
errors: [
121+
{
122+
message:
123+
'The computed property cannot be used in `data()` because it is before initialization.',
124+
line: 6,
125+
column: 23
126+
}
127+
]
128+
}
129+
]
130+
})

tools/new-rule.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const logger = console
3232
// Requirements
3333
// ------------------------------------------------------------------------------
3434
35-
// ...
35+
const utils = require('../utils')
3636
3737
// ------------------------------------------------------------------------------
3838
// Helpers

0 commit comments

Comments
 (0)