Skip to content

Commit

Permalink
feat(eslint-config): add full support + migration guides (#39)
Browse files Browse the repository at this point in the history
Signed-off-by: Dirk de Visser <[email protected]>
  • Loading branch information
dirkdev98 authored Apr 20, 2024
1 parent a28474c commit 9e34c32
Show file tree
Hide file tree
Showing 29 changed files with 1,603 additions and 137 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ coverage
out
build
dist
.next

# Misc
.DS_Store
Expand Down
9 changes: 5 additions & 4 deletions apps/backend/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Multipart } from "@fastify/multipart";
import fastify, { FastifyHttpOptions, FastifyRequest } from "fastify";
import { ZodTypeProvider } from "fastify-type-provider-zod";
import * as http from "node:http";
import type * as http from "node:http";
import type { Multipart } from "@fastify/multipart";
import type { FastifyHttpOptions, FastifyRequest } from "fastify";
import fastify from "fastify";
import type { ZodTypeProvider } from "fastify-type-provider-zod";
import { z } from "zod";
import { basePlugin } from "./plugins/base.js";

Expand Down
5 changes: 3 additions & 2 deletions apps/backend/plugins/base.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import assert from "node:assert/strict";
import * as fs from "node:fs";
import { test } from "node:test";

import { buildApp } from "../app.js";

void test("base", async (t) => {
Expand All @@ -19,7 +18,7 @@ void test("base", async (t) => {
form.append("foo", "bar");
form.append("file1", await fs.openAsBlob("package.json"), "package.json");

const response = await fetch(server + "/base/multipart", {
const response = await fetch(`${server}/base/multipart`, {
method: "post",
body: form,
});
Expand Down Expand Up @@ -47,6 +46,7 @@ void test("base", async (t) => {
});

assert.equal(response.statusCode, 200);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
assert.equal(response.json().healthChecks.label, "HEALTHY");
});

Expand All @@ -58,6 +58,7 @@ void test("base", async (t) => {
});

assert.equal(response.statusCode, 500);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
assert.equal(response.json().healthChecks.label2, "FAIL");
});
});
Expand Down
17 changes: 7 additions & 10 deletions apps/backend/plugins/base.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import fastifyMultipart, { FastifyMultipartBaseOptions } from "@fastify/multipart";
import { RegisterOptions } from "fastify";
import fastifyCustomHealthCheck from "fastify-custom-healthcheck";
import fastifyHelmet from "@fastify/helmet";

import type { FastifyMultipartBaseOptions } from "@fastify/multipart";
import fastifyMultipart from "@fastify/multipart";
import type { RegisterOptions } from "fastify";
import fastifyCustomHealthCheck from "fastify-custom-healthcheck";
import fp from "fastify-plugin";
import {
serializerCompiler,
validatorCompiler,
ZodTypeProvider,
} from "fastify-type-provider-zod";
import { FastifyBase } from "../types.js";
import type { ZodTypeProvider } from "fastify-type-provider-zod";
import { serializerCompiler, validatorCompiler } from "fastify-type-provider-zod";
import type { FastifyBase } from "../types.js";

async function base(
fastify: FastifyBase,
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import closeWithGrace from "close-with-grace";
import { buildApp } from "./app.js";
import dotenv from "dotenv";
import { buildApp } from "./app.js";

dotenv.config({ path: [".env.local", ".env"] });

Expand Down
2 changes: 1 addition & 1 deletion apps/backend/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type http from "node:http";
import type { FastifyInstance } from "fastify";
import http from "node:http";

export type FastifyBase = FastifyInstance<
http.Server,
Expand Down
102 changes: 102 additions & 0 deletions packages/eslint-config/MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Migrating to @lightbase/eslint-config

## From @compas/eslint-plugin

Execute the following steps to migrate in a mostly compatible way from
`@compas/eslint-plugin` to this package. The main incompatibilities ares:

- Prefer `Array<>` types in JSDoc over `[]` types.
- Renamed rules like `@compas/event-stop` to `@lightbase/compas-event-stop`.

The migration can be done as follows:

- Remove the `@compas/eslint-plugin` dependency from your package.json.
- Install this package with `npm install --save-dev --exact @lightbase/eslint-config`
- Remove `.eslintrc`, `.eslintignore`, `.prettierignore` and `.prettierrc(.js)` files.
- Remove the `prettier` key from your package.json.
- Remove all existing `lint`, `format` and `pretty` scripts from your package.json.
- Create `eslint.config.js` in the root of your project and paste the below contents.
- Apply the [Commands](./README.md#commands) section from the README.
- Apply the [IDE](./README.md#ide) section from the README.
- Run `npm run lint` and fixup the remaining issues.
- Update your CI scripts to use the `npm run lint:ci` command.

```js
import { defineConfig } from "@lightbase/eslint-config";

export default defineConfig(
{
prettier: {
globalOverride: {
useTabs: false,
printWidth: 80,
},
},
},
{
ignores: ["**/*.d.ts"],
},
);
```

## From (internal) @lightbase/eslint-plugin

In these steps we will be removing the vendored eslint-plugin and use
`@lightbase/eslint-config` instead. The result should give almost the same experience as
before. The main incompatibilities are:

- Import related rules ban CommonJS style `require`'s. Check if the tool supports `.mjs`
config files or add file specific ignores.
- Prettier is enabled with
[`experimentalTernaries`](https://prettier.io/blog/2023/11/13/curious-ternaries)

The migration can be done by following these steps:

- Remove the `vendor/eslint-plugin` directory.
- Install this package with `npm install --save-dev --exact @lightbase/eslint-config`
- Remove `.eslintrc`, `.eslintignore`, `.prettierignore` and `.prettierrc(.js)` files.
- Remove the `prettier` key from your package.json.
- Remove all existing `lint`, `format` and `pretty` scripts from your package.json.
- Create `eslint.config.mjs` in the root of your project and paste the below contents.
- Note the _.mjs_ extension.
- Apply the [Commands](./README.md#commands) section from the README.
- Apply the [IDE](./README.md#ide) section from the README.
- Run `npm run lint` and fixup the remaining issues.
- This will most likely fail a few times. In some cases, the built-in Prettier setup is
not able to auto-fix in this migration. This won't be an issue while using the new
setup.
- You might need to add TS type checking for JavaScript files by adding ` "**/*.*js"` to
your `includes` in the `tsconfig.json`.
- Update your CI scripts to use the `npm run lint:ci` command.

```js
import { defineConfig } from "@lightbase/eslint-config";

export default defineConfig(
{
prettier: {
globalOverride: {
printWidth: 110,
useTabs: false,
arrowParens: "avoid",
},
},
typescript: {
// TODO: Start enabling some type check rules. See https://typescript-eslint.io/users/configs/#recommended-type-checked
disableTypeCheckedRules: true,
},
react: {
withNextJs: true,
},
},
{
files: ["**/generated/**/*.*"],
rules: {
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-vars": "off",
},
},
);
```
137 changes: 106 additions & 31 deletions packages/eslint-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,13 @@ Opinionated but configurable ESLint config. Fully includes linting and formattin
npm install --save-dev --exact @lightbase/eslint-config
```

The following dependencies are automatically installed as part of `peerDependencies`,
however custom versions can be installed via
Some configurations require manually installed plugins. For example

```shell
npm install --save-dev --exact eslint typescript-eslint
npm install --save-dev --exact eslint-plugin-react eslint-plugin-react-hooks
```

Some configurations require manually installed plugins.

[//]: # "TODO: update example if we have a good one"

```shell
npm install --save-dev --exact eslint-plugin-jsdoc
```
This is documented below.

## Usage

Expand All @@ -35,12 +28,14 @@ import { defineConfig } from "@lightbase/eslint-config";
export default defineConfig({});
```

### Commands

Add the following scripts to your `package.json`:

```json
{
"scripts": {
"lint": "eslint . --fix --cache --cache-strategy content --cache-location .cache/eslint/ --color",
"lint": "eslint . --fix --cache --cache-strategy content --cache-location .cache/eslint/",
"lint:ci": "eslint ."
}
}
Expand All @@ -50,32 +45,16 @@ Add the following scripts to your `package.json`:
### In a CommonJS project

Note, these steps will be obsolete with ESLint v9, which at the time of writing is in
alpha.
> [!NOTE]
>
> These steps will be obsolete with ESLint v9, which at the time of writing is released
> but not yet supported by all our plugins.
- Use `eslint.config.mjs` instead of `eslint.config.js`
- Specify `--config eslint.config.mjs` in the `package.json` scripts.

## Default configuration and options

### Custom configuration

`defineConfig` accepts custom ESLint configuration as the 'rest' parameter. For example:

```js
import { defineConfig } from "@lightbase/eslint-config";

export default defineConfig(
{
// Define config options, explained below.
},
{
// Ignore the packages/ directory.
ignores: ["packages/**"],
},
);
```

### Prettier

Prettier is configured to run on all markdown, json, yaml, JavaScript and TypeScript
Expand Down Expand Up @@ -141,6 +120,19 @@ export default defineConfig({
});
```

By default, we enable the recommended type checked rules from typescript-eslint. To
disable these rules, use:

```js
import { defineConfig } from "@lightbase/eslint-config";

export default defineConfig({
typescript: {
disableTypeCheckedRules: true,
},
});
```

### Markdown

A Markdown processor is installed by default. Its purpose is to extract code-blocks and
Expand All @@ -161,6 +153,89 @@ export default defineConfig(
);
```

### React

The config optionally supports enabling React and Next.js specific rules. Add the
following dependencies:

```shell
npm install --save-dev --exact eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y
```

If you use Next.js, make sure to also add `@next/eslint-plugin-next` via:

```shell
npm install --save-dev --exact @next/eslint-plugin-next
```

React is only support in combination with Typescript (see above), and can be enabled as
follows:

```js
import { defineConfig } from "@lightbase/eslint-config";

export default defineConfig({
react: {
withNextJs: true,
},
});
```

This enables all Next.js rules and various recommended rules for React, hooks usage and
JSX accessibility.

### Globals

The config by default includes all globals for Node.js, Browser and ES2021. You can use
other predefined presets via

```js
import { defineConfig } from "@lightbase/eslint-config";

export default defineConfig({
// Make sure to include the full setup.
globals: ["browser", "serviceworker"],
});
```

This enables environment-specific globals for all files. For a stricter setup, use custom
configuration as explained below

```js
import { defineConfig } from "@lightbase/eslint-config";
import globals from "globals";

export default defineConfig(
{},
{
files: ["**/*.js"],
languageOptions: {
globals: {
...globals.es2015,
},
},
},
);
```

### Custom configuration

`defineConfig` accepts custom ESLint configuration as the 'rest' parameter. For example:

```js
import { defineConfig } from "@lightbase/eslint-config";

export default defineConfig(
{
// Define config options, explained below.
},
{
// Ignore the packages/ directory.
ignores: ["packages/**"],
},
);
```

## IDE

### WebStorm
Expand Down
Loading

0 comments on commit 9e34c32

Please sign in to comment.