no-ref-as-operand.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /**
  2. * @author Yosuke Ota
  3. * See LICENSE file in root directory for full license.
  4. */
  5. 'use strict'
  6. const { extractRefObjectReferences } = require('../utils/ref-object-references')
  7. const utils = require('../utils')
  8. /**
  9. * @typedef {import('../utils/ref-object-references').RefObjectReferences} RefObjectReferences
  10. * @typedef {import('../utils/ref-object-references').RefObjectReferenceForIdentifier} RefObjectReferenceForIdentifier
  11. */
  12. /**
  13. * Checks whether the given identifier reference has been initialized with a ref object.
  14. * @param {RefObjectReferenceForIdentifier | null} data
  15. * @returns {data is RefObjectReferenceForIdentifier}
  16. */
  17. function isRefInit(data) {
  18. const init = data && data.variableDeclarator && data.variableDeclarator.init
  19. if (!init) {
  20. return false
  21. }
  22. return data.defineChain.includes(/** @type {any} */ (init))
  23. }
  24. module.exports = {
  25. meta: {
  26. type: 'suggestion',
  27. docs: {
  28. description:
  29. 'disallow use of value wrapped by `ref()` (Composition API) as an operand',
  30. categories: ['vue3-essential', 'vue2-essential'],
  31. url: 'https://eslint.vuejs.org/rules/no-ref-as-operand.html'
  32. },
  33. fixable: 'code',
  34. schema: [],
  35. messages: {
  36. requireDotValue:
  37. 'Must use `.value` to read or write the value wrapped by `{{method}}()`.'
  38. }
  39. },
  40. /** @param {RuleContext} context */
  41. create(context) {
  42. /** @type {RefObjectReferences} */
  43. let refReferences
  44. /**
  45. * @param {Identifier} node
  46. */
  47. function reportIfRefWrapped(node) {
  48. const data = refReferences.get(node)
  49. if (!isRefInit(data)) {
  50. return
  51. }
  52. context.report({
  53. node,
  54. messageId: 'requireDotValue',
  55. data: {
  56. method: data.method
  57. },
  58. fix(fixer) {
  59. return fixer.insertTextAfter(node, '.value')
  60. }
  61. })
  62. }
  63. return {
  64. Program() {
  65. refReferences = extractRefObjectReferences(context)
  66. },
  67. // if (refValue)
  68. /** @param {Identifier} node */
  69. 'IfStatement>Identifier'(node) {
  70. reportIfRefWrapped(node)
  71. },
  72. // switch (refValue)
  73. /** @param {Identifier} node */
  74. 'SwitchStatement>Identifier'(node) {
  75. reportIfRefWrapped(node)
  76. },
  77. // -refValue, +refValue, !refValue, ~refValue, typeof refValue
  78. /** @param {Identifier} node */
  79. 'UnaryExpression>Identifier'(node) {
  80. reportIfRefWrapped(node)
  81. },
  82. // refValue++, refValue--
  83. /** @param {Identifier} node */
  84. 'UpdateExpression>Identifier'(node) {
  85. reportIfRefWrapped(node)
  86. },
  87. // refValue+1, refValue-1
  88. /** @param {Identifier} node */
  89. 'BinaryExpression>Identifier'(node) {
  90. reportIfRefWrapped(node)
  91. },
  92. // refValue+=1, refValue-=1, foo+=refValue, foo-=refValue
  93. /** @param {Identifier & {parent: AssignmentExpression}} node */
  94. 'AssignmentExpression>Identifier'(node) {
  95. if (node.parent.operator === '=' && node.parent.left !== node) {
  96. return
  97. }
  98. reportIfRefWrapped(node)
  99. },
  100. // refValue || other, refValue && other. ignore: other || refValue
  101. /** @param {Identifier & {parent: LogicalExpression}} node */
  102. 'LogicalExpression>Identifier'(node) {
  103. if (node.parent.left !== node) {
  104. return
  105. }
  106. // Report only constants.
  107. const data = refReferences.get(node)
  108. if (
  109. !data ||
  110. !data.variableDeclaration ||
  111. data.variableDeclaration.kind !== 'const'
  112. ) {
  113. return
  114. }
  115. reportIfRefWrapped(node)
  116. },
  117. // refValue ? x : y
  118. /** @param {Identifier & {parent: ConditionalExpression}} node */
  119. 'ConditionalExpression>Identifier'(node) {
  120. if (node.parent.test !== node) {
  121. return
  122. }
  123. reportIfRefWrapped(node)
  124. },
  125. // `${refValue}`
  126. /** @param {Identifier} node */
  127. 'TemplateLiteral>Identifier'(node) {
  128. reportIfRefWrapped(node)
  129. },
  130. // refValue.x
  131. /** @param {Identifier & {parent: MemberExpression}} node */
  132. 'MemberExpression>Identifier'(node) {
  133. if (node.parent.object !== node) {
  134. return
  135. }
  136. const name = utils.getStaticPropertyName(node.parent)
  137. if (
  138. name === 'value' ||
  139. name == null ||
  140. // WritableComputedRef
  141. name === 'effect'
  142. ) {
  143. return
  144. }
  145. reportIfRefWrapped(node)
  146. }
  147. }
  148. }
  149. }