diff --git a/docs/rules/next-tick-style.md b/docs/rules/next-tick-style.md index 1224e93b9..c9e0fe5ef 100644 --- a/docs/rules/next-tick-style.md +++ b/docs/rules/next-tick-style.md @@ -2,13 +2,13 @@ pageClass: rule-details sidebarDepth: 0 title: vue/next-tick-style -description: enforce Promise or callback style in `nextTick` +description: enforce Promise, Await or callback style in `nextTick` since: v7.5.0 --- # vue/next-tick-style -> enforce Promise or callback style in `nextTick` +> enforce Promise, Await or callback style in `nextTick` - :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. @@ -52,13 +52,45 @@ Default is set to `promise`. ```json { - "vue/next-tick-style": ["error", "promise" | "callback"] + "vue/next-tick-style": ["error", "promise" | "await" | "callback"] } ``` - `"promise"` (default) ... requires using the promise version. +- `"await"` ... requires using the await syntax version. - `"callback"` ... requires using the callback version. Use this if you use a Vue version below v2.1.0. +### `"await"` + + + +```vue + +``` + + ### `"callback"` diff --git a/lib/rules/next-tick-style.js b/lib/rules/next-tick-style.js index 08cc85a1b..289d55c53 100644 --- a/lib/rules/next-tick-style.js +++ b/lib/rules/next-tick-style.js @@ -79,17 +79,44 @@ function isAwaitedPromise(callExpression) { ) } +/** + * @param {CallExpression} callExpression + * @returns {boolean} + */ + +/** + * @param {Expression | SpreadElement} callback + * @param {SourceCode} sourceCode + * @returns {string} + */ +function extractCallbackBody(callback, sourceCode) { + if ( + callback.type !== 'FunctionExpression' && + callback.type !== 'ArrowFunctionExpression' + ) { + return '' + } + + if (callback.body.type === 'BlockStatement') { + return sourceCode.getText(callback.body).trim() + } + + return sourceCode.getText(callback.body) +} + module.exports = { meta: { type: 'suggestion', docs: { - description: 'enforce Promise or callback style in `nextTick`', + description: 'enforce Promise, Await or callback style in `nextTick`', categories: undefined, url: 'https://eslint.vuejs.org/rules/next-tick-style.html' }, fixable: 'code', - schema: [{ enum: ['promise', 'callback'] }], + schema: [{ enum: ['promise', 'await', 'callback'] }], messages: { + useAwait: + 'Use the await keyword with the Promise returned by `nextTick` instead of passing a callback function or using `.then()`.', usePromise: 'Use the Promise returned by `nextTick` instead of passing a callback function.', useCallback: @@ -123,6 +150,66 @@ module.exports = { return } + if (preferredStyle === 'await') { + if ( + callExpression.arguments.length > 0 || + callExpression.parent.type !== 'AwaitExpression' + ) { + context.report({ + node, + messageId: 'useAwait', + fix(fixer) { + const sourceCode = context.getSourceCode() + + // Handle callback to await conversion + if (callExpression.arguments.length === 1) { + if (callExpression.parent.type !== 'ExpressionStatement') { + return null + } + + const [args] = callExpression.arguments + let callbackBody = null + + callbackBody = + args.type === 'ArrowFunctionExpression' || + (args.type === 'FunctionExpression' && !args.generator) + ? extractCallbackBody(args, sourceCode) + : `${sourceCode.getText(args)}()` + + const nextTickCaller = sourceCode.getText( + callExpression.callee + ) + return fixer.replaceText( + callExpression.parent, + `await ${nextTickCaller}();${callbackBody};` + ) + } + + // Handle promise to await conversion + if (isAwaitedPromise(callExpression)) { + const thenCall = callExpression.parent.parent + if (thenCall === null || thenCall.type !== 'CallExpression') { + return null + } + const [thenCallback] = thenCall.arguments + if (thenCallback) { + const thenCallbackBody = extractCallbackBody( + thenCallback, + sourceCode + ) + return fixer.replaceText( + thenCall, + `await ${sourceCode.getText(callExpression)};${thenCallbackBody}` + ) + } + } + return null + } + }) + } + + return + } if ( callExpression.arguments.length > 0 || !isAwaitedPromise(callExpression) diff --git a/tests/lib/rules/next-tick-style.js b/tests/lib/rules/next-tick-style.js index c143c8a40..899be6730 100644 --- a/tests/lib/rules/next-tick-style.js +++ b/tests/lib/rules/next-tick-style.js @@ -54,6 +54,18 @@ tester.run('next-tick-style', rule, { }`, options: ['promise'] }, + { + filename: 'test.vue', + code: ``, + options: ['await'] + }, { filename: 'test.vue', code: ``, + options: ['await'] + }, + { + filename: 'test.vue', + code: ``, options: ['callback'] } ], @@ -237,6 +265,98 @@ tester.run('next-tick-style', rule, { } ] }, + { + filename: 'test.vue', + code: ``, + output: ``, + options: ['await'], + errors: [ + { + message: + 'Use the await keyword with the Promise returned by `nextTick` instead of passing a callback function or using `.then()`.', + line: 4, + column: 16 + }, + { + message: + 'Use the await keyword with the Promise returned by `nextTick` instead of passing a callback function or using `.then()`.', + line: 5, + column: 15 + }, + { + message: + 'Use the await keyword with the Promise returned by `nextTick` instead of passing a callback function or using `.then()`.', + line: 6, + column: 11 + }, + { + message: + 'Use the await keyword with the Promise returned by `nextTick` instead of passing a callback function or using `.then()`.', + line: 8, + column: 16 + }, + { + message: + 'Use the await keyword with the Promise returned by `nextTick` instead of passing a callback function or using `.then()`.', + line: 9, + column: 15 + }, + { + message: + 'Use the await keyword with the Promise returned by `nextTick` instead of passing a callback function or using `.then()`.', + line: 10, + column: 11 + }, + { + message: + 'Use the await keyword with the Promise returned by `nextTick` instead of passing a callback function or using `.then()`.', + line: 12, + column: 16 + }, + { + message: + 'Use the await keyword with the Promise returned by `nextTick` instead of passing a callback function or using `.then()`.', + line: 13, + column: 15 + }, + { + message: + 'Use the await keyword with the Promise returned by `nextTick` instead of passing a callback function or using `.then()`.', + line: 14, + column: 11 + } + ] + }, { filename: 'test.vue', code: `