[RFC] Building a CLI for typescript-eslint #6020
Replies: 10 comments
-
Great write up @bradzacher! I agree that we have matured to the point where this is the main area where we can add real remaining value, as long as a clear design goal is that users can still use the existing ESLint tooling as they always have done (probably initially through a lot of duplication in the implementation, and then probably some kind of gnarly compat logic) then I am definitely all for this! |
Beta Was this translation helpful? Give feedback.
-
Are there folks from the ESLint side who should weigh in here? I'm not 100% familiar with all the issues as described; although the contents of the RFC make sense as described it's a pretty big jump for users to make an entirely CLI for them to use. An alternate approach would be to to work with the ESLint CLI maintainers and propose some kind of plugin system for the CLI. |
Beta Was this translation helpful? Give feedback.
-
@JoshuaKGoldberg that was the reason for my point about around not breaking any existing support. The CLI would be entirely opt in and be most relevant for folks having issues with the current generic approach. I would imagine most users would never even know/care that this additional tool would exist. I don’t want to speak for @bradzacher but give his thumbs up on my comment I imagine his own thinking is not a million miles away from that |
Beta Was this translation helpful? Give feedback.
-
Another reason we would not want any breakage in support is the supplementary tooling such as IDE plugins that we would not look to replace, the highly optimised and specific CLI would be purely additive to the current ecosystem |
Beta Was this translation helpful? Give feedback.
-
Yeah definitely we would have no choice but to maintain compat with the existing ESLint ecosystem because the IDE ecosystem relies on the standard method of operation. Essentially right now our advice for larger projects right now is to use lerna/nx/etc to run the ESLint process isolated on each of the projects when doing an entire repo lint. This OFC does require extra effort to ensure the configs are fully separated so they don't influence one another. With a custom CLI we could essentially automate it for people and give them a best-practice by default. The IDE usecase shouldn't matter for most of the OOMs because you usually work on a subset of files and thus a subset of projects. We can probably chat to the ESLint folks! This would definitely require a detailed RFC on that side before it could be worked into the CLI. We are like 60% of the ESLint userbase so we have some weight behind us. There are pros and cons to both approaches. |
Beta Was this translation helpful? Give feedback.
-
I imagine having a custom CLI would also provide a possible path to supporting While it'd not be directly compatible with the ecosystem, I think most tools and IDEs should fine as they should primarily be caring about how to "talk" to Am excited to see where this goes 🚀 |
Beta Was this translation helpful? Give feedback.
-
Initial proof of concept for the CLI: #4359 On our repo it looks to be consistently ~15-20s faster than a pure ESLint run. |
Beta Was this translation helpful? Give feedback.
-
@bradzacher Is this documented anywhere? I want a reference to make sure my configs are right. |
Beta Was this translation helpful? Give feedback.
-
@cyrfer we don't explicitly talk about running things in isolated threads, but it's the logical next step if you're running into OOMs after separating your tsconfigs. The root cause of OOMs is that we have to create TS data structures without cleaning them up.
So how can you solve the problem?
Another way is to just ignore the problem entirely! By using a separate nodejs process per package, you no longer need to worry about any of the above problems because you get 2gb of ram per package to play with! Which is the same limitation placed on a Which is why I proposed this CLI - we can do this for you automatically and save everyone trying to figure out how to make it work. |
Beta Was this translation helpful? Give feedback.
-
also cc @JoshuaKGoldberg based on our discussion the other day. |
Beta Was this translation helpful? Give feedback.
-
I want to explore is building a CLI for typescript-eslint.
A big issue we run into with our tooling is that we are "at the mercy" of ESLint's CLI. ESLint controls the process - how files are selected, when files are selected, in what order they are linted.
This is a problem because we can't do efficient memory management, nor can we properly prioritise work. We don't have any control or visibility into what files are being linted so we can't "dispose" of a program when it's done: we don't know if or when all the files that belong to the project will be linted (for example it's common that a user might lint a subset of a project). This forces us to keep every single project in memory for the duration of the lint run (which leads to OOMs on any non-trivial codebase).
Additionally due to the fact that ESLint has one API for all use cases we don't know which "state" ESLint is in. ESLint just exposes the
ESLint
API which is used in the "single-run" CLI usecase as well as the persistent IDE usecase.This means that we're stuck building for the worst case scenario like:
For (1) we've attempted to work around by attempting to detect the CLI usecase (#3512) - which definitely helps! But it has a few unfortunate shortcomings.
For (2) we could probably build on top of the (1) solution to free up programs automatically. But that kind of feels like hacks on hacks on hacks - and we're building a super complicated system which is brittle and pushes us further into a world where we can't get new contributors due to the complexity of our system.
It's worth noting that even for the CLI usecase (which we tend to think of as "one and done") - ESLint can lint a file up to 11 times when it is calculating fixes for a file (1 initial pass and 10 fix passes). Which does cause some unique problems with optimisations we might try to apply.
So I want to explore a new direction - a new CLI which allows us to "work around" these issues from the top down instead of the middle.
My proposal would be a simple CLI along the lines of this:
A keen eye would notice this proposed CLI is just a subset of the ESLint CLI. This is completely intentional.
At a high level - this would be the proposed workflow for the CLI:
FileEnumerator
to expand the passed paths/blobs to the list of actual files to lintn < 10
then apply fixes,n + 1
, go to (5.iii).By controlling the order and grouping of files - we can know exactly when we can free up the memory - allowing us to prevent OOMs due to having all programs in memory at once.
This also unlocks other possible optimisations for us like parallelisation!
We know that each project is "isolated" from one another - so we can run each project (eg step 5) in a separate thread. We could also bucket the non-type information files (step 3) in their own separate thread. This should mean that instead of having
O(nm)
performance wheren
is the number of projects andm
is the worst-case lint time for a project, instead we would haveO(m)
performance.If we wanted to - this would also let us rip out the "CLI detection" from the underlying CLI and thus simplify it again to reduce complexities. We could also choose to keep this so we can maintain some level of improved performance for people who continue to use the standard
eslint
binary.cc @typescript-eslint/core-team
Beta Was this translation helpful? Give feedback.
All reactions