From 26783f890feb2481557e3dcbd39d82e4dd44dab5 Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Sat, 2 Jan 2021 15:28:04 +0100 Subject: [PATCH 01/11] Add rule docs --- docs/rules/valid-next-tick.md | 73 +++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 docs/rules/valid-next-tick.md diff --git a/docs/rules/valid-next-tick.md b/docs/rules/valid-next-tick.md new file mode 100644 index 000000000..668868bef --- /dev/null +++ b/docs/rules/valid-next-tick.md @@ -0,0 +1,73 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/valid-next-tick +description: enforce valid `nextTick` function calls +--- +# vue/valid-next-tick + +> enforce valid `nextTick` function calls + +- :exclamation: ***This rule has not been released yet.*** +- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/essential"`, `"plugin:vue/vue3-strongly-recommended"`, `"plugin:vue/strongly-recommended"`, `"plugin:vue/vue3-recommended"` and `"plugin:vue/recommended"`. +- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule. + +## :book: Rule Details + +Calling `Vue.nextTick` or `vm.$nextTick` without passing a callback and without awaiting the returned Promise is likely a mistake (probably a missing `await`). + + + +```vue + +``` + + + +## :wrench: Options + +Nothing. + +## :books: Further Reading + +- [`Vue.nextTick` API in Vue 2](https://vuejs.org/v2/api/#Vue-nextTick) +- [`vm.$nextTick` API in Vue 2](https://vuejs.org/v2/api/#vm-nextTick) +- [Global API Treeshaking](https://v3.vuejs.org/guide/migration/global-api-treeshaking.html) +- [Global `nextTick` API in Vue 3](https://v3.vuejs.org/api/global-api.html#nexttick) +- [Instance `$nextTick` API in Vue 3](https://v3.vuejs.org/api/instance-methods.html#nexttick) + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/valid-next-tick.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/valid-next-tick.js) From b840577fda69299608fa4e5a2eb1231a4165e90b Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Sat, 2 Jan 2021 15:28:35 +0100 Subject: [PATCH 02/11] Add rule to collections --- docs/rules/README.md | 2 ++ lib/configs/essential.js | 1 + lib/configs/vue3-essential.js | 1 + lib/index.js | 1 + 4 files changed, 5 insertions(+) diff --git a/docs/rules/README.md b/docs/rules/README.md index 5f848467b..7b062dab8 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -86,6 +86,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | [vue/return-in-computed-property](./return-in-computed-property.md) | enforce that a return statement is present in computed property | | | [vue/return-in-emits-validator](./return-in-emits-validator.md) | enforce that a return statement is present in emits validator | | | [vue/use-v-on-exact](./use-v-on-exact.md) | enforce usage of `exact` modifier on `v-on` | | +| [vue/valid-next-tick](./valid-next-tick.md) | enforce valid `nextTick` function calls | :wrench: | | [vue/valid-template-root](./valid-template-root.md) | enforce valid template root | | | [vue/valid-v-bind](./valid-v-bind.md) | enforce valid `v-bind` directives | | | [vue/valid-v-cloak](./valid-v-cloak.md) | enforce valid `v-cloak` directives | | @@ -197,6 +198,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | [vue/require-valid-default-prop](./require-valid-default-prop.md) | enforce props default values to be valid | | | [vue/return-in-computed-property](./return-in-computed-property.md) | enforce that a return statement is present in computed property | | | [vue/use-v-on-exact](./use-v-on-exact.md) | enforce usage of `exact` modifier on `v-on` | | +| [vue/valid-next-tick](./valid-next-tick.md) | enforce valid `nextTick` function calls | :wrench: | | [vue/valid-template-root](./valid-template-root.md) | enforce valid template root | | | [vue/valid-v-bind-sync](./valid-v-bind-sync.md) | enforce valid `.sync` modifier on `v-bind` directives | | | [vue/valid-v-bind](./valid-v-bind.md) | enforce valid `v-bind` directives | | diff --git a/lib/configs/essential.js b/lib/configs/essential.js index dca31cd2e..50a548970 100644 --- a/lib/configs/essential.js +++ b/lib/configs/essential.js @@ -32,6 +32,7 @@ module.exports = { 'vue/require-valid-default-prop': 'error', 'vue/return-in-computed-property': 'error', 'vue/use-v-on-exact': 'error', + 'vue/valid-next-tick': 'error', 'vue/valid-template-root': 'error', 'vue/valid-v-bind-sync': 'error', 'vue/valid-v-bind': 'error', diff --git a/lib/configs/vue3-essential.js b/lib/configs/vue3-essential.js index 802cb62ed..9c6a3d907 100644 --- a/lib/configs/vue3-essential.js +++ b/lib/configs/vue3-essential.js @@ -53,6 +53,7 @@ module.exports = { 'vue/return-in-computed-property': 'error', 'vue/return-in-emits-validator': 'error', 'vue/use-v-on-exact': 'error', + 'vue/valid-next-tick': 'error', 'vue/valid-template-root': 'error', 'vue/valid-v-bind': 'error', 'vue/valid-v-cloak': 'error', diff --git a/lib/index.js b/lib/index.js index 21091a9f8..d9eb3c194 100644 --- a/lib/index.js +++ b/lib/index.js @@ -162,6 +162,7 @@ module.exports = { 'v-on-function-call': require('./rules/v-on-function-call'), 'v-on-style': require('./rules/v-on-style'), 'v-slot-style': require('./rules/v-slot-style'), + 'valid-next-tick': require('./rules/valid-next-tick'), 'valid-template-root': require('./rules/valid-template-root'), 'valid-v-bind-sync': require('./rules/valid-v-bind-sync'), 'valid-v-bind': require('./rules/valid-v-bind'), From 30c96dc7749c27b8a26c4bd1aff30c750d00e7ad Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Sat, 2 Jan 2021 15:29:00 +0100 Subject: [PATCH 03/11] Add rule tests --- tests/lib/rules/valid-next-tick.js | 222 +++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 tests/lib/rules/valid-next-tick.js diff --git a/tests/lib/rules/valid-next-tick.js b/tests/lib/rules/valid-next-tick.js new file mode 100644 index 000000000..fb1c9c39d --- /dev/null +++ b/tests/lib/rules/valid-next-tick.js @@ -0,0 +1,222 @@ +/** + * @fileoverview enforce valid `nextTick` function calls + * @author Flo Edelmann + * @copyright 2021 Flo Edelmann. All rights reserved. + * See LICENSE file in root directory for full license. + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const RuleTester = require('eslint').RuleTester +const rule = require('../../../lib/rules/valid-next-tick') + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +const tester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { + ecmaVersion: 2017, + sourceType: 'module' + } +}) + +tester.run('valid-next-tick', rule, { + valid: [ + { + filename: 'test.vue', + code: '' + }, + { + filename: 'test.vue', + code: `` + } + ], + invalid: [ + { + filename: 'test.vue', + code: ``, + output: null, + errors: [ + { + message: + 'Await the Promise returned by `nextTick` or pass a callback function.', + line: 4, + column: 11 + }, + { + message: + 'Await the Promise returned by `nextTick` or pass a callback function.', + line: 5, + column: 15 + }, + { + message: + 'Await the Promise returned by `nextTick` or pass a callback function.', + line: 6, + column: 16 + } + ] + }, + { + filename: 'test.vue', + code: ``, + output: ``, + errors: [ + { + message: + 'Await the Promise returned by `nextTick` or pass a callback function.', + line: 4, + column: 11 + }, + { + message: + 'Await the Promise returned by `nextTick` or pass a callback function.', + line: 5, + column: 15 + }, + { + message: + 'Await the Promise returned by `nextTick` or pass a callback function.', + line: 6, + column: 16 + } + ] + }, + { + filename: 'test.vue', + code: ``, + output: ``, + errors: [ + { + message: '`nextTick` is a function.', + line: 4, + column: 11 + }, + { + message: '`nextTick` is a function.', + line: 5, + column: 15 + }, + { + message: '`nextTick` is a function.', + line: 6, + column: 16 + }, + { + message: '`nextTick` is a function.', + line: 8, + column: 11 + }, + { + message: '`nextTick` is a function.', + line: 9, + column: 15 + }, + { + message: '`nextTick` is a function.', + line: 10, + column: 16 + } + ] + }, + { + filename: 'test.vue', + code: ``, + output: ``, + errors: [ + { + message: '`nextTick` is a function.', + line: 4, + column: 17 + }, + { + message: '`nextTick` is a function.', + line: 5, + column: 21 + }, + { + message: '`nextTick` is a function.', + line: 6, + column: 22 + } + ] + } + ] +}) From d4714e9e7f70f485f56812f7d6336fadce608802 Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Sat, 2 Jan 2021 15:50:38 +0100 Subject: [PATCH 04/11] Add more test cases --- docs/rules/valid-next-tick.md | 19 ++++++- tests/lib/rules/valid-next-tick.js | 83 ++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/docs/rules/valid-next-tick.md b/docs/rules/valid-next-tick.md index 668868bef..d094e1ef7 100644 --- a/docs/rules/valid-next-tick.md +++ b/docs/rules/valid-next-tick.md @@ -24,16 +24,31 @@ import { nextTick } from 'vue'; export default { async mounted() { - /* ✗ BAD */ + /* ✗ BAD: no callback function or awaited Promise */ nt(); Vue.nextTick(); this.$nextTick(); - /* ✗ BAD */ + /* ✗ BAD: no function call */ nt; Vue.nextTick; this.$nextTick; + /* ✗ BAD: too many parameters */ + nt(callback, anotherCallback); + Vue.nextTick(callback, anotherCallback); + this.$nextTick(callback, anotherCallback); + + /* ✗ BAD: both callback function and awaited Promise */ + nt(callback).then(anotherCallback); + Vue.nextTick(callback).then(anotherCallback); + this.$nextTick(callback).then(anotherCallback); + + /* ✗ BAD: both callback function and awaited Promise */ + await nt(callback); + await Vue.nextTick(callback); + await this.$nextTick(callback); + /* ✓ GOOD */ await nt(); await Vue.nextTick(); diff --git a/tests/lib/rules/valid-next-tick.js b/tests/lib/rules/valid-next-tick.js index fb1c9c39d..47a7a6d1f 100644 --- a/tests/lib/rules/valid-next-tick.js +++ b/tests/lib/rules/valid-next-tick.js @@ -217,6 +217,89 @@ tester.run('valid-next-tick', rule, { column: 22 } ] + }, + { + filename: 'test.vue', + code: ``, + output: null, + errors: [ + { + message: '`nextTick` expects zero or one parameters.', + line: 4, + column: 11 + }, + { + message: '`nextTick` expects zero or one parameters.', + line: 5, + column: 15 + }, + { + message: '`nextTick` expects zero or one parameters.', + line: 6, + column: 16 + } + ] + }, + { + filename: 'test.vue', + code: ``, + output: null, + errors: [ + { + message: + 'Either await the Promise or pass a callback function to `nextTick`.', + line: 4, + column: 11 + }, + { + message: + 'Either await the Promise or pass a callback function to `nextTick`.', + line: 5, + column: 15 + }, + { + message: + 'Either await the Promise or pass a callback function to `nextTick`.', + line: 6, + column: 16 + }, + { + message: + 'Either await the Promise or pass a callback function to `nextTick`.', + line: 8, + column: 17 + }, + { + message: + 'Either await the Promise or pass a callback function to `nextTick`.', + line: 9, + column: 21 + }, + { + message: + 'Either await the Promise or pass a callback function to `nextTick`.', + line: 10, + column: 22 + } + ] } ] }) From 3c6d8987a56324942eecf7e643bf39a020a65d0f Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Sat, 2 Jan 2021 16:27:00 +0100 Subject: [PATCH 05/11] Add rule implementation --- lib/rules/valid-next-tick.js | 167 +++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 lib/rules/valid-next-tick.js diff --git a/lib/rules/valid-next-tick.js b/lib/rules/valid-next-tick.js new file mode 100644 index 000000000..4d668a25f --- /dev/null +++ b/lib/rules/valid-next-tick.js @@ -0,0 +1,167 @@ +/** + * @fileoverview enforce valid `nextTick` function calls + * @author Flo Edelmann + * @copyright 2021 Flo Edelmann. All rights reserved. + * See LICENSE file in root directory for full license. + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const utils = require('../utils') +const { findVariable } = require('eslint-utils') + +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + +/** + * @param {Identifier} identifier + * @param {RuleContext} context + * @returns {ASTNode|undefined} + */ +function getVueNextTickParentNode(identifier, context) { + // Instance API: this.$nextTick() + if ( + identifier.name === '$nextTick' && + identifier.parent.type === 'MemberExpression' && + utils.isThis(identifier.parent.object, context) + ) { + return identifier.parent.parent + } + + // Vue 2 Global API: Vue.nextTick() + if ( + identifier.name === 'nextTick' && + identifier.parent.type === 'MemberExpression' && + identifier.parent.object.type === 'Identifier' && + identifier.parent.object.name === 'Vue' + ) { + return identifier.parent.parent + } + + // Vue 3 Global API: import { nextTick as nt } from 'vue'; nt() + const variable = findVariable(context.getScope(), identifier) + + if (variable != null && variable.defs.length === 1) { + const def = variable.defs[0] + if ( + def.type === 'ImportBinding' && + def.node.type === 'ImportSpecifier' && + def.node.imported.type === 'Identifier' && + def.node.imported.name === 'nextTick' && + def.node.parent.type === 'ImportDeclaration' && + def.node.parent.source.value === 'vue' + ) { + return identifier.parent + } + } + + return undefined +} + +/** + * @param {CallExpression} callExpression + * @returns {boolean} + */ +function isAwaitedPromise(callExpression) { + return ( + callExpression.parent.type === 'AwaitExpression' || + (callExpression.parent.type === 'MemberExpression' && + callExpression.parent.property.type === 'Identifier' && + callExpression.parent.property.name === 'then') + ) +} + +/** + * @param {ASTNode} node + * @returns {FunctionExpression|undefined} + */ +function getClosestFunction(node) { + while (node.parent !== null) { + node = /** @type {ASTNode} */ (node.parent) + + if (node.type === 'FunctionExpression') { + return node + } + } + + return undefined +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'enforce valid `nextTick` function calls', + categories: ['vue3-essential', 'essential'], + url: 'https://eslint.vuejs.org/rules/valid-next-tick.html' + }, + fixable: 'code', + schema: [] + }, + /** @param {RuleContext} context */ + create(context) { + return utils.defineVueVisitor(context, { + /** @param {Identifier} node */ + Identifier(node) { + const parentNode = getVueNextTickParentNode(node, context) + if (!parentNode) { + return + } + + if (parentNode.type !== 'CallExpression') { + context.report({ + node, + message: '`nextTick` is a function.', + fix(fixer) { + return fixer.insertTextAfter(node, '()') + } + }) + return + } + + if (parentNode.arguments.length === 0) { + if (!isAwaitedPromise(parentNode)) { + const closestFunction = getClosestFunction(parentNode) + const isAsyncFunction = closestFunction + ? closestFunction.async + : false + + context.report({ + node, + message: + 'Await the Promise returned by `nextTick` or pass a callback function.', + fix: isAsyncFunction + ? (fixer) => fixer.insertTextBefore(parentNode, 'await ') + : undefined + }) + } + return + } + + if (parentNode.arguments.length > 1) { + context.report({ + node, + message: '`nextTick` expects zero or one parameters.' + }) + return + } + + if (isAwaitedPromise(parentNode)) { + context.report({ + node, + message: + 'Either await the Promise or pass a callback function to `nextTick`.' + }) + } + } + }) + } +} From 4b9177c774ced7ddc889a856876eda52da87dcba Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Sat, 2 Jan 2021 17:31:01 +0100 Subject: [PATCH 06/11] Fix docs --- docs/rules/valid-next-tick.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/valid-next-tick.md b/docs/rules/valid-next-tick.md index d094e1ef7..3008c8924 100644 --- a/docs/rules/valid-next-tick.md +++ b/docs/rules/valid-next-tick.md @@ -20,7 +20,7 @@ Calling `Vue.nextTick` or `vm.$nextTick` without passing a callback and without ```vue ` + }, + + // https://github.com/vuejs/eslint-plugin-vue/pull/1404#discussion_r550937500 + { + filename: 'test.vue', + code: `` } ], invalid: [ From 8c257072327c53dd636f39fbdbedbfcb76350e29 Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Mon, 4 Jan 2021 18:59:20 +0100 Subject: [PATCH 10/11] Suggest `await` instead of fixing it --- lib/rules/valid-next-tick.js | 32 ++++---------- tests/lib/rules/valid-next-tick.js | 71 ++++++++++++++---------------- 2 files changed, 42 insertions(+), 61 deletions(-) diff --git a/lib/rules/valid-next-tick.js b/lib/rules/valid-next-tick.js index 685209a9a..620ac4683 100644 --- a/lib/rules/valid-next-tick.js +++ b/lib/rules/valid-next-tick.js @@ -75,22 +75,6 @@ function isAwaitedPromise(callExpression) { ) } -/** - * @param {ASTNode} node - * @returns {FunctionExpression|undefined} - */ -function getClosestFunction(node) { - while (node.parent !== null) { - node = /** @type {ASTNode} */ (node.parent) - - if (node.type === 'FunctionExpression') { - return node - } - } - - return undefined -} - // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ @@ -137,18 +121,18 @@ module.exports = { if (parentNode.arguments.length === 0) { if (!isAwaitedPromise(parentNode)) { - const closestFunction = getClosestFunction(parentNode) - const isAsyncFunction = closestFunction - ? closestFunction.async - : false - context.report({ node, message: 'Await the Promise returned by `nextTick` or pass a callback function.', - fix: isAsyncFunction - ? (fixer) => fixer.insertTextBefore(parentNode, 'await ') - : undefined + suggest: [ + { + desc: 'Add missing `await` statement.', + fix(fixer) { + return fixer.insertTextBefore(parentNode, 'await ') + } + } + ] }) } return diff --git a/tests/lib/rules/valid-next-tick.js b/tests/lib/rules/valid-next-tick.js index e23d8cbe9..abe669f21 100644 --- a/tests/lib/rules/valid-next-tick.js +++ b/tests/lib/rules/valid-next-tick.js @@ -73,7 +73,7 @@ tester.run('valid-next-tick', rule, { filename: 'test.vue', code: `` + } + ] }, { message: 'Await the Promise returned by `nextTick` or pass a callback function.', line: 5, - column: 15 + column: 15, + suggestions: [ + { + output: `` + } + ] }, { message: 'Await the Promise returned by `nextTick` or pass a callback function.', line: 6, - column: 16 - } - ] - }, - { - filename: 'test.vue', - code: ``, - output: ``, - errors: [ - { - message: - 'Await the Promise returned by `nextTick` or pass a callback function.', - line: 4, - column: 11 - }, - { - message: - 'Await the Promise returned by `nextTick` or pass a callback function.', - line: 5, - column: 15 - }, - { - message: - 'Await the Promise returned by `nextTick` or pass a callback function.', - line: 6, - column: 16 + }` + } + ] } ] }, From f21f15f6371a7243cda3f84093747e32c821dd0c Mon Sep 17 00:00:00 2001 From: Flo Edelmann Date: Tue, 5 Jan 2021 14:32:17 +0100 Subject: [PATCH 11/11] Add tests and logic for more false positives --- lib/rules/valid-next-tick.js | 67 ++++++++++++++++++---- tests/lib/rules/valid-next-tick.js | 92 +++++++++++++++++++++++++++++- 2 files changed, 145 insertions(+), 14 deletions(-) diff --git a/lib/rules/valid-next-tick.js b/lib/rules/valid-next-tick.js index 620ac4683..4e41abbf2 100644 --- a/lib/rules/valid-next-tick.js +++ b/lib/rules/valid-next-tick.js @@ -67,12 +67,46 @@ function getVueNextTickNode(identifier, context) { * @returns {boolean} */ function isAwaitedPromise(callExpression) { - return ( - callExpression.parent.type === 'AwaitExpression' || - (callExpression.parent.type === 'MemberExpression' && - callExpression.parent.property.type === 'Identifier' && - callExpression.parent.property.name === 'then') - ) + if (callExpression.parent.type === 'AwaitExpression') { + // cases like `await nextTick()` + return true + } + + if (callExpression.parent.type === 'ReturnStatement') { + // cases like `return nextTick()` + return true + } + + if ( + callExpression.parent.type === 'MemberExpression' && + callExpression.parent.property.type === 'Identifier' && + callExpression.parent.property.name === 'then' + ) { + // cases like `nextTick().then()` + return true + } + + if ( + callExpression.parent.type === 'VariableDeclarator' || + callExpression.parent.type === 'AssignmentExpression' + ) { + // cases like `let foo = nextTick()` or `foo = nextTick()` + return true + } + + if ( + callExpression.parent.type === 'ArrayExpression' && + callExpression.parent.parent.type === 'CallExpression' && + callExpression.parent.parent.callee.type === 'MemberExpression' && + callExpression.parent.parent.callee.object.type === 'Identifier' && + callExpression.parent.parent.callee.object.name === 'Promise' && + callExpression.parent.parent.callee.property.type === 'Identifier' + ) { + // cases like `Promise.all([nextTick()])` + return true + } + + return false } // ------------------------------------------------------------------------------ @@ -103,6 +137,22 @@ module.exports = { const parentNode = nextTickNode.parent + if ( + parentNode.type === 'CallExpression' && + parentNode.callee !== nextTickNode + ) { + // cases like `foo.then(nextTick)` are allowed + return + } + + if ( + parentNode.type === 'VariableDeclarator' || + parentNode.type === 'AssignmentExpression' + ) { + // cases like `let foo = nextTick` or `foo = nextTick` are allowed + return + } + if (parentNode.type !== 'CallExpression') { context.report({ node, @@ -114,11 +164,6 @@ module.exports = { return } - if (parentNode.callee !== nextTickNode) { - // cases like `foo.then(nextTick)` are allowed - return - } - if (parentNode.arguments.length === 0) { if (!isAwaitedPromise(parentNode)) { context.report({ diff --git a/tests/lib/rules/valid-next-tick.js b/tests/lib/rules/valid-next-tick.js index abe669f21..220e43417 100644 --- a/tests/lib/rules/valid-next-tick.js +++ b/tests/lib/rules/valid-next-tick.js @@ -66,6 +66,53 @@ tester.run('valid-next-tick', rule, { foo.then(this.$nextTick, catchHandler); } }` + }, + + // https://github.com/vuejs/eslint-plugin-vue/pull/1404#discussion_r550936410 + { + filename: 'test.vue', + code: `` + }, + + // https://github.com/vuejs/eslint-plugin-vue/pull/1404#discussion_r550936933 + { + filename: 'test.vue', + code: `` + }, + + // https://github.com/vuejs/eslint-plugin-vue/pull/1404#discussion_r551769969 + { + filename: 'test.vue', + code: `` } ], invalid: [ @@ -203,7 +250,7 @@ tester.run('valid-next-tick', rule, { async mounted() { await nt; await Vue.nextTick; - await this.$nextTick; + return this.$nextTick; } }`, output: ``, errors: [ @@ -228,10 +275,49 @@ tester.run('valid-next-tick', rule, { { message: '`nextTick` is a function.', line: 6, - column: 22 + column: 23 + } + ] + }, + + // https://github.com/vuejs/eslint-plugin-vue/pull/1404#discussion_r550936933 + { + filename: 'test.vue', + code: ``, + output: ``, + errors: [ + { + message: '`nextTick` is a function.', + line: 4, + column: 24 + }, + { + message: '`nextTick` is a function.', + line: 5, + column: 28 + }, + { + message: '`nextTick` is a function.', + line: 6, + column: 29 } ] }, + { filename: 'test.vue', code: `