‘liusuyi’
2023-10-21 94023628bd9c5e6bf724c37371a19b60d338b291
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/**
 * @fileoverview Don't introduce side effects in computed properties
 * @author Michał Sajnóg
 */
'use strict'
 
const utils = require('../utils')
 
/**
 * @typedef {import('../utils').VueObjectData} VueObjectData
 * @typedef {import('../utils').ComponentComputedProperty} ComponentComputedProperty
 */
 
// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
 
module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'disallow side effects in computed properties',
      categories: ['vue3-essential', 'essential'],
      url:
        'https://eslint.vuejs.org/rules/no-side-effects-in-computed-properties.html'
    },
    fixable: null,
    schema: []
  },
  /** @param {RuleContext} context */
  create(context) {
    /** @type {Map<ObjectExpression, ComponentComputedProperty[]>} */
    const computedPropertiesMap = new Map()
 
    /**
     * @typedef {object} ScopeStack
     * @property {ScopeStack | null} upper
     * @property {BlockStatement | Expression | null} body
     */
    /**
     * @type {ScopeStack | null}
     */
    let scopeStack = null
 
    /** @param {FunctionExpression | ArrowFunctionExpression | FunctionDeclaration} node */
    function onFunctionEnter(node) {
      scopeStack = {
        upper: scopeStack,
        body: node.body
      }
    }
 
    function onFunctionExit() {
      scopeStack = scopeStack && scopeStack.upper
    }
 
    return utils.defineVueVisitor(context, {
      onVueObjectEnter(node) {
        computedPropertiesMap.set(node, utils.getComputedProperties(node))
      },
      ':function': onFunctionEnter,
      ':function:exit': onFunctionExit,
 
      /**
       * @param {(Identifier | ThisExpression) & {parent: MemberExpression}} node
       * @param {VueObjectData} data
       */
      'MemberExpression > :matches(Identifier, ThisExpression)'(
        node,
        { node: vueNode }
      ) {
        if (!scopeStack) {
          return
        }
        const targetBody = scopeStack.body
        const computedProperty = /** @type {ComponentComputedProperty[]} */ (computedPropertiesMap.get(
          vueNode
        )).find((cp) => {
          return (
            cp.value &&
            node.loc.start.line >= cp.value.loc.start.line &&
            node.loc.end.line <= cp.value.loc.end.line &&
            targetBody === cp.value
          )
        })
        if (!computedProperty) {
          return
        }
 
        if (!utils.isThis(node, context)) {
          return
        }
        const mem = node.parent
        if (mem.object !== node) {
          return
        }
 
        const invalid = utils.findMutating(mem)
        if (invalid) {
          context.report({
            node: invalid.node,
            message: 'Unexpected side effect in "{{key}}" computed property.',
            data: { key: computedProperty.key || 'Unknown' }
          })
        }
      }
    })
  }
}