├── .editorconfig
├── .github
├── FUNDING.yml
└── workflows
│ ├── feature.yaml
│ └── main.yaml
├── .gitignore
├── .husky
└── pre-commit
├── .releaserc
├── COMPARISON_TABLE.md
├── INCOMPATIBLE_RULES.md
├── LICENSE
├── README.md
├── bin
└── generate-typescript-compatibility-rules.js
├── compare
├── compare.js
├── find-deprecated.js
├── package-lock.json
├── package.json
└── utilities.js
├── configurations
├── auto.test.ts
├── auto.ts
├── ava.ts
├── browser.ts
├── canonical.ts
├── graphql.ts
├── index.ts
├── jest.ts
├── jsdoc.ts
├── json.ts
├── jsx-a11y.ts
├── lodash.ts
├── mocha.ts
├── module.ts
├── next.ts
├── node.ts
├── prettier.ts
├── react.ts
├── regexp.ts
├── typescript-compatibility.ts
├── typescript-type-checking.ts
├── typescript.ts
├── vitest.ts
├── yaml.ts
└── zod.ts
├── eslint.config.ts
├── package-lock.json
├── package.json
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 2
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: gajus
2 | patreon: gajus
3 |
--------------------------------------------------------------------------------
/.github/workflows/feature.yaml:
--------------------------------------------------------------------------------
1 | jobs:
2 | test:
3 | environment: release
4 | name: Test
5 | runs-on: ubuntu-latest
6 | steps:
7 | - name: setup repository
8 | uses: actions/checkout@v3
9 | with:
10 | fetch-depth: 0
11 | - name: setup node.js
12 | uses: actions/setup-node@v3
13 | with:
14 | node-version: '22'
15 | - run: npm install
16 | - run: npm run lint
17 | - run: npm run test
18 | - run: npm run build
19 | timeout-minutes: 10
20 | name: Test and build
21 | on:
22 | pull_request:
23 | branches:
24 | - main
25 | types:
26 | - opened
27 | - synchronize
28 | - reopened
29 | - ready_for_review
30 |
--------------------------------------------------------------------------------
/.github/workflows/main.yaml:
--------------------------------------------------------------------------------
1 | jobs:
2 | test:
3 | environment: release
4 | name: Test
5 | runs-on: ubuntu-latest
6 | steps:
7 | - name: setup repository
8 | uses: actions/checkout@v3
9 | with:
10 | fetch-depth: 0
11 | - name: setup node.js
12 | uses: actions/setup-node@v3
13 | with:
14 | node-version: '22'
15 | - run: npm install
16 | - run: npm run lint
17 | - run: npm run test
18 | - run: npm run build
19 | - env:
20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
22 | run: npx semantic-release
23 | name: Test, build and release
24 | on:
25 | push:
26 | branches:
27 | - main
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | coverage
2 | dist
3 | node_modules
4 | *.log
5 | .*
6 | !.editorconfig
7 | !.eslintignore
8 | !.eslintrc
9 | !.gitattributes
10 | !.github
11 | !.gitignore
12 | !.husky
13 | !.lintstagedrc.js
14 | !.npmignore
15 | !.npmrc
16 | !.prettierignore
17 | !.prettierrc
18 | !.releaserc
19 | !.yarnrc
20 | pnpm-lock.yaml
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npm run test
5 |
--------------------------------------------------------------------------------
/.releaserc:
--------------------------------------------------------------------------------
1 | {
2 | "branches": [
3 | "main"
4 | ],
5 | "plugins": [
6 | "@semantic-release/commit-analyzer",
7 | "@semantic-release/github",
8 | "@semantic-release/npm"
9 | ]
10 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2023, Gajus Kuizinas (https://gajus.com/)
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | * Neither the name of the Gajus Kuizinas (https://gajus.com/) nor the
12 | names of its contributors may be used to endorse or promote products
13 | derived from this software without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL GAJUS KUIZINAS BE LIABLE FOR ANY
19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Canonical ESLint Config
2 |
3 | [](https://www.npmjs.org/package/eslint-config-canonical)
4 |
5 | The most comprehensive code style guide.
6 |
7 | Canonical consists of 1,000+ rules (40% auto-fixable), some of which are [custom written](https://github.com/gajus/eslint-plugin-canonical) for Canonical. Canonical goal is to reduce noise in code version control and promote use of the latest ES features.
8 |
9 | ## Usage
10 |
11 | Most projects should simply extend from [`canonical/auto`](#canonicalauto-ruleset):
12 |
13 | ```ts
14 | // eslint.config.ts
15 | import auto from 'eslint-config-canonical/auto';
16 | import tseslint from 'typescript-eslint';
17 |
18 | export default tseslint.config(auto);
19 | ```
20 |
21 | ## Rulesets
22 |
23 | > **Note** Most projects should just use [`canonical/auto`](#canonicalauto-ruleset) and override settings when necessary for individual frameworks or file patterns (e.g. vitest vs ava).
24 |
25 | This package includes the following rulesets:
26 |
27 | * [`canonical/auto`](./configurations/auto.ts) – The Canonical code style guide.
28 | * [`canonical/ava`](./configurations/ava.ts) – for projects that use [AVA](https://ava.li/).
29 | * [`canonical/browser`](./configurations/browser.ts) – for projects that use DOM and other browser APIs.
30 | * [`canonical/graphql`](./configurations/graphql.ts) – for projects that use [GraphQL](https://graphql.org/).
31 | * [`canonical/jest`](./configurations/jest.ts) – for projects that use [jest](https://facebook.github.io/jest/).
32 | * [`canonical/jsdoc`](./configurations/jsdoc.ts) – for projects that use [JSDoc](https://jsdoc.app/).
33 | * [`canonical/json`](./configurations/json.ts) – for projects that use JSON.
34 | * [`canonical/jsx-a11y`](./configurations/jsx-a11y.ts) – for projects that use [React](https://facebook.github.io/react/) and want to include [accessibility checks](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y).
35 | * [`canonical/lodash`](./configurations/lodash.ts) – for projects that use [lodash](https://lodash.com/).
36 | * [`canonical/mocha`](./configurations/mocha.ts) – for projects that use [Mocha](https://mochajs.org/).
37 | * [`canonical/module`](./configurations/module.ts) – for projects that use ESM modules.
38 | * [`canonical/next`](./configurations/next.ts) – for projects that use [Next.js](https://nextjs.org/).
39 | * [`canonical/node`](./configurations/node.ts) – for projects that use Node.js.
40 | * [`canonical/prettier`](./configurations/prettier.ts) – applies [Prettier](https://prettier.io/) formatting.
41 | * [`canonical/react`](./configurations/react.ts) – for projects that use [React](https://facebook.github.io/react/).
42 | * [`canonical/regexp`](./configurations/regexp.ts) – for projects that use regular expressions.
43 | * [`canonical/typescript-type-checking`](./configurations/typescript-type-checking.ts) – for projects that use [TypeScript](http://typescriptlang.org/) and want additional rules that require type information (rules using type information take longer to run).
44 | * [`canonical/typescript`](./configurations/typescript.ts) – for projects that use [TypeScript](http://typescriptlang.org/).
45 | * [`canonical/vitest`](./configurations/vitest.ts) – for projects that use [Vitest](https://vitest.dev/).
46 | * [`canonical/yaml`](./configurations/yaml.ts) – for projects that use YAML.
47 | * [`canonical/zod`](./configurations/zod.ts) – for projects that use [Zod](https://github.com/colinhacks/zod).
48 |
49 | ## `canonical/auto` ruleset
50 |
51 | [`canonical/auto`](./configurations/auto.ts) is a special ruleset that uses [overrides](https://eslint.org/docs/user-guide/configuring/configuration-files#how-do-overrides-work) to only apply relevant style guides. This reduces the linting time and the number of false-positives.
52 |
53 | `canonical/auto` can be fine-tuned using `overrides` just like any other ESLint ruleset, e.g.
54 |
55 | ```json
56 | {
57 | "extends": [
58 | "canonical/auto"
59 | ],
60 | "overrides": [
61 | {
62 | "extends": [
63 | "canonical/jsx-a11y"
64 | ],
65 | "files": "*.tsx"
66 | },
67 | {
68 | "extends": [
69 | "canonical/vitest"
70 | ],
71 | "files": "*.test.{ts,tsx}"
72 | }
73 | ],
74 | "root": true
75 | }
76 | ```
77 |
78 | ### Compatibility with Prettier
79 |
80 | For the most part, Prettier and Canonical are already compatible. There are only a few transformations that are incompatible, e.g. Prettier enforces line-length and Canonical does not. As such, there is no good reason to use both. However, if you wish to use Prettier, you can do so by using `canonical/prettier` ruleset, which uses [`eslint-plugin-prettier`](https://www.npmjs.com/package/eslint-plugin-prettier) to apply Prettier formatting after applying Canonical rules.
81 |
82 | ```json
83 | {
84 | "extends": [
85 | "canonical",
86 | "canonical/jsdoc",
87 | "canonical/regexp",
88 | "canonical/react",
89 | "canonical/typescript",
90 | "canonical/jest",
91 | "canonical/prettier"
92 | ]
93 | }
94 | ```
95 |
96 | > **Note** The reason for using Prettier as an ESLint plugin (as opposed to a separate tool) is because having multiple tools that apply formatting complicates IDE and other tooling setup.
97 |
98 | > **Note** Your local `.prettierrc` is going to be ignored when using `canonical/prettier`.
99 |
100 | ### Compatibility with other style guides
101 |
102 | Since Canonical style guide includes more rules than any other style guide, you can have your codebase compatible with a specific style guide (e.g. [airbnb](https://www.npmjs.com/package/eslint-config-airbnb)) and benefit from Canonical for rules that are not covered by the other guide. All you have to do is extend from the Canonical style guide before extending from the desired style guide, e.g.
103 |
104 | ```json
105 | {
106 | "extends": [
107 | "canonical",
108 | "canonical/jsdoc",
109 | "canonical/regexp",
110 | "canonical/react",
111 | "airbnb"
112 | ]
113 | }
114 | ```
115 |
116 | ## Integrations
117 |
118 | ### Visual Studio Code
119 |
120 | Use the [dbaeumer.vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) extension that Microsoft provides officially.
121 |
122 | Example `.vscode/settings.json`:
123 |
124 | ```json
125 | {
126 | "eslint.validate": [
127 | "css",
128 | "html",
129 | "javascript",
130 | "javascriptreact",
131 | "json",
132 | "markdown",
133 | "typescript",
134 | "typescriptreact",
135 | "yaml"
136 | ]
137 | }
138 | ```
139 |
140 | The setting below turns on _Auto Fix_ for all providers including ESLint:
141 |
142 | ```json
143 | {
144 | "editor.codeActionsOnSave": {
145 | "source.fixAll.eslint": true
146 | },
147 | "editor.defaultFormatter": "dbaeumer.vscode-eslint",
148 | "editor.formatOnSave": true
149 | }
150 | ```
151 |
152 | Additionally, we found it that being explicit about which formatter you are using for each file improves DX:
153 |
154 | ```json
155 | {
156 | "[css][html][javascript][javascriptreact][json][markdown][typescript][typescriptreact][yaml]": {
157 | "editor.defaultFormatter": "dbaeumer.vscode-eslint"
158 | }
159 | }
160 | ```
161 |
162 | While not required if you've configured explicit formatter for each file type, I advise that you explicitly disable `prettier` extension in your project:
163 |
164 | ```json
165 | {
166 | "prettier.enable": false,
167 | }
168 | ```
169 |
170 | Sharing these settings in your project should be sufficient to prevent local settings accidentally overriding the desired formatter behavior.
171 |
172 | ## Benchmark
173 |
174 | ### Canonical vs Prettier
175 |
176 | This benchmark compares running ESLint using Canonical style guide against a project with 3,000+ files VS linting the same project using Prettier.
177 |
178 | ```
179 | System:
180 | OS: macOS 11.6
181 | CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
182 | Memory: 64.00 GB
183 | npmPackages:
184 | eslint: 8.1.0
185 | prettier: 2.4.1
186 | ```
187 |
188 | As you may expect, Prettier is going to complete checks quicker – this is because it runs a lot fewer transforms and it only runs style checks (as opposed to static analyses).
189 |
190 | The first time you run ESLint, it will take significantly more time. However, if you enable [`--cache`](https://eslint.org/docs/user-guide/command-line-interface#--cache), then the follow up checks will complete in no time.
191 |
192 | ```bash
193 | $ time prettier src
194 | 27.06s user
195 | 1.74s system
196 | 166% cpu
197 | 17.335 total
198 |
199 | $ eslint --cache src
200 | 182.43s user
201 | 9.13s system
202 | 126% cpu
203 | 2:31.22 total
204 |
205 | $ eslint --cache src
206 | 1.96s user
207 | 0.39s system
208 | 107% cpu
209 | 2.188 total
210 | ```
211 |
212 | Using ESLint cache will dramatically improve ESLint running time by ensuring that only changed files are linted. This is useful if you are using ESLint to run `pre-commit` / `pre-push` [git hooks](https://git-scm.com/docs/githooks) or otherwise depend on these checks completing in real-time.
213 |
214 | Additionally, if performance is a consideration, you may consider:
215 |
216 | * [`jest-eslint-runner`](https://github.com/jest-community/jest-runner-eslint)
217 | * [Integrations](#integrations)
218 |
219 | These options provide near instant feedback just how you are used to when using Prettier.
220 |
221 | ## Table of Comparison
222 |
223 | [COMPARISON_TABLE.md](./COMPARISON_TABLE.md)
224 |
225 | ## Versioning Policy
226 |
227 | All breaking changes will bump the major version as per the semver convention. Therefore, every new rule addition will increase the major version.
228 |
229 | ## Development
230 |
231 | First, run `npm install` and then `npm run setup-dev`. Then, any time that ESLint dependencies are updated you must:
232 |
233 | 1. Run `npm run generate-typescript-compatibility-rules` script. It disables and override any TypeScript rules that are incompatible with ESLint built-in rules.
234 | 1. Run `npm run compare` script. It generates ruleset comparison table, updates README.md, and identifies rules that are not configured.
235 |
236 | ## Incompatible rules
237 |
238 | [INCOMPATIBLE_RULES.md](./INCOMPATIBLE_RULES.md)
239 |
--------------------------------------------------------------------------------
/bin/generate-typescript-compatibility-rules.js:
--------------------------------------------------------------------------------
1 | import eslintConfiguration from '../configurations/eslintrc';
2 | import typescriptRules from '@typescript-eslint/eslint-plugin';
3 | import { builtinRules } from 'eslint/use-at-your-own-risk';
4 |
5 | const builtinRuleNames = Object.keys(Object.fromEntries(builtinRules));
6 | const typescriptRuleNames = Object.keys(typescriptRules);
7 |
8 | const incompatibleRuleNames = [];
9 |
10 | for (const typescriptRuleName of typescriptRuleNames) {
11 | if (builtinRuleNames.includes(typescriptRuleName)) {
12 | incompatibleRuleNames.push(typescriptRuleName);
13 | }
14 | }
15 |
16 | const appendRules = {};
17 |
18 | for (const incompatibleRuleName of incompatibleRuleNames) {
19 | if (!eslintConfiguration.rules[incompatibleRuleName] === undefined) {
20 | continue;
21 | }
22 |
23 | appendRules[incompatibleRuleName] = 0;
24 | appendRules['@typescript-eslint/' + incompatibleRuleName] =
25 | eslintConfiguration.rules[incompatibleRuleName];
26 | }
27 |
28 | const orderedRules = {
29 | rules: Object.fromEntries(Object.entries(appendRules).sort()),
30 | };
31 |
32 | // eslint-disable-next-line no-console
33 | console.log('export default ' + JSON.stringify(orderedRules, '', ' '));
34 |
--------------------------------------------------------------------------------
/compare/compare.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | /* eslint-disable complexity */
3 |
4 | const {
5 | getConfigurationRules,
6 | getLoadedRules,
7 | getRuleConfiguration,
8 | getRuleLink,
9 | isRuleEnabled,
10 | normalizeConfiguration,
11 | } = require('./utilities');
12 | const { readFile, writeFile } = require('node:fs/promises');
13 | const { resolve } = require('node:path');
14 | const stringify = require('safe-stable-stringify');
15 |
16 | const getIncompatibleRuleNames = (canonicalRules, comparedRules) => {
17 | const incompatibleRuleNames = [];
18 |
19 | for (const ruleName of Object.keys(comparedRules)) {
20 | if (!isRuleEnabled(comparedRules[ruleName]?.[0])) {
21 | continue;
22 | }
23 |
24 | const canonicalRuleConfiguration = stringify(
25 | normalizeConfiguration(canonicalRules[ruleName]),
26 | null,
27 | ' ',
28 | );
29 | const comparedRuleConfiguration = stringify(
30 | normalizeConfiguration(comparedRules[ruleName]),
31 | null,
32 | ' ',
33 | );
34 |
35 | if (canonicalRuleConfiguration === comparedRuleConfiguration) {
36 | continue;
37 | }
38 |
39 | incompatibleRuleNames.push(ruleName);
40 | }
41 |
42 | return incompatibleRuleNames;
43 | };
44 |
45 | const createIncompatibleRuleSummary = (
46 | urlSafeName,
47 | comparedName,
48 | canonicalRules,
49 | comparedRules,
50 | ) => {
51 | // We are ignoring these rules because their configuration is breaking table layout.
52 | const ignoreRuleNames = [
53 | 'no-restricted-globals',
54 | 'no-restricted-syntax',
55 | 'capitalized-comments',
56 | 'react/sort-comp',
57 | 'no-restricted-properties',
58 | ];
59 |
60 | const rows = [];
61 |
62 | const incompatibleRuleNames = getIncompatibleRuleNames(
63 | canonicalRules,
64 | comparedRules,
65 | );
66 |
67 | for (const incompatibleRuleName of incompatibleRuleNames) {
68 | if (ignoreRuleNames.includes(incompatibleRuleName)) {
69 | continue;
70 | }
71 |
72 | const canonicalRuleConfiguration = stringify(
73 | normalizeConfiguration(canonicalRules[incompatibleRuleName]),
74 | null,
75 | ' ',
76 | );
77 | const comparedRuleConfiguration = stringify(
78 | normalizeConfiguration(comparedRules[incompatibleRuleName]),
79 | null,
80 | ' ',
81 | );
82 |
83 | rows.push(
84 | `
85 |
86 |
87 | ${incompatibleRuleName}
88 | (back to comparison table 👆)
89 |
90 | |
91 |
92 |
93 | ${canonicalRuleConfiguration}
|
94 | ${comparedRuleConfiguration}
|
95 |
96 | `.trim(),
97 | );
98 | }
99 |
100 | return [
101 | '### ' + comparedName + ' Incompatible Rules',
102 | '',
103 | ...rows,
104 | '
',
105 | ].join('\n');
106 | };
107 |
108 | (async () => {
109 | const loadedRules = await getLoadedRules();
110 |
111 | const canonicalRules = await getConfigurationRules({
112 | extends: [
113 | 'canonical/ava',
114 | 'canonical/browser',
115 | 'canonical/jest',
116 | 'canonical/json',
117 | 'canonical/jsx-a11y',
118 | 'canonical/lodash',
119 | 'canonical/mocha',
120 | 'canonical/module',
121 | 'canonical/next',
122 | 'canonical/node',
123 | 'canonical/react',
124 | 'canonical/typescript',
125 | 'canonical/yaml',
126 |
127 | // The order is important!
128 | // The last ruleset overrides rules in previous rulesets.
129 | // This affects rules that are overridden in specific configs, e.g.
130 | // typescript disabled no-duplicate-imports but enables @import/no-duplicates.
131 | 'canonical',
132 | ],
133 | });
134 |
135 | const airbnbRules = await getConfigurationRules({
136 | extends: ['airbnb'],
137 | });
138 |
139 | const googleRules = await getConfigurationRules({
140 | extends: ['google'],
141 | });
142 |
143 | const standardRules = await getConfigurationRules({
144 | extends: ['standard'],
145 | });
146 |
147 | const xoRules = await getConfigurationRules({
148 | extends: ['xo'],
149 | });
150 |
151 | const ruleNames = Object.keys(loadedRules);
152 |
153 | const markdownLines = [
154 | '',
155 | '|Rule|CN|[AB](https://www.npmjs.com/package/eslint-config-airbnb)|[GG](https://www.npmjs.com/package/eslint-config-google)|[SD](https://www.npmjs.com/package/eslint-config-standard)|[XO](https://github.com/xojs/eslint-config-xo)|',
156 | '|---|---|---|---|---|---|',
157 | ];
158 |
159 | let fixableRuleCount = 0;
160 |
161 | const airbnbIncompatibleRuleNames = getIncompatibleRuleNames(
162 | canonicalRules,
163 | airbnbRules,
164 | );
165 | const googleIncompatibleRuleNames = getIncompatibleRuleNames(
166 | canonicalRules,
167 | googleRules,
168 | );
169 | const standardIncompatibleRuleNames = getIncompatibleRuleNames(
170 | canonicalRules,
171 | standardRules,
172 | );
173 | const xoIncompatibleRuleNames = getIncompatibleRuleNames(
174 | canonicalRules,
175 | xoRules,
176 | );
177 |
178 | for (const ruleName of ruleNames) {
179 | if (loadedRules[ruleName]?.meta?.fixable) {
180 | fixableRuleCount++;
181 | }
182 |
183 | markdownLines.push(
184 | '|' +
185 | getRuleLink(ruleName) +
186 | '' +
189 | (loadedRules[ruleName]?.meta?.fixable ? ' 🛠' : '') +
190 | (loadedRules[ruleName]?.meta?.deprecated ? ' ⛔️' : '') +
191 | '|' +
192 | getRuleConfiguration(canonicalRules, ruleName) +
193 | '|' +
194 | getRuleConfiguration(airbnbRules, ruleName) +
195 | (airbnbIncompatibleRuleNames.includes(ruleName)
196 | ? '?'
197 | : '') +
198 | '|' +
199 | getRuleConfiguration(googleRules, ruleName) +
200 | (googleIncompatibleRuleNames.includes(ruleName)
201 | ? '?'
202 | : '') +
203 | '|' +
204 | getRuleConfiguration(standardRules, ruleName) +
205 | (standardIncompatibleRuleNames.includes(ruleName)
206 | ? '?'
207 | : '') +
208 | '|' +
209 | getRuleConfiguration(xoRules, ruleName) +
210 | (xoIncompatibleRuleNames.includes(ruleName)
211 | ? '?'
212 | : '') +
213 | '|',
214 | );
215 | }
216 |
217 | markdownLines.push('');
218 |
219 | const README_PATH = resolve(__dirname, '../COMPARISON_TABLE.md');
220 |
221 | await writeFile(
222 | README_PATH,
223 | (await readFile(README_PATH, 'utf8')).replace(
224 | /[\s\S]+/u,
225 | markdownLines.join('\n'),
226 | ),
227 | );
228 |
229 | await writeFile(
230 | README_PATH,
231 | (await readFile(README_PATH, 'utf8')).replace(
232 | /[\s\S]+/u,
233 | '\n' +
234 | [
235 | createIncompatibleRuleSummary(
236 | 'airbnb',
237 | 'AirBnb',
238 | canonicalRules,
239 | airbnbRules,
240 | ),
241 | createIncompatibleRuleSummary(
242 | 'google',
243 | 'Google',
244 | canonicalRules,
245 | googleRules,
246 | ),
247 | createIncompatibleRuleSummary(
248 | 'standard',
249 | 'Standard',
250 | canonicalRules,
251 | standardRules,
252 | ),
253 | createIncompatibleRuleSummary('xo', 'XO', canonicalRules, xoRules),
254 | ].join('\n\n') +
255 | '\n',
256 | ),
257 | );
258 |
259 | const ignoreDisabled = [
260 | 'camelcase',
261 | 'capitalized-comments',
262 | 'class-methods-use-this',
263 | 'import/named',
264 | 'import/no-unresolved',
265 | 'import/prefer-default-export',
266 | 'jsx-a11y/control-has-associated-label',
267 | 'jsx-a11y/lang',
268 | 'jsx-a11y/no-autofocus',
269 | 'max-classes-per-file',
270 | 'max-depth',
271 | 'max-len',
272 | 'max-nested-callbacks',
273 | 'max-params',
274 | 'multiline-ternary',
275 | 'new-cap',
276 | 'newline-per-chained-call',
277 | 'no-await-in-loop',
278 | 'no-continue',
279 | 'no-else-return',
280 | 'no-empty-function',
281 | 'no-invalid-this',
282 | 'no-mixed-operators',
283 | 'no-nested-ternary',
284 | 'no-plusplus',
285 | 'no-restricted-globals',
286 | 'no-restricted-imports',
287 | 'no-restricted-properties',
288 | 'no-restricted-syntax',
289 | 'no-return-await',
290 | 'no-underscore-dangle',
291 | 'object-curly-spacing',
292 | 'prefer-template',
293 | 'react/destructuring-assignment',
294 | 'react/forbid-foreign-prop-types',
295 | 'react/forbid-prop-types',
296 | 'react/jsx-filename-extension',
297 | 'react/jsx-one-expression-per-line',
298 | 'react/jsx-props-no-spreading',
299 | 'react/jsx-wrap-multilines',
300 | 'react/no-unescaped-entities',
301 | 'react/react-in-jsx-scope',
302 | 'react/require-default-props',
303 | ];
304 |
305 | for (const ruleName of ruleNames) {
306 | if (
307 | !ignoreDisabled.includes(ruleName) &&
308 | loadedRules[ruleName]?.meta?.deprecated !== true &&
309 | !isRuleEnabled(canonicalRules[ruleName]?.[0]) &&
310 | (isRuleEnabled(airbnbRules[ruleName]?.[0]) ||
311 | isRuleEnabled(googleRules[ruleName]?.[0]) ||
312 | isRuleEnabled(standardRules[ruleName]?.[0]) ||
313 | isRuleEnabled(xoRules[ruleName]?.[0]))
314 | ) {
315 | console.warn('disabled rule "' + ruleName + '"', {
316 | airbnb: airbnbRules[ruleName],
317 | canonical: canonicalRules[ruleName],
318 | google: googleRules[ruleName],
319 | standard: standardRules[ruleName],
320 | xo: xoRules[ruleName],
321 | });
322 | }
323 | }
324 |
325 | const ignoreUnused = [
326 | // Deprecated in documentation.
327 | // Not reflected in meta.
328 | 'jsx-a11y/no-onchange',
329 | ];
330 |
331 | for (const ruleName of ruleNames) {
332 | if (
333 | !ignoreUnused.includes(ruleName) &&
334 | loadedRules[ruleName]?.meta?.deprecated !== true &&
335 | !canonicalRules[ruleName]
336 | ) {
337 | console.warn('unused rule "' + ruleName + '"', {
338 | airbnb: airbnbRules[ruleName],
339 | google: googleRules[ruleName],
340 | standard: standardRules[ruleName],
341 | xo: xoRules[ruleName],
342 | });
343 | }
344 | }
345 |
346 | for (const ruleName of ruleNames) {
347 | if (loadedRules[ruleName]?.meta?.deprecated && canonicalRules[ruleName]) {
348 | console.warn('deprecated rule "' + ruleName + '"');
349 | }
350 | }
351 |
352 | console.log(
353 | 'Canonical rules: ' +
354 | Object.keys(canonicalRules).length +
355 | ' (' +
356 | Math.round(
357 | (fixableRuleCount / Object.keys(canonicalRules).length) * 100,
358 | ) +
359 | '% auto-fixable)',
360 | );
361 | console.log('Airbnb rules: ' + Object.keys(airbnbRules).length);
362 | console.log('Google rules: ' + Object.keys(googleRules).length);
363 | console.log('Standard rules: ' + Object.keys(standardRules).length);
364 | console.log('XO rules: ' + Object.keys(xoRules).length);
365 | })();
366 |
--------------------------------------------------------------------------------
/compare/find-deprecated.js:
--------------------------------------------------------------------------------
1 | /**
2 | * https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/825
3 | * https://github.com/eslint/eslint/issues/15292
4 | */
5 |
6 | const { getLoadedRules } = require('./utilities');
7 | const got = require('got');
8 |
9 | (async () => {
10 | const loadedRules = await getLoadedRules();
11 |
12 | const ruleNames = Object.keys(loadedRules);
13 |
14 | for (const ruleName of ruleNames) {
15 | if (loadedRules[ruleName]?.meta?.deprecated) {
16 | continue;
17 | }
18 |
19 | const ruleDocumentationUrl = loadedRules[ruleName]?.meta?.docs?.url;
20 |
21 | if (!ruleDocumentationUrl) {
22 | continue;
23 | }
24 |
25 | const response = await got(ruleDocumentationUrl, {
26 | resolveBodyOnly: true,
27 | });
28 |
29 | if (response.toLowerCase().includes('deprecated')) {
30 | // eslint-disable-next-line no-console
31 | console.warn(
32 | '⚠️ ' +
33 | ruleName +
34 | ' suspected improperly deprecated rule (' +
35 | ruleDocumentationUrl +
36 | ')',
37 | );
38 | }
39 | }
40 | })();
41 |
--------------------------------------------------------------------------------
/compare/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "eslint": "^8.49.0",
4 | "eslint-config-airbnb": "^19.0.4",
5 | "eslint-config-canonical": "^41.2.1",
6 | "eslint-config-google": "^0.14.0",
7 | "eslint-config-standard": "^17.1.0",
8 | "eslint-config-xo": "^0.43.1",
9 | "eslint-plugin-jsx-a11y": "^6.7.1",
10 | "eslint-plugin-n": "^16.2.0",
11 | "eslint-plugin-standard": "^4.1.0",
12 | "got": "^12.0.0",
13 | "safe-stable-stringify": "^2.4.3",
14 | "typescript": "^5.2.2"
15 | },
16 | "scripts": {
17 | "compare": "node compare.js"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/compare/utilities.js:
--------------------------------------------------------------------------------
1 | const { ESLint } = require('eslint');
2 | const { builtinRules } = require('eslint/use-at-your-own-risk');
3 |
4 | const getConfigurationPluginNames = async (configuration) => {
5 | const engine = new ESLint({
6 | baseConfig: configuration,
7 | useEslintrc: false,
8 | });
9 |
10 | const calculatedConfiguration =
11 | await engine.calculateConfigForFile('./compare');
12 |
13 | return calculatedConfiguration.plugins;
14 | };
15 |
16 | const getPluginRules = (pluginName) => {
17 | // eslint-disable-next-line import/no-dynamic-require
18 | const { rules } = require(
19 | pluginName.startsWith('@')
20 | ? pluginName + '/eslint-plugin'
21 | : 'eslint-plugin-' + pluginName,
22 | );
23 |
24 | return Object.fromEntries(
25 | Object.entries(rules).map(([ruleName, ruleConfiguration]) => {
26 | return [pluginName + '/' + ruleName, ruleConfiguration];
27 | }),
28 | );
29 | };
30 |
31 | const configurationNames = [
32 | 'airbnb',
33 | 'google',
34 | 'standard',
35 | 'canonical',
36 | 'canonical/ava',
37 | 'canonical/browser',
38 | 'canonical/jest',
39 | 'canonical/json',
40 | 'canonical/lodash',
41 | 'canonical/mocha',
42 | 'canonical/module',
43 | 'canonical/node',
44 | 'canonical/react',
45 | 'canonical/typescript',
46 | 'canonical/yaml',
47 | ];
48 |
49 | const getLoadedRules = async () => {
50 | const usedPluginNames = [];
51 |
52 | for (const configurationName of configurationNames) {
53 | const configurationUsedPluginNames = await getConfigurationPluginNames({
54 | extends: [configurationName],
55 | root: true,
56 | });
57 |
58 | for (const configurationUsedPluginName of configurationUsedPluginNames) {
59 | if (!usedPluginNames.includes(configurationUsedPluginNames)) {
60 | usedPluginNames.push(configurationUsedPluginName);
61 | }
62 | }
63 | }
64 |
65 | let loadedRules = {
66 | ...Object.fromEntries(builtinRules),
67 | };
68 |
69 | for (const usedPluginName of usedPluginNames) {
70 | loadedRules = {
71 | ...loadedRules,
72 | ...getPluginRules(usedPluginName),
73 | };
74 | }
75 |
76 | return Object.fromEntries(
77 | Object.entries(loadedRules).sort((a, b) => {
78 | return a[0].localeCompare(b[0]);
79 | }),
80 | );
81 | };
82 |
83 | /**
84 | * Determines what rules are going to be used for a given ESLint configuration.
85 | */
86 | const getConfigurationRules = async (configuration) => {
87 | const engine = new ESLint({
88 | baseConfig: configuration,
89 | useEslintrc: false,
90 | });
91 |
92 | const calculatedConfiguration =
93 | await engine.calculateConfigForFile('./compare');
94 |
95 | return calculatedConfiguration.rules;
96 | };
97 |
98 | const getRuleLink = (ruleName) => {
99 | if (!ruleName.includes('/')) {
100 | return (
101 | '[`' +
102 | ruleName +
103 | '`](https://eslint.org/docs/latest/rules/' +
104 | ruleName +
105 | ')'
106 | );
107 | }
108 |
109 | if (ruleName.startsWith('fp/')) {
110 | return (
111 | '[`' +
112 | ruleName +
113 | '`](https://github.com/jfmengels/eslint-plugin-fp/blob/master/docs/rules/' +
114 | ruleName.replace(/^fp\//u, '') +
115 | '.md)'
116 | );
117 | }
118 |
119 | if (ruleName.startsWith('ava/')) {
120 | return (
121 | '[`' +
122 | ruleName +
123 | '`](https://github.com/avajs/eslint-plugin-ava/blob/main/docs/rules/' +
124 | ruleName.replace(/^ava\//u, '') +
125 | '.md)'
126 | );
127 | }
128 |
129 | if (ruleName.startsWith('canonical/')) {
130 | return (
131 | '[`' +
132 | ruleName +
133 | '`](https://github.com/gajus/eslint-plugin-canonical#eslint-plugin-canonical-rules-' +
134 | ruleName.replace(/^canonical\//u, '') +
135 | ')'
136 | );
137 | }
138 |
139 | if (ruleName.startsWith('eslint-comments/')) {
140 | return (
141 | '[`' +
142 | ruleName +
143 | '`](https://github.com/mysticatea/eslint-plugin-eslint-comments/blob/master/docs/rules/' +
144 | ruleName.replace(/^eslint-comments\//u, '') +
145 | '.md)'
146 | );
147 | }
148 |
149 | if (ruleName.startsWith('unicorn/')) {
150 | return (
151 | '[`' +
152 | ruleName +
153 | '`](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/' +
154 | ruleName.replace(/^unicorn\//u, '') +
155 | '.md)'
156 | );
157 | }
158 |
159 | if (ruleName.startsWith('jsdoc/')) {
160 | return (
161 | '[`' +
162 | ruleName +
163 | '`](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/' +
164 | ruleName.replace(/^jsdoc\//u, '') +
165 | '.md)'
166 | );
167 | }
168 |
169 | if (ruleName.startsWith('import/')) {
170 | return (
171 | '[`' +
172 | ruleName +
173 | '`](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/' +
174 | ruleName.replace(/^import\//u, '') +
175 | '.md)'
176 | );
177 | }
178 |
179 | if (ruleName.startsWith('react/')) {
180 | return (
181 | '[`' +
182 | ruleName +
183 | '`](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/' +
184 | ruleName.replace(/^react\//u, '') +
185 | '.md)'
186 | );
187 | }
188 |
189 | if (ruleName.startsWith('promise/')) {
190 | return (
191 | '[`' +
192 | ruleName +
193 | '`](https://github.com/eslint-community/eslint-plugin-promise/blob/main/docs/rules' +
194 | ruleName.replace(/^promise\//u, '') +
195 | '.md)'
196 | );
197 | }
198 |
199 | if (ruleName.startsWith('lodash/')) {
200 | return (
201 | '[`' +
202 | ruleName +
203 | '`](https://github.com/wix-incubator/eslint-plugin-lodash/blob/master/docs/rules/' +
204 | ruleName.replace(/^lodash\//u, '') +
205 | '.md)'
206 | );
207 | }
208 |
209 | if (ruleName.startsWith('mocha/')) {
210 | return (
211 | '[`' +
212 | ruleName +
213 | '`](https://github.com/lo1tuma/eslint-plugin-mocha/blob/main/docs/rules/' +
214 | ruleName.replace(/^mocha\//u, '') +
215 | '.md)'
216 | );
217 | }
218 |
219 | if (ruleName.startsWith('n/')) {
220 | return (
221 | '[`' +
222 | ruleName +
223 | '`](https://github.com/eslint-community/eslint-plugin-n/tree/master/docs/rules/' +
224 | ruleName.replace(/^n\//u, '') +
225 | '.md)'
226 | );
227 | }
228 |
229 | if (ruleName.startsWith('node/')) {
230 | return (
231 | '[`' +
232 | ruleName +
233 | '`](https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/' +
234 | ruleName.replace(/^node\//u, '') +
235 | '.md)'
236 | );
237 | }
238 |
239 | if (ruleName.startsWith('jsx-a11y/')) {
240 | return (
241 | '[`' +
242 | ruleName +
243 | '`](https://github.com/infofarmer/eslint-plugin-jsx-a11y/blob/main/docs/rules/' +
244 | ruleName.replace(/^jsx-a11y\//u, '') +
245 | '.md)'
246 | );
247 | }
248 |
249 | if (ruleName.startsWith('jest/')) {
250 | return (
251 | '[`' +
252 | ruleName +
253 | '`](https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/' +
254 | ruleName.replace(/^jest\//u, '') +
255 | '.md)'
256 | );
257 | }
258 |
259 | if (ruleName.startsWith('jsonc/')) {
260 | return (
261 | '[`' +
262 | ruleName +
263 | '`](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/' +
264 | ruleName.replace(/^jsonc\//u, '') +
265 | '.html)'
266 | );
267 | }
268 |
269 | if (ruleName.startsWith('@typescript-eslint/')) {
270 | return (
271 | '[`' +
272 | ruleName +
273 | '`](https://typescript-eslint.io/rules/' +
274 | ruleName.replace(/^@typescript-eslint\//u, '') +
275 | ')'
276 | );
277 | }
278 |
279 | if (ruleName.startsWith('yml/')) {
280 | return (
281 | '[`' +
282 | ruleName +
283 | '`](https://ota-meshi.github.io/eslint-plugin-yml/rules/' +
284 | ruleName.replace(/^yml\//u, '') +
285 | '.html)'
286 | );
287 | }
288 |
289 | return '`' + ruleName + '`';
290 | };
291 |
292 | const isRuleEnabled = (ruleValue) => {
293 | if (ruleValue === 1 || ruleValue === 'warn') {
294 | return true;
295 | }
296 |
297 | if (ruleValue === 2 || ruleValue === 'error') {
298 | return true;
299 | }
300 |
301 | return false;
302 | };
303 |
304 | const normalizeConfiguration = (configuration) => {
305 | if (!configuration) {
306 | return ['off'];
307 | }
308 |
309 | const nextConfiguration = [...configuration];
310 |
311 | if (typeof nextConfiguration[0] === 'number') {
312 | if (nextConfiguration[0] === 0) {
313 | nextConfiguration[0] = 'off';
314 | } else if (nextConfiguration[0] === 1) {
315 | nextConfiguration[0] = 'warn';
316 | } else if (nextConfiguration[0] === 2) {
317 | nextConfiguration[0] = 'error';
318 | }
319 | }
320 |
321 | return nextConfiguration;
322 | };
323 |
324 | const describeRuleValue = (ruleValue) => {
325 | if (ruleValue === undefined) {
326 | return '👻';
327 | }
328 |
329 | if (ruleValue === 0 || ruleValue === 'off') {
330 | return '❌';
331 | }
332 |
333 | if (ruleValue === 1 || ruleValue === 'warn') {
334 | return '⚠️';
335 | }
336 |
337 | if (ruleValue === 2 || ruleValue === 'error') {
338 | return '🚨';
339 | }
340 |
341 | return false;
342 | };
343 |
344 | const getRuleConfiguration = (ruleset, ruleName) => {
345 | const ruleValueDescription = describeRuleValue(ruleset[ruleName]);
346 |
347 | if (ruleValueDescription) {
348 | return ruleValueDescription;
349 | }
350 |
351 | return describeRuleValue(ruleset[ruleName][0]);
352 | };
353 |
354 | module.exports = {
355 | getConfigurationRules,
356 | getLoadedRules,
357 | getRuleConfiguration,
358 | getRuleLink,
359 | isRuleEnabled,
360 | normalizeConfiguration,
361 | };
362 |
--------------------------------------------------------------------------------
/configurations/auto.test.ts:
--------------------------------------------------------------------------------
1 | import test from 'ava';
2 |
3 | test('foo', (t) => {
4 | t.pass();
5 | });
6 |
--------------------------------------------------------------------------------
/configurations/auto.ts:
--------------------------------------------------------------------------------
1 | import * as canonical from './canonical.js';
2 | import * as graphql from './graphql.js';
3 | import * as jsdoc from './jsdoc.js';
4 | import * as json from './json.js';
5 | import * as prettier from './prettier.js';
6 | import * as react from './react.js';
7 | import * as regexp from './regexp.js';
8 | import * as typescript from './typescript.js';
9 | import * as yaml from './yaml.js';
10 | import tseslint from 'typescript-eslint';
11 |
12 | export default tseslint.config(
13 | jsdoc.recommended,
14 | canonical.recommended,
15 | typescript.recommended,
16 | regexp.recommended,
17 | react.recommended,
18 | prettier.recommended,
19 | json.recommended,
20 | yaml.recommended,
21 | graphql.recommended,
22 | );
23 |
--------------------------------------------------------------------------------
/configurations/ava.ts:
--------------------------------------------------------------------------------
1 | import ava from 'eslint-plugin-ava';
2 | import unicorn from 'eslint-plugin-unicorn';
3 | import tseslint from 'typescript-eslint';
4 |
5 | export const recommended = tseslint.config({
6 | files: ['**/*.test.{js,ts,tsx}'],
7 | plugins: {
8 | ava,
9 | unicorn,
10 | },
11 | rules: {
12 | 'ava/assertion-arguments': 2,
13 | 'ava/hooks-order': 2,
14 | 'ava/max-asserts': [2, 5],
15 | 'ava/no-async-fn-without-await': 2,
16 | 'ava/no-duplicate-modifiers': 2,
17 | 'ava/no-identical-title': 2,
18 | 'ava/no-ignored-test-files': 2,
19 | 'ava/no-import-test-files': 0,
20 | 'ava/no-incorrect-deep-equal': 2,
21 | 'ava/no-inline-assertions': 2,
22 | 'ava/no-nested-tests': 2,
23 | 'ava/no-only-test': 2,
24 | 'ava/no-skip-assert': 2,
25 | 'ava/no-skip-test': 2,
26 | 'ava/no-todo-implementation': 2,
27 | 'ava/no-todo-test': 2,
28 | 'ava/no-unknown-modifiers': 2,
29 | 'ava/prefer-async-await': 2,
30 | 'ava/prefer-power-assert': 0,
31 | 'ava/prefer-t-regex': 2,
32 | 'ava/test-title': 2,
33 | 'ava/test-title-format': 0,
34 | 'ava/use-t': 2,
35 | 'ava/use-t-throws-async-well': 2,
36 | 'ava/use-t-well': 2,
37 | 'ava/use-test': 2,
38 | 'ava/use-true-false': 2,
39 | 'id-length': [
40 | 2,
41 | {
42 | exceptions: ['_', '$', 'a', 'b', 'x', 'y', 't'],
43 | min: 2,
44 | },
45 | ],
46 | 'unicorn/consistent-function-scoping': 0,
47 | },
48 | });
49 |
--------------------------------------------------------------------------------
/configurations/browser.ts:
--------------------------------------------------------------------------------
1 | import unicorn from 'eslint-plugin-unicorn';
2 | import globals from 'globals';
3 | import tseslint from 'typescript-eslint';
4 |
5 | export const recommended = tseslint.config({
6 | languageOptions: {
7 | globals: {
8 | ...globals.browser,
9 | },
10 | },
11 | plugins: {
12 | unicorn,
13 | },
14 | rules: {
15 | 'unicorn/prefer-dom-node-append': 2,
16 | 'unicorn/prefer-dom-node-dataset': 2,
17 | 'unicorn/prefer-dom-node-remove': 2,
18 | 'unicorn/prefer-dom-node-text-content': 2,
19 | 'unicorn/prefer-keyboard-event-key': 2,
20 | 'unicorn/prefer-modern-dom-apis': 2,
21 | },
22 | });
23 |
--------------------------------------------------------------------------------
/configurations/canonical.ts:
--------------------------------------------------------------------------------
1 | import stylisticPlugin from '@stylistic/eslint-plugin';
2 | import canonicalPlugin from 'eslint-plugin-canonical';
3 | import eslintComments from 'eslint-plugin-eslint-comments';
4 | import importPlugin from 'eslint-plugin-import';
5 | import perfectionist from 'eslint-plugin-perfectionist';
6 | import promisePlugin from 'eslint-plugin-promise';
7 | import unicornPlugin from 'eslint-plugin-unicorn';
8 | import tseslint from 'typescript-eslint';
9 |
10 | export const recommended = tseslint.config({
11 | files: ['**/*.{js,jsx,cjs,mjs,ts,tsx}'],
12 | plugins: {
13 | '@stylistic': stylisticPlugin,
14 | canonical: canonicalPlugin,
15 | 'eslint-comments': eslintComments,
16 | import: importPlugin,
17 | perfectionist,
18 | promise: promisePlugin,
19 | unicorn: unicornPlugin,
20 | },
21 | rules: {
22 | ...perfectionist.configs['recommended-natural'].rules,
23 | '@stylistic/array-bracket-newline': [
24 | 2,
25 | {
26 | minItems: 1,
27 | multiline: true,
28 | },
29 | ],
30 | '@stylistic/array-bracket-spacing': [2, 'never'],
31 | '@stylistic/array-element-newline': [
32 | 2,
33 | {
34 | minItems: 1,
35 | multiline: true,
36 | },
37 | ],
38 | '@stylistic/arrow-parens': [2, 'always'],
39 | '@stylistic/arrow-spacing': [
40 | 2,
41 | {
42 | after: true,
43 | before: true,
44 | },
45 | ],
46 | '@stylistic/block-spacing': [2, 'always'],
47 | '@stylistic/brace-style': [
48 | 2,
49 | '1tbs',
50 | {
51 | allowSingleLine: false,
52 | },
53 | ],
54 | '@stylistic/comma-dangle': [
55 | 2,
56 | {
57 | arrays: 'always-multiline',
58 | exports: 'always-multiline',
59 | functions: 'always-multiline',
60 | imports: 'always-multiline',
61 | objects: 'always-multiline',
62 | },
63 | ],
64 | '@stylistic/comma-spacing': [
65 | 2,
66 | {
67 | after: true,
68 | before: false,
69 | },
70 | ],
71 | '@stylistic/comma-style': [2, 'last'],
72 | '@stylistic/computed-property-spacing': [2, 'never'],
73 | '@stylistic/dot-location': [2, 'property'],
74 | '@stylistic/eol-last': 2,
75 | '@stylistic/func-call-spacing': [2, 'never'],
76 | '@stylistic/function-call-argument-newline': [2, 'consistent'],
77 | '@stylistic/function-call-spacing': [2, 'never'],
78 | '@stylistic/generator-star-spacing': [
79 | 2,
80 | {
81 | after: true,
82 | before: false,
83 | },
84 | ],
85 | '@stylistic/implicit-arrow-linebreak': [2, 'beside'],
86 | '@stylistic/indent': [2, 2],
87 | '@stylistic/jsx-quotes': [2, 'prefer-single'],
88 | '@stylistic/key-spacing': [
89 | 2,
90 | {
91 | afterColon: true,
92 | beforeColon: false,
93 | },
94 | ],
95 | '@stylistic/keyword-spacing': [
96 | 2,
97 | {
98 | after: true,
99 | before: true,
100 | },
101 | ],
102 | '@stylistic/line-comment-position': [
103 | 2,
104 | {
105 | position: 'above',
106 | },
107 | ],
108 | '@stylistic/linebreak-style': [2, 'unix'],
109 | '@stylistic/lines-around-comment': 0,
110 | '@stylistic/lines-between-class-members': [2, 'always'],
111 | '@stylistic/max-len': 0,
112 | '@stylistic/multiline-comment-style': 0,
113 | '@stylistic/multiline-ternary': 0,
114 | '@stylistic/new-parens': 2,
115 | '@stylistic/newline-per-chained-call': 0,
116 | '@stylistic/no-confusing-arrow': 2,
117 | '@stylistic/no-extra-parens': 2,
118 | '@stylistic/no-extra-semi': 2,
119 | '@stylistic/no-floating-decimal': 2,
120 | '@stylistic/no-mixed-operators': 0,
121 | '@stylistic/no-mixed-spaces-and-tabs': 2,
122 | '@stylistic/no-multi-spaces': 2,
123 | '@stylistic/no-multiple-empty-lines': [
124 | 2,
125 | {
126 | max: 1,
127 | maxBOF: 0,
128 | maxEOF: 1,
129 | },
130 | ],
131 | '@stylistic/no-tabs': 2,
132 | '@stylistic/no-trailing-spaces': 2,
133 | '@stylistic/no-whitespace-before-property': 2,
134 | '@stylistic/nonblock-statement-body-position': [2, 'below'],
135 | '@stylistic/object-curly-newline': [
136 | 2,
137 | {
138 | ExportDeclaration: 'always',
139 | ImportDeclaration: 'always',
140 | ObjectExpression: {
141 | minProperties: 1,
142 | multiline: true,
143 | },
144 | ObjectPattern: {
145 | minProperties: 1,
146 | multiline: true,
147 | },
148 | },
149 | ],
150 | '@stylistic/object-curly-spacing': 0,
151 | '@stylistic/object-property-newline': [
152 | 2,
153 | {
154 | allowAllPropertiesOnSameLine: false,
155 | },
156 | ],
157 | '@stylistic/one-var-declaration-per-line': 2,
158 | '@stylistic/operator-linebreak': [2, 'after'],
159 | '@stylistic/padded-blocks': [2, 'never'],
160 | '@stylistic/padding-line-between-statements': [
161 | 2,
162 | {
163 | blankLine: 'always',
164 | next: '*',
165 | prev: 'multiline-block-like',
166 | },
167 | ],
168 | '@stylistic/quote-props': [
169 | 2,
170 | 'as-needed',
171 | {
172 | numbers: true,
173 | },
174 | ],
175 | '@stylistic/quotes': [2, 'single'],
176 | '@stylistic/rest-spread-spacing': [2, 'never'],
177 | '@stylistic/semi': [2, 'always'],
178 | '@stylistic/semi-spacing': [
179 | 2,
180 | {
181 | after: true,
182 | before: false,
183 | },
184 | ],
185 | '@stylistic/space-before-blocks': [2, 'always'],
186 | '@stylistic/space-before-function-paren': [2, 'always'],
187 | '@stylistic/space-in-parens': [2, 'never'],
188 | '@stylistic/space-infix-ops': 2,
189 | '@stylistic/space-unary-ops': [
190 | 2,
191 | {
192 | nonwords: false,
193 | words: true,
194 | },
195 | ],
196 | '@stylistic/spaced-comment': [2, 'always'],
197 | '@stylistic/switch-colon-spacing': [
198 | 2,
199 | {
200 | after: true,
201 | before: false,
202 | },
203 | ],
204 | '@stylistic/template-curly-spacing': [2, 'never'],
205 | '@stylistic/template-tag-spacing': [2, 'never'],
206 | '@stylistic/wrap-iife': [2, 'inside'],
207 | '@stylistic/wrap-regex': 0,
208 | '@stylistic/yield-star-spacing': [
209 | 2,
210 | {
211 | after: true,
212 | before: false,
213 | },
214 | ],
215 | 'accessor-pairs': 2,
216 | 'array-callback-return': 2,
217 | 'arrow-body-style': [2, 'always'],
218 | 'block-scoped-var': 2,
219 | camelcase: 0,
220 | 'canonical/destructuring-property-newline': [
221 | 2,
222 | {
223 | allowAllPropertiesOnSameLine: false,
224 | },
225 | ],
226 | 'canonical/export-specifier-newline': 2,
227 | 'canonical/filename-match-exported': 2,
228 | 'canonical/filename-match-regex': [
229 | 1,
230 | {
231 | ignoreExporting: false,
232 | // https://regex101.com/r/wTCJVg/1
233 | regex: '^[A-Za-z]+(?:[A-Za-z0-9]*\\.[A-Za-z0-9]+)*\\d*$',
234 | },
235 | ],
236 | 'canonical/filename-no-index': 0,
237 | 'canonical/id-match': [
238 | 2,
239 | '(^[A-Za-z]+(?:[A-Z\\d][a-z\\d]*)*\\d*$)|(^[A-Z]+(_[A-Z\\d]+)*(_\\d$)*$)|(^(_|\\$)$)',
240 | {
241 | ignoreDestructuring: true,
242 | ignoreNamedImports: true,
243 | onlyDeclarations: true,
244 | properties: true,
245 | },
246 | ],
247 | 'canonical/import-specifier-newline': 2,
248 | 'canonical/no-import-namespace-destructure': 2,
249 | 'canonical/no-restricted-strings': 0,
250 | 'canonical/no-use-extend-native': 2,
251 | 'capitalized-comments': 0,
252 | 'class-methods-use-this': 0,
253 | complexity: [2, 30],
254 | 'consistent-return': 2,
255 | 'consistent-this': [2, 'self'],
256 | 'constructor-super': 2,
257 | curly: 2,
258 | 'default-case': 0,
259 | 'default-case-last': 0,
260 | 'default-param-last': 2,
261 | 'dot-notation': 2,
262 | eqeqeq: 2,
263 | 'eslint-comments/disable-enable-pair': [
264 | 2,
265 | {
266 | allowWholeFile: true,
267 | },
268 | ],
269 | 'eslint-comments/no-aggregating-enable': 2,
270 | 'eslint-comments/no-duplicate-disable': 2,
271 | 'eslint-comments/no-restricted-disable': 0,
272 | 'eslint-comments/no-unlimited-disable': 2,
273 | 'eslint-comments/no-unused-disable': 0,
274 | 'eslint-comments/no-unused-enable': 0,
275 | 'eslint-comments/no-use': 0,
276 | 'eslint-comments/require-description': 0,
277 | 'for-direction': 2,
278 | 'func-name-matching': 2,
279 | 'func-names': [2, 'never'],
280 | 'func-style': [2, 'expression'],
281 | 'function-paren-newline': [2, 'consistent'],
282 | 'getter-return': 2,
283 | 'grouped-accessor-pairs': [2, 'getBeforeSet'],
284 | 'guard-for-in': 2,
285 | 'id-denylist': 0,
286 | 'id-length': [
287 | 2,
288 | {
289 | exceptions: ['_', '$', 'a', 'b', 'x', 'y', 'z'],
290 | min: 2,
291 | },
292 | ],
293 | 'id-match': 0,
294 | 'import/consistent-type-specifier-style': [2, 'prefer-inline'],
295 | 'import/default': 2,
296 | 'import/dynamic-import-chunkname': 0,
297 | 'import/export': 2,
298 | 'import/exports-last': 0,
299 | 'import/extensions': 0,
300 | 'import/first': 2,
301 | 'import/group-exports': 0,
302 | 'import/max-dependencies': 0,
303 | 'import/named': 0,
304 | 'import/namespace': 0,
305 | 'import/newline-after-import': 2,
306 | 'import/no-absolute-path': 2,
307 | 'import/no-amd': 2,
308 | 'import/no-anonymous-default-export': 0,
309 | 'import/no-commonjs': 0,
310 | 'import/no-cycle': 2,
311 | 'import/no-default-export': 0,
312 | 'import/no-deprecated': 1,
313 | 'import/no-duplicates': [
314 | 2,
315 | {
316 | considerQueryString: true,
317 | 'prefer-inline': true,
318 | },
319 | ],
320 | 'import/no-dynamic-require': 2,
321 | 'import/no-extraneous-dependencies': [
322 | 2,
323 | {
324 | devDependencies: true,
325 | optionalDependencies: true,
326 | peerDependencies: true,
327 | },
328 | ],
329 | 'import/no-import-module-exports': 0,
330 | 'import/no-internal-modules': 0,
331 | 'import/no-mutable-exports': 2,
332 | 'import/no-named-as-default': 2,
333 | 'import/no-named-as-default-member': 2,
334 | 'import/no-named-default': 2,
335 | 'import/no-named-export': 0,
336 | 'import/no-namespace': 0,
337 | 'import/no-nodejs-modules': 0,
338 | 'import/no-relative-packages': 0,
339 | 'import/no-relative-parent-imports': 0,
340 | 'import/no-restricted-paths': 0,
341 | 'import/no-self-import': 2,
342 | 'import/no-unassigned-import': 2,
343 | 'import/no-unresolved': 0,
344 | 'import/no-unused-modules': 0,
345 | 'import/no-useless-path-segments': [
346 | 2,
347 | {
348 | noUselessIndex: true,
349 | },
350 | ],
351 | 'import/no-webpack-loader-syntax': 2,
352 | 'import/order': 0,
353 | 'import/prefer-default-export': 0,
354 | 'import/unambiguous': 0,
355 | 'init-declarations': 0,
356 | 'max-classes-per-file': 0,
357 | 'max-depth': 0,
358 | 'max-lines': 0,
359 | 'max-lines-per-function': 0,
360 | 'max-nested-callbacks': 0,
361 | 'max-params': 0,
362 | 'max-statements': 0,
363 | 'max-statements-per-line': [
364 | 2,
365 | {
366 | max: 1,
367 | },
368 | ],
369 | 'new-cap': 0,
370 | 'no-alert': 2,
371 | 'no-array-constructor': 2,
372 | 'no-async-promise-executor': 2,
373 | 'no-await-in-loop': 0,
374 | 'no-bitwise': 2,
375 | 'no-caller': 2,
376 | 'no-case-declarations': 2,
377 | 'no-class-assign': 2,
378 | 'no-compare-neg-zero': 2,
379 | 'no-cond-assign': 2,
380 | 'no-console': 2,
381 | 'no-const-assign': 2,
382 | 'no-constant-condition': 0,
383 | 'no-constructor-return': 2,
384 | 'no-continue': 0,
385 | 'no-control-regex': 2,
386 | 'no-debugger': 2,
387 | 'no-delete-var': 2,
388 | 'no-div-regex': 2,
389 | 'no-dupe-args': 2,
390 | 'no-dupe-class-members': 2,
391 | 'no-dupe-else-if': 2,
392 | 'no-dupe-keys': 2,
393 | 'no-duplicate-case': 2,
394 | 'no-duplicate-imports': 0,
395 | 'no-else-return': 0,
396 | 'no-empty': 2,
397 | 'no-empty-character-class': 2,
398 | 'no-empty-pattern': 2,
399 | 'no-eq-null': 2,
400 | 'no-eval': 2,
401 | 'no-ex-assign': 2,
402 | 'no-extend-native': 2,
403 | 'no-extra-bind': 2,
404 | 'no-extra-boolean-cast': 2,
405 | 'no-extra-label': 2,
406 |
407 | 'no-fallthrough': 0,
408 | 'no-func-assign': 2,
409 | 'no-global-assign': 2,
410 | 'no-implicit-coercion': 2,
411 | 'no-implicit-globals': 2,
412 | 'no-implied-eval': 2,
413 | 'no-import-assign': 2,
414 | 'no-inline-comments': 2,
415 | 'no-inner-declarations': 2,
416 | 'no-invalid-regexp': 2,
417 | 'no-invalid-this': 0,
418 | 'no-irregular-whitespace': 2,
419 | 'no-iterator': 2,
420 | 'no-label-var': 2,
421 | 'no-labels': 2,
422 | 'no-lone-blocks': 2,
423 | 'no-lonely-if': 2,
424 | 'no-loop-func': 2,
425 | 'no-loss-of-precision': 2,
426 | 'no-magic-numbers': 0,
427 | 'no-misleading-character-class': 2,
428 | 'no-multi-assign': 2,
429 | 'no-multi-str': 2,
430 | 'no-negated-condition': 2,
431 | 'no-nested-ternary': 0,
432 | 'no-new': 2,
433 | 'no-new-func': 2,
434 | 'no-new-object': 2,
435 | 'no-new-symbol': 2,
436 | 'no-new-wrappers': 2,
437 | 'no-nonoctal-decimal-escape': 2,
438 | 'no-obj-calls': 2,
439 | 'no-octal': 2,
440 | 'no-octal-escape': 2,
441 | 'no-param-reassign': [
442 | 2,
443 | {
444 | props: false,
445 | },
446 | ],
447 | 'no-plusplus': 0,
448 | 'no-promise-executor-return': 2,
449 | 'no-proto': 2,
450 | 'no-prototype-builtins': 2,
451 | 'no-redeclare': [
452 | 2,
453 | {
454 | builtinGlobals: true,
455 | },
456 | ],
457 | 'no-regex-spaces': 2,
458 | 'no-restricted-exports': 0,
459 | 'no-restricted-globals': 0,
460 | 'no-restricted-imports': 0,
461 | 'no-restricted-properties': 0,
462 | 'no-restricted-syntax': 0,
463 | 'no-return-assign': 2,
464 | 'no-script-url': 2,
465 | 'no-self-assign': 2,
466 | 'no-self-compare': 2,
467 | 'no-sequences': 2,
468 | 'no-setter-return': 2,
469 | 'no-shadow': [
470 | 2,
471 | {
472 | builtinGlobals: false,
473 | hoist: 'all',
474 | },
475 | ],
476 | 'no-shadow-restricted-names': 2,
477 | 'no-sparse-arrays': 2,
478 | 'no-template-curly-in-string': 2,
479 | 'no-ternary': 0,
480 | 'no-this-before-super': 2,
481 | 'no-throw-literal': 2,
482 | 'no-undef': 2,
483 | 'no-undef-init': 2,
484 | 'no-undefined': 0,
485 | 'no-underscore-dangle': 0,
486 | 'no-unexpected-multiline': 2,
487 | 'no-unmodified-loop-condition': 2,
488 | 'no-unneeded-ternary': 2,
489 | 'no-unreachable': 0,
490 | 'no-unreachable-loop': 2,
491 | 'no-unsafe-finally': 2,
492 | 'no-unsafe-negation': 2,
493 | 'no-unsafe-optional-chaining': 2,
494 | 'no-unused-expressions': 2,
495 | 'no-unused-labels': 2,
496 | 'no-unused-private-class-members': 0,
497 | 'no-unused-vars': 2,
498 | 'no-use-before-define': [
499 | 2,
500 | {
501 | classes: true,
502 | functions: false,
503 | variables: true,
504 | },
505 | ],
506 | 'no-useless-backreference': 2,
507 | 'no-useless-call': 2,
508 | 'no-useless-catch': 2,
509 | 'no-useless-computed-key': 2,
510 | 'no-useless-concat': 2,
511 | 'no-useless-constructor': 2,
512 | 'no-useless-escape': 2,
513 | 'no-useless-rename': [
514 | 2,
515 | {
516 | ignoreDestructuring: false,
517 | ignoreExport: false,
518 | ignoreImport: false,
519 | },
520 | ],
521 | 'no-useless-return': 2,
522 | 'no-var': 2,
523 | 'no-void': [
524 | 2,
525 | {
526 | allowAsStatement: true,
527 | },
528 | ],
529 | 'no-warning-comments': [
530 | 1,
531 | {
532 | location: 'start',
533 | terms: ['todo', '@toto'],
534 | },
535 | ],
536 | 'no-with': 2,
537 | 'object-shorthand': [2, 'always'],
538 | 'one-var': [2, 'never'],
539 | 'operator-assignment': [2, 'always'],
540 | 'perfectionist/sort-imports': [
541 | 2,
542 | {
543 | groups: [],
544 | ignoreCase: true,
545 | maxLineLength: undefined,
546 | newlinesBetween: 'never',
547 | type: 'natural',
548 | },
549 | ],
550 | 'prefer-arrow-callback': 2,
551 | 'prefer-const': 2,
552 | 'prefer-destructuring': 0,
553 | 'prefer-exponentiation-operator': 2,
554 | 'prefer-named-capture-group': 0,
555 | 'prefer-numeric-literals': 2,
556 | 'prefer-object-spread': 2,
557 | 'prefer-promise-reject-errors': 2,
558 | 'prefer-regex-literals': [
559 | 2,
560 | {
561 | disallowRedundantWrapping: true,
562 | },
563 | ],
564 | 'prefer-rest-params': 2,
565 | 'prefer-spread': 2,
566 | 'prefer-template': 0,
567 | 'promise/param-names': 2,
568 | 'promise/prefer-await-to-callbacks': 0,
569 | 'promise/prefer-await-to-then': 2,
570 | 'promise/valid-params': 2,
571 | radix: 2,
572 | 'require-atomic-updates': 2,
573 | 'require-await': 0,
574 | 'require-unicode-regexp': 2,
575 | 'require-yield': 2,
576 | 'semi-style': [2, 'last'],
577 | 'sort-imports': 0,
578 | 'sort-keys': 0,
579 | 'sort-vars': 2,
580 | strict: [2, 'never'],
581 | 'symbol-description': 2,
582 | 'unicode-bom': [2, 'never'],
583 | 'unicorn/better-regex': 2,
584 | 'unicorn/catch-error-name': [
585 | 'error',
586 | {
587 | name: 'error',
588 | },
589 | ],
590 | 'unicorn/consistent-destructuring': 0,
591 | 'unicorn/consistent-function-scoping': 2,
592 | 'unicorn/custom-error-definition': 0,
593 | 'unicorn/empty-brace-spaces': 2,
594 | 'unicorn/error-message': 2,
595 | 'unicorn/escape-case': 2,
596 | 'unicorn/expiring-todo-comments': 0,
597 | 'unicorn/explicit-length-check': 0,
598 | 'unicorn/filename-case': 0,
599 | 'unicorn/import-style': 0,
600 | 'unicorn/new-for-builtins': 2,
601 | 'unicorn/no-abusive-eslint-disable': 2,
602 | 'unicorn/no-array-callback-reference': 0,
603 | 'unicorn/no-array-for-each': 2,
604 | 'unicorn/no-array-method-this-argument': 2,
605 | 'unicorn/no-array-reduce': [
606 | 'error',
607 | {
608 | allowSimpleOperations: true,
609 | },
610 | ],
611 | 'unicorn/no-array-reverse': 2,
612 | 'unicorn/no-await-expression-member': 0,
613 | 'unicorn/no-console-spaces': 0,
614 | 'unicorn/no-document-cookie': 2,
615 | 'unicorn/no-empty-file': 2,
616 | 'unicorn/no-for-loop': 2,
617 | 'unicorn/no-hex-escape': 2,
618 | 'unicorn/no-instanceof-array': 2,
619 | 'unicorn/no-invalid-remove-event-listener': 2,
620 | 'unicorn/no-keyword-prefix': 0,
621 | 'unicorn/no-lonely-if': 2,
622 | 'unicorn/no-nested-ternary': 2,
623 | 'unicorn/no-new-array': 2,
624 | 'unicorn/no-new-buffer': 2,
625 | 'unicorn/no-null': 0,
626 | 'unicorn/no-object-as-default-parameter': 2,
627 | 'unicorn/no-process-exit': 0,
628 | 'unicorn/no-static-only-class': 2,
629 | 'unicorn/no-thenable': 2,
630 | 'unicorn/no-this-assignment': 2,
631 | 'unicorn/no-unnecessary-array-flat-depth': 2,
632 | 'unicorn/no-unnecessary-array-splice-count': 2,
633 | 'unicorn/no-unreadable-array-destructuring': 0,
634 | 'unicorn/no-unreadable-iife': 2,
635 | 'unicorn/no-unused-properties': 2,
636 | 'unicorn/no-useless-error-capture-stack-trace': 2,
637 | 'unicorn/no-useless-fallback-in-spread': 2,
638 | 'unicorn/no-useless-length-check': 2,
639 | 'unicorn/no-useless-promise-resolve-reject': 2,
640 | 'unicorn/no-useless-spread': 2,
641 | 'unicorn/no-useless-switch-case': 0,
642 | 'unicorn/no-useless-undefined': 0,
643 | 'unicorn/no-zero-fractions': 2,
644 | 'unicorn/number-literal-case': 2,
645 | 'unicorn/numeric-separators-style': [
646 | 2,
647 | {
648 | number: {
649 | groupLength: 3,
650 | minimumDigits: 0,
651 | },
652 | },
653 | ],
654 | 'unicorn/prefer-add-event-listener': 0,
655 | 'unicorn/prefer-array-find': 2,
656 | 'unicorn/prefer-array-flat': 2,
657 | 'unicorn/prefer-array-flat-map': 2,
658 | 'unicorn/prefer-array-index-of': 2,
659 | 'unicorn/prefer-array-some': 2,
660 | 'unicorn/prefer-at': 0,
661 | 'unicorn/prefer-blob-reading-methods': 2,
662 | 'unicorn/prefer-class-fields': 2,
663 | 'unicorn/prefer-code-point': 2,
664 | 'unicorn/prefer-date-now': 2,
665 | 'unicorn/prefer-default-parameters': 2,
666 | 'unicorn/prefer-export-from': 2,
667 | 'unicorn/prefer-import-meta-properties': 2,
668 | 'unicorn/prefer-includes': 2,
669 | 'unicorn/prefer-json-parse-buffer': 2,
670 | 'unicorn/prefer-math-trunc': 2,
671 | 'unicorn/prefer-modern-math-apis': 2,
672 | 'unicorn/prefer-native-coercion-functions': 2,
673 | 'unicorn/prefer-negative-index': 0,
674 | 'unicorn/prefer-number-properties': 2,
675 | 'unicorn/prefer-object-from-entries': 2,
676 | 'unicorn/prefer-optional-catch-binding': 2,
677 | 'unicorn/prefer-prototype-methods': 0,
678 | 'unicorn/prefer-query-selector': 2,
679 | 'unicorn/prefer-reflect-apply': 2,
680 | 'unicorn/prefer-regexp-test': 2,
681 | 'unicorn/prefer-set-has': 0,
682 | 'unicorn/prefer-single-call': 0,
683 | 'unicorn/prefer-spread': 0,
684 | 'unicorn/prefer-string-replace-all': 2,
685 | 'unicorn/prefer-string-slice': 2,
686 | 'unicorn/prefer-string-starts-ends-with': 2,
687 | 'unicorn/prefer-string-trim-start-end': 2,
688 | 'unicorn/prefer-switch': 0,
689 | 'unicorn/prefer-ternary': 0,
690 | 'unicorn/prefer-top-level-await': 0,
691 | 'unicorn/prefer-type-error': 2,
692 | 'unicorn/prevent-abbreviations': [
693 | 2,
694 | {
695 | checkProperties: false,
696 | replacements: {
697 | args: false,
698 | pkg: false,
699 | props: false,
700 | ref: false,
701 | rel: false,
702 | },
703 | },
704 | ],
705 | 'unicorn/relative-url-style': [2, 'never'],
706 | 'unicorn/require-array-join-separator': 2,
707 | 'unicorn/require-module-specifiers': 2,
708 | 'unicorn/require-number-to-fixed-digits-argument': 2,
709 | 'unicorn/require-post-message-target-origin': 2,
710 | 'unicorn/string-content': 0,
711 | 'unicorn/template-indent': 2,
712 | 'unicorn/text-encoding-identifier-case': 2,
713 | 'unicorn/throw-new-error': 2,
714 | 'use-isnan': 2,
715 | 'valid-typeof': [
716 | 2,
717 | {
718 | requireStringLiterals: true,
719 | },
720 | ],
721 | 'vars-on-top': 2,
722 | yoda: [2, 'never'],
723 | },
724 | settings: {
725 | 'import/extensions': ['.js', '.jsx', '.mjs', '.ts', '.tsx'],
726 | },
727 | });
728 |
--------------------------------------------------------------------------------
/configurations/graphql.ts:
--------------------------------------------------------------------------------
1 | import * as graphqlPlugin from '@graphql-eslint/eslint-plugin';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | files: ['**/*.graphql'],
6 | languageOptions: {
7 | parser: graphqlPlugin,
8 | },
9 | plugins: {
10 | '@graphql-eslint': graphqlPlugin,
11 | },
12 | rules: {
13 | '@graphql-eslint/alphabetize': [
14 | 2,
15 | {
16 | arguments: [
17 | 'FieldDefinition',
18 | 'Field',
19 | 'DirectiveDefinition',
20 | 'Directive',
21 | ],
22 | fields: [
23 | 'ObjectTypeDefinition',
24 | 'InterfaceTypeDefinition',
25 | 'InputObjectTypeDefinition',
26 | ],
27 | selections: ['OperationDefinition', 'FragmentDefinition'],
28 | values: ['EnumTypeDefinition'],
29 | variables: ['OperationDefinition'],
30 | },
31 | ],
32 | '@graphql-eslint/fields-on-correct-type': 2,
33 | '@graphql-eslint/fragments-on-composite-type': 2,
34 | '@graphql-eslint/input-name': 2,
35 | '@graphql-eslint/known-argument-names': 2,
36 | '@graphql-eslint/known-directives': 2,
37 | '@graphql-eslint/known-type-names': 2,
38 | '@graphql-eslint/lone-anonymous-operation': 2,
39 | '@graphql-eslint/lone-schema-definition': 2,
40 | '@graphql-eslint/match-document-filename': [
41 | 2,
42 | {
43 | fileExtension: '.graphql',
44 | },
45 | ],
46 | '@graphql-eslint/naming-convention': [
47 | 2,
48 | {
49 | allowLeadingUnderscore: true,
50 | EnumTypeDefinition: 'PascalCase',
51 | EnumValueDefinition: 'UPPER_CASE',
52 | FieldDefinition: 'camelCase',
53 | InputObjectTypeDefinition: 'PascalCase',
54 | InputValueDefinition: 'camelCase',
55 | InterfaceTypeDefinition: 'PascalCase',
56 | ObjectTypeDefinition: 'PascalCase',
57 | ScalarTypeDefinition: 'PascalCase',
58 | UnionTypeDefinition: 'PascalCase',
59 | },
60 | ],
61 | '@graphql-eslint/no-anonymous-operations': 2,
62 | '@graphql-eslint/no-case-insensitive-enum-values-duplicates': 2,
63 | '@graphql-eslint/no-deprecated': 2,
64 | '@graphql-eslint/no-duplicate-fields': 2,
65 | '@graphql-eslint/no-fragment-cycles': 2,
66 | '@graphql-eslint/no-hashtag-description': 2,
67 | '@graphql-eslint/no-scalar-result-type-on-mutation': 2,
68 | '@graphql-eslint/no-typename-prefix': 2,
69 | '@graphql-eslint/no-unreachable-types': 2,
70 | '@graphql-eslint/one-field-subscriptions': 2,
71 | '@graphql-eslint/overlapping-fields-can-be-merged': 2,
72 | '@graphql-eslint/possible-fragment-spread': 2,
73 | '@graphql-eslint/possible-type-extension': 2,
74 | '@graphql-eslint/provided-required-arguments': 2,
75 | '@graphql-eslint/require-deprecation-reason': 2,
76 | '@graphql-eslint/scalar-leafs': 2,
77 | '@graphql-eslint/unique-argument-names': 2,
78 | '@graphql-eslint/unique-directive-names': 2,
79 | '@graphql-eslint/unique-directive-names-per-location': 2,
80 | '@graphql-eslint/unique-enum-value-names': 2,
81 | '@graphql-eslint/unique-field-definition-names': 2,
82 | '@graphql-eslint/unique-input-field-names': 2,
83 | '@graphql-eslint/unique-operation-types': 2,
84 | '@graphql-eslint/unique-type-names': 2,
85 | '@graphql-eslint/unique-variable-names': 2,
86 | '@graphql-eslint/value-literals-of-correct-type': 2,
87 | '@graphql-eslint/variables-are-input-types': 2,
88 | '@graphql-eslint/variables-in-allowed-position': 2,
89 | },
90 | });
91 |
--------------------------------------------------------------------------------
/configurations/index.ts:
--------------------------------------------------------------------------------
1 | export * as ava from './ava.js';
2 | export * as browser from './browser.js';
3 | export * as canonical from './canonical.js';
4 | export * as graphql from './graphql.js';
5 | export * as jest from './jest.js';
6 | export * as jsdoc from './jsdoc.js';
7 | export * as json from './json.js';
8 | export * as jsxA11y from './jsx-a11y.js';
9 | export * as lodash from './lodash.js';
10 | export * as mocha from './mocha.js';
11 | export * as moduleRules from './module.js';
12 | export * as next from './next.js';
13 | export * as node from './node.js';
14 | export * as prettier from './prettier.js';
15 | export * as react from './react.js';
16 | export * as regexp from './regexp.js';
17 | export * as typescript from './typescript.js';
18 | export * as vitest from './vitest.js';
19 | export * as yaml from './yaml.js';
20 | export * as zod from './zod.js';
21 |
--------------------------------------------------------------------------------
/configurations/jest.ts:
--------------------------------------------------------------------------------
1 | import jestPlugin from 'eslint-plugin-jest';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | languageOptions: {
6 | globals: jestPlugin.environments.globals.globals,
7 | },
8 | plugins: {
9 | jest: jestPlugin,
10 | },
11 | rules: {
12 | 'jest/consistent-test-it': 2,
13 | 'jest/expect-expect': 2,
14 | 'jest/max-nested-describe': [
15 | 2,
16 | {
17 | max: 5,
18 | },
19 | ],
20 | 'jest/no-alias-methods': 2,
21 | 'jest/no-commented-out-tests': 2,
22 | 'jest/no-conditional-expect': 2,
23 | 'jest/no-conditional-in-test': 2,
24 | 'jest/no-deprecated-functions': 2,
25 | 'jest/no-disabled-tests': 1,
26 | 'jest/no-done-callback': 2,
27 | 'jest/no-duplicate-hooks': 2,
28 | 'jest/no-export': 2,
29 | 'jest/no-focused-tests': 2,
30 | 'jest/no-hooks': 0,
31 | 'jest/no-identical-title': 2,
32 | 'jest/no-interpolation-in-snapshots': 2,
33 | 'jest/no-jasmine-globals': 2,
34 | 'jest/no-large-snapshots': 0,
35 | 'jest/no-mocks-import': 2,
36 | 'jest/no-restricted-matchers': 0,
37 | 'jest/no-standalone-expect': 2,
38 | 'jest/no-test-prefixes': 2,
39 | 'jest/no-test-return-statement': 2,
40 | 'jest/prefer-called-with': 2,
41 | 'jest/prefer-comparison-matcher': 2,
42 | 'jest/prefer-expect-assertions': [
43 | 2,
44 | {
45 | onlyFunctionsWithAsyncKeyword: true,
46 | },
47 | ],
48 | 'jest/prefer-expect-resolves': 2,
49 | 'jest/prefer-hooks-on-top': 2,
50 | 'jest/prefer-lowercase-title': 0,
51 | 'jest/prefer-snapshot-hint': 2,
52 | 'jest/prefer-spy-on': 2,
53 | 'jest/prefer-strict-equal': 0,
54 | 'jest/prefer-to-be': 2,
55 | 'jest/prefer-to-contain': 2,
56 | 'jest/prefer-to-have-length': 2,
57 | 'jest/prefer-todo': 2,
58 | 'jest/require-hook': 0,
59 | 'jest/require-to-throw-message': 2,
60 | 'jest/require-top-level-describe': 2,
61 | 'jest/unbound-method': 0,
62 | 'jest/valid-describe-callback': 2,
63 | 'jest/valid-expect': 2,
64 | 'jest/valid-expect-in-promise': 2,
65 | 'jest/valid-title': 2,
66 | },
67 | });
68 |
--------------------------------------------------------------------------------
/configurations/jsdoc.ts:
--------------------------------------------------------------------------------
1 | import jsdocPlugin from 'eslint-plugin-jsdoc';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | files: ['**/*.{js,jsx,cjs,mjs,ts,tsx}'],
6 | plugins: {
7 | jsdoc: jsdocPlugin,
8 | },
9 | rules: {
10 | 'jsdoc/check-access': 2,
11 | 'jsdoc/check-alignment': 2,
12 | 'jsdoc/check-examples': 0,
13 | 'jsdoc/check-indentation': 0,
14 | 'jsdoc/check-line-alignment': 0,
15 | 'jsdoc/check-param-names': 2,
16 | 'jsdoc/check-property-names': 2,
17 | 'jsdoc/check-syntax': 2,
18 | 'jsdoc/check-tag-names': [
19 | 2,
20 | {
21 | definedTags: ['jest-environment', 'jest-environment-options'],
22 | },
23 | ],
24 | 'jsdoc/check-types': 2,
25 | 'jsdoc/check-values': 2,
26 | 'jsdoc/empty-tags': 2,
27 | 'jsdoc/implements-on-classes': 2,
28 | 'jsdoc/match-description': 0,
29 | 'jsdoc/match-name': 0,
30 | 'jsdoc/multiline-blocks': [
31 | 2,
32 | {
33 | noMultilineBlocks: false,
34 | noSingleLineBlocks: true,
35 | },
36 | ],
37 | 'jsdoc/no-bad-blocks': 2,
38 | 'jsdoc/no-defaults': 2,
39 | 'jsdoc/no-missing-syntax': 0,
40 | 'jsdoc/no-multi-asterisks': 2,
41 | 'jsdoc/no-restricted-syntax': 0,
42 | 'jsdoc/no-types': 0,
43 | 'jsdoc/no-undefined-types': 2,
44 | 'jsdoc/require-asterisk-prefix': 2,
45 | 'jsdoc/require-description': 0,
46 | 'jsdoc/require-description-complete-sentence': 0,
47 | 'jsdoc/require-example': 0,
48 | 'jsdoc/require-file-overview': 0,
49 | 'jsdoc/require-hyphen-before-param-description': 0,
50 | 'jsdoc/require-jsdoc': 0,
51 | 'jsdoc/require-param': 0,
52 | 'jsdoc/require-param-description': 0,
53 | 'jsdoc/require-param-name': 2,
54 | 'jsdoc/require-param-type': 0,
55 | 'jsdoc/require-property': 2,
56 | 'jsdoc/require-property-description': 2,
57 | 'jsdoc/require-property-name': 2,
58 | 'jsdoc/require-property-type': 2,
59 | 'jsdoc/require-returns': 0,
60 | 'jsdoc/require-returns-check': 0,
61 | 'jsdoc/require-returns-description': 0,
62 | 'jsdoc/require-returns-type': 0,
63 | 'jsdoc/require-throws': 0,
64 | 'jsdoc/require-yields': 0,
65 | 'jsdoc/require-yields-check': 0,
66 | 'jsdoc/tag-lines': [2, 'never'],
67 | 'jsdoc/valid-types': 2,
68 | },
69 | });
70 |
--------------------------------------------------------------------------------
/configurations/json.ts:
--------------------------------------------------------------------------------
1 | import jsoncPlugin from 'eslint-plugin-jsonc';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | files: ['**/*.json'],
6 | languageOptions: {
7 | parser: jsoncPlugin,
8 | },
9 | plugins: {
10 | jsonc: jsoncPlugin,
11 | },
12 | rules: {
13 | 'jsonc/array-bracket-newline': [
14 | 2,
15 | {
16 | minItems: 1,
17 | multiline: true,
18 | },
19 | ],
20 | 'jsonc/array-bracket-spacing': 2,
21 | 'jsonc/array-element-newline': [2, 'always'],
22 | 'jsonc/auto': 0,
23 | 'jsonc/comma-dangle': 2,
24 | 'jsonc/comma-style': [2, 'last'],
25 | 'jsonc/indent': [2, 2],
26 | 'jsonc/key-name-casing': 0,
27 | 'jsonc/key-spacing': 2,
28 | 'jsonc/no-bigint-literals': 2,
29 | 'jsonc/no-binary-expression': 2,
30 | 'jsonc/no-binary-numeric-literals': 2,
31 | 'jsonc/no-comments': 2,
32 | 'jsonc/no-dupe-keys': 2,
33 | 'jsonc/no-escape-sequence-in-identifier': 2,
34 | 'jsonc/no-floating-decimal': 2,
35 | 'jsonc/no-hexadecimal-numeric-literals': 2,
36 | 'jsonc/no-infinity': 2,
37 | 'jsonc/no-multi-str': 2,
38 | 'jsonc/no-nan': 2,
39 | 'jsonc/no-number-props': 2,
40 | 'jsonc/no-numeric-separators': 2,
41 | 'jsonc/no-octal': 2,
42 | 'jsonc/no-octal-escape': 2,
43 | 'jsonc/no-octal-numeric-literals': 2,
44 | 'jsonc/no-parenthesized': 2,
45 | 'jsonc/no-plus-sign': 2,
46 | 'jsonc/no-regexp-literals': 2,
47 | 'jsonc/no-sparse-arrays': 2,
48 | 'jsonc/no-template-literals': 2,
49 | 'jsonc/no-undefined-value': 2,
50 | 'jsonc/no-unicode-codepoint-escapes': 2,
51 | 'jsonc/no-useless-escape': 2,
52 | 'jsonc/object-curly-newline': 2,
53 | 'jsonc/object-curly-spacing': 2,
54 | 'jsonc/object-property-newline': 2,
55 | 'jsonc/quote-props': 2,
56 | 'jsonc/quotes': 2,
57 | 'jsonc/sort-array-values': 0,
58 | 'jsonc/sort-keys': [
59 | 2,
60 | 'asc',
61 | {
62 | caseSensitive: false,
63 | natural: true,
64 | },
65 | ],
66 | 'jsonc/space-unary-ops': 2,
67 | 'jsonc/valid-json-number': 2,
68 | 'jsonc/vue-custom-block/no-parsing-error': 2,
69 | },
70 | });
71 |
--------------------------------------------------------------------------------
/configurations/jsx-a11y.ts:
--------------------------------------------------------------------------------
1 | import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | plugins: {
6 | 'jsx-a11y': jsxA11yPlugin,
7 | },
8 | rules: {
9 | 'jsx-a11y/alt-text': 2,
10 | 'jsx-a11y/anchor-has-content': 2,
11 | 'jsx-a11y/anchor-is-valid': 2,
12 | 'jsx-a11y/aria-activedescendant-has-tabindex': 2,
13 | 'jsx-a11y/aria-props': 2,
14 | 'jsx-a11y/aria-proptypes': 2,
15 | 'jsx-a11y/aria-role': 2,
16 | 'jsx-a11y/aria-unsupported-elements': 2,
17 | 'jsx-a11y/autocomplete-valid': 2,
18 | 'jsx-a11y/click-events-have-key-events': 2,
19 | 'jsx-a11y/control-has-associated-label': 0,
20 | 'jsx-a11y/heading-has-content': 2,
21 | 'jsx-a11y/html-has-lang': 2,
22 | 'jsx-a11y/iframe-has-title': 2,
23 | 'jsx-a11y/img-redundant-alt': 2,
24 | 'jsx-a11y/interactive-supports-focus': [
25 | 2,
26 | {
27 | tabbable: [
28 | 'button',
29 | 'checkbox',
30 | 'link',
31 | 'searchbox',
32 | 'spinbutton',
33 | 'switch',
34 | 'textbox',
35 | ],
36 | },
37 | ],
38 | 'jsx-a11y/label-has-associated-control': 2,
39 | 'jsx-a11y/lang': 0,
40 | 'jsx-a11y/media-has-caption': 2,
41 | 'jsx-a11y/mouse-events-have-key-events': 2,
42 | 'jsx-a11y/no-access-key': 2,
43 | 'jsx-a11y/no-autofocus': 0,
44 | 'jsx-a11y/no-distracting-elements': 2,
45 | 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
46 | 2,
47 | {
48 | canvas: ['img'],
49 | tr: ['none', 'presentation'],
50 | },
51 | ],
52 | 'jsx-a11y/no-noninteractive-element-interactions': [
53 | 2,
54 | {
55 | alert: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
56 | body: ['onError', 'onLoad'],
57 | dialog: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
58 | handlers: [
59 | 'onClick',
60 | 'onError',
61 | 'onLoad',
62 | 'onMouseDown',
63 | 'onMouseUp',
64 | 'onKeyPress',
65 | 'onKeyDown',
66 | 'onKeyUp',
67 | ],
68 | iframe: ['onError', 'onLoad'],
69 | img: ['onError', 'onLoad'],
70 | },
71 | ],
72 | 'jsx-a11y/no-noninteractive-element-to-interactive-role': [
73 | 2,
74 | {
75 | fieldset: ['radiogroup', 'presentation'],
76 | li: ['menuitem', 'option', 'row', 'tab', 'treeitem'],
77 | ol: [
78 | 'listbox',
79 | 'menu',
80 | 'menubar',
81 | 'radiogroup',
82 | 'tablist',
83 | 'tree',
84 | 'treegrid',
85 | ],
86 | table: ['grid'],
87 | td: ['gridcell'],
88 | ul: [
89 | 'listbox',
90 | 'menu',
91 | 'menubar',
92 | 'radiogroup',
93 | 'tablist',
94 | 'tree',
95 | 'treegrid',
96 | ],
97 | },
98 | ],
99 | 'jsx-a11y/no-noninteractive-tabindex': [
100 | 2,
101 | {
102 | allowExpressionValues: true,
103 | roles: ['tabpanel'],
104 | tags: [],
105 | },
106 | ],
107 | 'jsx-a11y/no-redundant-roles': 2,
108 | 'jsx-a11y/no-static-element-interactions': [
109 | 2,
110 | {
111 | allowExpressionValues: true,
112 | handlers: [
113 | 'onClick',
114 | 'onMouseDown',
115 | 'onMouseUp',
116 | 'onKeyPress',
117 | 'onKeyDown',
118 | 'onKeyUp',
119 | ],
120 | },
121 | ],
122 | 'jsx-a11y/role-has-required-aria-props': 2,
123 | 'jsx-a11y/role-supports-aria-props': 2,
124 | 'jsx-a11y/scope': 2,
125 | 'jsx-a11y/tabindex-no-positive': 2,
126 | },
127 | });
128 |
--------------------------------------------------------------------------------
/configurations/lodash.ts:
--------------------------------------------------------------------------------
1 | import lodashPlugin from 'eslint-plugin-lodash';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | plugins: {
6 | lodash: lodashPlugin,
7 | },
8 | rules: {
9 | 'lodash/callback-binding': 2,
10 | 'lodash/chain-style': [2, 'explicit'],
11 | 'lodash/chaining': [2, 'always'],
12 | 'lodash/collection-method-value': 2,
13 | 'lodash/collection-ordering': 2,
14 | 'lodash/collection-return': 2,
15 | 'lodash/consistent-compose': [2, 'flow'],
16 | 'lodash/identity-shorthand': [2, 'always'],
17 | 'lodash/import-scope': 0,
18 | 'lodash/matches-prop-shorthand': 2,
19 | 'lodash/matches-shorthand': [2, 'always', 3],
20 | 'lodash/no-commit': 2,
21 | 'lodash/no-double-unwrap': 2,
22 | 'lodash/no-extra-args': 2,
23 | 'lodash/no-unbound-this': 2,
24 | 'lodash/path-style': 0,
25 | 'lodash/prefer-compact': 2,
26 | 'lodash/prefer-constant': 0,
27 | 'lodash/prefer-filter': [2, 3],
28 | 'lodash/prefer-find': 2,
29 | 'lodash/prefer-flat-map': 2,
30 | 'lodash/prefer-get': [2, 3],
31 | 'lodash/prefer-immutable-method': 2,
32 | 'lodash/prefer-includes': 2,
33 | 'lodash/prefer-invoke-map': 0,
34 | 'lodash/prefer-is-nil': 2,
35 | 'lodash/prefer-lodash-chain': 2,
36 | 'lodash/prefer-lodash-method': 0,
37 | 'lodash/prefer-lodash-typecheck': 2,
38 | 'lodash/prefer-map': 2,
39 | 'lodash/prefer-matches': 2,
40 | 'lodash/prefer-noop': 0,
41 | 'lodash/prefer-over-quantifier': 2,
42 | 'lodash/prefer-reject': [1, 3],
43 | 'lodash/prefer-some': [
44 | 2,
45 | {
46 | includeNative: false,
47 | },
48 | ],
49 | 'lodash/prefer-startswith': 0,
50 | 'lodash/prefer-thru': 2,
51 | 'lodash/prefer-times': 2,
52 | 'lodash/prefer-wrapper-method': 2,
53 | 'lodash/preferred-alias': 2,
54 | 'lodash/prop-shorthand': 2,
55 | 'lodash/unwrap': 2,
56 | },
57 | });
58 |
--------------------------------------------------------------------------------
/configurations/mocha.ts:
--------------------------------------------------------------------------------
1 | import mochaPlugin from 'eslint-plugin-mocha';
2 | import globals from 'globals';
3 | import tseslint from 'typescript-eslint';
4 |
5 | export const recommended = tseslint.config({
6 | languageOptions: {
7 | globals: globals.mocha,
8 | },
9 | plugins: {
10 | mocha: mochaPlugin,
11 | },
12 | rules: {
13 | 'mocha/handle-done-callback': 2,
14 | 'mocha/max-top-level-suites': [
15 | 2,
16 | {
17 | limit: 1,
18 | },
19 | ],
20 | 'mocha/no-async-describe': 2,
21 | 'mocha/no-empty-description': 2,
22 | 'mocha/no-exclusive-tests': 2,
23 | 'mocha/no-exports': 2,
24 | 'mocha/no-global-tests': 2,
25 | 'mocha/no-hooks': 0,
26 | 'mocha/no-hooks-for-single-case': 2,
27 | 'mocha/no-identical-title': 2,
28 | 'mocha/no-mocha-arrows': 0,
29 | 'mocha/no-nested-tests': 2,
30 | 'mocha/no-pending-tests': 2,
31 | 'mocha/no-return-and-callback': 2,
32 | 'mocha/no-return-from-async': 2,
33 | 'mocha/no-setup-in-describe': 2,
34 | 'mocha/no-sibling-hooks': 2,
35 | 'mocha/no-skipped-tests': 2,
36 | 'mocha/no-synchronous-tests': 0,
37 | 'mocha/no-top-level-hooks': 2,
38 | 'mocha/prefer-arrow-callback': 0,
39 | 'mocha/valid-suite-description': 0,
40 | 'mocha/valid-test-description': 0,
41 | },
42 | });
43 |
--------------------------------------------------------------------------------
/configurations/module.ts:
--------------------------------------------------------------------------------
1 | import unicornPlugin from 'eslint-plugin-unicorn';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | plugins: {
6 | unicorn: unicornPlugin,
7 | },
8 | rules: {
9 | 'unicorn/prefer-module': 2,
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/configurations/next.ts:
--------------------------------------------------------------------------------
1 | import nextPlugin from '@next/eslint-plugin-next';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | plugins: {
6 | next: nextPlugin,
7 | },
8 | rules: {
9 | 'next/google-font-display': 1,
10 | 'next/google-font-preconnect': 1,
11 | 'next/inline-script-id': 2,
12 | 'next/link-passhref': 1,
13 | 'next/next-script-for-ga': 1,
14 | 'next/no-css-tags': 1,
15 | 'next/no-document-import-in-page': 2,
16 | 'next/no-duplicate-head': 2,
17 | 'next/no-head-import-in-document': 2,
18 | 'next/no-html-link-for-pages': 1,
19 | 'next/no-img-element': 1,
20 | 'next/no-page-custom-font': 1,
21 | 'next/no-script-in-document': 2,
22 | 'next/no-script-in-head': 2,
23 | 'next/no-server-import-in-page': 2,
24 | 'next/no-sync-scripts': 1,
25 | 'next/no-title-in-document-head': 1,
26 | 'next/no-typos': 1,
27 | 'next/no-unwanted-polyfillio': 1,
28 | },
29 | });
30 |
--------------------------------------------------------------------------------
/configurations/node.ts:
--------------------------------------------------------------------------------
1 | import importPlugin from 'eslint-plugin-import';
2 | import nPlugin from 'eslint-plugin-n';
3 | import unicornPlugin from 'eslint-plugin-unicorn';
4 | import globals from 'globals';
5 | import tseslint from 'typescript-eslint';
6 |
7 | export const recommended = tseslint.config({
8 | languageOptions: {
9 | globals: {
10 | ...globals.node,
11 | },
12 | },
13 | plugins: {
14 | import: importPlugin,
15 | // eslint-disable-next-line id-length
16 | n: nPlugin,
17 | unicorn: unicornPlugin,
18 | },
19 | rules: {
20 | 'n/callback-return': 2,
21 | 'n/exports-style': [2, 'module.exports'],
22 | 'n/file-extension-in-import': 0,
23 | 'n/global-require': 2,
24 | 'n/handle-callback-err': 2,
25 | 'n/no-callback-literal': 2,
26 | 'n/no-deprecated-api': 2,
27 | 'n/no-exports-assign': 2,
28 | 'n/no-extraneous-import': 2,
29 | 'n/no-extraneous-require': 2,
30 | 'n/no-missing-import': 0,
31 | 'n/no-missing-require': 0,
32 | 'n/no-mixed-requires': 0,
33 | 'n/no-new-require': 2,
34 | 'n/no-path-concat': 2,
35 | 'n/no-process-env': 2,
36 | 'n/no-process-exit': 2,
37 | 'n/no-restricted-import': 0,
38 | 'n/no-restricted-require': 0,
39 | 'n/no-sync': 0,
40 | 'n/no-unpublished-bin': 2,
41 | 'n/no-unpublished-import': 0,
42 | 'n/no-unpublished-require': 0,
43 | 'n/no-unsupported-features/es-builtins': 0,
44 | 'n/no-unsupported-features/es-syntax': 0,
45 | 'n/no-unsupported-features/node-builtins': 0,
46 | 'n/prefer-global/buffer': 2,
47 | 'n/prefer-global/console': 2,
48 | 'n/prefer-global/process': 2,
49 | 'n/prefer-global/text-decoder': 2,
50 | 'n/prefer-global/text-encoder': 2,
51 | 'n/prefer-global/url': 2,
52 | 'n/prefer-global/url-search-params': 2,
53 | 'n/prefer-promises/dns': 2,
54 | 'n/prefer-promises/fs': 2,
55 | 'n/process-exit-as-throw': 2,
56 | 'n/shebang': [
57 | 2,
58 | {
59 | convertPath: {
60 | 'src/**/*.js': ['^src/(.+?)\\.js$', 'dist/$1.js'],
61 | },
62 | },
63 | ],
64 | 'unicorn/prefer-node-protocol': 2,
65 | },
66 | });
67 |
--------------------------------------------------------------------------------
/configurations/prettier.ts:
--------------------------------------------------------------------------------
1 | import prettierPlugin from 'eslint-plugin-prettier';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | files: ['**/*.{js,jsx,cjs,mjs,ts,tsx}'],
6 | plugins: {
7 | prettier: prettierPlugin,
8 | },
9 | rules: {
10 | '@stylistic/array-bracket-newline': 0,
11 | '@stylistic/array-element-newline': 0,
12 | '@stylistic/brace-style': 0,
13 | '@stylistic/comma-dangle': 0,
14 | '@stylistic/generator-star-spacing': 0,
15 | '@stylistic/implicit-arrow-linebreak': 0,
16 | '@stylistic/indent': 0,
17 | '@stylistic/jsx-quotes': 0,
18 | '@stylistic/line-comment-position': 0,
19 | '@stylistic/linebreak-style': 0,
20 | '@stylistic/member-delimiter-style': 0,
21 | '@stylistic/no-confusing-arrow': 0,
22 | '@stylistic/no-extra-parens': 0,
23 | '@stylistic/no-trailing-spaces': 0,
24 | '@stylistic/nonblock-statement-body-position': 0,
25 | '@stylistic/object-curly-newline': 0,
26 | '@stylistic/object-property-newline': 0,
27 | '@stylistic/operator-linebreak': 0,
28 | '@stylistic/quotes': 0,
29 | '@stylistic/space-before-function-paren': 0,
30 | '@stylistic/space-in-parens': 0,
31 | '@typescript-eslint/indent': 0,
32 | 'arrow-body-style': 0,
33 | 'canonical/destructuring-property-newline': 0,
34 | 'canonical/export-specifier-newline': 0,
35 | 'canonical/import-specifier-newline': 0,
36 | 'function-paren-newline': 0,
37 | 'no-inline-comments': 0,
38 | 'prefer-arrow-callback': 0,
39 | 'prettier/prettier': [
40 | 2,
41 | {
42 | arrowParens: 'always',
43 | bracketSameLine: false,
44 | bracketSpacing: true,
45 | endOfLine: 'lf',
46 | printWidth: 80,
47 | proseWrap: 'preserve',
48 | quoteProps: 'as-needed',
49 | semi: true,
50 | singleAttributePerLine: true,
51 | singleQuote: true,
52 | tabWidth: 2,
53 | trailingComma: 'all',
54 | useTabs: false,
55 | },
56 | {
57 | usePrettierrc: false,
58 | },
59 | ],
60 | 'react/jsx-curly-newline': 0,
61 | 'react/jsx-indent': 0,
62 | 'react/jsx-indent-props': 0,
63 | 'react/jsx-newline': 0,
64 | 'unicorn/no-nested-ternary': 0,
65 | 'unicorn/template-indent': 0,
66 | },
67 | });
68 |
--------------------------------------------------------------------------------
/configurations/react.ts:
--------------------------------------------------------------------------------
1 | import canonicalPlugin from 'eslint-plugin-canonical';
2 | import reactPlugin from 'eslint-plugin-react';
3 | import reactHooksPlugin from 'eslint-plugin-react-hooks';
4 | import globals from 'globals';
5 | import tseslint from 'typescript-eslint';
6 |
7 | export const recommended = tseslint.config({
8 | files: ['**/*.{jsx,tsx}'],
9 | languageOptions: {
10 | globals: {
11 | ...globals.browser,
12 | },
13 | parserOptions: {
14 | ecmaFeatures: {
15 | jsx: true,
16 | },
17 | },
18 | },
19 | plugins: {
20 | canonical: canonicalPlugin,
21 | react: reactPlugin,
22 | 'react-hooks': reactHooksPlugin,
23 | },
24 | rules: {
25 | 'canonical/sort-react-dependencies': 2,
26 | 'react-hooks/exhaustive-deps': 2,
27 | 'react-hooks/rules-of-hooks': 2,
28 | 'react/boolean-prop-naming': 0,
29 | 'react/button-has-type': 2,
30 | 'react/default-props-match-prop-types': 2,
31 | 'react/destructuring-assignment': 0,
32 | 'react/display-name': 0,
33 | 'react/forbid-component-props': 2,
34 | 'react/forbid-dom-props': 0,
35 | 'react/forbid-elements': 0,
36 | 'react/forbid-foreign-prop-types': 0,
37 | 'react/forbid-prop-types': 0,
38 | 'react/function-component-definition': [
39 | 2,
40 | {
41 | namedComponents: 'arrow-function',
42 | unnamedComponents: 'arrow-function',
43 | },
44 | ],
45 | 'react/hook-use-state': 2,
46 | 'react/iframe-missing-sandbox': 2,
47 | 'react/jsx-boolean-value': [2, 'never'],
48 | 'react/jsx-child-element-spacing': 0,
49 | 'react/jsx-closing-bracket-location': [2, 'line-aligned'],
50 | 'react/jsx-closing-tag-location': 0,
51 | 'react/jsx-curly-brace-presence': [
52 | 2,
53 | {
54 | children: 'never',
55 | props: 'never',
56 | },
57 | ],
58 | 'react/jsx-curly-newline': 2,
59 | 'react/jsx-curly-spacing': [
60 | 2,
61 | 'never',
62 | {
63 | allowMultiline: true,
64 | },
65 | ],
66 | 'react/jsx-equals-spacing': [2, 'never'],
67 | 'react/jsx-filename-extension': 0,
68 | 'react/jsx-first-prop-new-line': [2, 'multiline-multiprop'],
69 | 'react/jsx-fragments': [2, 'syntax'],
70 | 'react/jsx-handler-names': [
71 | 2,
72 | {
73 | checkInlineFunction: false,
74 | checkLocalVariables: false,
75 | eventHandlerPrefix: 'handle',
76 | eventHandlerPropPrefix: 'on',
77 | },
78 | ],
79 | 'react/jsx-indent': [2, 2],
80 | 'react/jsx-indent-props': [2, 2],
81 | 'react/jsx-key': [
82 | 2,
83 | {
84 | checkFragmentShorthand: true,
85 | checkKeyMustBeforeSpread: true,
86 | },
87 | ],
88 | 'react/jsx-max-depth': 0,
89 | 'react/jsx-max-props-per-line': [
90 | 2,
91 | {
92 | maximum: 3,
93 | when: 'multiline',
94 | },
95 | ],
96 | 'react/jsx-newline': [
97 | 2,
98 | {
99 | prevent: true,
100 | },
101 | ],
102 | 'react/jsx-no-bind': [
103 | 2,
104 | {
105 | allowArrowFunctions: true,
106 | allowBind: false,
107 | ignoreRefs: true,
108 | },
109 | ],
110 | 'react/jsx-no-comment-textnodes': 2,
111 | 'react/jsx-no-constructed-context-values': 2,
112 | 'react/jsx-no-duplicate-props': 2,
113 | 'react/jsx-no-literals': 0,
114 | 'react/jsx-no-script-url': 2,
115 | 'react/jsx-no-target-blank': 2,
116 | 'react/jsx-no-undef': 2,
117 | 'react/jsx-no-useless-fragment': [
118 | 2,
119 | {
120 | allowExpressions: true,
121 | },
122 | ],
123 | 'react/jsx-one-expression-per-line': 0,
124 | 'react/jsx-pascal-case': [
125 | 2,
126 | {
127 | ignore: [
128 | 'h{}',
129 | 'h2',
130 | 'h3',
131 | 'h4',
132 | 'h5',
133 | 'h6',
134 | 'p',
135 | 'a',
136 | 'ul',
137 | 'ol',
138 | 'li',
139 | 'img',
140 | 'div',
141 | 'span',
142 | 'dl',
143 | 'dt',
144 | 'dd',
145 | ],
146 | },
147 | ],
148 | 'react/jsx-props-no-multi-spaces': 2,
149 | 'react/jsx-props-no-spreading': 0,
150 | 'react/jsx-sort-props': 0,
151 | 'react/jsx-tag-spacing': [
152 | 2,
153 | {
154 | afterOpening: 'never',
155 | beforeSelfClosing: 'always',
156 | closingSlash: 'never',
157 | },
158 | ],
159 | 'react/jsx-uses-react': 2,
160 | 'react/jsx-uses-vars': 2,
161 | 'react/jsx-wrap-multilines': 0,
162 | 'react/no-access-state-in-setstate': 2,
163 | 'react/no-adjacent-inline-elements': 0,
164 | 'react/no-array-index-key': 2,
165 | 'react/no-arrow-function-lifecycle': 2,
166 | 'react/no-children-prop': 2,
167 | 'react/no-danger': 2,
168 | 'react/no-danger-with-children': 2,
169 | 'react/no-deprecated': 2,
170 | 'react/no-did-mount-set-state': 2,
171 | 'react/no-did-update-set-state': 2,
172 | 'react/no-direct-mutation-state': 2,
173 | 'react/no-find-dom-node': 2,
174 | 'react/no-invalid-html-attribute': 2,
175 | 'react/no-is-mounted': 2,
176 | 'react/no-multi-comp': 0,
177 | 'react/no-namespace': 2,
178 | 'react/no-redundant-should-component-update': 2,
179 | 'react/no-render-return-value': 2,
180 | 'react/no-set-state': 2,
181 | 'react/no-string-refs': 2,
182 | 'react/no-this-in-sfc': 2,
183 | 'react/no-typos': 2,
184 | 'react/no-unescaped-entities': 0,
185 | 'react/no-unknown-property': 2,
186 | 'react/no-unsafe': 2,
187 | 'react/no-unstable-nested-components': 2,
188 | 'react/no-unused-class-component-methods': 2,
189 | 'react/no-unused-prop-types': 2,
190 | 'react/no-unused-state': 2,
191 | 'react/no-will-update-set-state': 2,
192 | 'react/prefer-es6-class': 2,
193 | 'react/prefer-exact-props': 0,
194 | 'react/prefer-read-only-props': 2,
195 | 'react/prefer-stateless-function': [
196 | 2,
197 | {
198 | ignorePureComponents: true,
199 | },
200 | ],
201 | 'react/prop-types': 2,
202 | 'react/react-in-jsx-scope': 0,
203 | 'react/require-default-props': 0,
204 | 'react/require-optimization': 0,
205 | 'react/require-render-return': 2,
206 | 'react/self-closing-comp': 2,
207 | 'react/sort-comp': 0,
208 | 'react/sort-default-props': 2,
209 | 'react/sort-prop-types': 2,
210 | 'react/state-in-constructor': [2, 'always'],
211 | 'react/static-property-placement': 0,
212 | 'react/style-prop-object': 2,
213 | 'react/void-dom-elements-no-children': 2,
214 | },
215 | settings: {
216 | react: {
217 | version: 'detect',
218 | },
219 | },
220 | });
221 |
--------------------------------------------------------------------------------
/configurations/regexp.ts:
--------------------------------------------------------------------------------
1 | import regexpPlugin from 'eslint-plugin-regexp';
2 | import tseslint from 'typescript-eslint';
3 |
4 | /**
5 | * https://github.com/ota-meshi/eslint-plugin-regexp/blob/master/lib/configs/recommended.ts
6 | */
7 | export const recommended = tseslint.config({
8 | files: ['**/*.{js,jsx,cjs,mjs,ts,tsx}'],
9 | plugins: {
10 | regexp: regexpPlugin,
11 | },
12 | rules: {
13 | 'no-control-regex': 2,
14 | 'no-empty-character-class': 0,
15 | 'no-invalid-regexp': 'off',
16 | 'no-misleading-character-class': 2,
17 | 'no-regex-spaces': 2,
18 | 'no-useless-backreference': 'off',
19 | 'prefer-regex-literals': 2,
20 | 'regexp/confusing-quantifier': 'warn',
21 | 'regexp/control-character-escape': 2,
22 | 'regexp/match-any': 2,
23 | 'regexp/negation': 2,
24 | 'regexp/no-dupe-characters-character-class': 2,
25 | 'regexp/no-dupe-disjunctions': 2,
26 | 'regexp/no-empty-alternative': 'warn',
27 | 'regexp/no-empty-capturing-group': 2,
28 | 'regexp/no-empty-group': 2,
29 | 'regexp/no-empty-lookarounds-assertion': 2,
30 | 'regexp/no-escape-backspace': 2,
31 | 'regexp/no-invalid-regexp': 2,
32 | 'regexp/no-invisible-character': 2,
33 | 'regexp/no-lazy-ends': 'warn',
34 | 'regexp/no-legacy-features': 2,
35 | 'regexp/no-non-standard-flag': 2,
36 | 'regexp/no-obscure-range': 2,
37 | 'regexp/no-optional-assertion': 2,
38 | 'regexp/no-potentially-useless-backreference': 'warn',
39 | 'regexp/no-super-linear-backtracking': 2,
40 | 'regexp/no-trivially-nested-assertion': 2,
41 | 'regexp/no-trivially-nested-quantifier': 2,
42 | 'regexp/no-unused-capturing-group': 2,
43 | 'regexp/no-useless-assertions': 2,
44 | 'regexp/no-useless-backreference': 2,
45 | 'regexp/no-useless-character-class': 2,
46 | 'regexp/no-useless-dollar-replacements': 2,
47 | 'regexp/no-useless-escape': 2,
48 | 'regexp/no-useless-flag': 'warn',
49 | 'regexp/no-useless-lazy': 2,
50 | 'regexp/no-useless-non-capturing-group': 2,
51 | 'regexp/no-useless-quantifier': 2,
52 | 'regexp/no-useless-range': 2,
53 | 'regexp/no-useless-two-nums-quantifier': 2,
54 | 'regexp/no-zero-quantifier': 2,
55 | 'regexp/optimal-lookaround-quantifier': 'warn',
56 | 'regexp/optimal-quantifier-concatenation': 2,
57 | 'regexp/prefer-character-class': 2,
58 | 'regexp/prefer-d': 2,
59 | 'regexp/prefer-plus-quantifier': 2,
60 | 'regexp/prefer-predefined-assertion': 2,
61 | 'regexp/prefer-question-quantifier': 2,
62 | 'regexp/prefer-range': 2,
63 | 'regexp/prefer-star-quantifier': 2,
64 | 'regexp/prefer-unicode-codepoint-escapes': 2,
65 | 'regexp/prefer-w': 2,
66 | 'regexp/sort-flags': 2,
67 | 'regexp/strict': 2,
68 | },
69 | });
70 |
--------------------------------------------------------------------------------
/configurations/typescript-compatibility.ts:
--------------------------------------------------------------------------------
1 | import tseslint from 'typescript-eslint';
2 |
3 | export const recommended = tseslint.config({
4 | files: ['**/*.{ts,tsx}'],
5 | rules: {
6 | '@typescript-eslint/default-param-last': 2,
7 | '@typescript-eslint/indent': [2, 2],
8 | '@typescript-eslint/init-declarations': 0,
9 | '@typescript-eslint/no-array-constructor': 2,
10 | '@typescript-eslint/no-dupe-class-members': 2,
11 | '@typescript-eslint/no-invalid-this': 0,
12 | '@typescript-eslint/no-loop-func': 2,
13 | '@typescript-eslint/no-loss-of-precision': 2,
14 | '@typescript-eslint/no-magic-numbers': 0,
15 | '@typescript-eslint/no-redeclare': [
16 | 2,
17 | {
18 | builtinGlobals: true,
19 | },
20 | ],
21 | '@typescript-eslint/no-restricted-imports': 0,
22 | '@typescript-eslint/no-shadow': [
23 | 2,
24 | {
25 | builtinGlobals: false,
26 | hoist: 'all',
27 | },
28 | ],
29 | '@typescript-eslint/no-unused-expressions': [
30 | 2,
31 | {
32 | allowShortCircuit: false,
33 | allowTaggedTemplates: false,
34 | allowTernary: false,
35 | enforceForJSX: false,
36 | },
37 | ],
38 | '@typescript-eslint/no-unused-vars': 2,
39 | '@typescript-eslint/no-use-before-define': [
40 | 2,
41 | {
42 | classes: true,
43 | functions: false,
44 | variables: true,
45 | },
46 | ],
47 | '@typescript-eslint/no-useless-constructor': 2,
48 | '@typescript-eslint/require-await': 0,
49 | 'block-spacing': 0,
50 | 'default-param-last': 0,
51 | 'dot-notation': 0,
52 | 'func-call-spacing': 0,
53 | indent: 0,
54 | 'init-declarations': 0,
55 | 'no-array-constructor': 0,
56 | 'no-dupe-class-members': 0,
57 | 'no-duplicate-imports': 0,
58 | 'no-empty-function': 0,
59 | 'no-implied-eval': 0,
60 | 'no-invalid-this': 0,
61 | 'no-loop-func': 0,
62 | 'no-loss-of-precision': 0,
63 | 'no-magic-numbers': 0,
64 | 'no-redeclare': 0,
65 | 'no-restricted-imports': 0,
66 | 'no-shadow': 0,
67 | 'no-throw-literal': 0,
68 | 'no-unused-expressions': 0,
69 | 'no-unused-vars': 0,
70 | 'no-use-before-define': 0,
71 | 'no-useless-constructor': 0,
72 | 'react/prop-types': 0,
73 | 'require-await': 0,
74 | },
75 | });
76 |
--------------------------------------------------------------------------------
/configurations/typescript-type-checking.ts:
--------------------------------------------------------------------------------
1 | import typescriptEslintPlugin from '@typescript-eslint/eslint-plugin';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | plugins: {
6 | '@typescript-eslint': typescriptEslintPlugin,
7 | },
8 | rules: {
9 | '@typescript-eslint/await-thenable': 2,
10 | '@typescript-eslint/consistent-type-exports': [
11 | 2,
12 | {
13 | fixMixedExportsWithInlineTypeSpecifier: true,
14 | },
15 | ],
16 | '@typescript-eslint/dot-notation': 2,
17 | '@typescript-eslint/no-base-to-string': [
18 | 2,
19 | {
20 | ignoredTypeNames: ['RegExp'],
21 | },
22 | ],
23 | '@typescript-eslint/no-confusing-void-expression': [
24 | 2,
25 | {
26 | ignoreArrowShorthand: true,
27 | ignoreVoidOperator: false,
28 | },
29 | ],
30 | '@typescript-eslint/no-floating-promises': [
31 | 2,
32 | {
33 | ignoreIIFE: true,
34 | ignoreVoid: true,
35 | },
36 | ],
37 | '@typescript-eslint/no-for-in-array': 2,
38 | '@typescript-eslint/no-implied-eval': 2,
39 | '@typescript-eslint/no-meaningless-void-operator': [
40 | 2,
41 | {
42 | checkNever: true,
43 | },
44 | ],
45 | '@typescript-eslint/no-misused-promises': [
46 | 2,
47 | {
48 | checksConditionals: true,
49 | checksVoidReturn: {
50 | arguments: true,
51 | attributes: false,
52 | properties: true,
53 | returns: true,
54 | variables: true,
55 | },
56 | },
57 | ],
58 | '@typescript-eslint/no-unnecessary-qualifier': 2,
59 | '@typescript-eslint/prefer-includes': 2,
60 | '@typescript-eslint/prefer-nullish-coalescing': [
61 | 2,
62 | {
63 | ignoreConditionalTests: true,
64 | ignoreMixedLogicalExpressions: true,
65 | },
66 | ],
67 | '@typescript-eslint/prefer-optional-chain': 2,
68 | '@typescript-eslint/prefer-readonly': [
69 | 2,
70 | {
71 | onlyInlineLambdas: true,
72 | },
73 | ],
74 | '@typescript-eslint/prefer-reduce-type-parameter': 2,
75 | '@typescript-eslint/prefer-regexp-exec': 2,
76 | '@typescript-eslint/prefer-return-this-type': 2,
77 | '@typescript-eslint/prefer-string-starts-ends-with': 2,
78 | '@typescript-eslint/promise-function-async': 2,
79 | '@typescript-eslint/require-array-sort-compare': [
80 | 2,
81 | {
82 | ignoreStringArrays: false,
83 | },
84 | ],
85 | '@typescript-eslint/return-await': [2, 'always'],
86 | '@typescript-eslint/switch-exhaustiveness-check': 2,
87 | '@typescript-eslint/unbound-method': [
88 | 2,
89 | {
90 | ignoreStatic: true,
91 | },
92 | ],
93 | },
94 | });
95 |
--------------------------------------------------------------------------------
/configurations/typescript.ts:
--------------------------------------------------------------------------------
1 | import * as typescriptCompatibility from './typescript-compatibility.js';
2 | import stylisticPlugin from '@stylistic/eslint-plugin';
3 | import typescriptEslintPlugin from '@typescript-eslint/eslint-plugin';
4 | import * as eslintParser from '@typescript-eslint/parser';
5 | import canonicalPlugin from 'eslint-plugin-canonical';
6 | import functionalPlugin from 'eslint-plugin-functional';
7 | import tseslint from 'typescript-eslint';
8 |
9 | export const recommended = tseslint.config({
10 | files: ['**/*.{ts,tsx}'],
11 | languageOptions: {
12 | parser: eslintParser,
13 | parserOptions: {
14 | project: true,
15 | },
16 | },
17 | plugins: {
18 | '@stylistic': stylisticPlugin,
19 | '@typescript-eslint': typescriptEslintPlugin,
20 | canonical: canonicalPlugin,
21 | functional: functionalPlugin,
22 | },
23 | rules: {
24 | '@stylistic/member-delimiter-style': [
25 | 2,
26 | {
27 | multiline: {
28 | delimiter: 'comma',
29 | requireLast: true,
30 | },
31 | overrides: {
32 | interface: {
33 | multiline: {
34 | delimiter: 'semi',
35 | requireLast: true,
36 | },
37 | },
38 | },
39 | singleline: {
40 | delimiter: 'comma',
41 | requireLast: false,
42 | },
43 | },
44 | ],
45 | '@stylistic/type-annotation-spacing': [
46 | 2,
47 | {
48 | after: true,
49 | before: false,
50 | overrides: {
51 | arrow: {
52 | after: true,
53 | before: true,
54 | },
55 | },
56 | },
57 | ],
58 | '@typescript-eslint/adjacent-overload-signatures': 2,
59 | '@typescript-eslint/array-type': [
60 | 2,
61 | {
62 | default: 'array-simple',
63 | },
64 | ],
65 | '@typescript-eslint/ban-ts-comment': [
66 | 2,
67 | {
68 | 'ts-check': true,
69 | 'ts-expect-error': 'allow-with-description',
70 | 'ts-ignore': false,
71 | 'ts-nocheck': false,
72 | },
73 | ],
74 | '@typescript-eslint/ban-tslint-comment': 2,
75 | '@typescript-eslint/ban-types': 0,
76 | '@typescript-eslint/class-literal-property-style': [2, 'fields'],
77 | '@typescript-eslint/consistent-indexed-object-style': 0,
78 | '@typescript-eslint/consistent-type-assertions': [
79 | 2,
80 | {
81 | assertionStyle: 'as',
82 | objectLiteralTypeAssertions: 'allow',
83 | },
84 | ],
85 | '@typescript-eslint/consistent-type-definitions': [2, 'type'],
86 | '@typescript-eslint/consistent-type-imports': [
87 | 2,
88 | {
89 | prefer: 'type-imports',
90 | },
91 | ],
92 | '@typescript-eslint/default-param-last': 2,
93 | '@typescript-eslint/explicit-function-return-type': 0,
94 | '@typescript-eslint/explicit-member-accessibility': 0,
95 | '@typescript-eslint/explicit-module-boundary-types': 0,
96 | '@typescript-eslint/member-ordering': 0,
97 | '@typescript-eslint/method-signature-style': [2, 'property'],
98 | '@typescript-eslint/naming-convention': [
99 | 2,
100 | {
101 | format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
102 | selector: 'variable',
103 | },
104 | ],
105 | '@typescript-eslint/no-confusing-non-null-assertion': 2,
106 | '@typescript-eslint/no-dynamic-delete': 2,
107 | '@typescript-eslint/no-empty-function': 0,
108 | '@typescript-eslint/no-empty-interface': [
109 | 'error',
110 | {
111 | allowSingleExtends: false,
112 | },
113 | ],
114 | '@typescript-eslint/no-explicit-any': [
115 | 2,
116 | {
117 | ignoreRestArgs: true,
118 | },
119 | ],
120 | '@typescript-eslint/no-extra-non-null-assertion': 2,
121 | '@typescript-eslint/no-extraneous-class': 2,
122 | '@typescript-eslint/no-inferrable-types': [
123 | 2,
124 | {
125 | ignoreParameters: true,
126 | ignoreProperties: true,
127 | },
128 | ],
129 | '@typescript-eslint/no-invalid-void-type': [
130 | 2,
131 | {
132 | allowAsThisParameter: true,
133 | allowInGenericTypeArguments: true,
134 | },
135 | ],
136 | '@typescript-eslint/no-misused-new': 2,
137 | '@typescript-eslint/no-namespace': [
138 | 2,
139 | {
140 | allowDeclarations: true,
141 | },
142 | ],
143 | '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 2,
144 | '@typescript-eslint/no-non-null-asserted-optional-chain': 2,
145 | '@typescript-eslint/no-non-null-assertion': 2,
146 | '@typescript-eslint/no-parameter-properties': 0,
147 | '@typescript-eslint/no-require-imports': 2,
148 | '@typescript-eslint/no-restricted-imports': 0,
149 | '@typescript-eslint/no-this-alias': [
150 | 2,
151 | {
152 | allowDestructuring: true,
153 | allowedNames: ['self'],
154 | },
155 | ],
156 | '@typescript-eslint/no-unnecessary-boolean-literal-compare': 0,
157 | '@typescript-eslint/no-unnecessary-condition': 0,
158 | '@typescript-eslint/no-unnecessary-type-arguments': 0,
159 | '@typescript-eslint/no-unnecessary-type-assertion': 0,
160 | '@typescript-eslint/no-unnecessary-type-constraint': 2,
161 | '@typescript-eslint/no-unsafe-argument': 0,
162 | '@typescript-eslint/no-unsafe-assignment': 0,
163 | '@typescript-eslint/no-unsafe-call': 0,
164 | '@typescript-eslint/no-unsafe-member-access': 0,
165 | '@typescript-eslint/no-unsafe-return': 0,
166 | '@typescript-eslint/no-var-requires': 2,
167 | '@typescript-eslint/non-nullable-type-assertion-style': 0,
168 | '@typescript-eslint/prefer-as-const': 2,
169 | '@typescript-eslint/prefer-enum-initializers': 2,
170 | '@typescript-eslint/prefer-for-of': 2,
171 | '@typescript-eslint/prefer-function-type': 2,
172 | '@typescript-eslint/prefer-literal-enum-member': 2,
173 | '@typescript-eslint/prefer-namespace-keyword': 2,
174 | '@typescript-eslint/prefer-ts-expect-error': 2,
175 | '@typescript-eslint/restrict-plus-operands': 0,
176 | '@typescript-eslint/restrict-template-expressions': 0,
177 | '@typescript-eslint/strict-boolean-expressions': 0,
178 | '@typescript-eslint/triple-slash-reference': [
179 | 2,
180 | {
181 | lib: 'never',
182 | path: 'never',
183 | types: 'never',
184 | },
185 | ],
186 | '@typescript-eslint/typedef': 0,
187 | '@typescript-eslint/unified-signatures': 2,
188 | 'canonical/prefer-inline-type-import': 2,
189 | 'default-param-last': 0,
190 | 'import/no-dynamic-require': 0,
191 | 'jsdoc/require-property-type': 0,
192 | 'n/global-require': 0,
193 | 'n/no-missing-import': 0,
194 | 'no-duplicate-imports': 0,
195 | 'no-undef': 0,
196 | 'no-use-before-define': 0,
197 | 'spaced-comment': [
198 | 2,
199 | 'always',
200 | {
201 | markers: ['/'],
202 | },
203 | ],
204 | ...typescriptCompatibility.recommended[0].rules,
205 | },
206 | settings: {
207 | 'import/extensions': ['.ts', '.tsx'],
208 | 'import/external-module-folders': ['node_modules', 'node_modules/@types'],
209 | 'import/parsers': {
210 | '@typescript-eslint/parser': ['.ts', '.tsx'],
211 | },
212 | 'import/resolver': {
213 | typescript: {
214 | extensions: ['.ts', '.tsx'],
215 | },
216 | },
217 | jsdoc: {
218 | mode: 'typescript',
219 | },
220 | node: {
221 | tryExtensions: ['.ts', '.tsx', '.js', '.json'],
222 | },
223 | },
224 | });
225 |
--------------------------------------------------------------------------------
/configurations/vitest.ts:
--------------------------------------------------------------------------------
1 | import vitestPlugin from '@vitest/eslint-plugin';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | plugins: {
6 | vitest: vitestPlugin,
7 | },
8 | rules: {
9 | 'vitest/expect-expect': 2,
10 | 'vitest/lower-case-title': 0,
11 | 'vitest/max-nested-describe': 2,
12 | 'vitest/no-conditional-tests': 2,
13 | 'vitest/no-disabled-tests': 2,
14 | 'vitest/no-focused-tests': [
15 | 2,
16 | {
17 | fixable: false,
18 | },
19 | ],
20 | 'vitest/no-identical-title': 2,
21 | },
22 | });
23 |
--------------------------------------------------------------------------------
/configurations/yaml.ts:
--------------------------------------------------------------------------------
1 | import yamlPlugin from 'eslint-plugin-yml';
2 | import tseslint from 'typescript-eslint';
3 | import yamlEslintParser from 'yaml-eslint-parser';
4 |
5 | export const recommended = tseslint.config({
6 | files: ['**/*.yaml'],
7 | languageOptions: {
8 | parser: yamlEslintParser,
9 | },
10 | plugins: {
11 | yml: yamlPlugin,
12 | },
13 | rules: {
14 | 'yml/block-mapping': 2,
15 | 'yml/block-mapping-question-indicator-newline': 2,
16 | 'yml/block-sequence': [2, 'always'],
17 | 'yml/block-sequence-hyphen-indicator-newline': 2,
18 | 'yml/flow-mapping-curly-newline': 2,
19 | 'yml/flow-mapping-curly-spacing': 2,
20 | 'yml/flow-sequence-bracket-newline': 2,
21 | 'yml/flow-sequence-bracket-spacing': 2,
22 | 'yml/indent': [2, 2],
23 | 'yml/key-name-casing': 0,
24 | 'yml/key-spacing': 2,
25 | 'yml/no-empty-document': 2,
26 | 'yml/no-empty-key': 2,
27 | 'yml/no-empty-mapping-value': 2,
28 | 'yml/no-empty-sequence-entry': 2,
29 | 'yml/no-irregular-whitespace': 2,
30 | 'yml/no-multiple-empty-lines': [
31 | 2,
32 | {
33 | max: 0,
34 | },
35 | ],
36 | 'yml/no-tab-indent': 2,
37 | 'yml/plain-scalar': 0,
38 | 'yml/quotes': [
39 | 2,
40 | {
41 | avoidEscape: true,
42 | prefer: 'single',
43 | },
44 | ],
45 | 'yml/require-string-key': 2,
46 | 'yml/sort-keys': [
47 | 2,
48 | 'asc',
49 | {
50 | caseSensitive: false,
51 | natural: true,
52 | },
53 | ],
54 | 'yml/sort-sequence-values': 0,
55 | 'yml/spaced-comment': 2,
56 | 'yml/vue-custom-block/no-parsing-error': 2,
57 | },
58 | });
59 |
--------------------------------------------------------------------------------
/configurations/zod.ts:
--------------------------------------------------------------------------------
1 | import zodPlugin from 'eslint-plugin-zod';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export const recommended = tseslint.config({
5 | plugins: {
6 | zod: zodPlugin,
7 | },
8 | rules: {
9 | 'zod/prefer-enum': 2,
10 | 'zod/require-strict': 2,
11 | },
12 | });
13 |
--------------------------------------------------------------------------------
/eslint.config.ts:
--------------------------------------------------------------------------------
1 | import auto from './configurations/auto.js';
2 | import * as ava from './configurations/ava.js';
3 | import * as node from './configurations/node.js';
4 | import tseslint from 'typescript-eslint';
5 |
6 | export default tseslint.config(
7 | node.recommended,
8 | ...auto,
9 | ava.recommended,
10 | {
11 | rules: {
12 | 'n/global-require': 0,
13 | },
14 | },
15 | {
16 | ignores: ['**/package-lock.json', '**/pnpm-lock.yaml', '**/dist', '**/.*'],
17 | },
18 | );
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": {
3 | "email": "gajus@gajus.com",
4 | "name": "Gajus Kuizinas",
5 | "url": "http://gajus.com"
6 | },
7 | "ava": {
8 | "extensions": {
9 | "ts": "module"
10 | },
11 | "files": [
12 | "configurations/auto.test.ts"
13 | ],
14 | "nodeArguments": [
15 | "--import=tsimp/import"
16 | ]
17 | },
18 | "dependencies": {
19 | "@graphql-eslint/eslint-plugin": "^4.4.0",
20 | "@next/eslint-plugin-next": "^15.5.0",
21 | "@stylistic/eslint-plugin": "^4.2.0",
22 | "@typescript-eslint/eslint-plugin": "^8.40.0",
23 | "@typescript-eslint/parser": "^8.40.0",
24 | "@vitest/eslint-plugin": "^1.3.4",
25 | "eslint-config-prettier": "^10.1.8",
26 | "eslint-import-resolver-typescript": "^4.4.4",
27 | "eslint-plugin-ava": "^15.1.0",
28 | "eslint-plugin-canonical": "^5.1.3",
29 | "eslint-plugin-eslint-comments": "^3.2.0",
30 | "eslint-plugin-fp": "^2.3.0",
31 | "eslint-plugin-functional": "^9.0.2",
32 | "eslint-plugin-import": "npm:eslint-plugin-import-x@^4.16.1",
33 | "eslint-plugin-jest": "^28.11.0",
34 | "eslint-plugin-jsdoc": "^50.6.8",
35 | "eslint-plugin-jsonc": "^2.20.1",
36 | "eslint-plugin-jsx-a11y": "^6.10.2",
37 | "eslint-plugin-lodash": "^8.0.0",
38 | "eslint-plugin-mocha": "^10.5.0",
39 | "eslint-plugin-modules-newline": "0.0.6",
40 | "eslint-plugin-n": "^17.21.3",
41 | "eslint-plugin-perfectionist": "^4.15.0",
42 | "eslint-plugin-prettier": "^5.5.4",
43 | "eslint-plugin-promise": "^7.2.1",
44 | "eslint-plugin-react": "^7.37.5",
45 | "eslint-plugin-react-hooks": "^6.1.0",
46 | "eslint-plugin-regexp": "^2.10.0",
47 | "eslint-plugin-unicorn": "^60.0.0",
48 | "eslint-plugin-yml": "^1.18.0",
49 | "eslint-plugin-zod": "^1.4.0",
50 | "globals": "^16.3.0",
51 | "graphql": "^16.11.0",
52 | "prettier": "^3.6.2",
53 | "ramda": "^0.30.1",
54 | "typescript-eslint": "^8.40.0",
55 | "yaml-eslint-parser": "^1.3.0"
56 | },
57 | "description": "Canonical ESLint Shareable Config",
58 | "devDependencies": {
59 | "@semantic-release/commit-analyzer": "^13.0.1",
60 | "@semantic-release/github": "^11.0.4",
61 | "@semantic-release/npm": "^12.0.2",
62 | "ava": "^6.4.1",
63 | "eslint": "^9.34.0",
64 | "got": "^14.4.7",
65 | "husky": "^9.1.7",
66 | "semantic-release": "^24.2.7",
67 | "tsimp": "^2.0.12",
68 | "typescript": "^5.9.2"
69 | },
70 | "engines": {
71 | "node": ">=16.0.0"
72 | },
73 | "exports": {
74 | ".": {
75 | "import": "./dist/configurations/index.js",
76 | "types": "./dist/configurations/index.d.ts"
77 | },
78 | "./*": {
79 | "import": "./dist/configurations/*.js",
80 | "types": "./dist/configurations/*.d.ts"
81 | }
82 | },
83 | "files": [
84 | "dist"
85 | ],
86 | "keywords": [
87 | "eslint",
88 | "config",
89 | "canonical"
90 | ],
91 | "license": "BSD-3-Clause",
92 | "main": "./eslintrc.js",
93 | "name": "eslint-config-canonical",
94 | "peerDependencies": {
95 | "eslint": "^9"
96 | },
97 | "repository": {
98 | "type": "git",
99 | "url": "https://github.com/gajus/eslint-config-canonical"
100 | },
101 | "scripts": {
102 | "build": "tsc",
103 | "compare": "(cd compare; node compare.js)",
104 | "find-deprecated": "(cd compare; node find-deprecated.js)",
105 | "generate-typescript-compatibility-rules": "node bin/generate-typescript-compatibility-rules.js > configurations/typescript-compatibility.js && eslint configurations/typescript-compatibility.js --fix",
106 | "lint": "eslint --report-unused-disable-directives .",
107 | "setup-dev": "(npm link; cd compare; npm install; npm link eslint-config-canonical; node compare.js)",
108 | "test": "ava --verbose --serial"
109 | },
110 | "type": "module",
111 | "version": "1.0.0"
112 | }
113 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "declaration": true,
5 | "esModuleInterop": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "lib": [
8 | "es2021"
9 | ],
10 | "module": "NodeNext",
11 | "moduleResolution": "NodeNext",
12 | "noImplicitAny": false,
13 | "noImplicitReturns": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": false,
16 | "outDir": "dist",
17 | "skipLibCheck": true,
18 | "strict": true,
19 | "target": "ES2024"
20 | },
21 | "exclude": [
22 | "dist",
23 | "node_modules"
24 | ],
25 | "include": [
26 | "eslint.config.ts",
27 | "configurations"
28 | ]
29 | }
--------------------------------------------------------------------------------