diff --git a/docs/rules/README.md b/docs/rules/README.md index d38c44a2b..16185b231 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -313,6 +313,7 @@ For example: | [vue/no-restricted-v-bind](./no-restricted-v-bind.md) | disallow specific argument in `v-bind` | | | [vue/no-static-inline-styles](./no-static-inline-styles.md) | disallow static inline `style` attributes | | | [vue/no-template-target-blank](./no-template-target-blank.md) | disallow target="_blank" attribute without rel="noopener noreferrer" | | +| [vue/no-this-in-before-route-enter](./no-this-in-before-route-enter.md) | disallow this usage in a beforeRouteEnter method | | | [vue/no-unregistered-components](./no-unregistered-components.md) | disallow using components that are not registered inside templates | | | [vue/no-unsupported-features](./no-unsupported-features.md) | disallow unsupported Vue.js syntax on the specified version | :wrench: | | [vue/no-unused-properties](./no-unused-properties.md) | disallow unused properties | | diff --git a/docs/rules/no-this-in-before-route-enter.md b/docs/rules/no-this-in-before-route-enter.md new file mode 100644 index 000000000..7409b48ea --- /dev/null +++ b/docs/rules/no-this-in-before-route-enter.md @@ -0,0 +1,115 @@ +--- +pageClass: rule-details +sidebarDepth: 0 +title: vue/no-this-in-before-route-enter +description: disallow this usage in a beforeRouteEnter method +--- +# vue/no-this-in-before-route-enter + +> disallow this usage in a beforeRouteEnter method + +- :exclamation: ***This rule has not been released yet.*** + +## Rule Details + +Because lack of `this` in the `beforeRouteEnter` [(docs)](https://router.vuejs.org/guide/advanced/navigation-guards.html#in-component-guards). This behavior isn't obvious, so it's pretty easy to make a `TypeError`. Especially during some refactor. + +Bad: + + + +```vue + +``` + + + +Bad: + + + +```vue + +``` + + + +Bad: + + + +```vue + +``` + + + + +Bad: + + + +```vue + +``` + + + +Good: + + + +```vue + +``` + + + +### Options + +Nothing. + +## When Not To Use It + +When [vue-router](https://router.vuejs.org/) is not installed. + +## Further Reading + +[vue-router - in-component-guards](https://router.vuejs.org/guide/advanced/navigation-guards.html#in-component-guards) + +## :mag: Implementation + +- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-this-in-before-route-enter.js) +- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-this-in-before-route-enter.js) diff --git a/lib/index.js b/lib/index.js index 1379e284e..20ecde959 100644 --- a/lib/index.js +++ b/lib/index.js @@ -113,6 +113,7 @@ module.exports = { 'no-template-shadow': require('./rules/no-template-shadow'), 'no-template-target-blank': require('./rules/no-template-target-blank'), 'no-textarea-mustache': require('./rules/no-textarea-mustache'), + 'no-this-in-before-route-enter': require('./rules/no-this-in-before-route-enter'), 'no-unregistered-components': require('./rules/no-unregistered-components'), 'no-unsupported-features': require('./rules/no-unsupported-features'), 'no-unused-components': require('./rules/no-unused-components'), diff --git a/lib/rules/no-this-in-before-route-enter.js b/lib/rules/no-this-in-before-route-enter.js new file mode 100644 index 000000000..968f45c18 --- /dev/null +++ b/lib/rules/no-this-in-before-route-enter.js @@ -0,0 +1,91 @@ +/** + * @fileoverview Don't use this in a beforeRouteEnter method + * @author Przemyslaw Jan Beigert + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Requirements +// ------------------------------------------------------------------------------ + +const utils = require('../utils') + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'disallow this usage in a beforeRouteEnter method', + categories: null, + url: 'https://eslint.vuejs.org/rules/no-this-in-before-route-enter.html' + }, + fixable: null, + schema: [], + messages: { + disallow: + "'beforeRouteEnter' does NOT have access to `this` component instance. https://router.vuejs.org/guide/advanced/navigation-guards.html#in-component-guards." + } + }, + /** @param {RuleContext} context */ + create(context) { + /** + * @typedef {object} ScopeStack + * @property {ScopeStack | null} upper + * @property {FunctionExpression | FunctionDeclaration} node + * @property {boolean} beforeRouteEnter + */ + /** @type {Set} */ + const beforeRouteEnterFunctions = new Set() + /** @type {ScopeStack | null} */ + let scopeStack = null + + /** + * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node + */ + function onFunctionEnter(node) { + if (node.type === 'ArrowFunctionExpression') { + return + } + scopeStack = { + upper: scopeStack, + node, + beforeRouteEnter: beforeRouteEnterFunctions.has( + /** @type {never} */ (node) + ) + } + } + + /** + * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node + */ + function onFunctionExit(node) { + if (scopeStack && scopeStack.node === node) { + scopeStack = scopeStack.upper + } + } + return utils.defineVueVisitor(context, { + onVueObjectEnter(node) { + const beforeRouteEnter = utils.findProperty(node, 'beforeRouteEnter') + if ( + beforeRouteEnter && + beforeRouteEnter.value.type === 'FunctionExpression' + ) { + beforeRouteEnterFunctions.add(beforeRouteEnter.value) + } + }, + ':function': onFunctionEnter, + ':function:exit': onFunctionExit, + ThisExpression(node) { + if (scopeStack && scopeStack.beforeRouteEnter) { + context.report({ + node, + messageId: 'disallow' + }) + } + } + }) + } +} diff --git a/tests/lib/rules/no-this-in-before-route-enter.js b/tests/lib/rules/no-this-in-before-route-enter.js new file mode 100644 index 000000000..1171959ae --- /dev/null +++ b/tests/lib/rules/no-this-in-before-route-enter.js @@ -0,0 +1,190 @@ +/** + * @fileoverview Don't use "this" i a beforeRouteEnter method + * @author Przemyslaw Jan Beigert + */ +'use strict' + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/no-this-in-before-route-enter') +const RuleTester = require('eslint').RuleTester + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const template = (beforeRouteEnter) => ` + + + + +` + +const functionTemplate = (beforeRouteEnter) => ` + + +` + +const ruleTester = new RuleTester({ + parser: require.resolve('vue-eslint-parser'), + parserOptions: { ecmaVersion: 2020, sourceType: 'module' } +}) +ruleTester.run('no-this-in-before-route-enter', rule, { + valid: [ + template(''), + template('const variable = 42;'), + template('someFunction(42)'), + ` + + +