no-multiple-slot-args.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /**
  2. * @author Yosuke Ota
  3. * See LICENSE file in root directory for full license.
  4. */
  5. 'use strict'
  6. // ------------------------------------------------------------------------------
  7. // Requirements
  8. // ------------------------------------------------------------------------------
  9. const utils = require('../utils')
  10. const { findVariable } = require('eslint-utils')
  11. // ------------------------------------------------------------------------------
  12. // Rule Definition
  13. // ------------------------------------------------------------------------------
  14. module.exports = {
  15. meta: {
  16. type: 'problem',
  17. docs: {
  18. description: 'disallow to pass multiple arguments to scoped slots',
  19. categories: ['vue3-recommended', 'recommended'],
  20. url: 'https://eslint.vuejs.org/rules/no-multiple-slot-args.html'
  21. },
  22. fixable: null,
  23. schema: [],
  24. messages: {
  25. unexpected: 'Unexpected multiple arguments.',
  26. unexpectedSpread: 'Unexpected spread argument.'
  27. }
  28. },
  29. /** @param {RuleContext} context */
  30. create(context) {
  31. /**
  32. * Verify the given node
  33. * @param {MemberExpression | Identifier} node The node to verify
  34. */
  35. function verify(node) {
  36. const parent = node.parent
  37. if (
  38. parent.type === 'VariableDeclarator' &&
  39. parent.id.type === 'Identifier'
  40. ) {
  41. // const foo = this.$scopedSlots.foo
  42. verifyReferences(parent.id)
  43. return
  44. }
  45. if (
  46. parent.type === 'AssignmentExpression' &&
  47. parent.right === node &&
  48. parent.left.type === 'Identifier'
  49. ) {
  50. // foo = this.$scopedSlots.foo
  51. verifyReferences(parent.left)
  52. return
  53. }
  54. if (parent.type !== 'CallExpression' || parent.arguments.includes(node)) {
  55. return
  56. }
  57. if (!parent.arguments.length) {
  58. return
  59. }
  60. if (parent.arguments.length > 1) {
  61. context.report({
  62. node: parent.arguments[1],
  63. messageId: 'unexpected'
  64. })
  65. }
  66. if (parent.arguments[0].type === 'SpreadElement') {
  67. context.report({
  68. node: parent.arguments[0],
  69. messageId: 'unexpectedSpread'
  70. })
  71. }
  72. }
  73. /**
  74. * Verify the references of the given node.
  75. * @param {Identifier} node The node to verify
  76. */
  77. function verifyReferences(node) {
  78. const variable = findVariable(context.getScope(), node)
  79. if (!variable) {
  80. return
  81. }
  82. for (const reference of variable.references) {
  83. if (!reference.isRead()) {
  84. continue
  85. }
  86. /** @type {Identifier} */
  87. const id = reference.identifier
  88. verify(id)
  89. }
  90. }
  91. return utils.defineVueVisitor(context, {
  92. /** @param {MemberExpression} node */
  93. MemberExpression(node) {
  94. const object = utils.skipChainExpression(node.object)
  95. if (object.type !== 'MemberExpression') {
  96. return
  97. }
  98. const name = utils.getStaticPropertyName(object)
  99. if (!name || (name !== '$slots' && name !== '$scopedSlots')) {
  100. return
  101. }
  102. if (!utils.isThis(object.object, context)) {
  103. return
  104. }
  105. verify(node)
  106. }
  107. })
  108. }
  109. }