-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
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
feat: support async plugin's pre/post #16862
feat: support async plugin's pre/post #16862
Conversation
await wait(50); | ||
this.file.ast.program.body[0].value = "success" |
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.
checking that promise is actually awaited
Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/58233 |
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.
This looks good -- can you also ad a test with an async pre
, which transforms the AST in such a way that makes sure that it's being awaited before running the visitors?
@nicolo-ribaudo there is one, it reassigns variable in the closure. If pre would not be awaited, visitor will print "failure" instead of "success". I also have question regarding typings. Should we update typings of pre/post to reflect that they are supporting Async result? |
I tried changing them in Where we do function chainMaybeAsync<Args extends any[]>(
a: undefined | ((...args: Args) => void | Promise<void>),
b: undefined | ((...args: Args) => void | Promise<void>),
) {
if (!a) return b;
if (!b) return a;
return function (this: unknown, ...args: Args) {
const res = a.apply(this, args);
if (res != null && typeof (res as Promise<void>).then === "function") {
return (res as Promise<void>).then(() => b.apply(this, args));
}
return b.apply(this, args);
};
} You can add a test with a plugin A that has plugin B in its |
9637819
to
295a9d7
Compare
@nicolo-ribaudo i changed types for pre/post and fixed chaining. Also added two more tests for pre/post with inheritance. The pipelines are failing for unknown reason. Could you check what's wrong? |
3049bd1
to
a448bbf
Compare
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.
It just needed a rebase.
I left a minor comment, but this looks good now!
return { | ||
inherits: pluginC, | ||
async post() { | ||
await wait(50); |
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.
Could you make pluginC async and keep pluginB sync? So we have async-sync-async
, testing both combinations :)
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.
Done, test for pre
uses sync-async-async and test for post
uses async-sync-async.
a448bbf
to
3601be9
Compare
@nicolo-ribaudo what about passing pre?: (this: S, file: File, ctx: {isAsync: boolean}) => void | Promise<void>;
post?: (this: S, file: File, ctx: {isAsync: boolean}) => void | Promise<void>; Or might be as a property on a PluginPass object? I'm afraid it would be a bit tricky to implement no-breaking logic for the plugins without that flag. |
Whops sorry, I missed the question. I would prefer to expose it on |
3601be9
to
166c7ff
Compare
@nicolo-ribaudo i've added a new property |
/** | ||
* The working directory that Babel's programmatic options are loaded | ||
* relative to. | ||
*/ | ||
cwd: string; | ||
|
||
// The absolute path of the file being compiled. | ||
/** The absolute path of the file being compiled. */ | ||
filename: string | void; |
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.
With /**
style of comment it's a jsdoc description which could be used as quick documentation in IDE.
file: File, | ||
key?: string | null, | ||
options?: Options, | ||
isAsync?: boolean, |
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've added isAsync
as optional argument to the last position, so it shouldn't be a breaking change to anyone who uses PluginPass
constructor directly. However, i would like to add it as required argument after the file
. Let me know if that change is acceptable.
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.
We only export PluginPass
as a type and not as a constructor (see packages/babel-core/src/index.ts
), so it should be fine to add it as a required property here.
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.
Actually, you can just remove the ?
s from the other arguments since they are always always passed. It's probably a leftover from when we converted the repo from Flow to TS.
@@ -308,7 +338,7 @@ describe("asynchronicity", () => { | |||
}); | |||
|
|||
describe("misc", () => { | |||
it("unknown preset in config file does not trigget unhandledRejection if caught", async () => { | |||
it("unknown preset in config file does not trigger unhandledRejection if caught", async () => { |
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.
fixed a typo
fn.apply(this, args); | ||
function chainMaybeAsync<Args extends any[], R extends void | Promise<void>>( | ||
a: undefined | ((...args: Args) => R), | ||
b: undefined | ((...args: Args) => R), |
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.
Here we assume a
and b
share the same return type, however, it is also possible that a
is sync while b
is async. I suggest we remove the R
constraint and type them as (...args: Args) => void | Promise<void>
. We can probably get rid of the final type cast as well.
For situations where both a
and b
are sync, we can add a type overload so that the chained function is still sync.
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 the current typing is fine:
- if
a
andb
are sync/undefined,R
isvoid
and thuschainMaybeAsync
returns(...args: Args) => void
. - if
a
is sync/undefined andb
is async/undefined,R
isvoid | Promise<void>
and thuschainMaybeAsync
returns(...args: Args) => void | Promise<void>
. Note that thevoid |
is correct, because ifb
is undefined thenchainMaybeAsync
returnsa
which returnsvoid
. - if
a
andb
are async/undefined,R
isPromise<void>
and thuschainMaybeAsync
returns(...args: Args) => Promise<void>
.
Also note that this function is only called when a
and b
are either both sync, or both void | Promise<void>
. We don't use it in mixed-type cases.
Also, a problem with a type overload is that every function is assignable to () => void
, so it would also cover the case returning a promise:
https://www.typescriptlang.org/play/?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXzAAsotUBZKATwCMQBBAZ0vQB46YBzB+EADwxCpg3KKkoBtALoA+ABQAoePCgAueGlCJSIYPAA+8WbIB0p2FzXsuASngBeafABuOLMGsAaRfGpqNILVQdfUMTM04GSwjbB2dXdy9rNTDjc0j4KwYYxxc3AG55UEhYBBR0bDwCYlIKGnomVkyefkFhZTEpOW9VdSEA7V0DI1NUiKibexz4kIAFGBwAWywGEBZc4GlPb19ezQGQ4fCLDOjJuLdZ+aWVtfjNxOSRtPGss-XLxeXV9ekC+ULwNA4PAICAMEgAIzJbLnQbwOafG4-P6g8Fwbh2KokchUWiMZhgWSICEeSHWArwAD0lIAegB+IA
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.
On the mixed-type cases, for example, if inherits.pre
is a sync function and plugin.pre
is an async function, then the merged pre
will be an async function.
Like you said, every function is assignable to () => void
, so the type checking can't help very much here. I am fine with leaving it as-is.
Thanks for your reviews and helps on this PR. |
Enable support of async pre/post functions in plugins + update tests. As discussed here #16860
I'm also wondering of adding a
isAsync
flag for pre/post functions arguments so plugins could adjust it's own logic based on that. However i don't know how exactly to shape this signature, my proposition is:Let me know what you think, maybe there is a better place for that.