66
77const utils = require ( '../utils' )
88
9+ /**
10+ * @typedef {import('vue-eslint-parser').AST.ESLintObjectExpression } ObjectExpression
11+ * @typedef {import('vue-eslint-parser').AST.ESLintMemberExpression } MemberExpression
12+ * @typedef {import('../utils').ComponentComputedProperty } ComponentComputedProperty
13+ */
14+
915// ------------------------------------------------------------------------------
1016// Rule Definition
1117// ------------------------------------------------------------------------------
@@ -23,6 +29,7 @@ module.exports = {
2329 } ,
2430
2531 create ( context ) {
32+ /** @type {Map<ObjectExpression, ComponentComputedProperty[]> } */
2633 const computedPropertiesMap = new Map ( )
2734 let scopeStack = { upper : null , body : null }
2835
@@ -34,53 +41,43 @@ module.exports = {
3441 scopeStack = scopeStack . upper
3542 }
3643
37- function verify ( node , targetBody , computedProperties ) {
38- computedProperties . forEach ( cp => {
39- if (
40- cp . value &&
41- node . loc . start . line >= cp . value . loc . start . line &&
42- node . loc . end . line <= cp . value . loc . end . line &&
43- targetBody === cp . value
44- ) {
45- context . report ( {
46- node : node ,
47- message : 'Unexpected side effect in "{{key}}" computed property.' ,
48- data : { key : cp . key }
49- } )
50- }
51- } )
52- }
53-
5444 return utils . defineVueVisitor ( context , {
55- ObjectExpression ( node , { node : vueNode } ) {
56- if ( node !== vueNode ) {
57- return
58- }
45+ onVueObjectEnter ( node ) {
5946 computedPropertiesMap . set ( node , utils . getComputedProperties ( node ) )
6047 } ,
6148 ':function' : onFunctionEnter ,
6249 ':function:exit' : onFunctionExit ,
6350
64- // this.xxx <=|+=|-=>
65- 'AssignmentExpression' ( node , { node : vueNode } ) {
66- if ( node . left . type !== 'MemberExpression' ) return
67- if ( utils . parseMemberExpression ( node . left ) [ 0 ] === 'this' ) {
68- verify ( node , scopeStack . body , computedPropertiesMap . get ( vueNode ) )
51+ 'MemberExpression > :matches(Identifier, ThisExpression)' ( node , { node : vueNode } ) {
52+ const targetBody = scopeStack . body
53+ const computedProperty = computedPropertiesMap . get ( vueNode ) . find ( cp => {
54+ return (
55+ cp . value &&
56+ node . loc . start . line >= cp . value . loc . start . line &&
57+ node . loc . end . line <= cp . value . loc . end . line &&
58+ targetBody === cp . value
59+ )
60+ } )
61+ if ( ! computedProperty ) {
62+ return
6963 }
70- } ,
71- // this.xxx <++|-->
72- 'UpdateExpression > MemberExpression' ( node , { node : vueNode } ) {
73- if ( utils . parseMemberExpression ( node ) [ 0 ] === 'this' ) {
74- verify ( node , scopeStack . body , computedPropertiesMap . get ( vueNode ) )
64+
65+ if ( ! utils . isThis ( node , context ) ) {
66+ return
67+ }
68+ /** @type {MemberExpression } */
69+ const mem = node . parent
70+ if ( mem . object !== node ) {
71+ return
7572 }
76- } ,
77- // this.xxx.func()
78- 'CallExpression' ( node , { node : vueNode } ) {
79- const code = utils . parseMemberOrCallExpression ( node )
80- const MUTATION_REGEX = / ( t h i s .) ( (? ! ( c o n c a t | s l i c e | m a p | f i l t e r ) \( ) .) [ ^ \) ] * ( ( p u s h | p o p | s h i f t | u n s h i f t | r e v e r s e | s p l i c e | s o r t | c o p y W i t h i n | f i l l ) \( ) / g
8173
82- if ( MUTATION_REGEX . test ( code ) ) {
83- verify ( node , scopeStack . body , computedPropertiesMap . get ( vueNode ) )
74+ const invalid = utils . findMutating ( mem )
75+ if ( invalid ) {
76+ context . report ( {
77+ node : invalid . node ,
78+ message : 'Unexpected side effect in "{{key}}" computed property.' ,
79+ data : { key : computedProperty . key }
80+ } )
8481 }
8582 }
8683 }
0 commit comments