slot-attribute.js 4.1 KB

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