Skip to content

Commit e7b77aa

Browse files
authored
Report $set and $nextTick in computed properties (#1750)
* Report `set`/`nextTick` in `vue/no-side-effects-in-computed-properties` * Don't check `nextTick` in `no-side-effects-in-computed-properties` * Report `nextTick` in `no-async-in-computed-properties`
1 parent 2472ffa commit e7b77aa

4 files changed

+124
-4
lines changed

lib/rules/no-async-in-computed-properties.js

+26
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,24 @@ function isPromise(node) {
5858
return false
5959
}
6060

61+
/**
62+
* @param {CallExpression} node
63+
* @param {RuleContext} context
64+
*/
65+
function isNextTick(node, context) {
66+
const callee = utils.skipChainExpression(node.callee)
67+
if (callee.type === 'MemberExpression') {
68+
const name = utils.getStaticPropertyName(callee)
69+
return (
70+
(utils.isThis(callee.object, context) && name === '$nextTick') ||
71+
(callee.object.type === 'Identifier' &&
72+
callee.object.name === 'Vue' &&
73+
name === 'nextTick')
74+
)
75+
}
76+
return false
77+
}
78+
6179
// ------------------------------------------------------------------------------
6280
// Rule Definition
6381
// ------------------------------------------------------------------------------
@@ -90,6 +108,7 @@ module.exports = {
90108

91109
const expressionTypes = {
92110
promise: 'asynchronous action',
111+
nextTick: 'asynchronous action',
93112
await: 'await operator',
94113
async: 'async function declaration',
95114
new: 'Promise object',
@@ -211,6 +230,13 @@ module.exports = {
211230
'timed',
212231
info ? computedPropertiesMap.get(info.node) : null
213232
)
233+
} else if (isNextTick(node, context)) {
234+
verify(
235+
node,
236+
scopeStack.body,
237+
'nextTick',
238+
info ? computedPropertiesMap.get(info.node) : null
239+
)
214240
}
215241
},
216242

lib/rules/no-side-effects-in-computed-properties.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -83,15 +83,24 @@ module.exports = {
8383
)
8484
})
8585
if (computedProperty) {
86-
if (!utils.isThis(node, context)) {
87-
return
88-
}
8986
const mem = node.parent
9087
if (mem.object !== node) {
9188
return
9289
}
9390

94-
const invalid = utils.findMutating(mem)
91+
const isThis = utils.isThis(node, context)
92+
const isVue = node.type === 'Identifier' && node.name === 'Vue'
93+
94+
const isVueSet =
95+
mem.parent.type === 'CallExpression' &&
96+
mem.property.type === 'Identifier' &&
97+
((isThis && mem.property.name === '$set') ||
98+
(isVue && mem.property.name === 'set'))
99+
100+
const invalid = isVueSet
101+
? { node: mem.property }
102+
: isThis && utils.findMutating(mem)
103+
95104
if (invalid) {
96105
context.report({
97106
node: invalid.node,

tests/lib/rules/no-async-in-computed-properties.js

+63
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,69 @@ ruleTester.run('no-async-in-computed-properties', rule, {
627627
}
628628
]
629629
},
630+
{
631+
filename: 'test.vue',
632+
code: `
633+
new Vue({
634+
computed: {
635+
foo () {
636+
this.$nextTick(() => {})
637+
Vue.nextTick(() => {})
638+
return 'foo'
639+
}
640+
}
641+
})
642+
`,
643+
parserOptions,
644+
errors: [
645+
{
646+
message: 'Unexpected asynchronous action in "foo" computed property.',
647+
line: 5
648+
},
649+
{
650+
message: 'Unexpected asynchronous action in "foo" computed property.',
651+
line: 6
652+
}
653+
]
654+
},
655+
{
656+
filename: 'test.vue',
657+
code: `
658+
new Vue({
659+
computed: {
660+
async foo () {
661+
await this.$nextTick()
662+
await Vue.nextTick()
663+
return 'foo'
664+
}
665+
}
666+
})
667+
`,
668+
parserOptions,
669+
errors: [
670+
{
671+
message:
672+
'Unexpected async function declaration in "foo" computed property.',
673+
line: 4
674+
},
675+
{
676+
message: 'Unexpected await operator in "foo" computed property.',
677+
line: 5
678+
},
679+
{
680+
message: 'Unexpected asynchronous action in "foo" computed property.',
681+
line: 5
682+
},
683+
{
684+
message: 'Unexpected await operator in "foo" computed property.',
685+
line: 6
686+
},
687+
{
688+
message: 'Unexpected asynchronous action in "foo" computed property.',
689+
line: 6
690+
}
691+
]
692+
},
630693
{
631694
filename: 'test.vue',
632695
code: `

tests/lib/rules/no-side-effects-in-computed-properties.js

+22
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,28 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
446446
'Unexpected side effect in "test3" computed property.'
447447
]
448448
},
449+
{
450+
// https://github.com/vuejs/eslint-plugin-vue/issues/1744
451+
code: `app.component('test', {
452+
computed: {
453+
fooBar() {
454+
this.$set(this, 'foo', 'lorem');
455+
Vue.set(this, 'bar', 'ipsum');
456+
return this.foo + ' ' + this.bar
457+
},
458+
}
459+
})`,
460+
errors: [
461+
{
462+
line: 4,
463+
message: 'Unexpected side effect in "fooBar" computed property.'
464+
},
465+
{
466+
line: 5,
467+
message: 'Unexpected side effect in "fooBar" computed property.'
468+
}
469+
]
470+
},
449471
{
450472
filename: 'test.vue',
451473
code: `

0 commit comments

Comments
 (0)