From fd7f027d445d1fe00548c74cd2a2e54ca54c940c Mon Sep 17 00:00:00 2001 From: Brian Sokol Date: Fri, 10 May 2024 15:46:23 -0500 Subject: [PATCH] update all documentation --- docs/eslint/advanced-composition.md | 77 ++++++------ docs/eslint/custom-configuration.md | 109 +++++++++++------ docs/eslint/flat-config-migration.md | 46 ++++--- docs/eslint/setup-monorepo.md | 171 +++++++++++++++++---------- docs/eslint/setup-polyrepo.md | 99 +++++++++------- 5 files changed, 305 insertions(+), 197 deletions(-) diff --git a/docs/eslint/advanced-composition.md b/docs/eslint/advanced-composition.md index f59de60f..5c62f529 100644 --- a/docs/eslint/advanced-composition.md +++ b/docs/eslint/advanced-composition.md @@ -33,50 +33,61 @@ Each configuration piece can be extended individually, or in combination with ot To extend the configuration with a single piece: -```json !#4 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/typescript", - "rules": { - ... - } -} +```javascript !#4 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.typescript +]; + +export default config; ``` ### Multiple pieces To extend the configuration with multiple pieces: -```json !#4 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": ["plugin:@workleap/core", "plugin:@workleap/typescript"], - "rules": { - ... - } -} +```javascript !#4-5 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.core, + ...workleapPlugin.configs.typescript, +]; + +export default config; ``` -## Lint additional files +The `concat` helper from [eslint-flat-config-utils](https://github.com/antfu/eslint-flat-config-utils) is useful to avoid having to spread arrays. + +```javascript !#1,4-7 eslint.config.js +import { concat } from "eslint-flat-config-utils"; +import workleapPlugin from "@workleap/eslint-config"; + +const config = concat( + workleapPlugin.configs.core, + workleapPlugin.configs.typescript, +); -The configuration pieces already targets which file extensions their linting rules will be applied to. If you wish to lint additional file extensions for a given piece you can add an ESLint [override block](https://eslint.org/docs/latest/use/configure/configuration-files#how-do-overrides-work): - -```json !#5-10 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": ["plugin:@workleap/react"], - "overrides": [ - { - "files": ["*.js", "*.jsx"], - "extends": "plugin:@workleap/react" - } - ] -} +export default config; ``` +## Lint additional files + +The configuration pieces already target which file extensions their linting rules will be applied to. If you wish to lint additional file extensions for a given piece you can override the `files` key as you would with any JavaScript object. Since Workleap shared configs are arrays, you will need to use a `map` function. + +```javascript !#4-7 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.react.map(conf => ({ + ...conf, + files: ["*.js", "*.jsx"], + })) +]; + +export default config; +``` diff --git a/docs/eslint/custom-configuration.md b/docs/eslint/custom-configuration.md index 58abe9aa..d1701f9d 100644 --- a/docs/eslint/custom-configuration.md +++ b/docs/eslint/custom-configuration.md @@ -16,45 +16,57 @@ For a list of the rules included with the default shared configurations, refer t You can disable a default rule by defining the rule locally with the `"off"` value: -```json !#5-7 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/web-application", - "rules": { - "no-var": "off" +```javascript !#5-9 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.webApplication, + { + rules: { + "no-var": "off" + } } -} +]; + +export default config; ``` ## Change a default rule severity You can update the severity of a rule by defining the rule locally with either the `"warn"` or `"error"` severity: -```json !#5-7 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/web-application", - "rules": { - "jsx-a11y/alt-text": "error" +```javascript !#5-9 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.webApplication, + { + rules: { + "jsx-a11y/alt-text": "error" + } } -} +]; + +export default config; ``` ## Change a default rule value You can update a default rule value by defining the rule locally with its new value: -```json !#5-7 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/web-application", - "rules": { - "quotes": ["warn", "single"] +```javascript !#5-9 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.webApplication, + { + rules: { + "quotes": ["warn", "single"] + } } -} +]; + +export default config; ``` !!!light @@ -65,16 +77,47 @@ Please, don't update your project configuration to use single quotes :sweat_smil You can add configure additional rules from a third party [ESLint plugin](https://eslint.org/docs/latest/use/configure/plugins): -```json !#4,6-8 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "plugins": ["unicorn"], - "extends": "plugin:@workleap/web-application", - "rules": { - "unicorn/better-regex": "error" +```javascript !#2,6-13 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; +import eslintPluginUnicorn from 'eslint-plugin-unicorn'; + +const config = [ + ...workleapPlugin.configs.webApplication, + { + plugins: { + unicorn: eslintPluginUnicorn, + }, + rules: { + "unicorn/better-regex": "error" + } } -} +]; + +export default config; +``` + +## `concat` helper + +If you are combining many configuration objects, it can be helpful to use the `concat` helper from [eslint-flat-config-utils](https://github.com/antfu/eslint-flat-config-utils) to avoid having to spread arrays. + +```javascript !#1,5,15 eslint.config.js +import { concat } from "eslint-flat-config-utils"; +import workleapPlugin from "@workleap/eslint-config"; +import eslintPluginUnicorn from 'eslint-plugin-unicorn'; + +const config = concat( + workleapPlugin.configs.webApplication, + { + plugins: { + unicorn: eslintPluginUnicorn, + }, + rules: { + "unicorn/better-regex": "error" + } + } +); + +export default config; ``` ## Start from scratch diff --git a/docs/eslint/flat-config-migration.md b/docs/eslint/flat-config-migration.md index 35f6ca93..03ca5c29 100644 --- a/docs/eslint/flat-config-migration.md +++ b/docs/eslint/flat-config-migration.md @@ -26,7 +26,7 @@ Create a file called `eslint.config.js`. `@workleap/eslint-config` is published ### Initial setup Import the `@workleap/eslint-config` module. Create a config array and set it as the default export. -```javascript +```javascript !#3-5 eslint.config.js import workleapPlugin from "@workleap/eslint-config"; const config = [ @@ -40,7 +40,7 @@ export default config; 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 +```javascript !#4-6 eslint.config.js import workleapPlugin from "@workleap/eslint-config"; const config = [ @@ -54,19 +54,12 @@ 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**, and **package.json**. +`@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**, and **package.json**. Some configs will additionally lint **React**, **JSX A11y**, and **Storybook**. See [available configurations](../eslint/#available-configurations). 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
JSX A11y
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
JSX A11y
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 +```javascript !#7 eslint.config.js import workleapPlugin from "@workleap/eslint-config"; const config = [ @@ -79,10 +72,15 @@ const config = [ export default config; ``` -In order to make it easier to combine config files, we recommend using [eslint-flat-config-utils](https://github.com/antfu/eslint-flat-config-utils). Wrap your configuration objects in the `concat` function to automatically combine and flatten arrays of configs. This returns a promise, which you can `await` before exporting. +In order to make it easier to combine config files, we recommend using [eslint-flat-config-utils](https://github.com/antfu/eslint-flat-config-utils). Wrap your configuration objects in the `concat` function to automatically combine and flatten arrays of configs. This returns a promise, which you can `await` before exporting. + +!!!Info +Awaiting this promise is not necessary if this is a top level config file. If this is in a monorepo package ([see below](#monorepo-packages)), then you can handle the promise automatically by using `concat` in the top level config file. +!!! You can override individual rules across all configs by adding another configuration object to the array: -```javascript +```javascript !#1,4,9-13,14 eslint.config.js +import { concat } from "eslint-flat-config-utils"; import workleapPlugin from "@workleap/eslint-config"; const config = await concat( @@ -111,7 +109,7 @@ We can mimic the old ESLint behavior by importing each package's ESLint config i ### 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 +```javascript !#4 eslint.config.js import workleapPlugin from "@workleap/eslint-config"; const config = { @@ -122,11 +120,11 @@ export default config; ``` You can also set custom ignore rules: -```javascript +```javascript !#6 eslint.config.js import { concat } from "eslint-flat-config-utils"; import workleapPlugin from "@workleap/eslint-config"; -const config = await concat( +const config = concat( workleapPlugin.configs.reactLibrary, ignores: ["build/"] ); @@ -138,11 +136,11 @@ export default config; Create a new `eslint.config.js` at the root of your project. Import the monorepo workspace config. -```javascript +```javascript !#8 eslint.config.js import { concat } from "eslint-flat-config-utils"; import workleapPlugin from "@workleap/eslint-config"; -const config = await concat( +const config = concat( { ignores: ["node_modules/"] }, @@ -154,13 +152,13 @@ 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 +```javascript !#1,3-4,11-12 eslint.config.js import { concat, extend } from "eslint-flat-config-utils"; import workleapPlugin from "@workleap/eslint-config"; import packageOneConfig from "./packages/one"; import packageTwoConfig from "./packages/two"; -const config = await concat( +const config = concat( { ignores: ["node_modules/"] }, @@ -178,11 +176,11 @@ With this setup, you can lint the entire project from the root, or from within e 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](advanced-composition). Here, we'll compose a config for a project that uses React and TypeScript. -```javascript +```javascript !#5-7 eslint.config.js import { concat } from "eslint-flat-config-utils"; import workleapPlugin from "@workleap/eslint-config"; -const config = await concat( +const config = concat( workleapPlugin.configs.core, workleapPlugin.configs.typescript, workleadPlugin.configs.react @@ -192,11 +190,11 @@ 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 +```javascript !#7-12 eslint.config.js import { concat } from "eslint-flat-config-utils"; import workleapPlugin from "@workleap/eslint-config"; -const config = await concat( +const config = concat( workleapPlugin.configs.core, workleapPlugin.configs.typescript, workleadPlugin.configs.react.map(conf => ( diff --git a/docs/eslint/setup-monorepo.md b/docs/eslint/setup-monorepo.md index 304cdf41..1f2662ac 100644 --- a/docs/eslint/setup-monorepo.md +++ b/docs/eslint/setup-monorepo.md @@ -13,7 +13,7 @@ toc: This monorepo setup has been tested with [PNPM workspaces](https://pnpm.io/workspaces). You may need a different setup for [NPM workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces) or [Yarn workspaces](https://classic.yarnpkg.com/lang/en/docs/workspaces/) because by default, those package managers **hoist dependencies** rather than installing them in isolation like PNPM. !!! -To lint a monorepo solution (**multiple projects** per repository), [ESLint](https://eslint.org/) must be setuped to lint the files at the root of the solution (the monorepo **workspace**) and the files of every project of the monorepo. Execute the following steps to setup ESLint for a monorepo solution. +To lint a monorepo solution (**multiple projects** per repository), [ESLint](https://eslint.org/) must be set up to lint the files at the root of the solution (the monorepo **workspace**) and the files of every project of the monorepo. Execute the following steps to setup ESLint for a monorepo solution. ## Setup the workspace @@ -37,7 +37,7 @@ npm install -D @workleap/eslint-plugin eslint typescript @typescript-eslint/pars ### Configure ESLint -First, create a configuration file named `.eslintrc.json` at the root of the solution workspace: +First, create a configuration file named `eslint.config.js` at the root of the solution workspace: ``` !#8 workspace @@ -47,48 +47,51 @@ workspace ├──────── ... ├────── package.json ├── package.json -├── .eslintrc.json +├── eslint.config.js ``` -Then, open the newly created file and extend the default configuration with the `monorepo-workspace` shared configurations: +!!!info +If your `package.json` does not specify `"type": "module"`, you should create an `eslint.config.mjs` file instead. +!!! -```json !#4 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/monorepo-workspace" -} +Then, open the newly created file and import the Workleap ESLint plugin (`@workleap/eslint-plugin`). Creat an array and spread `monorepoWorkspace` shared config provided by the plugin into the array :point_down: + +```javascript !#4 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.monorepoWorkspace +]; + +export default config; ``` -#### .eslintignore +### Ignoring files -ESLint can be configured to [ignore](https://eslint.org/docs/latest/use/configure/ignore) certain files and directories while linting by specifying one or more glob patterns. +ESLint can be configured to ignore certain files and directories while linting by specifying one or more glob patterns. -To do so, first, create a `.eslintignore` file at the root of the solution workspace: +To do so, add a configuration object with an `ignores` key at the top of your config array. -``` !#9 -workspace -├── packages -├──── app -├────── src -├──────── ... -├────── package.json -├── package.json -├── .eslintrc.json -├── .eslintignore -``` +```javascript !#4-15 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; -Then, open the newly created file and paste the following ignore rules: - -```bash .eslintignore -**/dist/* -node_modules -__snapshots__ -storybook-static -pnpm-lock.yaml -package-lock.json -*.md -!.storybook +const config = [ + { + ignores: [ + "**/dist/*", + "node_modules", + "**/__snapshots__", + "**/storybook-static", + "pnpm-lock.yaml", + "package-lock.json", + "**/*.md", + "!**/.storybook" + ] + }, + ...workleapPlugin.configs.monorepoWorkspace +]; + +export default config; ``` !!!info @@ -111,8 +114,7 @@ workspace ├──────── ... ├────── package.json ├── package.json -├── .eslintrc.json -├── .eslintignore +├── eslint.config.js ├── .editorconfig ``` @@ -145,8 +147,7 @@ workspace ├──────── ... ├────── package.json ├── package.json <------- (this one!) -├── .eslintrc.json -├── .eslintignore +├── eslint.config.js ├── .editorconfig ``` @@ -180,7 +181,7 @@ npm install -D @workleap/eslint-plugin ### Configure ESLint -First, create a configuration file named `.eslintrc.json` at the root of the project: +First, create a configuration file named `eslint.config.js` at the root of the project: ``` !#7 workspace @@ -189,49 +190,95 @@ workspace ├────── src ├──────── ... ├────── package.json -├────── .eslintrc.json +├────── eslint.config.js ├── package.json -├── .eslintrc.json -├── .eslintignore +├── eslint.config.js ├── .editorconfig ``` -Then, open the newly created file and extend the default configuration with one of the [shared configurations](/eslint/#available-configurations) provided by `@workleap/eslint-plugin` :point_down: +!!!info +If your `package.json` does not specify `"type": "module"`, you should create an `eslint.config.mjs` file instead. +!!! + +Then, open the newly created file and import the Workleap ESLint plugin (`@workleap/eslint-plugin`). Creat an array and spread one of the [shared configurations](default.md#available-configurations) provided by the plugin into the array :point_down: #### `web-application` For an application developed with TypeScript and React, use the following configuration: -```json !#4 packages/app/.eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/web-application" -} +```javascript !#4 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.webApplication +]; + +export default config; ``` #### `react-library` For a TypeScript library developed **with** React, use the following configuration: -```json !#4 packages/app/.eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/react-library" -} +```javascript !#4 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.reactLibrary +]; + +export default config; ``` #### `typescript-library` For a TypeScript library developed **without** React, use the following configuration: -```json !#4 packages/app/.eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/typescript-library" -} +```javascript !#4 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.typescriptLibrary +]; + +export default config; +``` + +#### Add to root + +!!!warning +This step is very important. If you do not add your nested config to the root config, your package may be improperly linted or not linted at all. +!!! + +ESLint does not search for nested configs, so you must import your package's config into the root config. We recommend using [eslint-flat-config-utils](https://github.com/antfu/eslint-flat-config-utils) to join these configuration objects and scope the nested config to its own directory. + +With `concat`, you do not need to spread the configuration array. `concat` will handle combining and flattening arrays of configuration objects. + +Be sure to provide the relative path to the root of each project when you use `extends`. This will ensure that all file and ignore globs are scoped only to that package. + +```javascript !#1,3,5,19 eslint.config.js +import { concat, extend } from "eslint-flat-config-utils"; +import workleapPlugin from "@workleap/eslint-config"; +import packageAppConfig from "./packages/app/eslint.config.js"; + +const config = concat( + { + ignores: [ + "**/dist/*", + "node_modules", + "**/__snapshots__", + "**/storybook-static", + "pnpm-lock.yaml", + "package-lock.json", + "**/*.md", + "!**/.storybook" + ] + }, + workleapPlugin.configs.monorepoWorkspace, + extend(packageAppConfig, "packages/app/"), +); + +export default config; ``` ## Custom configuration diff --git a/docs/eslint/setup-polyrepo.md b/docs/eslint/setup-polyrepo.md index 90d242d6..22bac4d3 100644 --- a/docs/eslint/setup-polyrepo.md +++ b/docs/eslint/setup-polyrepo.md @@ -31,80 +31,90 @@ npm install -D @workleap/eslint-plugin eslint @typescript-eslint/parser ## Configure ESLint -First, create a configuration file named `.eslintrc.json` at the root of the solution: +First, create a configuration file named `eslint.config.js` at the root of the solution: ``` !#5 root ├── src ├──── ... ├── package.json -├── .eslintrc.json +├── eslint.config.js ``` -Then, open the newly created file and extend the default configuration with one of the [shared configurations](default.md#available-configurations) provided by `@workleap/eslint-plugin` :point_down: +!!!info +If your `package.json` does not specify `"type": "module"`, you should create an `eslint.config.mjs` file instead. +!!! + +Then, open the newly created file and import the Workleap ESLint plugin (`@workleap/eslint-plugin`). Creat an array and spread one of the [shared configurations](default.md#available-configurations) provided by the plugin into the array :point_down: ### `web-application` For an application developed with TypeScript and React, use the following configuration: -```json !#4 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/web-application" -} +```javascript !#4 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.webApplication +]; + +export default config; ``` ### `react-library` For a TypeScript library developed **with** React, use the following configuration: -```json !#4 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/react-library" -} +```javascript !#4 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.reactLibrary +]; + +export default config; ``` ### `typescript-library` For a TypeScript library developed **without** React, use the following configuration: -```json !#4 .eslintrc.json -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "extends": "plugin:@workleap/typescript-library" -} +```javascript !#4 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + ...workleapPlugin.configs.typescriptLibrary +]; + +export default config; ``` -### .eslintignore +### Ignoring files -ESLint can be configured to [ignore](https://eslint.org/docs/latest/use/configure/ignore) certain files and directories while linting by specifying one or more glob patterns. +ESLint can be configured to ignore certain files and directories while linting by specifying one or more glob patterns. -To do so, first, create an `.eslintignore` file at the root of the solution: +To do so, add a configuration object with an `ignores` key at the top of your config array. -``` !#6 -root -├── src -├──── ... -├── package.json -├── .eslintrc.json -├── .eslintignore -``` +```javascript !#4-15 eslint.config.js +import workleapPlugin from "@workleap/eslint-config"; + +const config = [ + { + ignores: [ + "**/dist/*", + "node_modules", + "**/__snapshots__", + "**/storybook-static", + "pnpm-lock.yaml", + "package-lock.json", + "**/*.md", + "!**/.storybook" + ] + }, + ...workleapPlugin.configs.typescriptLibrary +]; -Then, open the newly created file and paste the following ignore rules: - -```bash .eslintignore -**/dist/* -node_modules -__snapshots__ -storybook-static -pnpm-lock.yaml -package-lock.json -*.md -!.storybook +export default config; ``` !!!info @@ -124,8 +134,7 @@ root ├── src ├──── ... ├── package.json -├── .eslintrc.json -├── .eslintignore +├── eslint.config.js ├── .editorconfig ```