Skip to content

Commit

Permalink
Add vue/no-deprecated-props-default-this rule (#1302)
Browse files Browse the repository at this point in the history
* Add `vue/no-deprecated-props-default-this` rule

* update comments
  • Loading branch information
ota-meshi committed Sep 23, 2020
1 parent 1acb37d commit 47ade60
Show file tree
Hide file tree
Showing 6 changed files with 367 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -50,6 +50,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
| [vue/no-deprecated-functional-template](./no-deprecated-functional-template.md) | disallow using deprecated the `functional` template (in Vue.js 3.0.0+) | |
| [vue/no-deprecated-html-element-is](./no-deprecated-html-element-is.md) | disallow using deprecated the `is` attribute on HTML elements (in Vue.js 3.0.0+) | |
| [vue/no-deprecated-inline-template](./no-deprecated-inline-template.md) | disallow using deprecated `inline-template` attribute (in Vue.js 3.0.0+) | |
| [vue/no-deprecated-props-default-this](./no-deprecated-props-default-this.md) | disallow props default function `this` access | |
| [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: |
| [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: |
| [vue/no-deprecated-slot-scope-attribute](./no-deprecated-slot-scope-attribute.md) | disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) | :wrench: |
Expand Down
70 changes: 70 additions & 0 deletions docs/rules/no-deprecated-props-default-this.md
@@ -0,0 +1,70 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-deprecated-props-default-this
description: disallow props default function `this` access
---
# vue/no-deprecated-props-default-this
> disallow props default function `this` access
- :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 the use of `this` within the props default value factory functions.
In Vue.js 3.0.0+, props default value factory functions no longer have access to `this`.

See [Migration Guide - Props Default Function `this` Access](https://v3.vuejs.org/guide/migration/props-default-this.html) for more details.

<eslint-code-block :rules="{'vue/no-deprecated-props-default-this': ['error']}">

```vue
<script>
export default {
props: {
a: String,
b: {
default () {
/* ✗ BAD */
return this.a
}
}
}
}
</script>
```

</eslint-code-block>

<eslint-code-block :rules="{'vue/no-deprecated-props-default-this': ['error']}">

```vue
<script>
export default {
props: {
a: String,
b: {
default (props) {
/* ✓ GOOD */
return props.a
}
}
}
}
</script>
```

</eslint-code-block>

## :wrench: Options

Nothing.

## :books: Further Reading

- [Migration Guide - Props Default Function `this` Access](https://v3.vuejs.org/guide/migration/props-default-this.html)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-props-default-this.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-props-default-this.js)
1 change: 1 addition & 0 deletions lib/configs/vue3-essential.js
Expand Up @@ -18,6 +18,7 @@ module.exports = {
'vue/no-deprecated-functional-template': 'error',
'vue/no-deprecated-html-element-is': 'error',
'vue/no-deprecated-inline-template': 'error',
'vue/no-deprecated-props-default-this': 'error',
'vue/no-deprecated-scope-attribute': 'error',
'vue/no-deprecated-slot-attribute': 'error',
'vue/no-deprecated-slot-scope-attribute': 'error',
Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -59,6 +59,7 @@ module.exports = {
'no-deprecated-functional-template': require('./rules/no-deprecated-functional-template'),
'no-deprecated-html-element-is': require('./rules/no-deprecated-html-element-is'),
'no-deprecated-inline-template': require('./rules/no-deprecated-inline-template'),
'no-deprecated-props-default-this': require('./rules/no-deprecated-props-default-this'),
'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'),
'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'),
'no-deprecated-slot-scope-attribute': require('./rules/no-deprecated-slot-scope-attribute'),
Expand Down
107 changes: 107 additions & 0 deletions lib/rules/no-deprecated-props-default-this.js
@@ -0,0 +1,107 @@
/**
* @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 props default function `this` access',
categories: ['vue3-essential'],
url:
'https://eslint.vuejs.org/rules/no-deprecated-props-default-this.html'
},
fixable: null,
schema: [],
messages: {
deprecated:
'Props default value factory functions no longer have access to `this`.'
}
},
/** @param {RuleContext} context */
create(context) {
/**
* @typedef {object} ScopeStack
* @property {ScopeStack | null} upper
* @property {FunctionExpression | FunctionDeclaration} node
* @property {boolean} propDefault
*/
/** @type {Set<FunctionExpression>} */
const propsDefault = new Set()
/** @type {ScopeStack | null} */
let scopeStack = null

/**
* @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
*/
function onFunctionEnter(node) {
if (node.type === 'ArrowFunctionExpression') {
return
}
if (scopeStack) {
scopeStack = {
upper: scopeStack,
node,
propDefault: false
}
} else if (node.type === 'FunctionExpression' && propsDefault.has(node)) {
scopeStack = {
upper: scopeStack,
node,
propDefault: true
}
}
}

/**
* @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
*/
function onFunctionExit(node) {
if (scopeStack && scopeStack.node === node) {
scopeStack = scopeStack.upper
}
}
return utils.defineVueVisitor(context, {
onVueObjectEnter(node) {
for (const prop of utils.getComponentProps(node)) {
if (prop.type !== 'object') {
continue
}
if (prop.value.type !== 'ObjectExpression') {
continue
}
const def = utils.findProperty(prop.value, 'default')
if (!def) {
continue
}
if (def.value.type !== 'FunctionExpression') {
continue
}
propsDefault.add(def.value)
}
},
':function': onFunctionEnter,
':function:exit': onFunctionExit,
ThisExpression(node) {
if (scopeStack && scopeStack.propDefault) {
context.report({
node,
messageId: 'deprecated'
})
}
}
})
}
}
187 changes: 187 additions & 0 deletions tests/lib/rules/no-deprecated-props-default-this.js
@@ -0,0 +1,187 @@
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
'use strict'

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const rule = require('../../../lib/rules/no-deprecated-props-default-this')
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-props-default-this', rule, {
valid: [
{
filename: 'test.vue',
code: `
<template><div /></template>
<script>
export default {
props: {
a: String,
b: {
default (props) {
return props.a
}
}
}
}
</script>
`
},
{
filename: 'test.vue',
code: `
<template><div /></template>
<script>
export default {
props: {
a: String,
b: {
default: () => {
return this.a
}
}
}
}
</script>
`
},
{
filename: 'test.vue',
code: `
<template><div /></template>
<script>
export default {
props: {
a: String,
b: {
default () {
return function () {
return this.a
}
}
}
}
}
</script>
`,
errors: [{}, {}]
},
{
filename: 'test.vue',
code: `
<template><div /></template>
<script>
const Foo = {
props: {
a: String,
b: {
default () {
return this.a
}
}
}
}
</script>
`
}
],

invalid: [
{
filename: 'test.vue',
code: `
<template><div /></template>
<script>
export default {
props: {
a: String,
b: {
default () {
return this.a
}
}
}
}
</script>
`,
errors: [
{
message:
'Props default value factory functions no longer have access to `this`.',
line: 9,
column: 24,
endLine: 9,
endColumn: 28
}
]
},
{
filename: 'test.vue',
code: `
<template><div /></template>
<script>
export default {
props: {
a: String,
b: {
default () {
return () => this.a
}
}
}
}
</script>
`,
errors: [
{
message:
'Props default value factory functions no longer have access to `this`.',
line: 9,
column: 30,
endLine: 9,
endColumn: 34
}
]
},
{
filename: 'test.vue',
code: `
<template><div /></template>
<script>
export default {
props: {
a: String,
b: {
default () {
return this.a
}
},
c: {
default () {
return this.a
}
}
}
}
</script>
`,
errors: [
'Props default value factory functions no longer have access to `this`.',
'Props default value factory functions no longer have access to `this`.'
]
}
]
})

0 comments on commit 47ade60

Please sign in to comment.