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
Rewriting Lebab as a Babel transformer? #138
Comments
That's an intriguing idea, but I to my understanding Babel does not fully preserve the original whitespace. For example, when transforming the following code, that has nothing to transform: /**
* My func
*/
function foo (y) {
// Hello world
if ( true ) {
// poor indentation
}
} Babel produces: /**
* My func
*/
function foo(y) {
// Hello world
if (true) {
// poor indentation
}
} It does preserve all the comments, but it pretty much reformats all the whitespace. This behavior is fine for Babel, as one only cares that the output is readable. In Lebab's case you'll want the output to be 100% the same as input when no transforms were done - so Lebab would not mess up whatever strange coding convention one has, and you could look at the diff of code before/after and only see the actual transformations without any whitespace noise. So it seems to me that this is not a feasible approach until Babel adopts a code generator more similar to Recast. |
On further investigation I discovered that it might be feasible to use the parser of Babel - Babylon. The AST it generates is mostly compatible with ESTree format used by Esprima and others, but there are some minor differences. |
Recast is parser-agnostic; you can call |
@benjamn Yep, Lebab is already using that run Espree parser instead. The problem with Babylon is that it doesn't produce AST that's fully compatible with ESTree spec. |
@benjamn So we could switch out |
A babel transform/babel = |
Well, Babel is optimized for code generation performance, and preserving formatting takes a bit more effort, but |
Ok I'm just wondering what we can do to make recast + babel interop better if that's possible. I think it would be nice to be able to write a babel transform like |
If this works (and I don't know why it wouldn't), then it shouldn't be hard: var recast = require("recast");
var ast = recast.parse(source, { parser: require("babylon") });
var result = require("babel-core").transformFromAst(ast, {
presets: ["lebab"],
ast: true
});
console.log(recast.print(result.ast).code); |
@benjamn would recast have to parse it like |
Letting
In other words, you could parse the AST yourself, and then call some other |
Ok I can test it my modifying babel's transformation step to take in a different parser/generator. I think the last thing is to accept babel AST nodes in ast-types? Since I recall benjamn/ast-types#132 too - should we make separate types for babel's 6 ast differences? |
@benjamn yay something like this seems to work babel/babel#3561 or it would be cool if we could subsitute with babel-types @mohebifar @nene I can help with converting when it's possible. Lebab can be a babel preset with plugins (could be a monorepo with lerna as well) |
As a update for this to happen:
|
babel/babel#3561 has landed! So writing a plugin that doesn't include the different AST nodes should work now https://github.com/babel/babylon#output |
Merged the 2 prs ^ so this is actually a possibility now! example here: fkling/astexplorer#161 |
Let me know if you want to discuss doing this at some point again! I know there's a lot on everyone's plate (and it will take a good amount of work) but just checking the option open (now that this can be done). Can also chat on our slack: http://slack.babeljs.io/ Could be a good opportunity for some simple beginner-friendly contribution issues as well if everything is planned out. cc @nene @mohebifar Could even try using https://github.com/jlongster/prettier as the generator but will require some testing |
I actually started out with switching the parser in Lebab over to Babylon. Wrote an adapter to convert Babylon node types to Esprima node types, this got most of the tests passing, but that's really a temporary solution. So got a bit stuck behind the task of converting the code really over to use Babylon AST (more like running out of free time). Thanks for the resources! |
Yeah definetely reach out if you have any questions/concerns: I know there's a lot to know and yes its certainly overwhelming. Slack or twitter: https://twitter.com/left_pad, https://twitter.com/babeljs.
I actually had to do this exact thing for https://github.com/babel/babel-eslint if you didn't know heh https://github.com/babel/babel-eslint/tree/master/babylon-to-espree. The ast differences are https://github.com/babel/babylon#output And also FYI we are also starting out an effort to make a plugin to output back to ESTree/Babel 5 output in the parser. babel/babylon#277 (@danez) which is basically the same logic but built-into the parser. The way we did it in babel-eslint is slow and really bad since we parse with babylon first and then change nodes around to fit the other format. Other resources if curious: https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md, http://henryzoo.com/babel-plugin-slides, http://astexplorer.net/ I made a few basic POC transforms a long time ago: http://astexplorer.net/#/sskXYR4icD/1, http://astexplorer.net/#/vd18PVxJ8J/2, http://astexplorer.net/#/zdTZE92PrZ/2, http://astexplorer.net/#/3sxHbt9I7u/1 (although not sure I'd do it the same way now) We can always add some better api methods |
I wonder how well does something like this babylon-to-espree work? Like what are the downsides of not using Babylon AST directly? For Lebab the speed isn't really a crucial aspect. Perhaps I'm better off with an adapter layer like this. I guess my main concern is coupling Lebab too tightly with the non-standard AST tree of Babylon, which is likely to fluctuate more than ESTree spec. Though I have to say I like some of the AST nodes of Babylon much more than their ESTree equivalents (especially the separate Literal nodes). |
Rewriting Lebab transforms as Babel transforms is another direction I'd really like to explore. But that will be a larger undertaking. I've accumulated a fair chunk of code in all these transforms. But I've also come across some of the limitations of the current system - especially with a need to track variables. Some sort of evolutionary approach would be nice to gradually reimplement the transforms as Babel-transforms, while keeping the others working alongside. |
Yeah the only downsides are speed plus hard to maintain + not enough tests (just a time/resource issue).
We aren't planning on making anymore changes
Yeah the changes I think are definetely nice especially for transforming which makes sense for Babel and for the AST, but in the end it doesn't interop well unless everyone agrees on doing ESTree 2.0 |
Is this still something we want to do for Lebab? I was wondering if we could ask this ask as part of https://developers.google.com/open-source/gsoc/? |
I do still have very much interest in doing this. Though I have drawn myself back from various open-source developments lately to just spend more time in the real world. Anyway, I have nothing against proposing this task for Google Summer of Code. It might even be better this way as I've found I'm better at cleaning up the code others have written than writing new stuff by myself from scratch. |
Cool 😄
Totally understandable and a good thing! Awesome, yeah I'll add it to the list, and hopefully we get some volunteers on this effort then. https://github.com/babel/babel/wiki/Google-Summer-of-Code-Proposals We'll just want to plan out how it should happen (I think we would all want it to be incremental so it's easy to review) |
Hi guys, I would like to work on this project under GSOC. Lately I have been learning AST and how to write plugins for babel. |
Hi there. I was busy slacking off during weekend :) Now finally found some time to review your code. Wrote quite a lot of comments. Unfortunately Github gists don't allow writing inline comments for particular lines of code. Tried to make my best without that. See: https://gist.github.com/nene/d9d9ec9802fd6e38cb4e212b8ca90b84 |
Also, one thing that we should consider rather sooner than later is the fact that ES6 imports are effectively var foo = require("foo");
...
foo = somethingElse(); Dealing with variable scoping is currently one of the main weaknesses of Lebab. Babel should provide us with much better scope information. |
Hi @nene Sorry for replying so late. Got busy in some college exams. Improving upon the points mentioned. And really like the "patterns matching in trees" blog post. It can help reduce a lot of code. Will start off by extracting that feature as a separate library from lebab so can be used in the current problem and such problems coming forth. |
I have one doubt.
Why need a better scope information if babel API handles it well itself? |
What I meant was that scope information provided by Babel seems to be better than the one we currently in Lebab get through a separate helper library. |
Hi @nene Apologies for disappearing had some personal crises. Wanted to confirm if this task is still up for grabs or can I resume working on it, irrespective of any summer of code challenge. Thanks |
Hi @rajatvijay, you are very much free to continue working on this. Barely anything has changed in the mean time. |
Yep, can totally be done outside of any program, was just a suggestion. Maybe can look at https://github.com/square/babel-codemod as well by @eventualbuddha |
This would still be amazing to do, for RGSoC we have a codemod for a TC39 proposal WIP which also could be a good candidate for this babel/babel#6048 |
Hello @nene @mohebifar I find lebab to be a really handy tool for doing large code base migrations from 5to6. I would like to talk to you on the subject of using lebab as a babel preset. I would like to know if this idea be implemented ? Can we make the use of tool better, if we internally integrate with babel without the user having to configure anything extra. Since this would involve re-writing of majority of transformations, I wish to hear your thoughts before moving further. EDIT: One of the reasons I am very interested about this project is the fact that it deals with writing code-mods and lets developers perform pain less migrations. Second reason is writing the project in the form of plugin based architecture. This would help us isolate one transformation and work on it to support latest changes made by tc39. Thirdly, it would remain in parity with the rest of the babel ecosystem. A developer would not need to configure anything provided he/she has babel already setup. |
Hi @abiduzz420 I don't quite understand what would be the benefit of implementing Lebab as a Babel preset. As I see it, to use a babel preset you would actually need to configure more than you currently need to do with Lebab (really, no configuration at all). And they way you use Babel is pretty different from the way you would use Lebab. You would use Lebab to perform the migration once, after which you won't have much use for it any more. In contrast to Babel that you'd keep continuously running. Also with Lebab you usually want the transforms to modify the existing files, versus Babel, where you'd rather output the generated sources to another file. Perhaps I misunderstood you. I have nothing against switching to Babylon parser and making use of some other existing Babel-related tools. |
I agree with you. Lebab is used as a one time tool and that it would require more configuration to set it up as preset rather than using it the way it is (no config). |
I tried to explain this in babel/babel#7296 (comment) and here already but I only thought we wanted to change Lebab internally to use babel, the cli would be the same, no configuration needed etc. Technically this could simply just mean creating each transform as a plugin (with a preset) and to turn on or turn off each plugin (or plugin option) by mapping those to the cli options. And the purpose of this is to support newer syntax in the parser as well as take advantage of tooling used in babel. The user doesn't need to know this change happens so it's basically a refactor. Lebab could just be a wrapper around a preset and run it (think of it as another babel-cli) |
Yes, this suddenly makes a lot of sense to me now (using it as another babel-cli). I did not make myself clear when I said plugin based transforms and keeping the cli intact. |
I would be very interested in seeing whether babel-codemod would work as a base for “lebab as a preset”. It should be fairly easy to do, and I can put together an example runner with an existing babel preset if that would help. This approach would be very similar to what @hzoo described as lebab being “another babel-cli”, as that’s pretty much what babel-codemod is, but with recast as a parser/printer to preserve as much formatting as possible. |
I have put together a babel plugin to transform require calls to import calls. Here is the repo for it. The plan is to compose lebab from such babel plugins and wrap it inside the lebab cli utility for backward compatibility. Let me know in what ways this plugin can be improved and should I move ahead in this direction to make others plugins too. |
@rajatvijay This looks like a great start! A few things that immediately pop to my mind:
|
@rajatvijay I extracted the While doing so I changed the API slightly:
In short, you should be able to just replace |
This has been fixed.
This is going to be my next step.
|
@nene the plugin has been published to npm with all the tests passing and using f-matches. Moving on to the new transform. |
Might I suggest following babel’s lead and using a monorepo for the lebab-as-babel-plugins packages? |
@eventualbuddha seems like a valid thing to do. Let me do a little research on monorepo, its cons, and management, then I can create the repo and place the babel-plugin-transform-require in that repo. |
I recommend using |
This is an explanation of how we use a monorepo with yarn and Ember. Not that much is Ember-specific. https://medium.com/square-corner-blog/ember-and-yarn-workspaces-fca69dc5d44a |
Thanks @niieani and @eventualbuddha. I have successfully made a monorepo for this, here @nene: I was working on transform-export plugin and was looking at the tests and got confused: it('should ignore function export when function name does not match with exported name', () => {
expectNoChange('exports.foo = function bar() {};');
}); Shouldn't this be transformed to |
Maybe. It depends on what else is in the file. If that were the only thing, then yes, and that's what esnext does. As long as you aren't changing the behavior of the program unexpectedly, you should make what upgrades you can. In this case, as long as there is no existing I recommend checking out the esnext tests and possibly implementation to help you. |
Currently lebab is doing so much from parsing to generating code. During the process, somethings are missing (Like nice warnings while parsing the code #136 or JSX Support #130) or even some bugs show up. I believe almost all of these issues have been considered and solved in babel.
Lebab's aim is basically transforming an old code to a modern code. That means the only thing that matters in lebab is transforming. So, making a babel transformer instead of re-implementing all that stuff can be an option for the future releases.
The text was updated successfully, but these errors were encountered: