Hyoban

Hyoban

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

Why ESLint

Introduction#

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 to break lines. Increasing the value of printWidth to avoid breaking a long string variable where it shouldn't be broken may result in the parameters of a multi-parameter function that you expect to break not being correctly broken. The problem with // prettier-ignore is similar to // @ts-ignore. Although we successfully tell the tool not to break the line 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 leave 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 control whether to use tab or space for indentation. Considering that the debate between tab and space is basically a 50/50 situation, if this option doesn't exist, Prettier would lose at least half of its users, right?

In addition, for options that are not as controversial, Prettier's inflexibility 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 hacks or look for alternative tools to replace Prettier.

Yes, we have other formatting tools to choose from, such as dprint, which performs better and provides more configuration options. However, it still doesn't solve the problem of when the code should break lines. 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 meet 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 put the current code into the TypeScript-ESLint playground and write a plugin based on the data structure displayed on the right side of the ESTree to implement my own requirements.

Extensibility#

Rewriting tools originally written in JavaScript using a more performant language can improve 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 the most popular languages. For example, if you use Biome, you temporarily cannot use it when writing Vue projects and still need to rely on 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 ESLint's plugin ecosystem 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 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 reassign the variable soon). In this case, we can use the eslint.codeActionsOnSave.rules setting.

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

Combined 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 will also allow 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 introduction 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 JavaScript speed.

Biome is preparing to implement Type-aware linter.

"Disadvantages" of ESLint 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 doesn't seem to be that important.

Real-time linting in the editor and linting during pre-commit 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 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. But in fact, 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 and troublesome upgrades#

ESLint 9.0 recently made many people feel that major version upgrades of ESLint are 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; we can dynamically generate configurations based on the dependencies installed in the project (only enable React hooks-related rules 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 upstream plugins to adapt 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 us upgrade.

Conclusion#

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

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. At the same time, it provides strict and typeChecked options for different levels of adjustment.

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