Skip to content

Commit 4692050

Browse files
committed
Add no-async-in-computed-properties rule
1 parent 4585ae3 commit 4692050

File tree

3 files changed

+569
-0
lines changed

3 files changed

+569
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Check if there are no asynchronous action inside computed properties (no-async-in-computed-properties)
2+
3+
Vue.js has a basic construct which lets computed properties collects dependencies and refreshes them thats why its really important to not use any of asynchronous action inside of them.
4+
5+
## :book: Rule Details
6+
7+
This rule is aimed at preventing asynchronous methods from being called in computed properties.
8+
9+
10+
:-1: Examples of **incorrect** code for this rule:
11+
12+
```js
13+
export default {
14+
computed: {
15+
pro () {
16+
setTimeout(() => { }, 0)
17+
return Promise.all([new Promise((resolve, reject) => {})])
18+
},
19+
foo: async function () {
20+
setInterval(() => { }, 0)
21+
return await someFunc()
22+
},
23+
bar () {
24+
requestAnimationFrame(() => {})
25+
return fetch(url).then(response => {})
26+
},
27+
yiel: function* () {
28+
yield 1
29+
yield* g1()
30+
}
31+
}
32+
}
33+
```
34+
35+
:+1: Examples of **correct** code for this rule:
36+
37+
```js
38+
export default {
39+
computed: {
40+
foo () {
41+
var bar = 0
42+
try {
43+
bar = bar / this.a
44+
} catch (e) {
45+
return 0
46+
} finally {
47+
return bar
48+
}
49+
}
50+
}
51+
}
52+
```
53+
54+
## :wrench: Options
55+
56+
Nothing.
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/**
2+
* @fileoverview Check if there are no async inside computed properties.
3+
* @author Armano
4+
*/
5+
'use strict'
6+
7+
const utils = require('../utils')
8+
9+
const PROMISE_FUNCTIONS = [
10+
'then',
11+
'catch',
12+
'finally'
13+
]
14+
15+
const PROMISE_METHODS = [
16+
'all',
17+
'race',
18+
'reject',
19+
'resolve'
20+
]
21+
22+
const TIMED_FUNCTIONS = [
23+
'setTimeout',
24+
'setInterval',
25+
'setImmediate',
26+
'requestAnimationFrame'
27+
]
28+
29+
function isTimedFunction (node) {
30+
return (
31+
node.callee.type === 'Identifier' &&
32+
TIMED_FUNCTIONS.indexOf(node.callee.name) !== -1
33+
) || (
34+
node.callee.type === 'MemberExpression' &&
35+
node.callee.object.name === 'window' && (
36+
TIMED_FUNCTIONS.indexOf(node.callee.property.name) !== -1 ||
37+
TIMED_FUNCTIONS.indexOf(node.callee.property.value) !== -1
38+
)
39+
) && node.arguments.length
40+
}
41+
42+
function isPromise (node) {
43+
if (node.type === 'CallExpression' && node.callee && node.callee.type === 'MemberExpression') {
44+
return ( // hello.PROMISE_FUNCTION()
45+
node.callee.property &&
46+
PROMISE_FUNCTIONS.indexOf(node.callee.property.name) !== -1
47+
) || ( // Promise.PROMISE_METHOD()
48+
node.callee.object.type === 'Identifier' &&
49+
node.callee.object.name === 'Promise' &&
50+
PROMISE_METHODS.indexOf(node.callee.property.name) !== -1
51+
) || ( // somePromise.ANYTHING()
52+
node.callee.object && isPromise(node.callee.object)
53+
)
54+
}
55+
return false
56+
}
57+
58+
function create (context) {
59+
const forbiddenNodes = []
60+
61+
function onFunctionEnter (node) {
62+
if (node.async) {
63+
forbiddenNodes.push({
64+
node: node,
65+
type: 'async'
66+
})
67+
}
68+
if (node.generator) {
69+
forbiddenNodes.push({
70+
node: node,
71+
type: 'yield*'
72+
})
73+
}
74+
}
75+
76+
return Object.assign({},
77+
{
78+
FunctionDeclaration: onFunctionEnter,
79+
80+
FunctionExpression: onFunctionEnter,
81+
82+
ArrowFunctionExpression: onFunctionEnter,
83+
84+
NewExpression (node) {
85+
if (node.callee.name === 'Promise') {
86+
forbiddenNodes.push({
87+
node: node,
88+
type: 'new'
89+
})
90+
}
91+
},
92+
93+
CallExpression (node) {
94+
if (isPromise(node)) {
95+
forbiddenNodes.push({
96+
node: node,
97+
type: 'promise'
98+
})
99+
}
100+
if (isTimedFunction(node)) {
101+
forbiddenNodes.push({
102+
node: node,
103+
type: 'timed'
104+
})
105+
}
106+
},
107+
108+
YieldExpression (node) {
109+
// await nodes are YieldExpression's with babel-eslint < 7.0.0
110+
forbiddenNodes.push({
111+
node: node,
112+
type: 'yield'
113+
})
114+
},
115+
116+
AwaitExpression (node) {
117+
forbiddenNodes.push({
118+
node: node,
119+
type: 'await'
120+
})
121+
}
122+
},
123+
utils.executeOnVueComponent(context, properties => {
124+
const computedProperties = utils.getComputedProperties(properties)
125+
126+
computedProperties.forEach(cp => {
127+
forbiddenNodes.forEach(el => {
128+
if (
129+
cp.value &&
130+
el.node.loc.start.line >= cp.value.loc.start.line &&
131+
el.node.loc.end.line <= cp.value.loc.end.line
132+
) {
133+
let message = `Unexpected asynchronous action in "{{name}}" computed property.`
134+
if (el.type === 'await') {
135+
message = `Unexpected await operator in "{{name}}" computed property.`
136+
} else if (el.type === 'yield') {
137+
message = `Unexpected yield keyword in "{{name}}" computed property.`
138+
} else if (el.type === 'yield*') {
139+
message = `Unexpected yield* expression in "{{name}}" computed property.`
140+
} else if (el.type === 'async') {
141+
message = `Unexpected async function declaration in "{{name}}" computed property.`
142+
} else if (el.type === 'promise') {
143+
} else if (el.type === 'new') {
144+
message = `Unexpected Promise object in "{{name}}" computed property.`
145+
} else if (el.type === 'timed') {
146+
message = `Unexpected timed function in "{{name}}" computed property.`
147+
}
148+
149+
context.report({
150+
node: el.node,
151+
message,
152+
data: {
153+
name: cp.key
154+
}
155+
})
156+
}
157+
})
158+
})
159+
})
160+
)
161+
}
162+
163+
// ------------------------------------------------------------------------------
164+
// Rule Definition
165+
// ------------------------------------------------------------------------------
166+
167+
module.exports = {
168+
create,
169+
meta: {
170+
docs: {
171+
description: 'Check if there are no async inside computed properties.',
172+
category: 'Best Practices',
173+
recommended: false
174+
},
175+
fixable: null,
176+
schema: []
177+
}
178+
}

0 commit comments

Comments
 (0)