A practical end-to-end guide for migrating an existing .eslintrc.json
config (ESLint v8 and before) to the new flat file config eslint.config.js
(ESLint v9 and above) in a CommonJS Node.js project including linting of unit-tests with Jest.
This article comes along with a public GitHub example repository enabling you to comprehend everything and easily switch between before/after migration state.
Table of Contents
Starting point: existing eslintrc configuration
In this migration guide we’ll use a very standard ESLint configuration set which should cover basic linting for the vast majority of your projects.
So far, the usual way to configure ESLint in Node.js was to place an .eslintrc.json
file in the root folder of the project. The below .eslintrc.json
covers all the mentioned points and serves as the basis for this guide.
{
"env": {
"node": true,
"commonjs": true,
"es6": true,
"jest": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"indent": [
"error",
4,
{
"SwitchCase": 1
}
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
],
"no-unused-vars": [
2,
{
"args": "after-used",
"argsIgnorePattern": "^_"
}
]
}
}
Additionally, you might have a .eslintignore
file placed in the root folder of the project to exclude files and paths from linting, e.g. to exclude the two directories conf
and coverage
– like so:
conf/
coverage/
Errors after upgrading ESLint to v9
Having this configuration in place you’ll notice that your environment, in this case VSCode, is coming up with an error after upgrading to ESLint v9. The highlighting of linting errors and warnings also isn’t working any more.
Having a look in the ESLint output quickly gives you the reason why.
[Info - 20:51:44] ESLint server is starting.
[Info - 20:51:44] ESLint server running in node v20.14.0
[Info - 20:51:44] ESLint server is running.
[Info - 20:51:46] ESLint library loaded from: /home/tsmx/projects/weather-tools/node_modules/eslint/lib/api.js
(node:4117) ESLintIgnoreWarning: The ".eslintignore" file is no longer supported. Switch to using the "ignores" property in "eslint.config.js": https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files
(Use `code --trace-warnings ...` to show where the warning was created)
[Error - 20:51:46] Calculating config file for file:///home/tsmx/projects/weather-tools/weather-tools.js) failed.
Error: Could not find config file.
Starting with ESLint v9.0.0, the default configuration was changed to flat file and eslintrc.json
as well as eslintignore
became deprecated. Although it’s possible to continue using eslintrc.json, it’s recommended to switch to the new file format being future-proof.
Migrating to the new flat file configuration
For a CommonJS project, the new flat file configuration is a normal JavaScript file called eslint.config.js
which is placed in the root folder and simply exports an array of ESLint configuration objects via module.exports
.
Installing needed dev dependencies
The flat file config doesn’t contain an env
section anymore that allows you to specify ESLint is running in Node.js and enabling Jest features for correct linting of unit-test files. Also, the recommended ruleset has been outsourced to an own module.
To include all these features in the new ESLint v9 configuration, you’ll need to install the following dependencies in your project.
As these dependencies are only used for ESLint, you should install them – like ESLint itself – as dev dependencies in your Node.js project.
# npm install @eslint/js eslint-plugin-jest globals --save-dev
Creating eslint.config.js
Next, in the root folder of your Node.js project, create an eslint.config.js
file with the following contents. This will lead to an almost identical, yet more customizable, linting behaviour as the old .eslintrc.json
did.
const { configs } = require('@eslint/js');
const jest = require('eslint-plugin-jest');
const globals = require('globals');
module.exports = [
configs.recommended,
{
languageOptions: {
ecmaVersion: 2018,
sourceType: 'commonjs',
globals: {
...globals.node,
...globals.jest,
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
}
},
rules: {
semi: 'error',
quotes: ['error', 'single'],
indent: ['error', 4, { 'SwitchCase': 1 }],
'no-unused-vars':
[
'warn',
{
'varsIgnorePattern': '^_',
'args': 'after-used',
'argsIgnorePattern': '^_'
}
]
},
ignores: ['conf/', 'coverage/']
},
{
languageOptions: {
globals: { ...globals.jest }
},
files: ['test/*.test.js'],
...jest.configs['flat/recommended'],
rules: {
...jest.configs['flat/recommended'].rules
}
}
];
Thats’s already it. Linting now should work again as expected and you can safely delete the old .eslintrc.json
as well as .eslintignore
in your project.
Breakdown of the new flat file configuration
As noted before, the flat file configuration is simply an exported array of ESLint configuration objects. Based on our eslintrc.json we want to migrate, this array will have three entries.
Part 1: Importing recommended ESLint ruleset
First element of the configuration array should be the recommended ruleset that is delivered by the @eslint/js package. This line is the replacement for the "extends": "eslint:recommended"
entry in the old eslintrc.
configs.recommended
Part 2: Custom rules for normal JavaScript code files and files to be ignored
Next object in the configuration array holds all our own custom rules and properties for normal JavaScript code files as well as the patterns of all files/folders that shout be ignored bei ESLint.
{
languageOptions: {
ecmaVersion: 2018,
sourceType: 'commonjs',
globals: {
...globals.node,
...globals.jest,
Atomics: 'readonly',
SharedArrayBuffer: 'readonly'
}
},
rules: {
semi: 'error',
quotes: ['error', 'single'],
indent: ['error', 4, { 'SwitchCase': 1 }],
'no-unused-vars':
[
'warn',
{
'varsIgnorePattern': '^_',
'args': 'after-used',
'argsIgnorePattern': '^_'
}
]
},
ignores: ['conf/', 'coverage/']
}
This section is quite self-explanatory when compared to the old eslintrc configuration. The key differences are:
The linting rules themselves are quite unchanged in the new configuration style.
Part 3: Rules for linting Jest test files
The last needed configuration object is for correct linting of Jest tests. There is no "jest": true
option anymore which was very simple. Instead, we’ll need to import the eslint-plugin-jest package and use recommended rules out of it. In the example, all Jest test files are located in the projects folder test/
and have the common extension .test.js
.
The resulting configuration object for our eslint.config.js
is:
{
languageOptions: {
globals: { ...globals.jest }
},
files: ['test/*.test.js'],
...jest.configs['flat/recommended'],
rules: {
...jest.configs['flat/recommended'].rules
}
}
This ensures a proper linting of all Jest tests located under test/
. If you have test files located in other/additional locations, simply add them to the files
property.
Note: If you use Node.js globals like process
in your Jest tests, you should add ...globals.node
to the globals property. This prevents ESLint from reporting those globals as undefined variables.
Example project on GitHub
To see a practical working example of a migration before and after, clone the eslintrc-to-flatfile GitHub repository and browse through the branches eslint-v8
and eslint-v9
. The code files contain several example errors in formatting, quoting etc. that should be highlighted as linting errors or warnings. For details see the code comments.
# clone the example project
git clone https://github.com/tsmx/eslintrc-to-flatfile.git
# check out/switch to the original ESLint v8 branch using eslintrc.json
git checkout eslint-v8
npm install
# check out/switch to the migrated ESLint v9 branch using new eslint.config.js
git checkout eslint-v9
npm install