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: Convert to ESLint Flat Config #207

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
7a3dca1
converting base files
bsokol-wl May 1, 2024
2cb766b
more updates
bsokol-wl May 2, 2024
28fba7e
fixing workleap plugin
bsokol-wl May 2, 2024
c7d8573
cleanup
bsokol-wl May 2, 2024
6c84195
documentation and bug fixes
bsokol-wl May 3, 2024
3d72e34
Refactor to consistent arrays
bsokol-wl May 6, 2024
8d2bcff
Removing unnecessary config values
bsokol-wl May 6, 2024
8464507
fixing node globals issues
bsokol-wl May 6, 2024
b6a5e1d
fixing failing tests
bsokol-wl May 6, 2024
c44f94a
fix import build error
bsokol-wl May 6, 2024
9ccb3c3
convert remaining eslint configs
bsokol-wl May 6, 2024
b6ed33c
let knip analyze eslint plugin
bsokol-wl May 6, 2024
217327a
Updating sample app
bsokol-wl May 6, 2024
10accd0
fix typo in ignore
bsokol-wl May 6, 2024
fce525a
update docs
bsokol-wl May 7, 2024
cc54e44
add limited scope helper
bsokol-wl May 7, 2024
cb81189
update config for single file
bsokol-wl May 7, 2024
67151c2
update to use antfu helpers
bsokol-wl May 8, 2024
ec737d1
remove unused leftovers
bsokol-wl May 8, 2024
a5d8188
update lockfile
bsokol-wl May 8, 2024
b8c6646
clean up samples
bsokol-wl May 8, 2024
565bb69
add react global
bsokol-wl May 8, 2024
9c5b87b
move configs back to individual packages
bsokol-wl May 9, 2024
ae5a507
remove useless test
bsokol-wl May 9, 2024
4aea643
Merge branch 'main' into feat/eslint-flat-config
bsokol-wl May 9, 2024
1647bae
update ci to use pnpm 9
bsokol-wl May 9, 2024
c5df596
clean up unused scripts
bsokol-wl May 9, 2024
538d663
re-add removed self-dependencies
bsokol-wl May 9, 2024
ae489a9
PR updates
bsokol-wl May 10, 2024
cf980fa
fix more PR comments
bsokol-wl May 10, 2024
8e46c5c
Fix knip
patricklafrance May 10, 2024
2dab2ae
fix config naming
bsokol-wl May 10, 2024
fd7f027
update all documentation
bsokol-wl May 10, 2024
f7e3f98
add knip issue info
bsokol-wl May 10, 2024
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
5 changes: 5 additions & 0 deletions .changeset/tidy-mangos-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@workleap/eslint-plugin": major
---

Convert all configs to flat config. Update documentation.
5 changes: 0 additions & 5 deletions .eslintignore

This file was deleted.

7 changes: 0 additions & 7 deletions .eslintrc.json

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
name: Install pnpm
id: pnpm-install
with:
version: 8
version: 9
run_install: false

- name: Get pnpm store directory
Expand Down
206 changes: 206 additions & 0 deletions docs/eslint/flat-config-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
---
order: 50
label: Migrate to Flat Config
meta:
title: Migrate to Flat Config
---

# Migrate to flat config

Flat config is the new default configuration format for ESLint. It is supported since version 8.23.0, and is the default starting in version 9. Detailed information regarding this format can be fount in 2 blog posts: [Background](https://eslint.org/blog/2022/08/new-config-system-part-1/) and [Introduction to flat config](https://eslint.org/blog/2022/08/new-config-system-part-2/), as well as the [official docs](https://eslint.org/docs/latest/use/configure/).

## High level differences

Previously, ESLint allowed you to use multiple formats to define your config files. Flat config can only use JavaScript. You can import plugins and pre-made configuration objects directly, and manipulate them as necessary.

The `extends` keyword has been removed. Now you simply add multiple _configuration objects_ to an array. Configuration objects will cascade, similar to the `overrides` block of the old config.

The `.eslintignore` file is no longer valid. If you need to exclude files from linting, add them to a configuration block under the `ignores` key.

Config files no longer rely on custom resolution implemented by ESLint. Since they import modules directly, they now rely on Node file resolution. Config files no longer merge down the file tree, either. Every config file acts as if it is the root config file. This means if you have nested configs as well as a true root config (like in a monorepo), you will want to set your top-level config to ignore directories that have their own config files.

## Basic migration steps

1. Create a file called `eslint.config.js`. `@workleap/eslint-config` is published in ESM, so if your project `type` in `package.json` is not `module`, then you should create an `eslint.config.mjs` file instead.

### Initial setup

Import the `@workleap/eslint-config` module. Create a config array and set it as the default export.
```javascript
import workleapPlugin from "@workleap/eslint-config";

const config = [

];

export default config;
```

### Ignoring files

ESLint will no longer use the `.eslintignore` file. If you have one of these files, create a new configuration object with an `ignores` key:

```javascript
import workleapPlugin from "@workleap/eslint-config";

const config = [
{
ignores: ["node_modules/", "dist/"]
}
];

export default config;
```

## Recommended setup - By project type

`@workleap/eslint-config` exposes some pre-built configs based on common project types. Each of these configs are properly set up for **JavaScript**, **TypeScript**, **Jest**, **Testing Library**, **MDX**, **package.json**, and **YAML**.

By convention, all configs are found at `workleapPlugin.configs`. A flat config can be a single object or an array, but for simplicity, all Workleap configs are exported as arrays. Therefore, each Workleap config must be spread (`...`) into the config array.

| Type | Config Key | Purpose | Additional Configs |
|---|---|---|---|
| Web Application | `configs.webApplication` | General purpose web application using React and TypeScript | React<br>JSX A11y<br>Storybook |
| TypeScript Library | `configs.typeScriptLibrary` | For building a TypeScript library to be consumed by another project | |
| React Library | `configs.reactLibrary` | For building a React library to be consumed by another project | React<br>JSX A11y<br>Storybook |
| Monorepo Workspace | `configs.monorepoWorkspace` | For the top level of a monorepo | |

For example, to configure ESLint for a React web application, add the project config to your config array:
```javascript
import workleapPlugin from "@workleap/eslint-config";

const config = [
{
ignores: ["node_modules/", "dist/"]
},
...workleapPlugin.configs.webApplication
];

export default config;
```

You can override individual rules across all configs by adding another configuration object to the array:
```javascript
import workleapPlugin from "@workleap/eslint-config";

const config = [
{
ignores: ["node_modules/", "dist/"]
},
...workleapPlugin.configs.webApplication,
{
rules: {
'react/jsx-uses-vars': 'error',
}
}
];

export default config;
```

You can now delete your previous `.eslintrc` config file, as well as your `.eslintignore` file, if you have one. Please refer to the [official migration guide](https://eslint.org/docs/latest/use/configure/migration-guide) for more details.

## Example monorepo setup

Monorepo setups can be more complex, because each package should have its own ESLint rules. With flat config, ESLint will use which ever `eslint.config.js` is the first to be found by traversing up from the directory in which the `eslint` command was run. This means that, by default, individual ESLint configs within monorepo packages will be ignored if ESLint is run from the root directory.

We can mimic the old ESLint behavior by importing each package's ESLint config into the top level and merging them together.

### Monorepo packages

Inside each monorepo package, create a `eslint.config.js` file. Add the config module that matches the package type. For example, a web application would use the `webApplication` config, while a component libary would use the `reactLibrary` config:
```javascript
import workleapPlugin from "@workleap/eslint-config";

const config = workleapPlugin.configs.reactLibrary;

export default config;
```

You can also set custom ignore rules:
```javascript
import workleapPlugin from "@workleap/eslint-config";

const config = [
...workleapPlugin.configs.reactLibrary,
ignores: ["build/"]
];

export default config;
```

### Top level

Create a new `eslint.config.js` at the root of your project. Import the monorepo workspace config.

In order to make it easier to scope config files to monorepo packages, we recommend using [eslint-flat-config-utils](https://github.com/antfu/eslint-flat-config-utils). The `concat` function makes it easier to join multiple configs together. Wrap your configuration objects in the `concat` function because it will automatically merege arrays.

```javascript
import { concat } from "eslint-flat-config-utils";
import workleapPlugin from "@workleap/eslint-config";

const config = concat(
{
ignores: ["node_modules/"]
},
workleapPlugin.configs.monorepoWorkspace
);

export default config;
```

Import each package's `eslint.config.js` and add them to the `concat` function. Wrap each of the package imports with the `extend` function, and provide the relative path to the root of each package. This will scope the files of that config to the given directory, including any ignores.

```javascript
import { concat } from "eslint-flat-config-utils";
import workleapPlugin from "@workleap/eslint-config";
import packageOneConfig from "./packages/one";
import packageTwoConfig from "./packages/two";

const config = concat(
{
ignores: ["node_modules/"]
},
workleapPlugin.configs.monorepoWorkspace,
extend(packageOneConfig, "packages/one/"),
extend(packageTwoConfig, "packages/two/"),
);

export default config;
```

With this setup, you can lint the entire project from the root, or from within each individual package directory.

## Advanced Configuration

We recommend using one of the "by project type" configurations for simplicity and consistency. But if you need to customize further, you can choose to combine [any individual configs](wl-web-configs/eslint/advanced-composition/). Here, we'll compose a config for a project that uses React and TypeScript.

```javascript
import workleapPlugin from "@workleap/eslint-config";

const config = [
...workleapPlugin.configs.core,
...workleapPlugin.configs.typescript,
...workleadPlugin.configs.react
];

export default config;
```

Some rules may need to be overridden within each nested config object. Since flat configs are entirely JavaScript, we can manipulate the underlying configuration objects directly. For example, to change the file type used by a config object:
```javascript
import workleapPlugin from "@workleap/eslint-config";

const config = [
...workleapPlugin.configs.core,
...workleapPlugin.configs.typescript,
...workleadPlugin.configs.react.map(conf => (
{
...conf,
files: ["*.js"]
}
))
];

export default config;
```
38 changes: 38 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import workleapPlugin from "@workleap/eslint-plugin";
import { concat, extend } from "eslint-flat-config-utils";
import packageBrowserslistConfig from "./packages/browserslist-config/eslint.config.mjs";
import packageEslintPlugin from "./packages/eslint-plugin/eslint.config.js";
import packagePostcssConfigs from "./packages/postcss-configs/eslint.config.js";
import packageStylelintConfigs from "./packages/stylelint-configs/eslint.config.mjs";
import packageSwcConfigs from "./packages/swc-configs/eslint.config.mjs";
import packageTsupConfigs from "./packages/tsup-configs/eslint.config.mjs";
import packageWebpackConfigs from "./packages/webpack-configs/eslint.config.js";
import sampleApp from "./sample/app/eslint.config.js";
import sampleComponents from "./sample/components/eslint.config.js";
import sampleUtils from "./sample/utils/eslint.config.js";

const config = concat(
{
ignores: [
"**/dist/",
"pnpm-lock.yaml",
"*.md",
"*.snap",
"**/node_modules/",
".github/"
]
},
workleapPlugin.configs.monorepoWorkspace,
extend(packageBrowserslistConfig, "packages/browserslist-config/"),
extend(packageEslintPlugin, "packages/eslint-plugin/"),
extend(packagePostcssConfigs, "packages/postcss-configs/"),
extend(packageStylelintConfigs, "packages/stylelint-configs/"),
extend(packageSwcConfigs, "packages/swc-configs/"),
extend(packageTsupConfigs, "packages/tsup-configs/"),
extend(packageWebpackConfigs, "packages/webpack-configs/"),
extend(sampleApp, "sample/app/"),
extend(sampleComponents, "sample/components/"),
extend(sampleUtils, "sample/utils/")
);

export default config;
2 changes: 0 additions & 2 deletions knip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,6 @@ const config: KnipConfig = {
"sample/utils": sampleUtilsConfig
},
ignoreWorkspaces: [
// Until it's migrated to ESLint 9.
"packages/eslint-plugin",
// Until it supports ESM.
"packages/stylelint-configs"
],
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@
"devDependencies": {
"@changesets/changelog-github": "0.5.0",
"@changesets/cli": "2.27.1",
"@typescript-eslint/parser": "7.6.0",
"@workleap/eslint-plugin": "workspace:*",
"@workleap/typescript-configs": "workspace:*",
"eslint": "8.57.0",
"eslint-flat-config-utils": "^0.2.4",
"installed-check": "9.3.0",
"jest": "29.7.0",
"knip": "5.9.4",
Expand Down
5 changes: 0 additions & 5 deletions packages/browserslist-config/.eslintrc.json

This file was deleted.

5 changes: 5 additions & 0 deletions packages/browserslist-config/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import workleapPlugin from "@workleap/eslint-plugin";

const config = workleapPlugin.configs.typescriptLibrary;

export default config;
Loading
Loading