no-deprecated-props-default-this.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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. // ------------------------------------------------------------------------------
  11. // Rule Definition
  12. // ------------------------------------------------------------------------------
  13. module.exports = {
  14. meta: {
  15. type: 'problem',
  16. docs: {
  17. description:
  18. 'disallow deprecated `this` access in props default function (in Vue.js 3.0.0+)',
  19. categories: ['vue3-essential'],
  20. url: 'https://eslint.vuejs.org/rules/no-deprecated-props-default-this.html'
  21. },
  22. fixable: null,
  23. schema: [],
  24. messages: {
  25. deprecated:
  26. 'Props default value factory functions no longer have access to `this`.'
  27. }
  28. },
  29. /** @param {RuleContext} context */
  30. create(context) {
  31. /**
  32. * @typedef {object} ScopeStack
  33. * @property {ScopeStack | null} upper
  34. * @property {FunctionExpression | FunctionDeclaration} node
  35. * @property {boolean} propDefault
  36. */
  37. /** @type {Set<FunctionExpression>} */
  38. const propsDefault = new Set()
  39. /** @type {ScopeStack | null} */
  40. let scopeStack = null
  41. /**
  42. * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
  43. */
  44. function onFunctionEnter(node) {
  45. if (node.type === 'ArrowFunctionExpression') {
  46. return
  47. }
  48. if (scopeStack) {
  49. scopeStack = {
  50. upper: scopeStack,
  51. node,
  52. propDefault: false
  53. }
  54. } else if (node.type === 'FunctionExpression' && propsDefault.has(node)) {
  55. scopeStack = {
  56. upper: scopeStack,
  57. node,
  58. propDefault: true
  59. }
  60. }
  61. }
  62. /**
  63. * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
  64. */
  65. function onFunctionExit(node) {
  66. if (scopeStack && scopeStack.node === node) {
  67. scopeStack = scopeStack.upper
  68. }
  69. }
  70. /**
  71. * @param {Expression|SpreadElement|null} node
  72. */
  73. function isFunctionIdentifier(node) {
  74. return node && node.type === 'Identifier' && node.name === 'Function'
  75. }
  76. /**
  77. * @param {Expression} node
  78. * @returns {boolean}
  79. */
  80. function hasFunctionType(node) {
  81. if (isFunctionIdentifier(node)) {
  82. return true
  83. }
  84. if (node.type === 'ArrayExpression') {
  85. return node.elements.some(isFunctionIdentifier)
  86. }
  87. return false
  88. }
  89. return utils.defineVueVisitor(context, {
  90. onVueObjectEnter(node) {
  91. for (const prop of utils.getComponentPropsFromOptions(node)) {
  92. if (prop.type !== 'object') {
  93. continue
  94. }
  95. if (prop.value.type !== 'ObjectExpression') {
  96. continue
  97. }
  98. const def = utils.findProperty(prop.value, 'default')
  99. if (!def) {
  100. continue
  101. }
  102. const type = utils.findProperty(prop.value, 'type')
  103. if (type && hasFunctionType(type.value)) {
  104. // ignore function type
  105. continue
  106. }
  107. if (def.value.type !== 'FunctionExpression') {
  108. continue
  109. }
  110. propsDefault.add(def.value)
  111. }
  112. },
  113. ':function': onFunctionEnter,
  114. ':function:exit': onFunctionExit,
  115. ThisExpression(node) {
  116. if (scopeStack && scopeStack.propDefault) {
  117. context.report({
  118. node,
  119. messageId: 'deprecated'
  120. })
  121. }
  122. }
  123. })
  124. }
  125. }