Skip to content

Commit 6fa050a

Browse files
authored
docs: update ESLint documentation with ESLint v9 (#9515)
### Description ESLint v9 is now the standard, with ESLint v8 past EOL as of October 5, 2024. This PR updates our docs to describe how to use ESLint v9 Flat Configs in monorepos. Additionally, the READMEs for the packages are updated in this PR.
1 parent 2dd46cc commit 6fa050a

File tree

3 files changed

+212
-124
lines changed

3 files changed

+212
-124
lines changed

docs/repo-docs/guides/tools/eslint.mdx

Lines changed: 126 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -12,132 +12,175 @@ ESLint is a static analysis tool for quickly finding and fixing problems in your
1212

1313
<CreateTurboCallout />
1414

15-
<Callout type="info">
16-
This page is written for ESLint v8. If you'd like to contribute to add a
17-
version for v9, the core team is happy to review your pull request.
18-
</Callout>
15+
In this guide, we'll cover:
1916

20-
## Installing ESLint
17+
- [ESLint v9 with Flat Configuration](#eslint-v9-flat-configs)
18+
- [ESLint v8 with legacy configuration](#eslint-v8-legacy)
19+
- [How to set up a `lint` task (applies to both versions)](#setting-up-a-lint-task)
2120

22-
Install ESLint into each package where you'd like to run it:
21+
We will share configurations across the monorepo's Workspace, ensuring configuration is consistent across packages and composable to maintain high cache hit ratios.
2322

24-
<PackageManagerTabs>
25-
<Tab>
23+
## ESLint v9 (Flat Configs)
2624

27-
```bash title="Terminal"
28-
npm install eslint --workspace=web --workspace=docs --workspace=@repo/ui --save-dev
29-
```
25+
Using ESLint v9's Flat Configs, we will end up with a file structure like this:
3026

31-
<Callout type="good-to-know">
32-
You can keep ESLint versions in sync using a tool like
33-
[syncpack](https://jamiemason.github.io/syncpack).
34-
</Callout>
27+
<Files>
28+
<Folder name="apps" defaultOpen>
29+
<Folder name="docs" defaultOpen>
30+
<File name="package.json" />
31+
<File name="eslint.config.js" green />
32+
</Folder>
33+
34+
<Folder name="web" defaultOpen>
35+
<File name="package.json" />
36+
<File name="eslint.config.js" green />
37+
</Folder>
38+
39+
</Folder>
40+
41+
<Folder name="packages" defaultOpen>
42+
<Folder name="eslint-config" defaultOpen>
43+
<File name="base.js" green />
44+
<File name="next.js" green />
45+
<File name="react-internal.js" green />
46+
<File name="package.json" />
47+
</Folder>
48+
49+
<Folder name="ui" defaultOpen>
50+
<File name="eslint.config.js" green />
51+
<File name="package.json" />
52+
</Folder>
53+
54+
</Folder>
55+
</Files>
56+
57+
This structure includes:
58+
59+
- A package called `@repo/eslint-config` in `./packages/eslint-config` that holds all ESLint configuration
60+
- Two applications, each with their own `eslint.config.js`
61+
- A `ui` package that also has its own `eslint.config.js`
62+
63+
### About the configuration package
64+
65+
The `@repo/eslint-config` package has three configuration files, `base.js`, `next.js`, and `react-internal.js`. They are [exported from `package.json`](https://github.com/vercel/turborepo/blob/main/examples/basic/packages/eslint-config/package.json#L6) so that they can be used by other packages, according to needs. Examples of the configurations can be found [in the Turborepo GitHub repository](https://github.com/vercel/turborepo/tree/main/examples/basic/packages/eslint-config) and are available in `npx create-turbo@latest`.
66+
67+
Notably, the `next.js` and `react-internal.js` configurations use the `base.js` configuration for consistency, extending it with more configuration for their respective requirements. Additionally, notice that [the `package.json` for `eslint-config`](https://github.com/vercel/turborepo/blob/main/examples/basic/packages/eslint-config/package.json) has all of the ESLint dependencies for the repository. This is useful, since it means we don't need to re-specify the dependencies in the packages that import `@repo/eslint-config`.
68+
69+
### Using the configuration package
70+
71+
In our `web` app, we first need to add `@repo/eslint-config` as a dependency.
72+
73+
<PackageManagerTabs>
74+
<Tab>
75+
```jsonc title="./apps/web/package.json"
76+
{
77+
"devDependencies": {
78+
"@repo/eslint-config": "*"
79+
}
80+
}
81+
```
82+
</Tab>
83+
<Tab>
84+
```jsonc title="./apps/web/package.json"
85+
{
86+
"devDependencies": {
87+
"@repo/eslint-config": "*"
88+
}
89+
}
90+
```
91+
</Tab>
92+
<Tab>
93+
```jsonc title="./apps/web/package.json"
94+
{
95+
"devDependencies": {
96+
"@repo/eslint-config": "workspace:*"
97+
}
98+
}
99+
```
100+
</Tab>
101+
</PackageManagerTabs>
35102

36-
</Tab>
103+
We can then import the configuration like this:
37104

38-
<Tab>
105+
```js title="./apps/web/eslint.config.js"
106+
import { nextJsConfig } from '@repo/eslint-config/next-js';
39107

40-
```bash title="Terminal"
41-
yarn workspace web add eslint --dev
42-
yarn workspace docs add eslint --dev
43-
yarn workspace @repo/ui add eslint --dev
108+
/** @type {import("eslint").Linter.Config} */
109+
export default nextJsConfig;
44110
```
45111

46-
</Tab>
112+
Additionally, you can add configuration specific to the package like this:
47113

48-
<Tab>
114+
```js title="./apps/web/eslint.config.js"
115+
import { nextJsConfig } from "@repo/eslint-config/next-js";
49116

50-
```bash title="Terminal"
51-
pnpm install eslint --save-dev --filter=@repo/ui --filter=docs --filter=web
117+
/** @type {import("eslint").Linter.Config} */
118+
export default [
119+
...nextJsConfig;
120+
// Other configurations
121+
]
52122
```
53123

54-
</Tab>
55-
</PackageManagerTabs>
124+
## ESLint v8 (Legacy)
56125

57-
## Sharing configuration
126+
<Callout type="warn">
127+
ESLint v8 is end-of-life as of October 5, 2024. We encourage you to upgrade to
128+
ESLint v9 or later. This documentation is here to help with existing projects
129+
that have not yet upgraded.
130+
</Callout>
58131

59-
Sharing an ESLint config across packages makes their source code more consistent. Let's imagine a Workspace like this:
132+
Using legacy configuration from ESLint v8 and lower, we will end up with a file structure like this:
60133

61134
<Files>
62135
<Folder name="apps" defaultOpen>
63136
<Folder name="docs" defaultOpen>
64137
<File name="package.json" />
65-
<File name=".eslintrc.js" />
138+
<File name=".eslintrc.js" green />
66139
</Folder>
67140

68141
<Folder name="web" defaultOpen>
69142
<File name="package.json" />
70-
<File name=".eslintrc.js" />
143+
<File name=".eslintrc.js" green />
71144
</Folder>
72145

73146
</Folder>
74147

75-
<Folder name="packages">
76-
<Folder name="eslint-config">
77-
<File name="next.js" />
78-
<File name="library.js" />
148+
<Folder name="packages" defaultOpen>
149+
<Folder name="eslint-config" defaultOpen>
150+
<File name="base.js" green />
151+
<File name="next.js" green />
152+
<File name="react-internal.js" green />
153+
<File name="package.json" />
154+
</Folder>
155+
156+
<Folder name="ui" defaultOpen>
157+
<File name=".eslintrc.js" green />
79158
<File name="package.json" />
80159
</Folder>
160+
81161
</Folder>
82162
</Files>
83163

84-
We've got a package called `@repo/eslint-config`, and two applications, each with their own `.eslintrc.js`.
164+
There's a package called `@repo/eslint-config`, and two applications, each with their own `.eslintrc.js`.
85165

86-
### Our `@repo/eslint-config` package
166+
### The `@repo/eslint-config` package
87167

88-
Our `@repo/eslint-config` file contains two files, `next.js`, and `library.js`. These are two different ESLint configs, which we can use in different workspaces, depending on our needs.
168+
The `@repo/eslint-config` file contains two files, `next.js`, and `library.js`. These are two different ESLint configurations, which we can use in different packages, depending on our needs.
89169

90-
Let's investigate the `next.js` lint configuration:
170+
A configuration for Next.js may look like this:
91171

92172
```js title="./packages/eslint-config/next.js"
93-
const { resolve } = require('node:path');
94-
95-
const project = resolve(process.cwd(), 'tsconfig.json');
96-
97-
/*
98-
* This is a custom ESLint configuration for use with
99-
* Next.js apps.
100-
*
101-
* This config extends the Vercel Engineering Style Guide.
102-
* For more information, see https://github.com/vercel/style-guide
103-
*
104-
*/
105-
173+
/* Custom ESLint configuration for use with Next.js apps. */
106174
module.exports = {
107175
extends: [
108-
require.resolve('@vercel/style-guide/eslint/node'),
109-
require.resolve('@vercel/style-guide/eslint/typescript'),
110-
require.resolve('@vercel/style-guide/eslint/browser'),
111-
require.resolve('@vercel/style-guide/eslint/react'),
112-
require.resolve('@vercel/style-guide/eslint/next'),
113-
// Turborepo custom eslint configuration configures the following rules:
114-
// - https://github.com/vercel/turborepo/blob/main/packages/eslint-plugin-turbo/docs/rules/no-undeclared-env-vars.md
115176
'eslint-config-turbo',
177+
'eslint-config-next',
178+
// ...your other ESLint configurations
116179
].map(require.resolve),
117-
parserOptions: {
118-
project,
119-
},
120-
globals: {
121-
React: true,
122-
JSX: true,
123-
},
124-
settings: {
125-
'import/resolver': {
126-
typescript: {
127-
project,
128-
},
129-
},
130-
},
131-
ignorePatterns: ['node_modules/', 'dist/'],
132-
// add rules configurations here
133-
rules: {
134-
'import/no-default-export': 'off',
135-
},
180+
// ...your other configuration
136181
};
137182
```
138183

139-
It's a typical ESLint config that extends the [Vercel style guide](https://github.com/vercel/style-guide), nothing fancy.
140-
141184
The `package.json` looks like this:
142185

143186
```json title="./packages/eslint-config/package.json"
@@ -146,8 +189,9 @@ The `package.json` looks like this:
146189
"version": "0.0.0",
147190
"private": true,
148191
"devDependencies": {
149-
"@vercel/style-guide": "latest",
150-
"eslint-config-turbo": "latest"
192+
"eslint": "^8",
193+
"eslint-config-turbo": "latest",
194+
"eslint-config-next": "latest"
151195
}
152196
}
153197
```
@@ -199,10 +243,6 @@ module.exports = {
199243

200244
By adding `@repo/eslint-config/next.js` to our `extends` array, we're telling ESLint to look for a package called `@repo/eslint-config`, and reference the file `next.js`.
201245

202-
### Summary
203-
204-
This setup ships by default when you [create a new monorepo](/repo/docs/crafting-your-repository#from-zero-to-turbo) with `npx create-turbo@latest`. You can also look at [our basic example](https://github.com/vercel/turborepo/tree/main/examples/basic) to see a working version.
205-
206246
## Setting up a `lint` task
207247

208248
The `package.json` for each package where you'd like to run ESLint should look like this:

packages/eslint-config-turbo/README.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,39 @@ npm install eslint --save-dev
1616
npm install eslint-config-turbo --save-dev
1717
```
1818

19-
## Usage
19+
## Usage (Flat Config `eslint.config.js`)
20+
21+
```js
22+
import turboConfig from "eslint-config-turbo/flat";
23+
24+
export default [
25+
...turboConfig,
26+
// Other configuration
27+
];
28+
```
29+
30+
You can also configure rules available in the configuration:
31+
32+
```js
33+
import turboConfig from "eslint-config-turbo/flat";
34+
35+
export default [
36+
...turboConfig,
37+
// Other configuration
38+
{
39+
rules: {
40+
"turbo/no-undeclared-env-vars": [
41+
"error",
42+
{
43+
allowList: ["^ENV_[A-Z]+$"],
44+
},
45+
],
46+
},
47+
},
48+
];
49+
```
50+
51+
## Usage (Legacy `eslintrc*`)
2052

2153
Add `turbo` to the extends section of your eslint configuration file. You can omit the `eslint-config-` prefix:
2254

@@ -25,3 +57,19 @@ Add `turbo` to the extends section of your eslint configuration file. You can om
2557
"extends": ["turbo"]
2658
}
2759
```
60+
61+
You can also configure rules available in the configuration:
62+
63+
```json
64+
{
65+
"plugins": ["turbo"],
66+
"rules": {
67+
"turbo/no-undeclared-env-vars": [
68+
"error",
69+
{
70+
"allowList": ["^ENV_[A-Z]+$"]
71+
}
72+
]
73+
}
74+
}
75+
```

0 commit comments

Comments
 (0)