no-unused-vars.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /**
  2. * @fileoverview disallow unused variable definitions of v-for directives or scope attributes.
  3. * @author 薛定谔的猫<hh_2013@foxmail.com>
  4. */
  5. 'use strict'
  6. const utils = require('../utils')
  7. /**
  8. * @typedef {VVariable['kind']} VariableKind
  9. */
  10. /**
  11. * Groups variables by directive kind.
  12. * @param {VElement} node The element node
  13. * @returns { { [kind in VariableKind]?: VVariable[] } } The variables of grouped by directive kind.
  14. */
  15. function groupingVariables(node) {
  16. /** @type { { [kind in VariableKind]?: VVariable[] } } */
  17. const result = {}
  18. for (const variable of node.variables) {
  19. const vars = result[variable.kind] || (result[variable.kind] = [])
  20. vars.push(variable)
  21. }
  22. return result
  23. }
  24. /**
  25. * Checks if the given variable was defined by destructuring.
  26. * @param {VVariable} variable the given variable to check
  27. * @returns {boolean} `true` if the given variable was defined by destructuring.
  28. */
  29. function isDestructuringVar(variable) {
  30. const node = variable.id
  31. /** @type {ASTNode | null} */
  32. let parent = node.parent
  33. while (parent) {
  34. if (
  35. parent.type === 'VForExpression' ||
  36. parent.type === 'VSlotScopeExpression'
  37. ) {
  38. return false
  39. }
  40. if (parent.type === 'Property' || parent.type === 'ArrayPattern') {
  41. return true
  42. }
  43. parent = parent.parent
  44. }
  45. return false
  46. }
  47. module.exports = {
  48. meta: {
  49. hasSuggestions: true,
  50. type: 'suggestion',
  51. docs: {
  52. description:
  53. 'disallow unused variable definitions of v-for directives or scope attributes',
  54. categories: ['vue3-essential', 'essential'],
  55. url: 'https://eslint.vuejs.org/rules/no-unused-vars.html'
  56. },
  57. fixable: null,
  58. schema: [
  59. {
  60. type: 'object',
  61. properties: {
  62. ignorePattern: {
  63. type: 'string'
  64. }
  65. },
  66. additionalProperties: false
  67. }
  68. ]
  69. },
  70. /** @param {RuleContext} context */
  71. create(context) {
  72. const option = context.options[0] || {}
  73. const ignorePattern = option.ignorePattern
  74. /** @type {RegExp | null} */
  75. let ignoreRegEx = null
  76. if (ignorePattern) {
  77. ignoreRegEx = new RegExp(ignorePattern, 'u')
  78. }
  79. return utils.defineTemplateBodyVisitor(context, {
  80. /**
  81. * @param {VElement} node
  82. */
  83. VElement(node) {
  84. const vars = groupingVariables(node)
  85. for (const variables of Object.values(vars)) {
  86. if (!variables) {
  87. continue
  88. }
  89. let hasAfterUsed = false
  90. for (let i = variables.length - 1; i >= 0; i--) {
  91. const variable = variables[i]
  92. if (variable.references.length > 0) {
  93. hasAfterUsed = true
  94. continue
  95. }
  96. if (ignoreRegEx != null && ignoreRegEx.test(variable.id.name)) {
  97. continue
  98. }
  99. if (hasAfterUsed && !isDestructuringVar(variable)) {
  100. // If a variable after the variable is used, it will be skipped.
  101. continue
  102. }
  103. context.report({
  104. node: variable.id,
  105. loc: variable.id.loc,
  106. message: `'{{name}}' is defined but never used.`,
  107. data: {
  108. name: variable.id.name
  109. },
  110. suggest:
  111. ignorePattern === '^_'
  112. ? [
  113. {
  114. desc: `Replace the ${variable.id.name} with _${variable.id.name}`,
  115. fix(fixer) {
  116. return fixer.replaceText(
  117. variable.id,
  118. `_${variable.id.name}`
  119. )
  120. }
  121. }
  122. ]
  123. : []
  124. })
  125. }
  126. }
  127. }
  128. })
  129. }
  130. }