no-unused-vars.js 4.1 KB

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