Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

⭐️New: Add vue/no-deprecated-slot-attribute rule #839

Merged
merged 5 commits into from Dec 26, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -151,6 +151,7 @@ For example:
| [vue/key-spacing](./key-spacing.md) | enforce consistent spacing between keys and values in object literal properties | :wrench: |
| [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | |
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
| [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: |
| [vue/no-empty-pattern](./no-empty-pattern.md) | disallow empty destructuring patterns | |
| [vue/no-restricted-syntax](./no-restricted-syntax.md) | disallow specified syntax | |
| [vue/object-curly-spacing](./object-curly-spacing.md) | enforce consistent spacing inside braces | :wrench: |
Expand Down
44 changes: 44 additions & 0 deletions docs/rules/no-deprecated-slot-attribute.md
@@ -0,0 +1,44 @@
---
pageClass: rule-details
sidebarDepth: 0
title: vue/no-deprecated-slot-attribute
description: disallow deprecated `slot` attribute (in Vue.js 2.6.0+)
---
# vue/no-deprecated-slot-attribute
> disallow deprecated `slot` attribute (in Vue.js 2.6.0+)

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

## :book: Rule Details

This rule reports deprecated `slot` attribute in Vue.js v2.6.0+.

<eslint-code-block fix :rules="{'vue/no-deprecated-slot-attribute': ['error']}">

```vue
<template>
<ListComponent>
<!-- ✓ GOOD -->
<template v-slot:name>
{{ props.title }}
</template>
</ListComponent>
<ListComponent>
<!-- ✗ BAD -->
<template slot="name">
{{ props.title }}
</template>
</ListComponent>
</template>
```

</eslint-code-block>

## :books: Further reading

- [API - slot](https://vuejs.org/v2/api/#slot-deprecated)

## :mag: Implementation

- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/no-deprecated-slot-attribute.js)
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/no-deprecated-slot-attribute.js)
1 change: 1 addition & 0 deletions lib/index.js
Expand Up @@ -35,6 +35,7 @@ module.exports = {
'no-async-in-computed-properties': require('./rules/no-async-in-computed-properties'),
'no-boolean-default': require('./rules/no-boolean-default'),
'no-confusing-v-for-v-if': require('./rules/no-confusing-v-for-v-if'),
'no-deprecated-slot-attribute': require('./rules/no-deprecated-slot-attribute'),
'no-dupe-keys': require('./rules/no-dupe-keys'),
'no-duplicate-attributes': require('./rules/no-duplicate-attributes'),
'no-empty-pattern': require('./rules/no-empty-pattern'),
Expand Down
28 changes: 28 additions & 0 deletions lib/rules/no-deprecated-slot-attribute.js
@@ -0,0 +1,28 @@
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
'use strict'

const utils = require('../utils')
const slotAttribute = require('./syntaxes/slot-attribute')

module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'disallow deprecated `slot` attribute (in Vue.js 2.6.0+)',
category: undefined,
url: 'https://eslint.vuejs.org/rules/no-deprecated-slot-attribute.html'
},
fixable: 'code',
schema: [],
messages: {
forbiddenSlotAttribute: '`slot` attributes are deprecated.'
}
},
create (context) {
const templateBodyVisitor = slotAttribute.createTemplateBodyVisitor(context)
return utils.defineTemplateBodyVisitor(context, templateBodyVisitor)
}
}
60 changes: 60 additions & 0 deletions lib/rules/syntaxes/slot-attribute.js
@@ -0,0 +1,60 @@
/**
* @author Yosuke Ota
* See LICENSE file in root directory for full license.
*/
'use strict'
module.exports = {
deprecated: '2.6.0',
createTemplateBodyVisitor (context) {
const sourceCode = context.getSourceCode()
/**
* Convert to `v-slot`.
* @param {object} fixer fixer
* @param {VAttribute | null} slotAttr node of `slot`
* @param {VAttribute | null} scopeAttr node of `scope`
* @returns {*} fix data
*/
function fixSlotToVSlot (fixer, slotAttr, scopeAttr) {
const nameArgument = slotAttr && slotAttr.value && slotAttr && slotAttr.value.value
? `:${slotAttr.value.value}`
mysticatea marked this conversation as resolved.
Show resolved Hide resolved
: ''
const scopeValue = scopeAttr && scopeAttr.value
? `=${sourceCode.getText(scopeAttr.value)}`
: ''

const replaceText = `v-slot${nameArgument}${scopeValue}`
const fixers = [
fixer.replaceText(slotAttr || scopeAttr, replaceText)
]
if (slotAttr && scopeAttr) {
fixers.push(fixer.remove(scopeAttr))
}
return fixers
}
/**
* Reports `slot` node
* @param {VAttribute} slotAttr node of `slot`
* @returns {void}
*/
function reportSlot (slotAttr) {
context.report({
node: slotAttr.key,
messageId: 'forbiddenSlotAttribute',
// fix to use `v-slot`
fix (fixer) {
const element = slotAttr.parent
const scopeAttr = element.attributes
.find(attr => attr.directive === true && attr.key.name && (
attr.key.name.name === 'slot-scope' ||
attr.key.name.name === 'scope'
))
return fixSlotToVSlot(fixer, slotAttr, scopeAttr)
}
})
}

return {
"VAttribute[directive=false][key.name='slot']": reportSlot
mysticatea marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
127 changes: 127 additions & 0 deletions tests/lib/rules/no-deprecated-slot-attribute.js
@@ -0,0 +1,127 @@
'use strict'

const RuleTester = require('eslint').RuleTester
const rule = require('../../../lib/rules/no-deprecated-slot-attribute.js')

const tester = new RuleTester({
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 2015
}
})

tester.run('no-deprecated-slot-attribute', rule, {
valid: [
`<template>
<LinkList>
<a v-slot:name />
</LinkList>
</template>`,
`<template>
<LinkList>
<a #name />
</LinkList>
</template>`,
`<template>
<LinkList>
<a v-slot="{a}" />
</LinkList>
</template>`,
`<template>
<LinkList>
<a #default="{a}" />
</LinkList>
</template>`,
`<template>
<LinkList>
<a />
</LinkList>
</template>`
],
invalid: [
{
code: `
<template>
<LinkList>
<a slot />
</LinkList>
</template>`,
output: `
<template>
<LinkList>
<a v-slot />
</LinkList>
</template>`,
errors: [
{
message: '`slot` attributes are deprecated.',
line: 4
}
]
},
{
code: `
<template>
<LinkList>
<a slot="name" />
</LinkList>
</template>`,
output: `
<template>
<LinkList>
<a v-slot:name />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, sorry, I had overlooked this in the previous review.

In some cases, slot and v-slot should be in different places.

  • v-slot must be on a <template> element which is a direct child of a custom element.
  • v-slot for the default slot can be on a custom element directly if there are no named slots.

So in this case, the <a> element must be wrapped by a <template> element.

In the example of nested default slots, it will be moved to the parent element.

Therefore, autofix may be tough to implement... 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late reply.
Thank you for pointing this out.

Sorry, I did not understand v-slot correctly.
I will change it so that it will only autofix if slot is attached to <template>.

</LinkList>
</template>`,
errors: [
{
message: '`slot` attributes are deprecated.',
line: 4
}
]
},
{
code: `
<template>
<LinkList>
<a slot="name" disabled slot-scope="{a}"/>
</LinkList>
</template>`,
output: `
<template>
<LinkList>
<a v-slot:name="{a}" disabled />
</LinkList>
</template>`,
errors: [
{
message: '`slot` attributes are deprecated.',
line: 4
}
]
},
{
code: `
<template>
<LinkList>
<template slot="name" scope="{a}">
<a />
</template>
</LinkList>
</template>`,
output: `
<template>
<LinkList>
<template v-slot:name="{a}" >
<a />
</template>
</LinkList>
</template>`,
errors: [
{
message: '`slot` attributes are deprecated.',
line: 4
}
]
}
]
})