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

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