-
Notifications
You must be signed in to change notification settings - Fork 350
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
Support TypeScript #424
Comments
I would happily help out here, but would like some guidelines. @benjamn |
This is going to be a lot of work, but it's totally doable, and parallelizable across multiple contributors! Recast's ability to handle arbitrary AST type systems simultaneously is one of its greatest strengths, so I'm happy to see it applied to TypeScript. At a high level, If unmodified code happens to contain an AST node that Recast doesn't know how to print, nothing will break, because Recast never has to generate new code for that node. However, if the AST changed in the vicinity of that AST node, Recast may have to use the pretty-printer. To be safe, of course, you need to teach Recast how to print any new types of AST nodes. The beauty is, there's no limit to how many different AST types you can add to the pretty-printer. They don't even have to represent the same language, as long as the AST type names are distinct. Now, in reality, Recast benefits from having a limited understanding the relationships between AST types. For example, you're seeing a failure here because Another place where it's going to be useful to understand the AST type system is here, where we check whether a node is an In short, I think we should get started on Once we have enough TypeScript definitions to capture a small TypeScript AST, as parsed by Babylon, then we can begin writing Recast test cases: const ast = recast.parse(typeScriptSource, {
// Recast can use any parser you like!
parse(source) {
// Getting the options right here may take some finesse.
// Presumably TypeScript isn't enabled by default, for example.
return babylon.parse(source, options);
}
});
// Should work even if Recast doesn't know how to print any of the new types.
recast.print(ast).code;
// Will fail unless Recast knows how to print everything.
recast.prettyPrint(ast).code; These test cases will fail until the pretty-printer learns how to print everything, but picking off new AST types one by one is fun, because you can feel the incremental progress. As a personal note, I'll be on vacation from this Thursday through Labor Day (September 4th), but I'm happy to answer questions before or after that. Cheers! 🎉 |
@benjamn thanks for taking your time writing this up! |
Has anyone started work on this at all? I may or may not give it a swing in some free time coming up if not. |
@chriskrycho Go for it! I haven't made any progress since my last comment. |
Hi, I'm beginning to use recast and I love it. (Although it's in the context of jscodeshift as a wrapper.) I am extremely interested in understanding how to use it with TypeScript. I have had mostly good success in using the Flow parser because there are many similarities, but the Flow parser only goes so far and the specific things that break for me are explicit typecasts (such as let x = <any>myVar; or let x = myVar as any; This kind of thing throws off the Flow parser and everything in the rest of the file gets pulled in as a big JSXToken. Can someone help me understand why the set of parsers available on the tool AST Explorer include many others beyond what Recasts supports? There is a "typescript" parser there that produces a fine AST representation of TypeScript files, including the syntax described above. I need help understanding what it would take to bridge Recast to one of these extended parsers. Thanks in advance! |
Did you see the comment on how to get started with this issue #424 (comment) ? Cheers |
Thanks, @skovhus . . . ! I'm a little new to the ecosystem here and so while I think I am beginning to understanding the relationships between jscodeshift, recast, and ast-types, I was hoping there was some way to get recast to use a parser that already exists that can handle TypeScript. (I'm guessing that the problem with some of the parsers available via "AST Explorer" is that many of them are not based on the ast-types library?) I will dig in more to try to understand what's going on; I'm sure some of my assumptions here are naive . . . |
I don't want to step in on someone else who already is looking at this, but we are betting pretty hard on Recast to maintain our codebase and are looking on starting to sprinkle some TypeScript in, and there doesn't seem to be anything better out there, so I'd consider jumping in on this. We do a lot of codemods, so we could even make progress using our real-world use cases to test progression. @chriskrycho did you start yet, or just I poke at it greenfield? |
Poke at it greenfield! I ended up with very busy holidays and made some progress on TS stuff in the Ember.js ecosystem but didn’t get back to this at all.
|
Question for you: from poking around and your explanation, the recast and ast-types bits look like they'll be pretty straightforward. There is one thing I'm not too sure, and it's what to do on the parser front. Using a custom parser, we have a couple of option (sorry, I'm sure my terminology will be completely off. Parser land is confusing): 1- Prettier and ESlint use typescript-eslint-parser. They use the primary typescript parser and convert to an AST compatible with those tools. Option 1 is a bit fuzzy to me, as the relationship between the babylon typescript support and typescript-eslint-parser seem a little blurry, but it allowed Prettier and ESlint to work, after all and the author was working actively with the TS team. It does generate an AST that could work here. Option 2 seems like it would work if i understand the ast-types architecture (which could work with any AST as long as we put it in the definition?). Removes the middleman, but probably can't reuse much because, as I understand it, the AST format is very different. Option 3 is only available in Babylon 7 i think, which is beta, but I have this working locally for a simple case. Seems like it would make things pretty simple (just use the babylon parser with the typescript and jsx plugin. Boom done) and a lot could be reused. On paper, using Babylon's typescript support seems like an obvious choice, but I was curious if anyone understood the relationship between it and typescript-eslint-parser a little better (digging through their issue tracker history, I'm having trouble putting the context together) |
@Phoenixmatrix I don't know much about |
good good. I'll run away with that for now. Btw, the Recast & ast-types design is really clever and nice to work with. Good work. Gives me a whole new level of appreciation for this project. |
I am enthusiastic about this too and have been wondering how to get
started. Let me know if you'd like any help!
…On Wed, Jan 31, 2018 at 9:37 AM, Francois Ward ***@***.***> wrote:
good good. I'll run away with that for now. Btw, the Recast & ast-types
design is really clever and nice to work with. Good work. Gives me a whole
new level of appreciation for this project.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#424 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ABokPkrqz_jBOqsZkirEWC_X_3Pdz0UJks5tQIjJgaJpZM4O40Yr>
.
|
Progress update: since recast and its dependencies were already on Babylon 7, I didn't have to worry about breaking changes on the existing stuff, which was quite convenient. I already have a local branch of ast-types and recast processing a couple of simple AST node types and pretty printing them correctly. While the TS ast nodes are different (and prefixed), most are very similar to Flow, so I have been able to use it as inspiration for both the ast and the pretty printer with some pretty trivial tweaks. I'll probably have a good portion of the simple cases working by the end of the week and then I'll push the branches up. The only cases things that will be even remotely challenging are:
|
Progress is going at a good clip. Using the existing babylon support as a base for the typescript stuff is making things much, much easier (I'm getting all of the javascript constructs for free and only have to implement babylon 7's TS* nodes, which are generally much simpler overall). I have most of the basic constructs (type annotations/declarations/aliases, most assertions and interfaces) working. You can see the progress in the following branches: my ast-types branch There's still a decent chunk of work to do, but most simple cases work now. Some things I didn't really pay attention:
Still, progress is progress. I'm basically going through the TypeScript documentation and copy pasting/tweaking the examples in the tests one by one. You can take a look at the basic printing tests to see what I've got working. |
Got most of the ast supported now. I'm playing a bit of wack-a-mole with the more advanced types, pasting examples from the doc, but there's fewer and fewer left to do. Getting there! Will probably make a little bit more progress tonight, then it will be time to get some eyes on my approach before spending too much time polishing. |
@Phoenixmatrix Awesome work! Just drop a note here whenever you're ready for feedback/approval. |
@Phoenixmatrix awesome that you started this! I have a question about the projects around. As I understood it, Babylon needs to be used as parser when working with TypeScript. But Babylons AST differ from the ESTree spec, doesn't that mean that one must also use I ask because I want to use the checking methods and tree walking usage (with TS support) in hoschi/yode#11 . I found |
@hoschi The only thing is that babylon is not the default parser. But Recast can take a babylon AST no problem. If all you need is to walk/check node, you might be able to get away with just using babylon/babel-types i think, and they already have stuff for typescript. My goal is to be able to make changes to code without altering the original code's formatting, and to do so at scale (hundreds/thousands of projects), thus the Recast typescript support effort. |
Just to add to @Phoenixmatrix's comment, Recast doesn't enforce any opinions about how you should traverse or modify the AST between The nice thing about So, for example, either of these two builder calls require("recast").types.builders.stringLiteral("hi")
require("ast-types").builders.stringLiteral("hi") will give you an AST node with require("@babel/types").stringLiteral("hi") Of course, require("@babel/types").literal("bye") does not work. Since require("ast-types").literal("hi again") works as you would expect when you're working with an ESTree AST. If there's anything |
@benjamn ok, ready to at least get some initial set of eyes before I go too much further. Again, there's a lot of work to be done so don't get too scared if things are a bit sloppy, but I'd like to get some input before I start gold plating things #461 and benjamn/ast-types#249 Obviously since the ast-types isn't pushed out, the travis builds will fail, but it should be normal for now. |
@Phoenixmatrix didn't see that, should have digged deeper. |
Now that Babylon@next (v7 beta) is archived and continued in the name of import babelParser from '@babel/parser';
const tsAst = parse(entryFile, {
parser: {
parse(source) {
return babelParser.parse(source, {
// additional options
sourceType: 'module',
plugins: ['jsx', 'typescript']
});
}
}
}); |
Hi everyone, thanks for your work on recast 👋🏼
I happened to call it just like that, but then just now ran into a parsing error localted in a recast->esprima dependency. I am a little confused, because I thought this API allowed me to use my own parser instead, what is esprima doing there? Thanks again! |
Could you please provide the stack trace and the example static code you
are parsing?
On Wed, Oct 20, 2021 at 3:59 AM Gregor ***@***.***> wrote:
Hi everyone, thanks for your work on recast 👋🏼
Now that ***@***.*** (v7 beta) is archived and continued in the name of
@babel/parser, we could get to use typescript and jsx with a custom
parser configuration with existing recast API:
import babelParser from ***@***.***/parser';
const tsAst = parse(entryFile, {
parser: {
parse(source) {
return babelParser.parse(source, {
// additional options
sourceType: 'module',
plugins: ['jsx', 'typescript']
});
}
}
});
I happened to call it just like that, but then just now ran into a parsing
error localted in a recast->esprima dependency. I am a little confused,
because I thought this API allowed me to use my own parser instead, what is
esprima doing there?
Thanks again!
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#424 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABPBLBQVSIVS3HZJ3G5DVKDUHZZHDANCNFSM4DXDIYVQ>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
--
Zhenyang Hua
|
Lines 36 to 44 in 3741e4d
@Gregoor If the babel parser doesn't provide the tokens, it will fallback to use esprima tokenizer. All you need to do is to set the |
That indeed did the track. Thank you so much! |
So, typescript syntax was merged in babylon would you support it here?
Im getting this error:
babel/babylon#523
babel/babel#5899
The text was updated successfully, but these errors were encountered: