Skip to content
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 support for Typescript in Rust Relay compiler #3182

Closed

Conversation

MaartenStaa
Copy link
Contributor

@MaartenStaa MaartenStaa commented Sep 11, 2020

See #3181 and #3180.

This PR adds a language option to the typegen_config and refactors the relay-typegen crate to allow completely different output based on the configured language, currently typescript and flow (which is the default when no language is configured, or an invalid one is set).

I also made some changes so the watchman query looks for .ts files when using typescript, and the output files also use .ts as their extension.

This probably needs a bit more work before it's completely ready. Off the top of my head:

  • In relay-compiler/build_project/artifact_content.rs some output is generated such as @flow and a commented out import type statement? Those should probably be left out for Typescript.
  • Currently the language used for typegen is Typescript if the language in the config file is "typescript", and Flow otherwise. We should probably display an error if an invalid language is set, e.g. "typesrcipt".
  • I was not able to actually test the compiler due to an error returned by Watchman: failed to parse query: must use ["suffix", "suffixstring"], and I don't have a clue why. This error was already returned before I made the changes to look for .ts files when using Typescript.
  • I did not at all look into relay-lsp and whether any changes are needed there for Typescript.

Looking forward to your feedback!

cc @kassens @josephsavona @maraisr

@josephsavona
Copy link
Contributor

Awesome! I took a quick look through and the high-level approach makes sense, of having typegen create an abstract AST of types and then having Flow/TS-specific writers to convert that AST to code. Thanks for the contribution!

I'll leave to @tyao1, @kassens, and @rbalicki2 for review

@zth
Copy link
Contributor

zth commented Sep 12, 2020

This is so cool, awesome work @MaartenStaa ! 😍

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kassens has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

// Ending in *.js or *.ts depending on the project lanague.
Expr::Suffix(vec![match &p.typegen_config.language {
TypegenLanguage::Flow => "js",
TypegenLanguage::Typescript => "ts",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with react, this has to be tsx. So could this be an array perhaps?

Copy link
Contributor Author

@MaartenStaa MaartenStaa Sep 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I adjusted it! I set it to look at .jsx as well, just in case people use that.

Edit: I committed this on my phone and see that it doesn't compile. I'll have to take a look at that later. I'm on vacation now so a bit limited.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maraisr Fixed the build now as well.

Copy link
Member

@kassens kassens left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, I want to repeat everyone here and say this is awesome and pretty much aligns with how I thought it'd be done ideally.

Did you come up with the way the fragment references are modeled in typescript or was this based on the existing (community driven) typescript types? I know this was pretty tricky to get right in Flow.


impl Default for TypegenLanguage {
fn default() -> Self {
Self::Flow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was even thinking of making Typescript the default, but just realized *.ts files wouldn't work with vanilla JS anymore. The Flow generated *.js files using Flow comment style syntax allows people to just treat them as pure JS files.

Maybe we could even make this optional and skip generating types altogether to make the files a bit smaller.

Expr::Suffix(match &p.typegen_config.language {
TypegenLanguage::Flow => vec![PathBuf::from("js"), PathBuf::from("jsx")],
TypegenLanguage::Typescript => {
vec![PathBuf::from("ts"), PathBuf::from("tsx")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a TS project, I suppose you'd have all your own product code in *.tsx? files or should this also include .js files for hybrid projects?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think generally its all ts or all js. But can't presume that, so think yeah maybe hybrids are nice?

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kassens has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@alloy
Copy link
Contributor

alloy commented Sep 14, 2020

Awesome, very nice to see this @MaartenStaa 👏

readonly $fragmentRefs: RecursiveFragment$ref,
};
-------------------------------------------------------------------------------
import type { PhotoFragment$ref } from "PhotoFragment.graphql";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to avoid any need to import these types, as that entirely removes the need to have a single artefact directory (or Haste). See for example this output of the existing TS plugin.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... I think this is actually a blocker for a merge. Typescript wouldn't know what that means (please correct me here).

At the very least just prefix with a ./,

-import type { PhotoFragment$ref } from "PhotoFragment.graphql";
+import type { PhotoFragment$ref } from "./PhotoFragment.graphql";

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah that’s a good point. @kassens can you amend that in your import?

@kassens
Copy link
Member

kassens commented Sep 15, 2020

  • I'd like to just have TS output be included as I'm not sure of a good way to make it pluggable and maintainable.
  • Unfortunately, I (and the Rest of FB Relay engineers) unfortunately have limited experience and time to polish the TS output and figure out how it's supposed to work.
  • This PR seems like a great start and make it easier to folks to start contributing changes.

Given the above, I'm somewhat inclined to merge this and have it open up for contributions to tweak the output in follow up PRs. Thoughts?

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kassens has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@maraisr
Copy link
Contributor

maraisr commented Sep 17, 2020

Yeah @kassens I think that is a great way forward. Also a great way for non-Rust contributors to have a go, as is not as daunting as starting something from scratch. (Mind you @MaartenStaa did just that 😛).

I'm super keen to investigate a cleaner named interface, and proper union types. And think it was outlined in another ticket already, around not merging unions and forcing things to be nullable.

readonly $fragmentRefs: ConditionField$ref,
};
-------------------------------------------------------------------------------
import { FragmentReference } from "relay-runtime";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think though maybe this should import type {} no?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a requirement, but definitely something to follow up with.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maraisr correct. It was a conscious decision to use this option since import type was a relatively recent addition to Typescript, and I wouldn't want to make this unusable for people on older versions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fragment reference import does currently use the type keyword, though.

@MaartenStaa
Copy link
Contributor Author

Given the above, I'm somewhat inclined to merge this and have it open up for contributions to tweak the output in follow up PRs. Thoughts?

This sounds like a reasonable way to move forward, though I feel somewhat uneasy about having this merged at this stage since I wasn't able to test this yet.

(Mind you @MaartenStaa did just that 😛).

To be fair, I have played around with Rust before in my spare time, so it's not like I dove in with zero experience.

I'm super keen to investigate a cleaner named interface, and proper union types. And think it was outlined in another ticket already, around not merging unions and forcing things to be nullable.

I think you may be referring to my other PR, and how we ended up here in the first place: #3178.

@alloy
Copy link
Contributor

alloy commented Sep 17, 2020

This sounds like a reasonable way to move forward, though I feel somewhat uneasy about having this merged at this stage since I wasn't able to test this yet.

I think it would be ok to merge (with at least the relative import change) and then it would be good to port e.g. the TS example that exists in the TS plugin repo to use the rust compiler and then iterate on any issues and the few discussed here over the next few weeks. Others (e.g. me) can chime in too.

As long as the relay team is ok with not putting out a public release until the dust has settled (@kassens ?).

@MaartenStaa What do you think?

@facebook-github-bot
Copy link
Contributor

@kassens merged this pull request in ab5c96b.

@mrjackdavis
Copy link

For those of you coming here from google to fix this error:

Watchman error: Watchman error: The watchman server reported an error: "failed to parse query: must use ["suffix", "suffixstring"]", while executing command

I had to upgrade watchman

brew upgrade watchman

And restart watchman

watchman shutdown-server

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants