Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(docs): update lint in handbook #5812

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 66 additions & 19 deletions docs/pages/repo/docs/handbook/linting/eslint.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Tabs, Tab } from "../../../../../components/Tabs";
import Callout from '../../../../../components/Callout'

# ESLint in a monorepo

## Installing ESLint

Keeping a single version of ESLint is recommended for simplicity. Because of this, we suggest installing ESLint as `devDependency` at the root of your monorepo, or utilizing a tool like [syncpack](https://jamiemason.github.io/syncpack/) to maintain the same version across workspaces.

## Sharing config

Sharing an ESLint config across workspaces can be a boon to productivity by making all your workspaces more consistent.
Expand All @@ -18,47 +23,85 @@ apps
└─ .eslintrc.js
packages
└─ eslint-config-custom
├─ index.js
├─ next.js
├─ library.js
└─ package.json
```

We've got a package called `eslint-config-custom`, and two applications, each with their own `.eslintrc.js`.

### Our `eslint-config-custom` package

Our `eslint-config-custom` file contains only a single file, `index.js`. It looks like this.
Our `eslint-config-custom` 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.

Let's investigate the `next.js` lint configuration:

```js filename="packages/eslint-config-custom/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
*
*/

```js filename="packages/eslint-config-custom/index.js"
module.exports = {
extends: ["next", "turbo", "prettier"],
extends: [
"@vercel/style-guide/eslint/node",
"@vercel/style-guide/eslint/typescript",
"@vercel/style-guide/eslint/browser",
"@vercel/style-guide/eslint/react",
"@vercel/style-guide/eslint/next",
// turborepo custom eslint configuration configures the following rules:
// - https://github.com/vercel/turbo/blob/main/packages/eslint-plugin-turbo/docs/rules/no-undeclared-env-vars.md
"eslint-config-turbo",
].map(require.resolve),
parserOptions: {
project,
},
globals: {
React: true,
JSX: true,
},
settings: {
"import/resolver": {
typescript: {
project,
},
},
},
ignorePatterns: ["node_modules/", "dist/"],
// add rules configurations here
rules: {
"@next/next/no-html-link-for-pages": "off",
"import/no-default-export": "off",
},
};
```

It's a typical ESLint config, nothing fancy.
It's a typical ESLint config that extends the [Vercel styleguide](https://github.com/vercel/style-guide), nothing fancy.

The `package.json` looks like this:

```json filename="packages/eslint-config-custom/package.json"
{
"name": "eslint-config-custom",
"main": "index.js",
"version": "1.0.0",
"dependencies": {
"eslint": "latest",
"eslint-config-next": "latest",
"eslint-config-prettier": "latest",
"eslint-plugin-react": "latest",
"eslint-config-turbo": "latest"
"license": "MIT",
"version": "0.0.0",
"private": true,
"devDependencies": {
"@vercel/style-guide": "^4.0.2",
"eslint-config-turbo": "^1.10.12"
}
}
```

Two things are notable here. First, the `main` field points to `index.js`. This allows files to easily [import this config](/repo/docs/handbook/sharing-code#anatomy-of-a-package).
```

Secondly, the ESLint dependencies are all listed here. This is useful - it means we don't need to re-specify the dependencies inside the apps which import `eslint-config-custom`.
Note that the ESLint dependencies are all listed here. This is useful - it means we don't need to re-specify the dependencies inside the apps which import `eslint-config-custom`.

### How to use the `eslint-config-custom` package

Expand Down Expand Up @@ -99,11 +142,15 @@ We can then import the config like this:
```js filename="apps/web/.eslintrc.js"
module.exports = {
root: true,
extends: ["custom"],
extends: ["custom/next"],
};
```

By adding `custom` to our `extends` array, we're telling ESLint to look for a package called `eslint-config-custom` - and it finds our workspace.
By adding `custom/next` to our `extends` array, we're telling ESLint to look for a package called `eslint-config-custom`, and reference the file `next.js`.

<Callout type="info">
You can avoid specifing the file by setting the entrypoint for your package as the file to load. For example, setting `main: 'next.js'` in the `package.json`, could be loaded as simply `extends: ["custom"]` in your `.eslintrc.js`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can avoid specifing the file by setting the entrypoint for your package as the file to load

This sentence feels a little confusing to me, but I'm not sure how to fix it. The following sentence makes it crystal clear so I feel like maybe keeping the first one brief would help.

I think that "You can avoid specifing the file by setting the entrypoint for your package." followed by the example is clear enough.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call - updated here: #5831

</Callout>

### Summary

Expand Down