-
-
Notifications
You must be signed in to change notification settings - Fork 681
Add no-async-in-computed-properties
rule
#72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add no-async-in-computed-properties
rule
#72
Conversation
39016d1
to
d4c4742
Compare
@michalsnik There is one commit there with you as author (i took some of your changes) to align with it 🗡 I cleaned up commits from merges and all tests are passing now 🔢 |
no-async-in-computed-properties
rule
Great @armano2, thank you so much for all the hard work! I'll review it in details later today, but so far it looks really good! :) I'm glad you figured out more cases, these all make perfect sense to me :) |
f60b332
to
4692050
Compare
@@ -0,0 +1,56 @@ | |||
# Check if there are no asynchronous action inside computed properties (no-async-in-computed-properties) | |||
|
|||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd go with simple:
Computed properties should be synchronous. Asynchronous actions inside them may not work as expected and can lead to an unexpected behaviour, that's why you should avoid them.
If you need async computed properties you might want to consider using additional plugin https://github.com/foxbenjaminfox/vue-async-computed
export default { | ||
computed: { | ||
pro () { | ||
setTimeout(() => { }, 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put each method causing errors in separate property
@@ -0,0 +1,178 @@ | |||
/** | |||
* @fileoverview Check if there are no async inside computed properties. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
asynchronous actions
|
||
function isTimedFunction (node) { | ||
return ( | ||
node.callee.type === 'Identifier' && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add additional node.type === 'CallExpression'
. It's good to always check the given node too, and not depend on implementation that is outside this function.
TIMED_FUNCTIONS.indexOf(node.callee.name) !== -1 | ||
) || ( | ||
node.callee.type === 'MemberExpression' && | ||
node.callee.object.name === 'window' && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a missing check: node.callee.object.type === 'Identifier' &&
node.callee.object.name === 'Promise' && | ||
PROMISE_METHODS.indexOf(node.callee.property.name) !== -1 | ||
) || ( // somePromise.ANYTHING() | ||
node.callee.object && isPromise(node.callee.object) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok this recursion is for checking chained promises, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michalsnik there can be expression like
el.foo.bar.catch()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so @armano2, in your example it would call isPromise
with memberExpression
-> el.foo.bar
which will automatically return false. What you're checking is:
something.crazy.then().test()
But I don't see a real case with this kind of node. I don't think you'll ever end up with custom method after promise method so I think we can even remove this last condition. Add more tests if you want, delete it and check if they all pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michalsnik you are completely right :D
thank you for explanation
if (node.generator) { | ||
forbiddenNodes.push({ | ||
node: node, | ||
type: 'yield*' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should call this type simply generator
el.node.loc.end.line <= cp.value.loc.end.line | ||
) { | ||
let message = `Unexpected asynchronous action in "{{name}}" computed property.` | ||
if (el.type === 'await') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see a lot of repetition here. Please think about creating a map with all types and corresponding names. Then return the message without repeating it :)
create, | ||
meta: { | ||
docs: { | ||
description: 'Check if there are no async inside computed properties.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
asynchronous actions
`, | ||
parserOptions: { ecmaVersion: 6, sourceType: 'module' }, | ||
errors: [{ | ||
message: 'Unexpected yield* expression in "foo" computed property.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generator function expression
would be more correct
Thank you for code review i adapted all requests into code except chained promises |
@@ -0,0 +1,64 @@ | |||
# Check if there are no asynchronous action inside computed properties (no-async-in-computed-properties) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actions
const forbiddenNodes = [] | ||
|
||
const messages = { | ||
promise: 'Unexpected asynchronous action in "{{name}}" computed property.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about:
const expressionTypes = {
promise: 'asynchronous action',
await: 'await operator',
yield: 'yield keyword',
generator: 'generator function expression',
async: 'async function declaration',
new: 'Promise object',
timed: 'timed function'
}
and then you could pass message as:
Unexpected {{expressionName}} in "{{propertyName}}" computed property.
with:
data: {
propertyName: cp.key,
expressionName: expressionTypes[cp.key]
}
What do you think? That would prevent you from duplicating the message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was not sure if those messages are correct at all. I'm terrible at describing stuff 🥇
if they are going to stay as they are i have no objections
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michalsnik updated
I put |
ok |
Alright, you can fix conflicts now :) |
597085f
to
c0cdedd
Compare
@michalsnik cleaned up and merged |
I'm not sure this rule should disallow export default {
computed: {
*numbers() {
yield 1
yield 2
yield 3
}
}
} |
@mysticatea i don't like this to but |
@armano2 I see. Hmm, how about disallowing |
@mysticatea yes its true and we Doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
For me yield can be called async-like.
but if you want i can remote it from support and add it as separate rule. |
Hmm, I'm ok with both versions but to be honest, who would ever want to set generator as computed property? It doesn't make sense at all. I think we can even remote this check and maybe add another rule for that but personally I don't think it's even necessary.. What do you think guys? |
@michalsnik yield/generator removed 🍡 |
Okay, one last thing @armano2 let's change the |
@michalsnik but we are checking |
3fafcbb
to
67c1086
Compare
This PR implements rules proposed in #53
DONE:
async
await
new Promise
.then
.catch
.finally
Promise.all()
Promise.race()
Promise.reject()
Promise.resolve()
setTimeout()
setInterval()
setImmediate()
requestAnimationFrame()