slot-attribute.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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. createTemplateBodyVisitor (context) {
  9. const sourceCode = context.getSourceCode()
  10. /**
  11. * Checks whether the given node can convert to the `v-slot`.
  12. * @param {VAttribute} slotAttr node of `slot`
  13. * @returns {boolean} `true` if the given node can convert to the `v-slot`
  14. */
  15. function canConvertFromSlotToVSlot (slotAttr) {
  16. if (slotAttr.parent.parent.name !== 'template') {
  17. return false
  18. }
  19. if (!slotAttr.value) {
  20. return true
  21. }
  22. const slotName = slotAttr.value.value
  23. // If non-Latin characters are included it can not be converted.
  24. return !/[^a-z]/i.test(slotName)
  25. }
  26. /**
  27. * Checks whether the given node can convert to the `v-slot`.
  28. * @param {VAttribute} slotAttr node of `v-bind:slot`
  29. * @returns {boolean} `true` if the given node can convert to the `v-slot`
  30. */
  31. function canConvertFromVBindSlotToVSlot (slotAttr) {
  32. if (slotAttr.parent.parent.name !== 'template') {
  33. return false
  34. }
  35. if (!slotAttr.value) {
  36. return true
  37. }
  38. if (!slotAttr.value.expression) {
  39. // parse error or empty expression
  40. return false
  41. }
  42. const slotName = sourceCode.getText(slotAttr.value.expression).trim()
  43. // If non-Latin characters are included it can not be converted.
  44. // It does not check the space only because `a>b?c:d` should be rejected.
  45. return !/[^a-z]/i.test(slotName)
  46. }
  47. /**
  48. * Convert to `v-slot`.
  49. * @param {object} fixer fixer
  50. * @param {VAttribute} slotAttr node of `slot`
  51. * @param {string | null} slotName name of `slot`
  52. * @param {boolean} vBind `true` if `slotAttr` is `v-bind:slot`
  53. * @returns {*} fix data
  54. */
  55. function fixSlotToVSlot (fixer, slotAttr, slotName, vBind) {
  56. const element = slotAttr.parent
  57. const scopeAttr = element.attributes
  58. .find(attr => attr.directive === true && attr.key.name && (
  59. attr.key.name.name === 'slot-scope' ||
  60. attr.key.name.name === 'scope'
  61. ))
  62. const nameArgument = slotName ? (vBind ? `:[${slotName}]` : `:${slotName}`) : ''
  63. const scopeValue = scopeAttr && scopeAttr.value
  64. ? `=${sourceCode.getText(scopeAttr.value)}`
  65. : ''
  66. const replaceText = `v-slot${nameArgument}${scopeValue}`
  67. const fixers = [
  68. fixer.replaceText(slotAttr || scopeAttr, replaceText)
  69. ]
  70. if (slotAttr && scopeAttr) {
  71. fixers.push(fixer.remove(scopeAttr))
  72. }
  73. return fixers
  74. }
  75. /**
  76. * Reports `slot` node
  77. * @param {VAttribute} slotAttr node of `slot`
  78. * @returns {void}
  79. */
  80. function reportSlot (slotAttr) {
  81. context.report({
  82. node: slotAttr.key,
  83. messageId: 'forbiddenSlotAttribute',
  84. // fix to use `v-slot`
  85. fix (fixer) {
  86. if (!canConvertFromSlotToVSlot(slotAttr)) {
  87. return null
  88. }
  89. const slotName = slotAttr.value &&
  90. slotAttr.value.value
  91. return fixSlotToVSlot(fixer, slotAttr, slotName, false)
  92. }
  93. })
  94. }
  95. /**
  96. * Reports `v-bind:slot` node
  97. * @param {VAttribute} slotAttr node of `v-bind:slot`
  98. * @returns {void}
  99. */
  100. function reportVBindSlot (slotAttr) {
  101. context.report({
  102. node: slotAttr.key,
  103. messageId: 'forbiddenSlotAttribute',
  104. // fix to use `v-slot`
  105. fix (fixer) {
  106. if (!canConvertFromVBindSlotToVSlot(slotAttr)) {
  107. return null
  108. }
  109. const slotName = slotAttr.value &&
  110. slotAttr.value.expression &&
  111. sourceCode.getText(slotAttr.value.expression).trim()
  112. return fixSlotToVSlot(fixer, slotAttr, slotName, true)
  113. }
  114. })
  115. }
  116. return {
  117. "VAttribute[directive=false][key.name='slot']": reportSlot,
  118. "VAttribute[directive=true][key.name.name='bind'][key.argument.name='slot']": reportVBindSlot
  119. }
  120. }
  121. }