Skip to content

Commit 47ade60

Browse files
authored
Add vue/no-deprecated-props-default-this rule (#1302)
* Add `vue/no-deprecated-props-default-this` rule * update comments
1 parent 1acb37d commit 47ade60

File tree

6 files changed

+367
-0
lines changed

6 files changed

+367
-0
lines changed

Diff for: docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
5050
| [vue/no-deprecated-functional-template](./no-deprecated-functional-template.md) | disallow using deprecated the `functional` template (in Vue.js 3.0.0+) | |
5151
| [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+) | |
5252
| [vue/no-deprecated-inline-template](./no-deprecated-inline-template.md) | disallow using deprecated `inline-template` attribute (in Vue.js 3.0.0+) | |
53+
| [vue/no-deprecated-props-default-this](./no-deprecated-props-default-this.md) | disallow props default function `this` access | |
5354
| [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: |
5455
| [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: |
5556
| [vue/no-deprecated-slot-scope-attribute](./no-deprecated-slot-scope-attribute.md) | disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) | :wrench: |

Diff for: docs/rules/no-deprecated-props-default-this.md

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-deprecated-props-default-this
5+
description: disallow props default function `this` access
6+
---
7+
# vue/no-deprecated-props-default-this
8+
> disallow props default function `this` access
9+
10+
- :gear: This rule is included in all of `"plugin:vue/vue3-essential"`, `"plugin:vue/vue3-strongly-recommended"` and `"plugin:vue/vue3-recommended"`.
11+
12+
## :book: Rule Details
13+
14+
This rule reports the use of `this` within the props default value factory functions.
15+
In Vue.js 3.0.0+, props default value factory functions no longer have access to `this`.
16+
17+
See [Migration Guide - Props Default Function `this` Access](https://v3.vuejs.org/guide/migration/props-default-this.html) for more details.
18+
19+
<eslint-code-block :rules="{'vue/no-deprecated-props-default-this': ['error']}">
20+
21+
```vue
22+
<script>
23+
export default {
24+
props: {
25+
a: String,
26+
b: {
27+
default () {
28+
/* ✗ BAD */
29+
return this.a
30+
}
31+
}
32+
}
33+
}
34+
</script>
35+
```
36+
37+
</eslint-code-block>
38+
39+
<eslint-code-block :rules="{'vue/no-deprecated-props-default-this': ['error']}">
40+
41+
```vue
42+
<script>
43+
export default {
44+
props: {
45+
a: String,
46+
b: {
47+
default (props) {
48+
/* ✓ GOOD */
49+
return props.a
50+
}
51+
}
52+
}
53+
}
54+
</script>
55+
```
56+
57+
</eslint-code-block>
58+
59+
## :wrench: Options
60+
61+
Nothing.
62+
63+
## :books: Further Reading
64+
65+
- [Migration Guide - Props Default Function `this` Access](https://v3.vuejs.org/guide/migration/props-default-this.html)
66+
67+
## :mag: Implementation
68+
69+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-props-default-this.js)
70+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-props-default-this.js)

Diff for: lib/configs/vue3-essential.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module.exports = {
1818
'vue/no-deprecated-functional-template': 'error',
1919
'vue/no-deprecated-html-element-is': 'error',
2020
'vue/no-deprecated-inline-template': 'error',
21+
'vue/no-deprecated-props-default-this': 'error',
2122
'vue/no-deprecated-scope-attribute': 'error',
2223
'vue/no-deprecated-slot-attribute': 'error',
2324
'vue/no-deprecated-slot-scope-attribute': 'error',

Diff for: lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ module.exports = {
5959
'no-deprecated-functional-template': require('./rules/no-deprecated-functional-template'),
6060
'no-deprecated-html-element-is': require('./rules/no-deprecated-html-element-is'),
6161
'no-deprecated-inline-template': require('./rules/no-deprecated-inline-template'),
62+
'no-deprecated-props-default-this': require('./rules/no-deprecated-props-default-this'),
6263
'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'),
6364
'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'),
6465
'no-deprecated-slot-scope-attribute': require('./rules/no-deprecated-slot-scope-attribute'),

Diff for: lib/rules/no-deprecated-props-default-this.js

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
13+
// ------------------------------------------------------------------------------
14+
// Rule Definition
15+
// ------------------------------------------------------------------------------
16+
17+
module.exports = {
18+
meta: {
19+
type: 'problem',
20+
docs: {
21+
description: 'disallow props default function `this` access',
22+
categories: ['vue3-essential'],
23+
url:
24+
'https://eslint.vuejs.org/rules/no-deprecated-props-default-this.html'
25+
},
26+
fixable: null,
27+
schema: [],
28+
messages: {
29+
deprecated:
30+
'Props default value factory functions no longer have access to `this`.'
31+
}
32+
},
33+
/** @param {RuleContext} context */
34+
create(context) {
35+
/**
36+
* @typedef {object} ScopeStack
37+
* @property {ScopeStack | null} upper
38+
* @property {FunctionExpression | FunctionDeclaration} node
39+
* @property {boolean} propDefault
40+
*/
41+
/** @type {Set<FunctionExpression>} */
42+
const propsDefault = new Set()
43+
/** @type {ScopeStack | null} */
44+
let scopeStack = null
45+
46+
/**
47+
* @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
48+
*/
49+
function onFunctionEnter(node) {
50+
if (node.type === 'ArrowFunctionExpression') {
51+
return
52+
}
53+
if (scopeStack) {
54+
scopeStack = {
55+
upper: scopeStack,
56+
node,
57+
propDefault: false
58+
}
59+
} else if (node.type === 'FunctionExpression' && propsDefault.has(node)) {
60+
scopeStack = {
61+
upper: scopeStack,
62+
node,
63+
propDefault: true
64+
}
65+
}
66+
}
67+
68+
/**
69+
* @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
70+
*/
71+
function onFunctionExit(node) {
72+
if (scopeStack && scopeStack.node === node) {
73+
scopeStack = scopeStack.upper
74+
}
75+
}
76+
return utils.defineVueVisitor(context, {
77+
onVueObjectEnter(node) {
78+
for (const prop of utils.getComponentProps(node)) {
79+
if (prop.type !== 'object') {
80+
continue
81+
}
82+
if (prop.value.type !== 'ObjectExpression') {
83+
continue
84+
}
85+
const def = utils.findProperty(prop.value, 'default')
86+
if (!def) {
87+
continue
88+
}
89+
if (def.value.type !== 'FunctionExpression') {
90+
continue
91+
}
92+
propsDefault.add(def.value)
93+
}
94+
},
95+
':function': onFunctionEnter,
96+
':function:exit': onFunctionExit,
97+
ThisExpression(node) {
98+
if (scopeStack && scopeStack.propDefault) {
99+
context.report({
100+
node,
101+
messageId: 'deprecated'
102+
})
103+
}
104+
}
105+
})
106+
}
107+
}

Diff for: tests/lib/rules/no-deprecated-props-default-this.js

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const rule = require('../../../lib/rules/no-deprecated-props-default-this')
12+
const RuleTester = require('eslint').RuleTester
13+
14+
// ------------------------------------------------------------------------------
15+
// Tests
16+
// ------------------------------------------------------------------------------
17+
18+
const ruleTester = new RuleTester({
19+
parser: require.resolve('vue-eslint-parser'),
20+
parserOptions: { ecmaVersion: 2020, sourceType: 'module' }
21+
})
22+
23+
ruleTester.run('no-deprecated-props-default-this', rule, {
24+
valid: [
25+
{
26+
filename: 'test.vue',
27+
code: `
28+
<template><div /></template>
29+
<script>
30+
export default {
31+
props: {
32+
a: String,
33+
b: {
34+
default (props) {
35+
return props.a
36+
}
37+
}
38+
}
39+
}
40+
</script>
41+
`
42+
},
43+
{
44+
filename: 'test.vue',
45+
code: `
46+
<template><div /></template>
47+
<script>
48+
export default {
49+
props: {
50+
a: String,
51+
b: {
52+
default: () => {
53+
return this.a
54+
}
55+
}
56+
}
57+
}
58+
</script>
59+
`
60+
},
61+
{
62+
filename: 'test.vue',
63+
code: `
64+
<template><div /></template>
65+
<script>
66+
export default {
67+
props: {
68+
a: String,
69+
b: {
70+
default () {
71+
return function () {
72+
return this.a
73+
}
74+
}
75+
}
76+
}
77+
}
78+
</script>
79+
`,
80+
errors: [{}, {}]
81+
},
82+
{
83+
filename: 'test.vue',
84+
code: `
85+
<template><div /></template>
86+
<script>
87+
const Foo = {
88+
props: {
89+
a: String,
90+
b: {
91+
default () {
92+
return this.a
93+
}
94+
}
95+
}
96+
}
97+
</script>
98+
`
99+
}
100+
],
101+
102+
invalid: [
103+
{
104+
filename: 'test.vue',
105+
code: `
106+
<template><div /></template>
107+
<script>
108+
export default {
109+
props: {
110+
a: String,
111+
b: {
112+
default () {
113+
return this.a
114+
}
115+
}
116+
}
117+
}
118+
</script>
119+
`,
120+
errors: [
121+
{
122+
message:
123+
'Props default value factory functions no longer have access to `this`.',
124+
line: 9,
125+
column: 24,
126+
endLine: 9,
127+
endColumn: 28
128+
}
129+
]
130+
},
131+
{
132+
filename: 'test.vue',
133+
code: `
134+
<template><div /></template>
135+
<script>
136+
export default {
137+
props: {
138+
a: String,
139+
b: {
140+
default () {
141+
return () => this.a
142+
}
143+
}
144+
}
145+
}
146+
</script>
147+
`,
148+
errors: [
149+
{
150+
message:
151+
'Props default value factory functions no longer have access to `this`.',
152+
line: 9,
153+
column: 30,
154+
endLine: 9,
155+
endColumn: 34
156+
}
157+
]
158+
},
159+
{
160+
filename: 'test.vue',
161+
code: `
162+
<template><div /></template>
163+
<script>
164+
export default {
165+
props: {
166+
a: String,
167+
b: {
168+
default () {
169+
return this.a
170+
}
171+
},
172+
c: {
173+
default () {
174+
return this.a
175+
}
176+
}
177+
}
178+
}
179+
</script>
180+
`,
181+
errors: [
182+
'Props default value factory functions no longer have access to `this`.',
183+
'Props default value factory functions no longer have access to `this`.'
184+
]
185+
}
186+
]
187+
})

0 commit comments

Comments
 (0)