You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
3.4 KiB
114 lines
3.4 KiB
/**
|
|
* @author Yosuke Ota
|
|
* See LICENSE file in root directory for full license.
|
|
*/
|
|
'use strict'
|
|
|
|
const canConvertToVSlotForElement = require('./utils/can-convert-to-v-slot')
|
|
|
|
module.exports = {
|
|
deprecated: '2.6.0',
|
|
supported: '>=2.5.0 <3.0.0',
|
|
/**
|
|
* @param {RuleContext} context
|
|
* @param {object} option
|
|
* @param {boolean} [option.fixToUpgrade]
|
|
* @returns {TemplateListener}
|
|
*/
|
|
createTemplateBodyVisitor(context, { fixToUpgrade } = {}) {
|
|
const sourceCode = context.getSourceCode()
|
|
const tokenStore =
|
|
sourceCode.parserServices.getTemplateBodyTokenStore &&
|
|
sourceCode.parserServices.getTemplateBodyTokenStore()
|
|
|
|
/**
|
|
* Checks whether the given node can convert to the `v-slot`.
|
|
* @param {VStartTag} startTag node of `<element v-slot ... >`
|
|
* @returns {boolean} `true` if the given node can convert to the `v-slot`
|
|
*/
|
|
function canConvertToVSlot(startTag) {
|
|
if (
|
|
!canConvertToVSlotForElement(startTag.parent, sourceCode, tokenStore)
|
|
) {
|
|
return false
|
|
}
|
|
|
|
const slotAttr = startTag.attributes.find(
|
|
(attr) => attr.directive === false && attr.key.name === 'slot'
|
|
)
|
|
if (slotAttr) {
|
|
// if the element have `slot` it can not be converted.
|
|
// Conversion of `slot` is done with `vue/no-deprecated-slot-attribute`.
|
|
return false
|
|
}
|
|
|
|
const vBindSlotAttr = startTag.attributes.find(
|
|
(attr) =>
|
|
attr.directive === true &&
|
|
attr.key.name.name === 'bind' &&
|
|
attr.key.argument &&
|
|
attr.key.argument.type === 'VIdentifier' &&
|
|
attr.key.argument.name === 'slot'
|
|
)
|
|
if (vBindSlotAttr) {
|
|
// if the element have `v-bind:slot` it can not be converted.
|
|
// Conversion of `v-bind:slot` is done with `vue/no-deprecated-slot-attribute`.
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* Convert to `v-slot`.
|
|
* @param {RuleFixer} fixer fixer
|
|
* @param {VDirective} scopeAttr node of `slot-scope`
|
|
* @returns {Fix[]} fix data
|
|
*/
|
|
function fixSlotScopeToVSlot(fixer, scopeAttr) {
|
|
const element = scopeAttr.parent.parent
|
|
const scopeValue =
|
|
scopeAttr && scopeAttr.value
|
|
? `=${sourceCode.getText(scopeAttr.value)}`
|
|
: ''
|
|
|
|
const replaceText = `v-slot${scopeValue}`
|
|
if (element.name === 'template') {
|
|
return [fixer.replaceText(scopeAttr, replaceText)]
|
|
} else {
|
|
const tokenBefore = tokenStore.getTokenBefore(scopeAttr)
|
|
return [
|
|
fixer.removeRange([tokenBefore.range[1], scopeAttr.range[1]]),
|
|
fixer.insertTextBefore(element, `<template ${replaceText}>\n`),
|
|
fixer.insertTextAfter(element, `\n</template>`)
|
|
]
|
|
}
|
|
}
|
|
/**
|
|
* Reports `slot-scope` node
|
|
* @param {VDirective} scopeAttr node of `slot-scope`
|
|
* @returns {void}
|
|
*/
|
|
function reportSlotScope(scopeAttr) {
|
|
context.report({
|
|
node: scopeAttr.key,
|
|
messageId: 'forbiddenSlotScopeAttribute',
|
|
fix(fixer) {
|
|
if (!fixToUpgrade) {
|
|
return null
|
|
}
|
|
// fix to use `v-slot`
|
|
const startTag = scopeAttr.parent
|
|
if (!canConvertToVSlot(startTag)) {
|
|
return null
|
|
}
|
|
return fixSlotScopeToVSlot(fixer, scopeAttr)
|
|
}
|
|
})
|
|
}
|
|
|
|
return {
|
|
"VAttribute[directive=true][key.name.name='slot-scope']": reportSlotScope
|
|
}
|
|
}
|
|
}
|