From 6fa050a986e6edeb3075886382746b040aae9dfb Mon Sep 17 00:00:00 2001 From: Anthony Shew Date: Mon, 25 Nov 2024 13:53:36 -0700 Subject: [PATCH] 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. --- docs/repo-docs/guides/tools/eslint.mdx | 212 +++++++++++++++---------- packages/eslint-config-turbo/README.md | 50 +++++- packages/eslint-plugin-turbo/README.md | 74 ++++----- 3 files changed, 212 insertions(+), 124 deletions(-) diff --git a/docs/repo-docs/guides/tools/eslint.mdx b/docs/repo-docs/guides/tools/eslint.mdx index f11dd047d5d41..14b30df52238f 100644 --- a/docs/repo-docs/guides/tools/eslint.mdx +++ b/docs/repo-docs/guides/tools/eslint.mdx @@ -12,132 +12,175 @@ ESLint is a static analysis tool for quickly finding and fixing problems in your - - This page is written for ESLint v8. If you'd like to contribute to add a - version for v9, the core team is happy to review your pull request. - +In this guide, we'll cover: -## Installing ESLint +- [ESLint v9 with Flat Configuration](#eslint-v9-flat-configs) +- [ESLint v8 with legacy configuration](#eslint-v8-legacy) +- [How to set up a `lint` task (applies to both versions)](#setting-up-a-lint-task) -Install ESLint into each package where you'd like to run it: +We will share configurations across the monorepo's Workspace, ensuring configuration is consistent across packages and composable to maintain high cache hit ratios. - - +## ESLint v9 (Flat Configs) -```bash title="Terminal" -npm install eslint --workspace=web --workspace=docs --workspace=@repo/ui --save-dev -``` +Using ESLint v9's Flat Configs, we will end up with a file structure like this: - - You can keep ESLint versions in sync using a tool like - [syncpack](https://jamiemason.github.io/syncpack). - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +This structure includes: + +- A package called `@repo/eslint-config` in `./packages/eslint-config` that holds all ESLint configuration +- Two applications, each with their own `eslint.config.js` +- A `ui` package that also has its own `eslint.config.js` + +### About the configuration package + +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`. + +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`. + +### Using the configuration package + +In our `web` app, we first need to add `@repo/eslint-config` as a dependency. + + + +```jsonc title="./apps/web/package.json" +{ + "devDependencies": { + "@repo/eslint-config": "*" + } +} +``` + + +```jsonc title="./apps/web/package.json" +{ + "devDependencies": { + "@repo/eslint-config": "*" + } +} +``` + + +```jsonc title="./apps/web/package.json" +{ + "devDependencies": { + "@repo/eslint-config": "workspace:*" + } +} +``` + + - +We can then import the configuration like this: - +```js title="./apps/web/eslint.config.js" +import { nextJsConfig } from '@repo/eslint-config/next-js'; -```bash title="Terminal" -yarn workspace web add eslint --dev -yarn workspace docs add eslint --dev -yarn workspace @repo/ui add eslint --dev +/** @type {import("eslint").Linter.Config} */ +export default nextJsConfig; ``` - +Additionally, you can add configuration specific to the package like this: - +```js title="./apps/web/eslint.config.js" +import { nextJsConfig } from "@repo/eslint-config/next-js"; -```bash title="Terminal" -pnpm install eslint --save-dev --filter=@repo/ui --filter=docs --filter=web +/** @type {import("eslint").Linter.Config} */ +export default [ + ...nextJsConfig; + // Other configurations +] ``` - - +## ESLint v8 (Legacy) -## Sharing configuration + + ESLint v8 is end-of-life as of October 5, 2024. We encourage you to upgrade to + ESLint v9 or later. This documentation is here to help with existing projects + that have not yet upgraded. + -Sharing an ESLint config across packages makes their source code more consistent. Let's imagine a Workspace like this: +Using legacy configuration from ESLint v8 and lower, we will end up with a file structure like this: - + - + - - - - + + + + + + + + + + + -We've got a package called `@repo/eslint-config`, and two applications, each with their own `.eslintrc.js`. +There's a package called `@repo/eslint-config`, and two applications, each with their own `.eslintrc.js`. -### Our `@repo/eslint-config` package +### The `@repo/eslint-config` package -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. +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. -Let's investigate the `next.js` lint configuration: +A configuration for Next.js may look like this: ```js title="./packages/eslint-config/next.js" -const { resolve } = require('node:path'); - -const project = resolve(process.cwd(), 'tsconfig.json'); - -/* - * This is a custom ESLint configuration for use with - * Next.js apps. - * - * This config extends the Vercel Engineering Style Guide. - * For more information, see https://github.com/vercel/style-guide - * - */ - +/* Custom ESLint configuration for use with Next.js apps. */ module.exports = { extends: [ - require.resolve('@vercel/style-guide/eslint/node'), - require.resolve('@vercel/style-guide/eslint/typescript'), - require.resolve('@vercel/style-guide/eslint/browser'), - require.resolve('@vercel/style-guide/eslint/react'), - require.resolve('@vercel/style-guide/eslint/next'), - // Turborepo custom eslint configuration configures the following rules: - // - https://github.com/vercel/turborepo/blob/main/packages/eslint-plugin-turbo/docs/rules/no-undeclared-env-vars.md 'eslint-config-turbo', + 'eslint-config-next', + // ...your other ESLint configurations ].map(require.resolve), - parserOptions: { - project, - }, - globals: { - React: true, - JSX: true, - }, - settings: { - 'import/resolver': { - typescript: { - project, - }, - }, - }, - ignorePatterns: ['node_modules/', 'dist/'], - // add rules configurations here - rules: { - 'import/no-default-export': 'off', - }, + // ...your other configuration }; ``` -It's a typical ESLint config that extends the [Vercel style guide](https://github.com/vercel/style-guide), nothing fancy. - The `package.json` looks like this: ```json title="./packages/eslint-config/package.json" @@ -146,8 +189,9 @@ The `package.json` looks like this: "version": "0.0.0", "private": true, "devDependencies": { - "@vercel/style-guide": "latest", - "eslint-config-turbo": "latest" + "eslint": "^8", + "eslint-config-turbo": "latest", + "eslint-config-next": "latest" } } ``` @@ -199,10 +243,6 @@ module.exports = { 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`. -### Summary - -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. - ## Setting up a `lint` task The `package.json` for each package where you'd like to run ESLint should look like this: diff --git a/packages/eslint-config-turbo/README.md b/packages/eslint-config-turbo/README.md index 834b887134852..fac8e1dd81f7e 100644 --- a/packages/eslint-config-turbo/README.md +++ b/packages/eslint-config-turbo/README.md @@ -16,7 +16,39 @@ npm install eslint --save-dev npm install eslint-config-turbo --save-dev ``` -## Usage +## Usage (Flat Config `eslint.config.js`) + +```js +import turboConfig from "eslint-config-turbo/flat"; + +export default [ + ...turboConfig, + // Other configuration +]; +``` + +You can also configure rules available in the configuration: + +```js +import turboConfig from "eslint-config-turbo/flat"; + +export default [ + ...turboConfig, + // Other configuration + { + rules: { + "turbo/no-undeclared-env-vars": [ + "error", + { + allowList: ["^ENV_[A-Z]+$"], + }, + ], + }, + }, +]; +``` + +## Usage (Legacy `eslintrc*`) Add `turbo` to the extends section of your eslint configuration file. You can omit the `eslint-config-` prefix: @@ -25,3 +57,19 @@ Add `turbo` to the extends section of your eslint configuration file. You can om "extends": ["turbo"] } ``` + +You can also configure rules available in the configuration: + +```json +{ + "plugins": ["turbo"], + "rules": { + "turbo/no-undeclared-env-vars": [ + "error", + { + "allowList": ["^ENV_[A-Z]+$"] + } + ] + } +} +``` diff --git a/packages/eslint-plugin-turbo/README.md b/packages/eslint-plugin-turbo/README.md index 21299b643f0f8..cc8f6367ffdb4 100644 --- a/packages/eslint-plugin-turbo/README.md +++ b/packages/eslint-plugin-turbo/README.md @@ -16,45 +16,9 @@ npm install eslint --save-dev npm install eslint-plugin-turbo --save-dev ``` -## Usage (Legacy `eslintrc*`) - -Add `turbo` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix: - -```json -{ - "plugins": ["turbo"] -} -``` - -Then configure the rules you want to use under the rules section. - -```json -{ - "rules": { - "turbo/no-undeclared-env-vars": "error" - } -} -``` - -## Example (Legacy `eslintrc*`) - -```json -{ - "plugins": ["turbo"], - "rules": { - "turbo/no-undeclared-env-vars": [ - "error", - { - "allowList": ["^ENV_[A-Z]+$"] - } - ] - } -} -``` - ## Usage (Flat Config `eslint.config.js`) -In ESLint v8, both the legacy system and the new flat config system are supported. In ESLint v9, only the new system will be supported. See the [official ESLint docs](https://eslint.org/docs/latest/use/configure/configuration-files). +ESLint v9 uses the Flat Config format seen below: ```js import turbo from "eslint-plugin-turbo"; @@ -100,3 +64,39 @@ export default [ }, ]; ``` + +## Usage (Legacy `eslintrc*`) + +Add `turbo` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix: + +```json +{ + "plugins": ["turbo"] +} +``` + +Then configure the rules you want to use under the rules section. + +```json +{ + "rules": { + "turbo/no-undeclared-env-vars": "error" + } +} +``` + +## Example (Legacy `eslintrc*`) + +```json +{ + "plugins": ["turbo"], + "rules": { + "turbo/no-undeclared-env-vars": [ + "error", + { + "allowList": ["^ENV_[A-Z]+$"] + } + ] + } +} +```