Skip to content

Commit

Permalink
feat(no-this-in-before-router-enter): create rule (#1506)
Browse files Browse the repository at this point in the history
* feat(no-this-in-before-router-enter): create rule

* Update lib/rules/no-this-in-before-route-enter.js

Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>

* Update docs/rules/no-this-in-before-route-enter.md

Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>

* feat(no-this-in-before-router-enter): create rule

Update lib/rules/no-this-in-before-route-enter.js

Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>

Update docs/rules/no-this-in-before-route-enter.md

Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>

* (feat): update test assertions for no-this-in-before-route-enter rule

* (feat): update docs for no-this-in-before-route-enter

Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>
  • Loading branch information
przemyslawjanpietrzak and ota-meshi committed Jun 9, 2021
1 parent ea6f9f0 commit 76f835a
Show file tree
Hide file tree
Showing 5 changed files with 398 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -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 | |
Expand Down
115 changes: 115 additions & 0 deletions 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: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>

## 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:

<eslint-code-block :rules="{'vue/no-this-in-before-route-enter': ['error']}">

```vue
<script>
export default {
beforeRouteEnter() {
this.method(); // Uncaught TypeError: Cannot read property 'method' of undefined
}
}
</script>
```

</eslint-code-block>

Bad:

<eslint-code-block :rules="{'vue/no-this-in-before-route-enter': ['error']}">

```vue
<script>
export default {
beforeRouteEnter() {
this.attribute = 42;
}
}
</script>
```

</eslint-code-block>

Bad:

<eslint-code-block :rules="{'vue/no-this-in-before-route-enter': ['error']}">

```vue
<script>
export default {
beforeRouteEnter() {
if (this.value === 42) {
}
}
}
</script>
```

</eslint-code-block>


Bad:

<eslint-code-block :rules="{'vue/no-this-in-before-route-enter': ['error']}">

```vue
<script>
export default {
beforeRouteEnter() {
this.attribute = this.method();
}
}
</script>
```

</eslint-code-block>

Good:

<eslint-code-block :rules="{'vue/no-this-in-before-route-enter': ['error']}">

```vue
<script>
export default {
beforeRouteEnter() {
// anything without this
}
}
</script>
```

</eslint-code-block>

### 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)
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -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'),
Expand Down
91 changes: 91 additions & 0 deletions 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<FunctionExpression>} */
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'
})
}
}
})
}
}

0 comments on commit 76f835a

Please sign in to comment.