slot-scope-attribute.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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: '>=2.5.0 <3.0.0',
  9. /**
  10. * @param {RuleContext} context
  11. * @param {object} option
  12. * @param {boolean} [option.fixToUpgrade]
  13. * @returns {TemplateListener}
  14. */
  15. createTemplateBodyVisitor(context, { fixToUpgrade } = {}) {
  16. const sourceCode = context.getSourceCode()
  17. /**
  18. * Checks whether the given node can convert to the `v-slot`.
  19. * @param {VStartTag} startTag node of `<element v-slot ... >`
  20. * @returns {boolean} `true` if the given node can convert to the `v-slot`
  21. */
  22. function canConvertToVSlot(startTag) {
  23. if (startTag.parent.name !== 'template') {
  24. return false
  25. }
  26. const slotAttr = startTag.attributes.find(
  27. (attr) => attr.directive === false && attr.key.name === 'slot'
  28. )
  29. if (slotAttr) {
  30. // if the element have `slot` it can not be converted.
  31. // Conversion of `slot` is done with `vue/no-deprecated-slot-attribute`.
  32. return false
  33. }
  34. const vBindSlotAttr = startTag.attributes.find(
  35. (attr) =>
  36. attr.directive === true &&
  37. attr.key.name.name === 'bind' &&
  38. attr.key.argument &&
  39. attr.key.argument.type === 'VIdentifier' &&
  40. attr.key.argument.name === 'slot'
  41. )
  42. if (vBindSlotAttr) {
  43. // if the element have `v-bind:slot` it can not be converted.
  44. // Conversion of `v-bind:slot` is done with `vue/no-deprecated-slot-attribute`.
  45. return false
  46. }
  47. return true
  48. }
  49. /**
  50. * Convert to `v-slot`.
  51. * @param {RuleFixer} fixer fixer
  52. * @param {VDirective} scopeAttr node of `slot-scope`
  53. * @returns {Fix} fix data
  54. */
  55. function fixSlotScopeToVSlot(fixer, scopeAttr) {
  56. const scopeValue =
  57. scopeAttr && scopeAttr.value
  58. ? `=${sourceCode.getText(scopeAttr.value)}`
  59. : ''
  60. const replaceText = `v-slot${scopeValue}`
  61. return fixer.replaceText(scopeAttr, replaceText)
  62. }
  63. /**
  64. * Reports `slot-scope` node
  65. * @param {VDirective} scopeAttr node of `slot-scope`
  66. * @returns {void}
  67. */
  68. function reportSlotScope(scopeAttr) {
  69. context.report({
  70. node: scopeAttr.key,
  71. messageId: 'forbiddenSlotScopeAttribute',
  72. fix(fixer) {
  73. if (!fixToUpgrade) {
  74. return null
  75. }
  76. // fix to use `v-slot`
  77. const startTag = scopeAttr.parent
  78. if (!canConvertToVSlot(startTag)) {
  79. return null
  80. }
  81. return fixSlotScopeToVSlot(fixer, scopeAttr)
  82. }
  83. })
  84. }
  85. return {
  86. "VAttribute[directive=true][key.name.name='slot-scope']": reportSlotScope
  87. }
  88. }
  89. }