Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Linter experiment: TypeScript no-floating-promises via inter-process communication #2218

Closed
Boshen opened this issue Jan 30, 2024 · 4 comments
Labels
A-linter Area - Linter E-Help Wanted Experience level - For the experienced collaborators

Comments

@Boshen
Copy link
Member

Boshen commented Jan 30, 2024

This is a call for help and community effort.

The goal is to get a working draft for typescript-eslint's no-floating-promises or conclude that this is not doable.

To get things working, we are going to neglect all performance impacts.

The idea is that we spawn a node process with the TypeScript server and communicate with it via http or rpc or whatever means necessary. It doesn't really matter how we spawn the server, within Rust or as a standalone server, as long as we can communicate with it.

The core logic from typescript-eslint is here: https://github.com/typescript-eslint/typescript-eslint/blob/a41ad155b5fee9177651439adb1c5131e7e6254f/packages/eslint-plugin/src/rules/no-floating-promises.ts#L221

@Boshen Boshen added E-Help Wanted Experience level - For the experienced collaborators A-linter Area - Linter labels Jan 30, 2024
@Boshen Boshen pinned this issue Jan 30, 2024
@valeneiko
Copy link
Contributor

valeneiko commented Feb 5, 2024

Summary

I had a look through tsserver protocol definitions (here) and source (here). I don't think TSServer API would be sufficient to implement type checker based lint rules. The only type related command I could see is typeDefinition, which returns the type definition at a given locaiton in a file, but this top-level information would not be sufficient for the lint rule. There is a chance we could resolve the type fully by recursively using go-to definition paired with type definition commands, but this does not seem practical.

There might be another option though: implement a TSServer like utility to give us access to type checker the way typescript-eslint does it.

Communication with TSServer

Communication with tsserver happens through stdin/stdout by writing and reading simple JSON messages. High level protocol description can be found here. And there is a simple example implemented in JS here. As part of looking into this I have implemented a simple POC in Rust, if you think it could be useful I can publish the repo.

The rest of documentation is in the form of comments in code. All the available commands are listed in protocol.ts and usually have a simple description atteched to the command arguments object. The corresponding implementation can be found in the session.ts. But looking through the code I did not see a way to access type checker API we need for lint rules.

Type checker API

The API used by typescript-eslint is the type checker that is part of the compiler API documented here. They directly import typescript and create required primitives to access the type checker. The relevant package containing the implementation is typescript-estree. There are some recently added experimental options to simplify this by relying more on typescript ProjectServices, but this mostly simplifies managing open files and projects, and still allows direct access to type checker. Relevant discussion with more details is here typescript-eslint/typescript-eslint#6575.

Alternative solution

Look into typescript-estree and build something similar in JS, importing typescript directly and exposing an API to load the project, open/close files and access the type checker. This seems doable, but calling methods over IPC might have too much overhead to make it sufficiently fast.

We might want to consider using JS code for anything to do with type checker. For example rules could have an associated JS file, that define functions that the rule could call through the IPC interface. The JS function would accept Typescript AST node as input, and perform all the necessary type checking logic on the JS side.

So with the no-floating-promises example, we would map the Rust AST node into TS one in Rust, and then call the isPromise function through the IPC for a TS node we just mapped. The isPromise would be implemented entirely in JS.

@valeneiko
Copy link
Contributor

I have implemented proof-of-concept in a separate repo: https://github.com/valeneiko/typecheck-server

It is not a full implementation of the lint rule, just the type checker related logic from it. We can pass an AST node from Rust side to JS side, and access type information about it (on JS side), but that should be enough to implement the rule.

Let me know your thoughts!

@Boshen
Copy link
Member Author

Boshen commented Feb 12, 2024

@camc314 Are you interested in implementing the rule on our side and integrate it with https://github.com/valeneiko/typecheck-server?

@camc314
Copy link
Collaborator

camc314 commented Feb 15, 2024

I'd love to, but my time is stretched very thin currently 😞 .

@oxc-project oxc-project locked and limited conversation to collaborators Mar 29, 2024
@Boshen Boshen converted this issue into discussion #2855 Mar 29, 2024
@Boshen Boshen unpinned this issue Mar 29, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
A-linter Area - Linter E-Help Wanted Experience level - For the experienced collaborators
Projects
None yet
Development

No branches or pull requests

3 participants