New ESLint flat config file format example
Tags: eslint, tutorial, webdev
Published: 2024-04-10 22:00:25

---
title: New ESLint flat config file format example
published: true
description: An example of a working config of the new ESLint format for a project with React and Typescript
tags: eslint, tutorial, webdev
# cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3e8gxx4amtatn54uty97.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2024-04-10 13:00 +0000
---

> <sub>This note does not pretend to be complete - in fact, I don’t even want to understand how this junk works under the hood. However, it might save someone a day of work.</sub>

For some reason, the ESLint developers once again changed (this 9.0.0 v. is the second or third time in my memory) the configuration file format and plugin packages, so now the old configs do not work, so adding a linter to a new project may cause a problem. In fact, thanks to the efforts of these wonderful people, even this note will most likely soon become useless.


## Official way
To get started, you can use the command that is recommended to use for setup in the official guide:

`npm init @eslint/config`

However, after this, simply running the linter will most likely not produce any result. Even if you run the linter with the correct command specifying the files to check, it still won’t work:
![ESLint does not work after installing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f46qrzgxytmtpeqe1qcr.jpg)

The most interesting thing here is that the [official guide](https://eslint.org/docs/latest/use/getting-started) gives a completely empty example of the config, as if you did not execute their command `npm init @eslint/config`

After running this command, the config will look something like this (if they haven’t changed anything again, lol):

```JS
import path from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
import pluginJs from "@eslint/js";

// mimic CommonJS variables -- not needed if using CommonJS
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
  baseDirectory: __dirname,
  recommendedConfig: pluginJs.configs.recommended
});

export default [
  ...compat.extends("standard-with-typescript")
];
```
**Example of the eslint.config.js for the new version**


## Another way
Since the official config, apparently, is quite crooked (and, to be honest, I don’t even know if it works correctly), you may want to install the linter yourself. For this config you will need to install the following packages in the project:
```JSON
{
  "devDependencies": {
    ... // your other deps
    "@eslint/eslintrc": "^3.0.2",
    "@eslint/js": "^9.0.0",
    "@typescript-eslint/eslint-plugin": "^6.21.0",
    "eslint": "^8.57.0",
    "eslint-config-standard-with-typescript": "^43.0.1",
    "eslint-plugin-import": "^2.29.1",
    "eslint-plugin-n": "^16.6.2",
    "eslint-plugin-promise": "^6.1.1"
  }
}
```

As i already said, just after install linter wouldn't work anyway, because you still need add files section in config and, possible add some your own rules to it.

I found out that if need to add rules to the config file, you must do it in a certain way: since we already have some rules created by config command, we don't need to just add new ones, but do it next way:

```JS
import path from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
import pluginJs from "@eslint/js";

// mimic CommonJS variables -- not needed if using CommonJS
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
  baseDirectory: __dirname,
  recommendedConfig: pluginJs.configs.recommended
});

export default [
  ...compat.extends("standard-with-typescript").map(
    config => ({
      ...config,
      files: ["**/*.tsx"],
      rules: {
         ...config.rules,
         "semi": "off", // For some reason the old rule is buggy and needs to be disabled IDK
         "@typescript-eslint/semi": ["error", "always"],
         "no-unreachable": "error", // Already in ts, but perhaps it catches more cases
         "@typescript-eslint/explicit-function-return-type": "off",
         "@typescript-eslint/strict-boolean-expressions": "off", // Often works incorrectly
         "@typescript-eslint/no-floating-promises": "off",
      }
    })
  ),
];

```
**Working example of eslint.config.js with [custom rules](https://eslint.org/docs/latest/rules/)**

Now you can start the linter with the following command (for some reason it is still necessary to additionally specify files in the console, i have no idea why):
`npx eslint 'src/**/*.{ts,tsx}' --no-ignore`

You can add it to your scripts section of `package.json` for easy launch via `npm run`.


## What's next? Code formatting?
The next stage, which may throw you into frustration, is that it turns out that the new linter now does not know how to properly format code out of the box.

Now a linter is purely a linter. Wow! Such great idea!

You can read about it [here (the indentation problem that no one wanted to fix)](https://github.com/typescript-eslint/typescript-eslint/issues/1824) and [here (about the integration between a linter and a formatter)](https://prettier.io/docs/en/integrating-with-linters.html), and [here is even more detailed information about the “problems” with using linters as formatters](https://typescript-eslint.io/troubleshooting/formatting/).

So, in addition to the file with the linter settings, you will also have to add a `.prettierrc` file with the prettier settings:
```JSON
{
  "tabWidth": 4,
  "singleQuote": true
}
```

...as well as install the prettier itself:

```BASH
npm install --save-dev eslint-plugin-prettier eslint-config-prettier
npm install --save-dev --save-exact prettier
```

Yes, and besides, you will once again have to correct the linter config. Here's the final version:

```JS
import path from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
import pluginJs from "@eslint/js";
import prettierConfig from 'eslint-config-prettier';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
  baseDirectory: __dirname,
  recommendedConfig: pluginJs.configs.recommended
});

export default [
  ...compat.extends("standard-with-typescript").map(
    config => ({
      ...config,
      files: ["**/*.tsx"],
      rules: {
         ...config.rules,
         // ...other your custom rules
      }
    })
  ),
  prettierConfig, // Turns off all ESLint rules that have the potential to interfere with Prettier rules.
  eslintPluginPrettierRecommended
];
```


> Note: While it is possible to pass options to Prettier via your ESLint configuration file, it is not recommended because editor extensions such as prettier-atom and prettier-vscode will read .prettierrc, but won't read settings from ESLint, which can lead to an inconsistent experience.

Well, now, after all these crutches and a bunch of ~~new garbage~~ wonderful files in the root of your project, you can now use the linter. I think those who have read to the end already understand why the linter is a lousy tool in general.

But everything should work now. I hope this is useful to someone.



> <sub><sub>As for my opinion, ESLint is absolute garbage, a perfect example of a garbage application, so if you have a choice, try looking for something else. In my company it is a standard tool, so for years i have had to deal with changing config formats, constant renaming of plugin packages (FOR THAT?) and compatibility problems caused by this junk software. ESLint you must burn in hell!</sub></sub>