Skip to content

Commit 878dd5c

Browse files
committed
Finalize initial logic for no-side-effects-in-computed-properties rule
1 parent 20deabd commit 878dd5c

File tree

3 files changed

+122
-28
lines changed

3 files changed

+122
-28
lines changed

lib/rules/no-side-effects-in-computed-properties.js

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,32 @@
66

77
const utils = require('../utils')
88

9+
const MUTATION_REGEX = /(this.)((?!(concat|slice)\().)*((push|pop|shift|unshift|reverse|splice|sort|copyWithin|fill)\()/g
10+
11+
function parseMemberExpression (node) {
12+
const members = []
13+
let memberExpression
14+
15+
if (node.type === 'MemberExpression') {
16+
memberExpression = node
17+
18+
while (memberExpression.type === 'MemberExpression') {
19+
if (memberExpression.property.type === 'Identifier') {
20+
members.push(memberExpression.property.name)
21+
}
22+
memberExpression = memberExpression.object
23+
}
24+
25+
if (memberExpression.type === 'ThisExpression') {
26+
members.push('this')
27+
} else if (memberExpression.type === 'Identifier') {
28+
members.push(memberExpression.name)
29+
}
30+
}
31+
32+
return members.reverse()
33+
}
34+
935
function getComputedProperties (componentProperties) {
1036
const computedPropertiesNode = componentProperties
1137
.filter(p =>
@@ -23,26 +49,64 @@ function getComputedProperties (componentProperties) {
2349
let value
2450

2551
if (cp.value.type === 'FunctionExpression') {
26-
value = cp.value.body.body
52+
value = cp.value.body
2753
} else if (cp.value.type === 'ObjectExpression') {
2854
value = cp.value.properties
2955
.filter(p =>
3056
p.key.type === 'Identifier' &&
3157
p.key.name === 'get' &&
3258
p.value.type === 'FunctionExpression'
3359
)
34-
.map(p => p.value.body.body)[0]
60+
.map(p => p.value.body)[0]
3561
}
3662

3763
return { key, value }
3864
})
3965
}
4066

4167
function create (context) {
42-
return utils.executeOnVueComponent(context, (properties) => {
43-
const computedProperties = getComputedProperties(properties)
44-
// to be continued...
45-
})
68+
const forbiddenNodes = []
69+
70+
return Object.assign({},
71+
{
72+
// this.xxx <=|+=|-=>
73+
'AssignmentExpression > MemberExpression' (node) {
74+
if (parseMemberExpression(node)[0] === 'this') {
75+
forbiddenNodes.push(node)
76+
}
77+
},
78+
// this.xxx <++|-->
79+
'UpdateExpression > MemberExpression' (node) {
80+
if (parseMemberExpression(node)[0] === 'this') {
81+
forbiddenNodes.push(node)
82+
}
83+
},
84+
// this.xxx.func()
85+
'CallExpression' (node) {
86+
const code = context.getSourceCode().getText(node)
87+
if (MUTATION_REGEX.test(code) || MUTATION_REGEX.test(code)) {
88+
forbiddenNodes.push(node)
89+
}
90+
}
91+
},
92+
utils.executeOnVueComponent(context, (properties) => {
93+
const computedProperties = getComputedProperties(properties)
94+
95+
computedProperties.forEach(cp => {
96+
forbiddenNodes.forEach(node => {
97+
if (
98+
node.loc.start.line >= cp.value.loc.start.line &&
99+
node.loc.end.line <= cp.value.loc.end.line
100+
) {
101+
context.report({
102+
node: node,
103+
message: `Unexpected side effect in "${cp.key}" computed property.`
104+
})
105+
}
106+
})
107+
})
108+
})
109+
)
46110
}
47111

48112
// ------------------------------------------------------------------------------

lib/utils/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -302,17 +302,17 @@ module.exports = {
302302
const _this = this
303303

304304
return {
305-
ExportDefaultDeclaration (node) {
305+
'ExportDefaultDeclaration:exit' (node) {
306306
// export default {} in .vue || .jsx
307307
if (!_this.isVueComponentFile(node, filePath)) return
308308
cb(node.declaration.properties)
309309
},
310-
CallExpression (node) {
310+
'CallExpression:exit' (node) {
311311
// Vue.component('xxx', {}) || component('xxx', {})
312312
if (!_this.isVueComponent(node)) return
313313
cb(node.arguments.slice(-1)[0].properties)
314314
},
315-
NewExpression (node) {
315+
'NewExpression:exit' (node) {
316316
// new Vue({})
317317
if (!_this.isVueInstance(node)) return
318318
cb(node.arguments[0].properties)

tests/lib/rules/no-side-effects-in-computed-properties.js

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -83,27 +83,63 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
8383
computed: {
8484
test1() {
8585
this.firstName = 'lorem'
86+
asd.qwe.zxc = 'lorem'
8687
return this.firstName + ' ' + this.lastName
8788
},
8889
test2() {
89-
return this.something.reverse()
90+
this.count += 2;
91+
this.count++;
92+
return this.count;
9093
},
9194
test3() {
95+
return this.something.reverse()
96+
},
97+
test4() {
9298
const test = this.another.something.push('example')
9399
return 'something'
94100
},
95-
test4: {
101+
}
102+
})`,
103+
parserOptions,
104+
errors: [{
105+
line: 4,
106+
message: 'Unexpected side effect in "test1" computed property.'
107+
}, {
108+
line: 9,
109+
message: 'Unexpected side effect in "test2" computed property.'
110+
}, {
111+
line: 10,
112+
message: 'Unexpected side effect in "test2" computed property.'
113+
}, {
114+
line: 14,
115+
message: 'Unexpected side effect in "test3" computed property.'
116+
}, {
117+
line: 17,
118+
message: 'Unexpected side effect in "test4" computed property.'
119+
}]
120+
},
121+
{
122+
code: `Vue.component('test', {
123+
computed: {
124+
test1: {
96125
get() {
97126
this.firstName = 'lorem'
98127
return this.firstName + ' ' + this.lastName
99128
}
100129
},
101-
test5: {
130+
test2: {
131+
get() {
132+
this.count += 2;
133+
this.count++;
134+
return this.count;
135+
}
136+
},
137+
test3: {
102138
get() {
103139
return this.something.reverse()
104140
}
105141
},
106-
test6: {
142+
test4: {
107143
get() {
108144
const test = this.another.something.push('example')
109145
return 'something'
@@ -116,26 +152,20 @@ ruleTester.run('no-side-effects-in-computed-properties', rule, {
116152
})`,
117153
parserOptions,
118154
errors: [{
119-
line: 4,
120-
message: 'Unexpected side effect in "test1" computed property'
121-
}, {
122-
line: 8,
123-
message: 'Unexpected side effect in "test2" computed property'
155+
line: 5,
156+
message: 'Unexpected side effect in "test1" computed property.'
124157
}, {
125158
line: 11,
126-
message: 'Unexpected side effect in "test3" computed property'
127-
}, {
128-
line: 15,
129-
message: 'Unexpected side effect in "test4" computed property'
159+
message: 'Unexpected side effect in "test2" computed property.'
130160
}, {
131-
line: 21,
132-
message: 'Unexpected side effect in "test5" computed property'
161+
line: 12,
162+
message: 'Unexpected side effect in "test2" computed property.'
133163
}, {
134-
line: 26,
135-
message: 'Unexpected side effect in "test6" computed property'
164+
line: 18,
165+
message: 'Unexpected side effect in "test3" computed property.'
136166
}, {
137-
line: 31,
138-
message: 'Unexpected side effect in "test7" computed property'
167+
line: 23,
168+
message: 'Unexpected side effect in "test4" computed property.'
139169
}]
140170
}
141171
]

0 commit comments

Comments
 (0)