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 that compares the pros and cons of ESLint and other related tools, but rather an introduction to the reasons why I chose to use ESLint for code checking and formatting. While it is inevitable to mention other tools, I believe that the existence of each popular tool is due to its 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#

Less Stubborn Formatting#

Regarding why I do not use formatting tools like Prettier or dprint, I think Anthony Fu's article Why I don't use Prettier has made it clear enough. Here, I would like to add a bit of my own thoughts.

Configuring printWidth does not tell Prettier when we want to wrap lines. By increasing the value of printWidth to avoid wrapping a long string variable that shouldn't wrap, it may lead to a multi-parameter function's arguments that you expect to wrap not being wrapped correctly. The issue with // prettier-ignore is similar to // @ts-ignore; while we successfully tell the tool not to wrap here, we also lose other formatting rules that could have applied.

Although Prettier's philosophy is that users should not have to consider formatting configuration options, allowing you to confidently hand over your code to it to make it look nice, the existing configurable options may be one of the reasons why Prettier has become the most popular formatting tool in the JS community. Imagine, would you use a formatting tool that cannot control whether to indent with tab or space? Considering the tab vs space debate is basically a 50-50 situation, without this option, Prettier would at least have half as many users?

Moreover, for options that are relatively less controversial, Prettier's stubbornness may cause some inconvenience to users. For example, Prettier enforces keeping a blank line at the end of files, which is non-configurable. Although this behavior is supported by most and is beneficial for tools like git, those who cannot tolerate it have to look for some hacks or find other tools to replace Prettier.

Yes, we have other formatting tools to choose from, such as dprint, which performs better and offers more configuration options. However, it still does not solve the issue of when code should wrap. This is not a bug but rather determined by their working modes. Additionally, even though 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 existing rule options or writing plugins.

When I noticed that ESLint's Stylistic rules did not seem to handle spaces between multiple parameters in jsx, I only needed to put the current code into the typescript-eslint's playground and then write a plugin based on the data structure displayed on the right side by ESTree to meet my own needs.

Extensibility#

By rewriting tools originally written in js using a more performant language, better performance can be achieved. However, this often comes at a cost. Lint tools written in Rust generally cannot easily customize rules, so only the extremely popular rules in the ESLint community can be ported. This means we cannot have official ESLint plugins released immediately, such as the eslint-plugin-react-compiler released with the React Compiler; nor can we have niche but very useful plugins in the ESLint community, such as ESLint Plugin Command.

In addition to extending rules, ESLint also supports extending linting languages. Nowadays, you can easily use ESLint for code checking in languages like Vue, JSON, YAML, Toml, Markdown, Astro, Svelte, and more. However, lint tools written in native languages typically prioritize supporting the most mainstream languages. For example, if you use Biome, you temporarily cannot use it when writing Vue projects and still need to revert to ESLint. I do not like the inconsistency caused by using different tools across different projects.

Excellent Ecosystem#

In this section, I do not want to reiterate how rich ESLint's plugin ecosystem is; let's talk about the ESLint VSCode plugin. In addition to the auto-fix feature we use every day when saving, it also provides some other useful functionalities.

When using ESLint, there are some rules we want to auto-fix but not immediately upon saving. For example, removing unused imports or changing let to const right away (as we might soon reassign the variable). At this point, we can set eslint.codeActionsOnSave.rules.

{
  "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 then automatically fix them before committing.

Another very useful setting is eslint.rules.customizations. Earlier, we disabled the auto-fix for certain rules, but the editor still displays them as errors. With this setting, we can lower the severity of these rules or turn them off completely.

{
  "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 turn off the error display of ESLint Stylistic rules in the editor while retaining their auto-fix functionality. In the next version, it will also allow you to adjust the severity of all auto-fixable rules.

Type-aware Lint Rules#

Rust-based lint tools are fast, but they lack the ability to lint using type information. Josh Goldberg provided a detailed introduction in Rust-Based JavaScript Linters: Fast, But No Typed Linting Right Now.

Oxlint has recently made attempts, but this seems to have led it back to JavaScript's speed.

Biome is starting to prepare to implement a Type-aware linter.

ESLint "Disadvantages" That I Don't Care Much About#

Performance#

You can see many benchmarks showing that tools like oxlint and biome far outperform ESLint. However, from my usage scenario, performance issues do not seem to be that important.

Real-time linting in the editor and linting during pre-commit generally only need to check a small number of files; we can leave the complete linting process to CI. CI does not block our local development process; we only need to lint specific files locally when CI reports an error.

The performance issues I encounter in the editor still arise when the project gradually grows, and enabling type-checking rules causes noticeable delays in saving operations. However, we do not have to completely compromise by turning off type-checking 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, I 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, which is a lightweight checking tool seamlessly integrated with the TypeScript language server.

Unofficial Recommendations#

Both ESLint and typescript-eslint have officially decided to deprecate formatting-related rules and do not recommend using ESLint for formatting, but instead recommend using it in conjunction with formatting tools like Prettier. However, I do not see this as a problem; deprecating these rules and transferring their maintenance to the community 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#

The recent ESLint 9.0 has made many people feel that major version upgrades of ESLint are quite complex, with the main issues being that the new configuration file format requires us to rewrite configurations, and breaking changes in the API cause many used plugins to be unusable in 9.0.

However, I think this is a temporary issue; the new configuration file brings many useful new tools and usages, which is more beneficial than harmful. For example, ESLint Config Inspector can help us write and test configuration files better; it can dynamically generate configurations based on the dependencies installed in the project (only enabling rules related to React hooks in projects that have React installed).

The issues caused by breaking changes in the API can also be resolved in various ways:

  1. Write PRs for 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 while retaining the old API for compatibility.
  2. Temporarily use ESLint v8 and wait for plugins to adapt (we can still use Flat Config).
  3. Use the officially released ESLint Compatibility Utilities to help us upgrade.

Conclusion#

It is important to emphasize again that these are just my personal feelings and opinions, and there may be areas where my considerations are not accurate. I welcome you to communicate with me and share your views.

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

If you mainly write TypeScript and React, I also recommend trying my ESLint config. My philosophy for configuring rules is to use the rules preset by plugins as much as possible, adjusting them according to my habits, while providing 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.