123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- /**
- * @author Yosuke Ota
- * See LICENSE file in root directory for full license.
- */
- 'use strict'
- const canConvertToVSlot = require('./utils/can-convert-to-v-slot')
- const casing = require('../../utils/casing')
- module.exports = {
- deprecated: '2.6.0',
- supported: '<3.0.0',
- /** @param {RuleContext} context @returns {TemplateListener} */
- createTemplateBodyVisitor(context) {
- const options = context.options[0] || {}
- /** @type {Set<string>} */
- const ignore = new Set(options.ignore)
- const sourceCode = context.getSourceCode()
- const tokenStore =
- sourceCode.parserServices.getTemplateBodyTokenStore &&
- sourceCode.parserServices.getTemplateBodyTokenStore()
- /**
- * Checks whether the given node can convert to the `v-slot`.
- * @param {VAttribute} slotAttr node of `slot`
- * @returns {boolean} `true` if the given node can convert to the `v-slot`
- */
- function canConvertFromSlotToVSlot(slotAttr) {
- if (!canConvertToVSlot(slotAttr.parent.parent, sourceCode, tokenStore)) {
- return false
- }
- if (!slotAttr.value) {
- return true
- }
- const slotName = slotAttr.value.value
- // If other than alphanumeric, underscore and hyphen characters are included it can not be converted.
- return !/[^\w\-]/u.test(slotName)
- }
- /**
- * Checks whether the given node can convert to the `v-slot`.
- * @param {VDirective} slotAttr node of `v-bind:slot`
- * @returns {boolean} `true` if the given node can convert to the `v-slot`
- */
- function canConvertFromVBindSlotToVSlot(slotAttr) {
- if (!canConvertToVSlot(slotAttr.parent.parent, sourceCode, tokenStore)) {
- return false
- }
- if (!slotAttr.value) {
- return true
- }
- if (!slotAttr.value.expression) {
- // parse error or empty expression
- return false
- }
- return slotAttr.value.expression.type === 'Identifier'
- }
- /**
- * Convert to `v-slot`.
- * @param {RuleFixer} fixer fixer
- * @param {VAttribute|VDirective} slotAttr node of `slot`
- * @param {string | null} slotName name of `slot`
- * @param {boolean} vBind `true` if `slotAttr` is `v-bind:slot`
- * @returns {IterableIterator<Fix>} fix data
- */
- function* fixSlotToVSlot(fixer, slotAttr, slotName, vBind) {
- const startTag = slotAttr.parent
- const scopeAttr = startTag.attributes.find(
- (attr) =>
- attr.directive === true &&
- attr.key.name &&
- (attr.key.name.name === 'slot-scope' ||
- attr.key.name.name === 'scope')
- )
- let nameArgument = ''
- if (slotName) {
- nameArgument = vBind ? `:[${slotName}]` : `:${slotName}`
- }
- const scopeValue =
- scopeAttr && scopeAttr.value
- ? `=${sourceCode.getText(scopeAttr.value)}`
- : ''
- const replaceText = `v-slot${nameArgument}${scopeValue}`
- const element = startTag.parent
- if (element.name === 'template') {
- yield fixer.replaceText(slotAttr || scopeAttr, replaceText)
- if (slotAttr && scopeAttr) {
- yield fixer.remove(scopeAttr)
- }
- } else {
- yield fixer.remove(slotAttr || scopeAttr)
- if (slotAttr && scopeAttr) {
- yield fixer.remove(scopeAttr)
- }
- const vFor = startTag.attributes.find(
- (attr) => attr.directive && attr.key.name.name === 'for'
- )
- const vForText = vFor ? `${sourceCode.getText(vFor)} ` : ''
- if (vFor) {
- yield fixer.remove(vFor)
- }
- yield fixer.insertTextBefore(
- element,
- `<template ${vForText}${replaceText}>\n`
- )
- yield fixer.insertTextAfter(element, `\n</template>`)
- }
- }
- /**
- * Reports `slot` node
- * @param {VAttribute} slotAttr node of `slot`
- * @returns {void}
- */
- function reportSlot(slotAttr) {
- const componentName = slotAttr.parent.parent.rawName
- if (
- ignore.has(componentName) ||
- ignore.has(casing.pascalCase(componentName)) ||
- ignore.has(casing.kebabCase(componentName))
- ) {
- return
- }
- context.report({
- node: slotAttr.key,
- messageId: 'forbiddenSlotAttribute',
- // fix to use `v-slot`
- *fix(fixer) {
- if (!canConvertFromSlotToVSlot(slotAttr)) {
- return
- }
- const slotName = slotAttr.value && slotAttr.value.value
- yield* fixSlotToVSlot(fixer, slotAttr, slotName, false)
- }
- })
- }
- /**
- * Reports `v-bind:slot` node
- * @param {VDirective} slotAttr node of `v-bind:slot`
- * @returns {void}
- */
- function reportVBindSlot(slotAttr) {
- context.report({
- node: slotAttr.key,
- messageId: 'forbiddenSlotAttribute',
- // fix to use `v-slot`
- *fix(fixer) {
- if (!canConvertFromVBindSlotToVSlot(slotAttr)) {
- return
- }
- const slotName =
- slotAttr.value &&
- slotAttr.value.expression &&
- sourceCode.getText(slotAttr.value.expression).trim()
- yield* fixSlotToVSlot(fixer, slotAttr, slotName, true)
- }
- })
- }
- return {
- "VAttribute[directive=false][key.name='slot']": reportSlot,
- "VAttribute[directive=true][key.name.name='bind'][key.argument.name='slot']":
- reportVBindSlot
- }
- }
- }
|