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 14 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.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ yarn-error.log*
.env.production.local

dist
.cache
167 changes: 167 additions & 0 deletions docs/eslint/flat-config-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
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.
bsokol-wl marked this conversation as resolved.
Show resolved Hide resolved

## 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 shoudl create an `eslint.config.mjs` file instead.
bsokol-wl marked this conversation as resolved.
Show resolved Hide resolved

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;
```

You can choose to combine [any individual configs](wl-web-configs/eslint/advanced-composition/) or chose a one of the "by project type" configs (more info below). Here, we'll compose a config for a project that uses React and TypeScript. By convention, all configs are found at `workleapPlugin.configs`. Each 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.
bsokol-wl marked this conversation as resolved.
Show resolved Hide resolved
```javascript
import workleapPlugin from "@workleap/eslint-config";

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

export default config;
```

Each config is pre-configured to look for the most common relevant file types. If you need to override the file types, you can create new config objects that extend existing ones.
```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;
```

You can use a similar pattern to override rules within a config object, or you can add a new config object to override all config objects above it.
```javascript
import workleapPlugin from "@workleap/eslint-config";

const config = [
...workleapPlugin.configs.core,
...workleapPlugin.configs.typescript,
...workleadPlugin.configs.react,
{
rules: {
'react/jsx-uses-vars': 'error',
bsokol-wl marked this conversation as resolved.
Show resolved Hide resolved
}
}
];

export default config;
```

To ignore files, you can add a config block at the top of your config array.
```javascript
import workleapPlugin from "@workleap/eslint-config";

const config = [
{
ignors: ["*.md", "dist/"]
},
...workleapPlugin.configs.core,
...workleapPlugin.configs.typescript,
...workleadPlugin.configs.react
];

export default config;
```

You can now delete your previous `.eslintrc`-style 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.
bsokol-wl marked this conversation as resolved.
Show resolved Hide resolved

## By project type setup

`@workleap/eslint-config` also exposes some pre-combined configs based on common project types. Each of these includes proper configs for JavaScript, TypeScript, Jest, Testing Library, MDX, package.json, and YAML.

| 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 | |

## Example monorepo setup

Monorepo setup can be more complex, because each workspace should have it's own ESLint config file.

### Top level

Create a new `eslint.config.js`. Import the monorepo workspace config. Ignore the directory that contains your monorepo packages (and any other files you don't want linted).
```javascript
import workleapPlugin from "@workleap/eslint-config";

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

export default config;
```

Inside each monorepo package, create another `eslint.config.js` file. Add the config module that matches the pacakge 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 must run ESLint from each monorepo package. It 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. pnpm can be used to automate this process.

Add 2 scripts to the top leve `package.json`:
bsokol-wl marked this conversation as resolved.
Show resolved Hide resolved
```json
"scripts": {
"lint": "pnpm run \"/^lint:.*/\"",
"lint:eslint": "eslint . --max-warnings=0 --cache --cache-location node_modules/.cache/eslint",
"lint:eslint-packages": "pnpm -r --parallel --if-present --aggregate-output lint:eslint",
}
```
- The `lint` script will run all scripts that start with "lint:" inside this `package.json`.
bsokol-wl marked this conversation as resolved.
Show resolved Hide resolved
- `lint:eslint` will run ESLint at the top level of the project. You can set up your ESLint flags however you like.
- `lint:eslint-packages` will simulataneously run the `lint:eslint` script within each package's `package.json` if it has been defined.


Add this script to the `package.json` of each monorepo package:
```json
"scripts": {
"lint:eslint": "eslint . --max-warnings=0 --cache --cache-location .cache/eslint"
}
```
Now, whenever you run `pnpm lint` at the root level, each monorepo package will also execute their `lint:eslint` commands.
19 changes: 19 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import workleapPlugin from "@workleap/eslint-plugin";

const config = [
{
ignores: [
"dist/",
"pnpm-lock.yaml",
"*.md",
"*.snap",
"node_modules/",
".github/",
"packages/",
"sample/"
]
},
...workleapPlugin.configs.monorepoWorkspace
];

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 @@ -19,6 +19,7 @@
"test": "jest",
"lint": "pnpm run \"/^lint:.*/\"",
"lint:eslint": "eslint . --max-warnings=0 --cache --cache-location node_modules/.cache/eslint",
"lint:eslint-packages": "pnpm -r --parallel --if-present --aggregate-output lint:eslint",
"lint:stylelint": "stylelint \"**/*.css\" --cache --cache-location node_modules/.cache/stylelint",
"lint:knip": "knip",
"lint:installed-check": "installed-check",
Expand All @@ -38,7 +39,6 @@
"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",
Expand Down
5 changes: 0 additions & 5 deletions packages/browserslist-config/.eslintrc.json

This file was deleted.

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

const config = [
...workleapPlugin.configs.typescriptLibrary
];

export default config;
3 changes: 2 additions & 1 deletion packages/browserslist-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"README.md"
],
"scripts": {
"build": "tsup"
"build": "tsup",
"lint:eslint": "eslint . --max-warnings=0 --cache --cache-location .cache/eslint"
bsokol-wl marked this conversation as resolved.
Show resolved Hide resolved
},
"devDependencies": {
"@workleap/eslint-plugin": "workspace:*",
Expand Down
107 changes: 67 additions & 40 deletions packages/eslint-plugin/ADVANCED_USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,56 +15,83 @@ The `@workleap/eslint-plugin` package exposes the following configuration parts:

Each configuration block can be extended individually, or in combination with others, to compose your custom ESLint configuration.

Here's an example of how to use the TypeScript configuration block in your .eslintrc.json file:
Here's an example of how to use the TypeScript configuration block in your eslint.config.js file:

```json
{
"extends": ["plugin:@workleap/typescript"],
"rules": {
// your custom rules
}
}
```javascript
import workleapPlugin from "@workleap/eslint-plugin";

const config = [
workleapPlugin.configs.typescript,
{
rules: {
// your custom rules
}
}
];

export default config;
```
Similarly, here's an example of how to use the react configuration block:

```json
{
"extends": ["plugin:@workleap/react"],
"rules": {
// your custom rules
}
}
Some premade configurations are already combinations of configs. Here's an example of how to use the react configuration block:

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

const config = [
...workleapPlugin.configs.react,
{
rules: {
// your custom rules
}
}
];

export default config;
```

And here's an example of how to use both TypeScript and react configuration blocks together:

```json
{
"extends": ["plugin:@workleap/typescript", "plugin:@workleap/react"],
"rules": {
// your custom rules
}
}
import workleapPlugin from "@workleap/eslint-plugin";

const config = [
workleapPlugin.configs.typescript,
...workleapPlugin.configs.react,
{
rules: {
// your custom rules
}
}
];

export default config;
```

Alternatively, if you want to lint files other than `.ts/.tsx/.js/.jsx`, you will need create different overwrite blocks
Alternatively, if you want to change any of the underlying configuration, you will have to create new configuration blocks to modify existing configuration blocks.

```json
{
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@workleap/typescript", "plugin:@workleap/react"],
"rules": {
// your custom rules
}
},
```javascript
import workleapPlugin from "@workleap/eslint-plugin";

const config = [
{
...workleapPlugin.configs.typescript,
files: ["*.ts"]
},
...workleapPlugin.configs.react.map(config => (
{
"files": ["*.mdx"],
"extends": ["plugin:@workleap/mdx"],
"rules": {
// your custom rules
}
...config,
files: ["*.jsx?"],
}
]
}
)),
{
...workleapPlugin.configs.mdx,
files: ["*.mdx"],
rules: {
...workleapPlugin.configs.mdx.rules,
// your custom rules
}
}
];

export default config;
```
7 changes: 7 additions & 0 deletions packages/eslint-plugin/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import workleapPlugin from "@workleap/eslint-plugin";

const config = [
...workleapPlugin.configs.typescriptLibrary
];

export default config;
Loading