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
Transform for the smart pipeline operator proposal #9179
Transform for the smart pipeline operator proposal #9179
Conversation
Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/9842/ |
Couple quick comments:
|
I think this is done by line 21 in babel-plugin-syntax-pipeline-operator/src/index.js but I'm not sure this is what you're talking about.
Good idea. I guess a first step on this direction would be having separate visitors and switching on the parseOpts proposal.
Yeah, they do without a problem. Maybe parser tests are different somehow? |
Thanks! |
Yeah, for example I put the two different decorators transformers in two different files (https://github.com/nicolo-ribaudo/babel/blob/33ac9a246c4b4e45f8f1b63122f4ba502f3cf32e/packages/babel-plugin-proposal-decorators/src/index.js) |
Nice. I'll definitely steal that idea. |
Just confirming: This version of the transformer uses IIFEs, so it doesn’t yet support |
Yeah . You're right, @js-choi |
Yes, great: thanks for your hard work, @thiagoarrais. @thiagoarrais and I have already talked about this, but to explain for other people’s benefit: Because this current transform uses IIFEs, it won’t work with operations that are dependent on the current function: namely In other words, even though this: async function () { return [g(), x |> f |> # + 1]; } …is the same as this: async function () { return [g(), ($ => $ + 1)(f(x))]; } …adding an async function () { return [g(), x |> f |> await # |> # + 1]; } …because that isn’t the same as this, which is an error: async function () { return [g(), ($ => $ + 1)(($ => await $)(f(x)))]; } So this IIFE-using transform is a preliminary approach. Eventually, we’ll have to write a function that finds expressions within statements and then recursively pulls them out into constant declarations. Such a function would convert code like this: async function () { return [g(), x |> f |> await # |> # + 1]; } …to code like this: async function () {
const $1 = g();
const $2 = x;
const $3 = f($2);
const $4 = await $3;
const $5 = $4 + 1;
return [$1, $5];
} I suspect that, in fact, the current F#-plus- In fact, the But even though this version of the transform might be preliminary, it’s still a great start! Thank you, @thiagoarrais. |
|
||
const minimalVisitor = { | ||
BinaryExpression: { | ||
exit(path) { |
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.
Why do we need to run this on exit?
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.
Some of the calls in this function need Expression
nodes and the smart proposal child nodes aren't expressions, but can be converted by the smart proposal visitor. Running on exit guarantees that the child nodes are already converted.
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 feels a bit like the logic is getting mixed between them. This might be a bit pendantic, but I'd extract the shared logic to a separate module and run them at different times.
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 could move this function to its own module and bind it to entry
(or the default BinaryExpression
) on minimal and to exit
on smart. But I'm not really sure about that. It feels like splitting hairs.
Plus: running on exit works great with the flattening PR that I've got in the works.
packages/babel-plugin-proposal-pipeline-operator/src/smartVisitor.js
Outdated
Show resolved
Hide resolved
This comment has been minimized.
This comment has been minimized.
); | ||
}, | ||
}, | ||
visitor: visitorsPerProposal[options.proposal], |
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 should add validation for the proposal
optoin.
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 syntax plugin already does this.
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.
Do we need to add it here too?
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.
In the decorators plugin I put the check both in the syntax and in the transform plugin, so that the error message uses the correct plugin name. It is a nice-to-have but it isn't really needed.
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.
Is it possible for that extra check by the transform plugin to be observable by a test? All the fixtures in all the transform plugins that I’ve read seem to integration-test both parsing and transforming.
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, you can test the error message by adding "error": "Expected error message"
in the options.json file of the fixture.
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.
Well, even then, adding such a test fixture still wouldn’t distinguish between the parser throwing the error versus the transform plugin throwing the error, 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.
Yeah that's right. But usually parser error messages are different from plugin error messages. That is the important part; not where the error actually comes from.
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 find the syntax plugin message (below) is clear enough in this case.
The pipeline operator plugin requires a 'proposal' option.
'proposal' must be one of: minimal, smart. More details:
https://babeljs.io/docs/en/next/babel-plugin-proposal-pipeline-operator
Maybe it should be moved to the transform plugin. What about holding it off for a later PR?
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.
Oh I didn't noticed that the link alreay points to the transform plugin. It is ok as-is 👍
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.
Thank you! Don't know why I initially thought that this PR would have been much bigger; it looks good to me 🙂
04d662f
to
7cef23b
Compare
Nice! I'm done here for now. Please let me know if you need anything else. |
Damn, I need to hurry up! |
@thiagoarrais: Great work, again. Let me know on whether you plan to tackle #9178 ( @mAAdhaTTah: No need to rush! We still don’t have |
7cef23b
to
1c2221f
Compare
Since, #9216 landed, I've rebased to the latest master and added a check for yield. |
Bumping this. Let me know if you need anything else, @nicolo-ribaudo. |
It looks good to me. I didn't merge it because we have a policy of two ✔️ from core team members, but reviews by @mAAdhaTTah and @js-choi would probably be equally valuable on this topic. |
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.
Nice job! Happy to see this moving forward. Let some questions / suggestions.
const visitorsPerProposal = { | ||
minimal: minimalVisitor, | ||
smart: smartVisitor, | ||
}; |
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.
Hoist to module root?
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.
Good idea. Wil do it.
@@ -0,0 +1,5 @@ | |||
const result = -2.2 // -2.2 | |||
|> Math.floor // -3 | |||
|> (() => Math.pow(#, 5)) // () => -243 |
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.
@js-choi Question on the spec behavior: if we didn't use a topic token here, this arrow would get executed?
-2.2
|> (x => Math.pow(x, 5))
This is also valid in Smart. Are we checking whether the token appears in the arrow body?
|
||
const minimalVisitor = { | ||
BinaryExpression: { | ||
exit(path) { |
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 feels a bit like the logic is getting mixed between them. This might be a bit pendantic, but I'd extract the shared logic to a separate module and run them at different times.
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.
LGTM (I don't know the spec well tho), thanks for the PR!
1c2221f
to
93dcf97
Compare
Released in 7.3! Thanks everyone https://twitter.com/NicoloRibaudo/status/1087473662696017922?s=20 |
Thank you everyone! I'll start work on flattening expressions (async/await) next. |
This implements a first draft for the smart pipeline proposal. It is based on IIFE and does not support async/await inside the pipelines yet. That will need to be added by a subsequent PR.
Relates to https://github.com/babel/babel/projects/9#card-15461643