Skip to content

Commit a5fd31e

Browse files
authored
⭐️New: Add vue/no-deprecated-slot-scope-attribute rule (#840)
* ⭐️New: Add vue/no-deprecated-slot-scope-attribute rule * Changed not to autofix, if it becomes an invalid attribute * Changed to not autofix, if if the element have `v-bind:slot` * Change to autofix
1 parent 3306b03 commit a5fd31e

6 files changed

+301
-0
lines changed

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ For example:
155155
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
156156
| [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: |
157157
| [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: |
158+
| [vue/no-deprecated-slot-scope-attribute](./no-deprecated-slot-scope-attribute.md) | disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+) | :wrench: |
158159
| [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns | |
159160
| [vue/no-reserved-component-names](./no-reserved-component-names.md) | disallow the use of reserved names in component definitions | |
160161
| [vue/no-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax | |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/no-deprecated-slot-scope-attribute
5+
description: disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+)
6+
---
7+
# vue/no-deprecated-slot-scope-attribute
8+
> disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+)
9+
10+
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
11+
12+
## :book: Rule Details
13+
14+
This rule reports deprecated `slot-scope` attribute in Vue.js v2.6.0+.
15+
16+
<eslint-code-block fix :rules="{'vue/no-deprecated-slot-scope-attribute': ['error']}">
17+
18+
```vue
19+
<template>
20+
<ListComponent>
21+
<!-- ✓ GOOD -->
22+
<template v-slot="props">
23+
{{ props.title }}
24+
</template>
25+
</ListComponent>
26+
<ListComponent>
27+
<!-- ✗ BAD -->
28+
<template slot-scope="props">
29+
{{ props.title }}
30+
</template>
31+
</ListComponent>
32+
</template>
33+
```
34+
35+
</eslint-code-block>
36+
37+
## :books: Further reading
38+
39+
- [API - slot-scope](https://vuejs.org/v2/api/#slot-scope-deprecated)
40+
41+
## :mag: Implementation
42+
43+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-slot-scope-attribute.js)
44+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-slot-scope-attribute.js)

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ module.exports = {
3939
'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),
4040
'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'),
4141
'no-deprecated-scope-attribute': require('./rules/no-deprecated-scope-attribute'),
42+
'no-deprecated-slot-scope-attribute': require('./rules/no-deprecated-slot-scope-attribute'),
4243
'no-dupe-keys': require('./rules/no-dupe-keys'),
4344
'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
4445
'no-empty-pattern': require('./rules/no-empty-pattern'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
const slotScopeAttribute = require('./syntaxes/slot-scope-attribute')
9+
10+
module.exports = {
11+
meta: {
12+
type: 'suggestion',
13+
docs: {
14+
description: 'disallow deprecated `slot-scope` attribute (in Vue.js 2.6.0+)',
15+
category: undefined,
16+
url: 'https://eslint.vuejs.org/rules/no-deprecated-slot-scope-attribute.html'
17+
},
18+
fixable: 'code',
19+
schema: [],
20+
messages: {
21+
forbiddenSlotScopeAttribute: '`slot-scope` are deprecated.'
22+
}
23+
},
24+
create (context) {
25+
const templateBodyVisitor = slotScopeAttribute.createTemplateBodyVisitor(context, { fixToUpgrade: true })
26+
return utils.defineTemplateBodyVisitor(context, templateBodyVisitor)
27+
}
28+
}
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* @author Yosuke Ota
3+
* See LICENSE file in root directory for full license.
4+
*/
5+
'use strict'
6+
module.exports = {
7+
deprecated: '2.6.0',
8+
supported: '2.5.0',
9+
createTemplateBodyVisitor (context, { fixToUpgrade } = {}) {
10+
const sourceCode = context.getSourceCode()
11+
12+
/**
13+
* Checks whether the given node can convert to the `v-slot`.
14+
* @param {VStartTag} startTag node of `<element v-slot ... >`
15+
* @returns {boolean} `true` if the given node can convert to the `v-slot`
16+
*/
17+
function canConvertToVSlot (startTag) {
18+
if (startTag.parent.name !== 'template') {
19+
return false
20+
}
21+
22+
const slotAttr = startTag.attributes
23+
.find(attr => attr.directive === false && attr.key.name === 'slot')
24+
if (slotAttr) {
25+
// if the element have `slot` it can not be converted.
26+
// Conversion of `slot` is done with `vue/no-deprecated-slot-attribute`.
27+
return false
28+
}
29+
30+
const vBindSlotAttr = startTag.attributes
31+
.find(attr =>
32+
attr.directive === true &&
33+
attr.key.name.name === 'bind' &&
34+
attr.key.argument &&
35+
attr.key.argument.name === 'slot')
36+
if (vBindSlotAttr) {
37+
// if the element have `v-bind:slot` it can not be converted.
38+
// Conversion of `v-bind:slot` is done with `vue/no-deprecated-slot-attribute`.
39+
return false
40+
}
41+
return true
42+
}
43+
44+
/**
45+
* Convert to `v-slot`.
46+
* @param {object} fixer fixer
47+
* @param {VAttribute | null} scopeAttr node of `slot-scope`
48+
* @returns {*} fix data
49+
*/
50+
function fixSlotScopeToVSlot (fixer, scopeAttr) {
51+
const scopeValue = scopeAttr && scopeAttr.value
52+
? `=${sourceCode.getText(scopeAttr.value)}`
53+
: ''
54+
55+
const replaceText = `v-slot${scopeValue}`
56+
return fixer.replaceText(scopeAttr, replaceText)
57+
}
58+
/**
59+
* Reports `slot-scope` node
60+
* @param {VAttribute} scopeAttr node of `slot-scope`
61+
* @returns {void}
62+
*/
63+
function reportSlotScope (scopeAttr) {
64+
context.report({
65+
node: scopeAttr.key,
66+
messageId: 'forbiddenSlotScopeAttribute',
67+
fix: fixToUpgrade
68+
// fix to use `v-slot`
69+
? (fixer) => {
70+
const startTag = scopeAttr.parent
71+
if (!canConvertToVSlot(startTag)) {
72+
return null
73+
}
74+
return fixSlotScopeToVSlot(fixer, scopeAttr)
75+
}
76+
: null
77+
})
78+
}
79+
80+
return {
81+
"VAttribute[directive=true][key.name.name='slot-scope']": reportSlotScope
82+
}
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
'use strict'
2+
3+
const RuleTester = require('eslint').RuleTester
4+
const rule = require('../../../lib/rules/no-deprecated-slot-scope-attribute')
5+
6+
const tester = new RuleTester({
7+
parser: 'vue-eslint-parser',
8+
parserOptions: {
9+
ecmaVersion: 2015
10+
}
11+
})
12+
13+
tester.run('no-deprecated-slot-scope-attribute', rule, {
14+
valid: [
15+
`<template>
16+
<LinkList>
17+
<template v-slot:name><a /></template>
18+
</LinkList>
19+
</template>`,
20+
`<template>
21+
<LinkList>
22+
<template #name><a /></template>
23+
</LinkList>
24+
</template>`,
25+
`<template>
26+
<LinkList>
27+
<template v-slot="{a}"><a /></template>
28+
</LinkList>
29+
</template>`,
30+
`<template>
31+
<LinkList v-slot="{a}">
32+
<a />
33+
</LinkList>
34+
</template>`,
35+
`<template>
36+
<LinkList>
37+
<template #default="{a}"><a /></template>
38+
</LinkList>
39+
</template>`,
40+
`<template>
41+
<LinkList>
42+
<a slot="name" />
43+
</LinkList>
44+
</template>`,
45+
`<template>
46+
<LinkList>
47+
<template><a /></template>
48+
</LinkList>
49+
</template>`,
50+
`<template>
51+
<LinkList>
52+
<a />
53+
</LinkList>`
54+
],
55+
invalid: [
56+
{
57+
code: `
58+
<template>
59+
<LinkList>
60+
<template slot-scope="{a}"><a /></template>
61+
</LinkList>
62+
</template>`,
63+
output: `
64+
<template>
65+
<LinkList>
66+
<template v-slot="{a}"><a /></template>
67+
</LinkList>
68+
</template>`,
69+
errors: [
70+
{
71+
message: '`slot-scope` are deprecated.',
72+
line: 4
73+
}
74+
]
75+
},
76+
{
77+
code: `
78+
<template>
79+
<LinkList>
80+
<template slot-scope><a /></template>
81+
</LinkList>
82+
</template>`,
83+
output: `
84+
<template>
85+
<LinkList>
86+
<template v-slot><a /></template>
87+
</LinkList>
88+
</template>`,
89+
errors: [
90+
{
91+
message: '`slot-scope` are deprecated.',
92+
line: 4
93+
}
94+
]
95+
},
96+
// cannot fix
97+
{
98+
code: `
99+
<template>
100+
<LinkList>
101+
<a slot-scope="{a}" />
102+
</LinkList>
103+
</template>`,
104+
output: null,
105+
errors: [
106+
{
107+
message: '`slot-scope` are deprecated.',
108+
line: 4
109+
}
110+
]
111+
},
112+
{
113+
code: `
114+
<template>
115+
<LinkList>
116+
<template slot-scope="{a}" slot="foo"><a /></template>
117+
</LinkList>
118+
</template>`,
119+
output: null,
120+
errors: [
121+
{
122+
message: '`slot-scope` are deprecated.',
123+
line: 4
124+
}
125+
]
126+
},
127+
{
128+
code: `
129+
<template>
130+
<LinkList>
131+
<template slot-scope="{a}" :slot="arg"><a /></template>
132+
</LinkList>
133+
</template>`,
134+
output: null,
135+
errors: [
136+
{
137+
message: '`slot-scope` are deprecated.',
138+
line: 4
139+
}
140+
]
141+
}
142+
]
143+
})

0 commit comments

Comments
 (0)