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/lib/rules/no-side-effects-in-computed-properties.js b/lib/rules/no-side-effects-in-computed-properties.js index a0d508014..6e43266fb 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 isVueSet = + mem.parent.type === 'CallExpression' && + mem.property.type === 'Identifier' && + ((isThis && mem.property.name === '$set') || + (isVue && mem.property.name === 'set')) + + const invalid = isVueSet + ? { node: mem.property } + : isThis && utils.findMutating(mem) + if (invalid) { context.report({ node: invalid.node, 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: ` 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..dc62f933b 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,28 @@ 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'); + 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.' + } + ] + }, { filename: 'test.vue', code: `