no-computed-properties-in-data.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  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. * @typedef {import('../utils').VueObjectData} VueObjectData
  12. */
  13. // ------------------------------------------------------------------------------
  14. // Rule Definition
  15. // ------------------------------------------------------------------------------
  16. module.exports = {
  17. meta: {
  18. type: 'problem',
  19. docs: {
  20. description: 'disallow accessing computed properties in `data`.',
  21. categories: ['vue3-essential', 'essential'],
  22. url: 'https://eslint.vuejs.org/rules/no-computed-properties-in-data.html'
  23. },
  24. fixable: null,
  25. schema: [],
  26. messages: {
  27. cannotBeUsed:
  28. 'The computed property cannot be used in `data()` because it is before initialization.'
  29. }
  30. },
  31. /** @param {RuleContext} context */
  32. create(context) {
  33. /** @type {Map<ObjectExpression, {data: FunctionExpression | ArrowFunctionExpression, computedNames:Set<string>}>} */
  34. const contextMap = new Map()
  35. /**
  36. * @typedef {object} ScopeStack
  37. * @property {ScopeStack | null} upper
  38. * @property {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
  39. */
  40. /** @type {ScopeStack | null} */
  41. let scopeStack = null
  42. return utils.compositingVisitors(
  43. {
  44. /**
  45. * @param {FunctionExpression | FunctionDeclaration | ArrowFunctionExpression} node
  46. */
  47. ':function'(node) {
  48. scopeStack = {
  49. upper: scopeStack,
  50. node
  51. }
  52. },
  53. ':function:exit'() {
  54. scopeStack = scopeStack && scopeStack.upper
  55. }
  56. },
  57. utils.defineVueVisitor(context, {
  58. onVueObjectEnter(node) {
  59. const dataProperty = utils.findProperty(node, 'data')
  60. if (
  61. !dataProperty ||
  62. (dataProperty.value.type !== 'FunctionExpression' &&
  63. dataProperty.value.type !== 'ArrowFunctionExpression')
  64. ) {
  65. return
  66. }
  67. const computedNames = new Set()
  68. for (const computed of utils.iterateProperties(
  69. node,
  70. new Set(['computed'])
  71. )) {
  72. computedNames.add(computed.name)
  73. }
  74. contextMap.set(node, { data: dataProperty.value, computedNames })
  75. },
  76. /**
  77. * @param {MemberExpression} node
  78. * @param {VueObjectData} vueData
  79. */
  80. MemberExpression(node, vueData) {
  81. if (!scopeStack || !utils.isThis(node.object, context)) {
  82. return
  83. }
  84. const ctx = contextMap.get(vueData.node)
  85. if (!ctx || ctx.data !== scopeStack.node) {
  86. return
  87. }
  88. const name = utils.getStaticPropertyName(node)
  89. if (!name || !ctx.computedNames.has(name)) {
  90. return
  91. }
  92. context.report({
  93. node,
  94. messageId: 'cannotBeUsed'
  95. })
  96. }
  97. })
  98. )
  99. }
  100. }