Hyoban

Hyoban

Don’t do what you should do, do you want.
x
github
email
telegram
follow

Why ESLint?

Preface#

This is not an article comparing the pros and cons of ESLint and other related tools, but an introduction to why I choose to use ESLint for code checking and formatting. Although other tools will inevitably be mentioned, I believe that each popular tool exists because they have their own characteristics and advantages. As users, we only need to choose the tools that suit our needs and preferences. If there are issues with the tools, we can help improve them through feedback and contributions.

Advantages of ESLint that I value#

More flexible formatting#

Regarding why I don't use formatting tools like Prettier or dprint, I think Anthony Fu's article Why I don't use Prettier has already explained it clearly enough. Here, I will add my own thoughts.

Configuring printWidth does not tell Prettier when we want line breaks. By increasing the value of printWidth to avoid line breaks in a long string variable where it shouldn't, you may end up with multiple arguments of a multi-parameter function that you expect to be line-broken not being line-broken correctly. The problem with // prettier-ignore is similar to // @ts-ignore. Although we successfully tell the tool not to line break here, we also lose the other formatting rules that could have been applied here.

Although Prettier's philosophy is that users don't need to consider formatting configuration options and can confidently hand over their code to it to make it beautiful, in reality, the existing configurable options may be one of the reasons why Prettier has become the most popular formatting tool in the JavaScript community. Imagine using a formatting tool that cannot be controlled to use tab or space for indentation. Considering that the debate between tab and space is basically a 50-50 situation, if this option does not exist, Prettier would lose at least half of its users, right?

In addition, for options that are relatively less debated, Prettier's stubbornness may cause some trouble for users. For example, Prettier enforces keeping an empty line at the end of a file, which is not configurable. Although this behavior is supported by most people and is beneficial for tools like Git, for those who cannot tolerate it, they have to find some hack or look for alternative tools to replace Prettier.

Yes, we have other formatting tools to choose from, such as dprint, which has better performance and more configuration options. However, it still does not solve the problem of when the code should be line-broken. This is not a bug, but a result of their own working mode. In addition, even if dprint has more configuration options, you can never satisfy everyone's needs. In the world of ESLint, you can easily implement your own needs by configuring the options of existing rules or writing plugins.

When I noticed that the existing rules in ESLint Stylistic seemed to not handle the spacing between multiple parameters in JSX, all I needed to do was to put the current code into the TypeScript-ESLint playground, and then write a plugin based on the data structure displayed on the right side of the ESTree to implement my own needs.

Extensibility#

By rewriting tools originally written in JavaScript using a more performant language, we can achieve better performance. However, this usually comes at a cost. Lint tools written in Rust, for example, generally cannot easily customize rules, so only rules that are extremely popular in the ESLint community can be ported. This means that we cannot have official ESLint plugins released at the same time, such as eslint-plugin-react-compiler released with React Compiler, or have niche but useful plugins in the ESLint community, such as ESLint Plugin Command.

In addition to extending rules, ESLint also supports extending the language for linting. Now you can easily use ESLint for code checking in languages such as Vue, JSON, YAML, Toml, Markdown, Astro, Svelte, and more. However, for lint tools written in native languages, they usually only prioritize supporting the most popular languages. For example, if you use Biome, you temporarily cannot use it when writing Vue projects and still need to fall back to ESLint. I don't like the inconsistency caused by using different tools in different projects.

Excellent ecosystem#

In this section, I don't want to mention how rich the plugin ecosystem of ESLint is again. Let's talk about the ESLint VSCode plugin. In addition to the automatic fix feature we use every day, it also provides some other useful features.

When using ESLint, there are some rules that we want to automatically fix, but not immediately when saving. For example, removing unused imports or immediately changing let to const (we may soon reassign the variable). In this case, we can use the eslint.codeActionsOnSave.rules setting.

{
  "eslint.codeActionsOnSave.rules": [
    "!prefer-const",
    "!unused-imports/no-unused-imports",
    "*"
  ]
}

With lint-staged and simple-git-hooks, we can ignore certain rules in the editor and automatically fix them before committing.

Another very useful setting is eslint.rules.customizations. We have disabled the automatic fix of some rules, but the editor still displays them as errors. With this setting, we can reduce the severity or completely disable these rules.

{
  "eslint.rules.customizations": [
    { "rule": "@stylistic/*", "severity": "off" },
    { "rule": "@stylistic/no-tabs", "severity": "default" },
    { "rule": "@stylistic/max-statements-per-line", "severity": "default" }
    { "rule": "antfu/consistent-list-newline", "severity": "off" },
    { "rule": "prefer-const", "severity": "off" },
    { "rule": "unused-imports/no-unused-imports", "severity": "off" },
    { "rule": "simple-import-sort/*", "severity": "off" },
  ]
}

This setting is very useful for using ESLint as a code formatting tool. We can directly disable the error display of rules in ESLint Stylistic in the editor while retaining their automatic fix functionality. In the next version, it also allows you to adjust the severity of all rules that can be automatically fixed.

Type-aware lint rules#

Lint tools based on Rust are fast, but they do not have the ability to lint based on type information. Josh Goldberg provides a detailed explanation in his article Rust-Based JavaScript Linters: Fast, But No Typed Linting Right Now.

oxlint recently made an attempt, but it seems to have brought it back to the speed of JavaScript.

Biome is preparing to implement Type-aware linter.

ESLint's "disadvantages" that I don't care about#

Performance#

You can see many benchmarks showing that tools like oxlint and biome have much better performance than ESLint. However, from my use case, performance issues don't seem to be that important.

Real-time linting in the editor and linting during precommit usually only need to check a small number of files, and we can leave the complete linting process to the CI. CI does not block our local development process, and we only need to lint specific files locally when there is an error in CI.

The cases where I still encounter performance issues in the editor are when the project gradually becomes larger, and enabling type-based rules causes noticeable delays in saving operations in the editor. But even in this case, we don't have to compromise completely and disable type-based rules. The flexibility of ESLint Flat Config allows us to disable specific rules in the editor. In the terminal or CI environment, we can still perform complete linting.

For my own ESLint Config, you can use the following configuration.

import defineConfig from "eslint-config-hyoban";

const isInEditor = !!(
  (process.env.VSCODE_PID ||
    process.env.VSCODE_CWD ||
    process.env.JETBRAINS_IDE ||
    process.env.VIM) &&
  !process.env.CI
);

export default defineConfig({
  typeChecked: isInEditor ? false : "essential",
});

You can also try tsslint, a lightweight checking tool seamlessly integrated with the TypeScript language server.

Unofficial recommendation#

Both ESLint and typescript-eslint have decided to deprecate formatting-related rules and do not recommend using ESLint for formatting. However, I don't think this is a problem. Deprecating these rules and transferring them to the community for maintenance is actually a good thing. We now have tools like ESLint Stylistic that are ready to use and perform very well.

Complex configuration, troublesome upgrades#

ESLint 9.0 recently made many people feel that the major version upgrade of ESLint is very complex. The main issues encountered are the new configuration file format requiring us to rewrite the configuration and breaking changes in the API causing many plugins to not work in 9.0.

However, I think this is a temporary problem. The new configuration file brings many useful new tools and usages, which outweigh the disadvantages. For example, ESLint Config Inspector can help us write and test configuration files better; dynamic configuration generation based on project dependencies (enabling React hooks-related rules only in projects with React).

The problems caused by breaking changes in the API can also be solved in various ways:

  1. Write a PR to adapt upstream plugins to ESLint v9. In many cases, we only need to modify a few lines of code to use the new API in ESLint v9 and keep the old API for compatibility.
  2. Temporarily use ESLint v8 and wait for plugin compatibility (we can still use Flat Config).
  3. Use the official ESLint Compatibility Utilities to help with the upgrade.

Conclusion#

It needs to be emphasized again that these are just my personal feelings and opinions, and there may be places where I have not considered them properly. I welcome you to communicate with me and share your views.

If you want to try ESLint All In One now, I highly recommend starting with Anthony Fu's ESLint config, which supports many languages and frameworks, and you can also configure it flexibly based on it.

If you mainly write TypeScript and React, I also recommend trying my ESLint config. The philosophy of configuring rules is to use the rule presets provided by plugins as much as possible, and adjust them according to my own habits, while providing strict and typeChecked options for different levels of adjustments.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.