Skip to content

Commit 774a169

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

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,30 @@
66

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

9+
function parseMemberExpression (node) {
10+
const members = []
11+
let memberExpression
12+
13+
if (node.type === 'MemberExpression') {
14+
memberExpression = node
15+
16+
while (memberExpression.type === 'MemberExpression') {
17+
if (memberExpression.property.type === 'Identifier') {
18+
members.push(memberExpression.property.name)
19+
}
20+
memberExpression = memberExpression.object
21+
}
22+
23+
if (memberExpression.type === 'ThisExpression') {
24+
members.push('this')
25+
} else if (memberExpression.type === 'Identifier') {
26+
members.push(memberExpression.name)
27+
}
28+
}
29+
30+
return members.reverse()
31+
}
32+
933
function getComputedProperties (componentProperties) {
1034
const computedPropertiesNode = componentProperties
1135
.filter(p =>
@@ -23,26 +47,66 @@ function getComputedProperties (componentProperties) {
2347
let value
2448

2549
if (cp.value.type === 'FunctionExpression') {
26-
value = cp.value.body.body
50+
value = cp.value.body
2751
} else if (cp.value.type === 'ObjectExpression') {
2852
value = cp.value.properties
2953
.filter(p =>
3054
p.key.type === 'Identifier' &&
3155
p.key.name === 'get' &&
3256
p.value.type === 'FunctionExpression'
3357
)
34-
.map(p => p.value.body.body)[0]
58+
.map(p => p.value.body)[0]
3559
}
3660

3761
return { key, value }
3862
})
3963
}
4064

4165
function create (context) {
42-
return utils.executeOnVueComponent(context, (properties) => {
43-
const computedProperties = getComputedProperties(properties)
44-
// to be continued...
45-
})
66+
const forbiddenNodes = []
67+
68+
return Object.assign({},
69+
{
70+
// this.xxx <=|+=|-=>
71+
'AssignmentExpression > MemberExpression' (node) {
72+
if (parseMemberExpression(node)[0] === 'this') {
73+
forbiddenNodes.push(node)
74+
}
75+
},
76+
// this.xxx <++|-->
77+
'UpdateExpression > MemberExpression' (node) {
78+
if (parseMemberExpression(node)[0] === 'this') {
79+
forbiddenNodes.push(node)
80+
}
81+
},
82+
// this.xxx.func()
83+
'CallExpression' (node) {
84+
const code = context.getSourceCode().getText(node)
85+
const MUTATION_REGEX = /(this.)((?!(concat|slice)\().)*((push|pop|shift|unshift|reverse|splice|sort|copyWithin|fill)\()/g
86+
87+
if (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)