diff --git a/docs/rules/README.md b/docs/rules/README.md index 1fe658682..16e0ef983 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -42,6 +42,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi | [vue/no-arrow-functions-in-watch](./no-arrow-functions-in-watch.md) | disallow using arrow functions to define watcher | | | [vue/no-async-in-computed-properties](./no-async-in-computed-properties.md) | disallow asynchronous actions in computed properties | | | [vue/no-deprecated-data-object-declaration](./no-deprecated-data-object-declaration.md) | disallow using deprecated object declaration on data (in Vue.js 3.0.0+) | :wrench: | +| [vue/no-deprecated-destroyed-lifecycle](./no-deprecated-destroyed-lifecycle.md) | disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+) | | | [vue/no-deprecated-dollar-listeners-api](./no-deprecated-dollar-listeners-api.md) | disallow using deprecated `$listeners` (in Vue.js 3.0.0+) | | | [vue/no-deprecated-dollar-scopedslots-api](./no-deprecated-dollar-scopedslots-api.md) | disallow using deprecated `$scopedSlots` (in Vue.js 3.0.0+) | :wrench: | | [vue/no-deprecated-events-api](./no-deprecated-events-api.md) | disallow using deprecated events api (in Vue.js 3.0.0+) | | diff --git a/docs/rules/no-deprecated-destroyed-lifecycle.md b/docs/rules/no-deprecated-destroyed-lifecycle.md new file mode 100644 index 000000000..f7f7cd8dc --- /dev/null +++ b/docs/rules/no-deprecated-destroyed-lifecycle.md @@ -0,0 +1,43 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/no-deprecated-destroyed-lifecycle +description: disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+) +--- +# vue/no-deprecated-destroyed-lifecycle +> disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+) + +- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`. + +## :book: Rule Details + +This rule reports use of deprecated `destroyed` and `beforeDestroy` lifecycle hooks. (in Vue.js 3.0.0+). + + + +```vue + +``` + + + +## :wrench: Options + +Nothing. + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-destroyed-lifecycle.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-destroyed-lifecycle.js) diff --git a/lib/configs/vue3-essential.js b/lib/configs/vue3-essential.js index b802623da..432df355b 100644 --- a/lib/configs/vue3-essential.js +++ b/lib/configs/vue3-essential.js @@ -10,6 +10,7 @@ module.exports = { 'vue/no-arrow-functions-in-watch': 'error', 'vue/no-async-in-computed-properties': 'error', 'vue/no-deprecated-data-object-declaration': 'error', + 'vue/no-deprecated-destroyed-lifecycle': 'error', 'vue/no-deprecated-dollar-listeners-api': 'error', 'vue/no-deprecated-dollar-scopedslots-api': 'error', 'vue/no-deprecated-events-api': 'error', diff --git a/lib/index.js b/lib/index.js index f962015c3..7eda5423d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -51,6 +51,7 @@ module.exports = { 'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'), 'no-custom-modifiers-on-v-model': require('./rules/no-custom-modifiers-on-v-model'), 'no-deprecated-data-object-declaration': require('./rules/no-deprecated-data-object-declaration'), + 'no-deprecated-destroyed-lifecycle': require('./rules/no-deprecated-destroyed-lifecycle'), 'no-deprecated-dollar-listeners-api': require('./rules/no-deprecated-dollar-listeners-api'), 'no-deprecated-dollar-scopedslots-api': require('./rules/no-deprecated-dollar-scopedslots-api'), 'no-deprecated-events-api': require('./rules/no-deprecated-events-api'), diff --git a/lib/rules/no-deprecated-destroyed-lifecycle.js b/lib/rules/no-deprecated-destroyed-lifecycle.js new file mode 100644 index 000000000..c3556084c --- /dev/null +++ b/lib/rules/no-deprecated-destroyed-lifecycle.js @@ -0,0 +1,101 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const utils = require('../utils') + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: 'problem', + docs: { + description: + 'disallow using deprecated `destroyed` and `beforeDestroy` lifecycle hooks (in Vue.js 3.0.0+)', + categories: ['vue3-essential'], + url: + 'https://eslint.vuejs.org/rules/no-deprecated-destroyed-lifecycle.html' + }, + fixable: null, + schema: [], + messages: { + deprecatedDestroyed: + 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.', + deprecatedBeforeDestroy: + 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.', + insteadUnmounted: 'Instead, change to `unmounted`.', + insteadBeforeUnmount: 'Instead, change to `beforeUnmount`.' + } + }, + /** @param {RuleContext} context */ + create(context) { + return utils.executeOnVue(context, (obj) => { + const destroyed = utils.findProperty(obj, 'destroyed') + + if (destroyed) { + context.report({ + node: destroyed.key, + messageId: 'deprecatedDestroyed', + // I don't know if they have exactly the same function, so don't do autofix. + suggest: [ + { + messageId: 'insteadUnmounted', + fix(fixer) { + return fix(fixer, destroyed, 'unmounted') + } + } + ] + }) + } + + const beforeDestroy = utils.findProperty(obj, 'beforeDestroy') + if (beforeDestroy) { + context.report({ + node: beforeDestroy.key, + messageId: 'deprecatedBeforeDestroy', + // I don't know if they have exactly the same function, so don't do autofix. + suggest: [ + { + messageId: 'insteadBeforeUnmount', + fix(fixer) { + return fix(fixer, beforeDestroy, 'beforeUnmount') + } + } + ] + }) + } + + /** + * @param {RuleFixer} fixer + * @param {Property} property + * @param {string} newName + */ + function fix(fixer, property, newName) { + if (property.computed) { + if ( + property.key.type === 'Literal' || + property.key.type === 'TemplateLiteral' + ) { + return fixer.replaceTextRange( + [property.key.range[0] + 1, property.key.range[1] - 1], + newName + ) + } + return null + } + if (property.shorthand) { + return fixer.insertTextBefore(property.key, `${newName}:`) + } + return fixer.replaceText(property.key, newName) + } + }) + } +} diff --git a/tests/lib/rules/no-deprecated-destroyed-lifecycle.js b/tests/lib/rules/no-deprecated-destroyed-lifecycle.js new file mode 100644 index 000000000..00dda6ed8 --- /dev/null +++ b/tests/lib/rules/no-deprecated-destroyed-lifecycle.js @@ -0,0 +1,377 @@ +/** + * @author Yosuke Ota + * See LICENSE file in root directory for full license. + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/no-deprecated-destroyed-lifecycle') + +const RuleTester = require('eslint').RuleTester + +// ------------------------------------------------------------------------------ +// Tests +// ------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { ecmaVersion: 2020, sourceType: 'module' } +}) +ruleTester.run('no-deprecated-destroyed-lifecycle', rule, { + valid: [ + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + }, + { + filename: 'test.vue', + code: ` + + ` + } + ], + invalid: [ + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: + 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.', + line: 4, + suggestions: [ + { + desc: 'Instead, change to `beforeUnmount`.', + output: ` + + ` + } + ] + }, + { + message: + 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.', + line: 5, + suggestions: [ + { + desc: 'Instead, change to `unmounted`.', + output: ` + + ` + } + ] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: + 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.', + line: 4, + suggestions: [ + { + desc: 'Instead, change to `beforeUnmount`.', + output: ` + + ` + } + ] + }, + { + message: + 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.', + line: 5, + suggestions: [ + { + desc: 'Instead, change to `unmounted`.', + output: ` + + ` + } + ] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: + 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.', + line: 12, + suggestions: [ + { + desc: 'Instead, change to `beforeUnmount`.', + output: ` + + ` + } + ] + }, + { + message: + 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.', + line: 13, + suggestions: [ + { + desc: 'Instead, change to `unmounted`.', + output: ` + + ` + } + ] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: + 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.', + line: 4, + suggestions: [ + { + desc: 'Instead, change to `beforeUnmount`.', + output: ` + + ` + } + ] + }, + { + message: + 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.', + line: 5, + suggestions: [ + { + desc: 'Instead, change to `unmounted`.', + output: ` + + ` + } + ] + } + ] + }, + { + filename: 'test.vue', + code: ` + + `, + errors: [ + { + message: + 'The `beforeDestroy` lifecycle hook is deprecated. Use `beforeUnmount` instead.', + line: 4, + suggestions: [ + { + desc: 'Instead, change to `beforeUnmount`.', + output: ` + + ` + } + ] + }, + { + message: + 'The `destroyed` lifecycle hook is deprecated. Use `unmounted` instead.', + line: 5, + suggestions: [ + { + desc: 'Instead, change to `unmounted`.', + output: ` + + ` + } + ] + } + ] + } + ] +})