From f60439134fef93d62ade264bb2735cbe2f46bd66 Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Wed, 29 Dec 2021 23:56:38 +0100 Subject: [PATCH 1/3] Report `set`/`nextTick` in `vue/no-side-effects-in-computed-properties` --- .../no-side-effects-in-computed-properties.js | 17 +++++++--- .../no-side-effects-in-computed-properties.js | 34 +++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/lib/rules/no-side-effects-in-computed-properties.js b/lib/rules/no-side-effects-in-computed-properties.js index a0d508014..d7a979f08 100644 --- a/lib/rules/no-side-effects-in-computed-properties.js +++ b/lib/rules/no-side-effects-in-computed-properties.js @@ -83,15 +83,24 @@ module.exports = { ) }) if (computedProperty) { - if (!utils.isThis(node, context)) { - return - } const mem = node.parent if (mem.object !== node) { return } - const invalid = utils.findMutating(mem) + const isThis = utils.isThis(node, context) + const isVue = node.type === 'Identifier' && node.name === 'Vue' + + const isSetOrNextTick = + mem.parent.type === 'CallExpression' && + mem.property.type === 'Identifier' && + ((isThis && ['$set', '$nextTick'].includes(mem.property.name)) || + (isVue && ['set', 'nextTick'].includes(mem.property.name))) + + const invalid = isSetOrNextTick + ? { node: mem.property } + : isThis && utils.findMutating(mem) + if (invalid) { context.report({ node: invalid.node, diff --git a/tests/lib/rules/no-side-effects-in-computed-properties.js b/tests/lib/rules/no-side-effects-in-computed-properties.js index 7c4aabe95..36889ae86 100644 --- a/tests/lib/rules/no-side-effects-in-computed-properties.js +++ b/tests/lib/rules/no-side-effects-in-computed-properties.js @@ -446,6 +446,40 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, { 'Unexpected side effect in "test3" computed property.' ] }, + { + // https://github.com/vuejs/eslint-plugin-vue/issues/1744 + code: `app.component('test', { + computed: { + fooBar() { + this.$set(this, 'foo', 'lorem'); + Vue.set(this, 'bar', 'ipsum'); + + this.$nextTick(() => {}); + Vue.nextTick(() => {}); + + return this.foo + ' ' + this.bar + }, + } + })`, + errors: [ + { + line: 4, + message: 'Unexpected side effect in "fooBar" computed property.' + }, + { + line: 5, + message: 'Unexpected side effect in "fooBar" computed property.' + }, + { + line: 7, + message: 'Unexpected side effect in "fooBar" computed property.' + }, + { + line: 8, + message: 'Unexpected side effect in "fooBar" computed property.' + } + ] + }, { filename: 'test.vue', code: ` From 74b5af9ac14f6c77ab6be53cd5e02a584bf8ecd7 Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Wed, 5 Jan 2022 14:51:46 +0100 Subject: [PATCH 2/3] Don't check `nextTick` in `no-side-effects-in-computed-properties` --- lib/rules/no-side-effects-in-computed-properties.js | 8 ++++---- .../rules/no-side-effects-in-computed-properties.js | 12 ------------ 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/lib/rules/no-side-effects-in-computed-properties.js b/lib/rules/no-side-effects-in-computed-properties.js index d7a979f08..6e43266fb 100644 --- a/lib/rules/no-side-effects-in-computed-properties.js +++ b/lib/rules/no-side-effects-in-computed-properties.js @@ -91,13 +91,13 @@ module.exports = { const isThis = utils.isThis(node, context) const isVue = node.type === 'Identifier' && node.name === 'Vue' - const isSetOrNextTick = + const isVueSet = mem.parent.type === 'CallExpression' && mem.property.type === 'Identifier' && - ((isThis && ['$set', '$nextTick'].includes(mem.property.name)) || - (isVue && ['set', 'nextTick'].includes(mem.property.name))) + ((isThis && mem.property.name === '$set') || + (isVue && mem.property.name === 'set')) - const invalid = isSetOrNextTick + const invalid = isVueSet ? { node: mem.property } : isThis && utils.findMutating(mem) diff --git a/tests/lib/rules/no-side-effects-in-computed-properties.js b/tests/lib/rules/no-side-effects-in-computed-properties.js index 36889ae86..dc62f933b 100644 --- a/tests/lib/rules/no-side-effects-in-computed-properties.js +++ b/tests/lib/rules/no-side-effects-in-computed-properties.js @@ -453,10 +453,6 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, { fooBar() { this.$set(this, 'foo', 'lorem'); Vue.set(this, 'bar', 'ipsum'); - - this.$nextTick(() => {}); - Vue.nextTick(() => {}); - return this.foo + ' ' + this.bar }, } @@ -469,14 +465,6 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, { { line: 5, message: 'Unexpected side effect in "fooBar" computed property.' - }, - { - line: 7, - message: 'Unexpected side effect in "fooBar" computed property.' - }, - { - line: 8, - message: 'Unexpected side effect in "fooBar" computed property.' } ] }, From 0cebd07dfd34ed2990210f417a2b3841629b24f2 Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Wed, 5 Jan 2022 16:12:14 +0100 Subject: [PATCH 3/3] Report `nextTick` in `no-async-in-computed-properties` --- lib/rules/no-async-in-computed-properties.js | 26 ++++++++ .../rules/no-async-in-computed-properties.js | 63 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/lib/rules/no-async-in-computed-properties.js b/lib/rules/no-async-in-computed-properties.js index b405c0475..a762d3f58 100644 --- a/lib/rules/no-async-in-computed-properties.js +++ b/lib/rules/no-async-in-computed-properties.js @@ -58,6 +58,24 @@ function isPromise(node) { return false } +/** + * @param {CallExpression} node + * @param {RuleContext} context + */ +function isNextTick(node, context) { + const callee = utils.skipChainExpression(node.callee) + if (callee.type === 'MemberExpression') { + const name = utils.getStaticPropertyName(callee) + return ( + (utils.isThis(callee.object, context) && name === '$nextTick') || + (callee.object.type === 'Identifier' && + callee.object.name === 'Vue' && + name === 'nextTick') + ) + } + return false +} + // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ @@ -90,6 +108,7 @@ module.exports = { const expressionTypes = { promise: 'asynchronous action', + nextTick: 'asynchronous action', await: 'await operator', async: 'async function declaration', new: 'Promise object', @@ -211,6 +230,13 @@ module.exports = { 'timed', info ? computedPropertiesMap.get(info.node) : null ) + } else if (isNextTick(node, context)) { + verify( + node, + scopeStack.body, + 'nextTick', + info ? computedPropertiesMap.get(info.node) : null + ) } }, diff --git a/tests/lib/rules/no-async-in-computed-properties.js b/tests/lib/rules/no-async-in-computed-properties.js index a244c7747..ba6c0cd02 100644 --- a/tests/lib/rules/no-async-in-computed-properties.js +++ b/tests/lib/rules/no-async-in-computed-properties.js @@ -627,6 +627,69 @@ ruleTester.run('no-async-in-computed-properties', rule, { } ] }, + { + filename: 'test.vue', + code: ` + new Vue({ + computed: { + foo () { + this.$nextTick(() => {}) + Vue.nextTick(() => {}) + return 'foo' + } + } + }) + `, + parserOptions, + errors: [ + { + message: 'Unexpected asynchronous action in "foo" computed property.', + line: 5 + }, + { + message: 'Unexpected asynchronous action in "foo" computed property.', + line: 6 + } + ] + }, + { + filename: 'test.vue', + code: ` + new Vue({ + computed: { + async foo () { + await this.$nextTick() + await Vue.nextTick() + return 'foo' + } + } + }) + `, + parserOptions, + errors: [ + { + message: + 'Unexpected async function declaration in "foo" computed property.', + line: 4 + }, + { + message: 'Unexpected await operator in "foo" computed property.', + line: 5 + }, + { + message: 'Unexpected asynchronous action in "foo" computed property.', + line: 5 + }, + { + message: 'Unexpected await operator in "foo" computed property.', + line: 6 + }, + { + message: 'Unexpected asynchronous action in "foo" computed property.', + line: 6 + } + ] + }, { filename: 'test.vue', code: `