Skip to content

Complete rewrite of ESLint #16482

@nzakas

Description

@nzakas

Introduction

ESLint was first released in 2013, meaning it will be ten years old next year. During that time, the way people write JavaScript has changed dramatically and we have been using the incremental approach to updating ESLint. This has served us well, as we've been able to keep up with changes fairly quickly while building off the same basic core as in 2013. However, I don't believe continually to make incremental changes will get us to where ESLint needs to go if it wants to be around in another ten years.

Even though we are close to rolling out the new config system, which is the first significant rearchitecture we've done, that effort is what led me to believe that it's time for a larger rewrite. We are still stuck on implementing things like async parsers and rules because it's difficult to plot a path forward that doesn't cause a lot of pain for a lot of users. This seems like the right time to stop and take stock of where we are and where we want to go.

Goals

I've been thinking about where I'd like ESLint to go next and have come up with several goals. These are pretty abstract at the moment, but here they are, in no particular order:

  1. Completely new codebase. Starting with a completely new repo will allow us to continue to maintain the current version of ESLint as long as necessary while ensuring we are making non-breaking changes on a new version.
  2. ESM with type checking. I don't want to rewrite in TypeScript, because I believe the core of ESLint should be vanilla JS, but I do think rewriting from scratch allows us to write in ESM and also use tsc with JSDoc comments to type check the project. This includes publishing type definitions in the packages.
  3. Runtime agnostic. ESLint should be able to run in any runtime, whether Node.js, Deno, the browser, or other. I'd like to focus on creating a core package (@eslint/core) that is runtime agnostic and then runtime specific packages (@eslint/node, @eslint/browser, etc.) that have any additional functionality needed for any given runtime. Yes, that means an officially supported browser version!
  4. Language agnostic. There's nothing about the core of ESLint that needs to be JavaScript specific. Calculating configurations, implementing rules, etc., are all pretty generic, so I'd like to pull the JavaScript-specific functionality out of the core and make it a plugin. Maybe @eslint/js? I envision a language implementation being distributed in a plugin that users can then assign to specific file patterns. (This would replace the parserForESLint() hack.) So ESLint could be used to lint any file format so long as someone as implemented an ESLint language API for it.
  5. New public APIs. Our public API right now is a pretty messy thanks to the incremental approach we've taken over the years. ESLint was never envisioned to have a public API beyond the Linter class (which started out as a linter object) and we've continued hacking on this. Right now we have both an ESLint class and a Linter class, which is confusing and they both do a lot more than just lint. I'd like to completely rethink the public API and provide both high-level APIs suitable for building things like StandardJS and the VSCode plugin and low-level APIs that adhere to the single-responsibility principal to make it possible to do more creative mixing and matching.
  6. Rust-based replacements. Once we have a more well-defined API, we may be able to swap out pieces into Rust-based alternatives for performance. This could look like creating NAPI modules written in Rust for Node.js, writing in Rust and compiling to WebAssembly, creating a standalone ESLint executable written in Rust that calls into the JavaScript portions, or other approaches.
  7. Async all the way down. Async parsing, rules...everything! We've had trouble making incremental progress with this, but building from scratch we can just make it work the way we want.
  8. Pluggable source code formatting. Stylistic rules are a pain, so I'd like to include source code formatting as a separate feature. And because it's ESLint, this feature should be pluggable, so you can even just plug-in Prettier to fulfill that role if you want.
  9. Reporters for output. The current formatters paradigm is limited: we can only have one at a time, we can't stream results as they complete, etc. I'd like to switch to a reporters model similar to what Mocha and Jest have.
  10. AST mutations for autofixing. This is something we've wanted for a long time. I see it as being in addition to the current text editing autofixes and not a direct replacement.

Maybes

These are some ideas that aren't fully hatched in my mind and I'm not sure how we might go about implementing them or even if they are good ideas, but they are worth exploring.

  • Make ESLint type-aware. This seems to be something we keep banging our heads against -- we just don't have any way of knowing what type of value a variable contains. If we knew that, we'd be able to catch a lot more errors. Maybe we could find a way to consume TypeScript data for this?
  • Make ESLint project-aware. More and more we are seeing people wanting to have some insights into the surrounding project and not just individual files. typescript-eslint and eslint-plugin-import both work on more than one file to get a complete picture of the project. Figuring out how this might work in the core seems worthwhile to explore.
  • Standalone ESLint executable. With Rust's ability to call into JavaScript, it might be worth exploring whether or not we could create a standalone ESLint executable that can be distributed without the need to install a separate runtime. Deno also has the ability to compile JavaScript into a standalone executable, so Rust isn't even required to try this.

Approach

For whatever we decide, the overall approach would be to start small and not try to have 100% compatibility with the current ESLint right off the bat. I think we'd added a lot of features that maybe aren't used as much, and so we would focus on getting the core experience right before adding, for example, every existing command line option.

Next steps

This obviously isn't a complete proposal. There would need to be a (massive) RFC taking into account all of the goals and ideas people have for the next generation of ESLint. My intent here is just to start the conversation rolling, solicit feedback from the team and community about what they'd like to see, and then figure out how to move forward from there.

This list is by no means exhaustive. This is the place to add all of your crazy wishlist items for ESLint's future because doing a complete rewrite means taking into account things we can't even consider now.

Metadata

Metadata

Assignees

No one assigned

    Labels

    breakingThis change is backwards-incompatiblecoreRelates to ESLint's core APIs and featuresneeds bikesheddingMinor details about this change need to be discussedneeds designImportant details about this change need to be discussed

    Type

    No type

    Projects

    Status

    Feedback Needed

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions