@monholm/eslint-config 2.0.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/configs/base.d.ts +2 -0
- package/dist/configs/base.d.ts.map +1 -0
- package/dist/configs/base.js +10 -0
- package/dist/configs/node.d.ts +2 -0
- package/dist/configs/node.d.ts.map +1 -0
- package/dist/configs/node.js +8 -0
- package/dist/configs/react.d.ts +2 -0
- package/dist/configs/react.d.ts.map +1 -0
- package/dist/configs/react.js +7 -0
- package/dist/rules/eslint.d.ts +3 -0
- package/dist/rules/eslint.d.ts.map +1 -0
- package/dist/rules/eslint.js +142 -0
- package/dist/rules/import.d.ts +3 -0
- package/dist/rules/import.d.ts.map +1 -0
- package/dist/rules/import.js +50 -0
- package/dist/rules/node.d.ts +3 -0
- package/dist/rules/node.d.ts.map +1 -0
- package/dist/rules/node.js +28 -0
- package/dist/rules/react.d.ts +3 -0
- package/dist/rules/react.d.ts.map +1 -0
- package/dist/rules/react.js +131 -0
- package/dist/rules/reactHooks.d.ts +3 -0
- package/dist/rules/reactHooks.d.ts.map +1 -0
- package/dist/rules/reactHooks.js +20 -0
- package/dist/rules/stylisticEslint.d.ts +3 -0
- package/dist/rules/stylisticEslint.d.ts.map +1 -0
- package/dist/rules/stylisticEslint.js +20 -0
- package/dist/rules/tsEslint.d.ts +3 -0
- package/dist/rules/tsEslint.d.ts.map +1 -0
- package/dist/rules/tsEslint.js +55 -0
- package/package.json +37 -12
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -4
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @monholm/eslint-config
|
|
2
|
+
|
|
3
|
+
Shared ESLint configurations using ESLint 9's flat config format.
|
|
4
|
+
|
|
5
|
+
## Formatting
|
|
6
|
+
|
|
7
|
+
This ESLint config does not include Prettier rules. We recommend:
|
|
8
|
+
|
|
9
|
+
- Setting up format-on-save in your editor with Prettier
|
|
10
|
+
- Running `prettier --check` in CI
|
|
11
|
+
|
|
12
|
+
## Getting Started
|
|
13
|
+
|
|
14
|
+
All configs in this package require TypeScript, meaning they can't be used in pure JavaScript projects.
|
|
15
|
+
|
|
16
|
+
### Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add -D @monholm/eslint-config eslint typescript typescript-eslint @eslint/js eslint-plugin-import @stylistic/eslint-plugin
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### TypeScript Configuration
|
|
23
|
+
|
|
24
|
+
The ESLint configuration uses `projectService` which automatically finds and uses your `tsconfig.json`. For this to work correctly, **all files being linted must be included in the tsconfig**.
|
|
25
|
+
|
|
26
|
+
The recommended approach is to use your root `tsconfig.json` for linting by omitting the `include` option, which will make TypeScript target all files by default. Then create a separate `tsconfig.build.json` for compilation that includes only the source files.
|
|
27
|
+
|
|
28
|
+
`tsconfig.json` (used for linting and type checking)
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"extends": "@monholm/tsconfig",
|
|
33
|
+
"exclude": ["node_modules", "dist"],
|
|
34
|
+
"compilerOptions": {"noEmit": true}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`tsconfig.build.json` (used for building)
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"extends": "./tsconfig.json",
|
|
43
|
+
"include": ["src"],
|
|
44
|
+
"compilerOptions": {"outDir": "dist", "noEmit": false}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Then build with `tsc -p tsconfig.build.json` and type check with `tsc`.
|
|
49
|
+
|
|
50
|
+
If you need to use a different tsconfig setup, please reference the typescript-eslint documentation on how to set up `projectService` instead of using the example eslint config provided later in this readme.
|
|
51
|
+
|
|
52
|
+
### Available Configs
|
|
53
|
+
|
|
54
|
+
This package uses [subpath exports](https://nodejs.org/api/packages.html#subpath-exports) in its `package.json`. You should import the configs as follows:
|
|
55
|
+
|
|
56
|
+
- **Base config:**
|
|
57
|
+
```js
|
|
58
|
+
import {baseConfig} from '@monholm/eslint-config';
|
|
59
|
+
```
|
|
60
|
+
- **Node.js config:**
|
|
61
|
+
```js
|
|
62
|
+
import {nodeConfig} from '@monholm/eslint-config/node';
|
|
63
|
+
```
|
|
64
|
+
- **React config:**
|
|
65
|
+
```js
|
|
66
|
+
import {reactConfig} from '@monholm/eslint-config/react';
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Setup
|
|
70
|
+
|
|
71
|
+
#### Base Config (TypeScript)
|
|
72
|
+
|
|
73
|
+
`eslint.config.js`
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import {baseConfig} from '@monholm/eslint-config';
|
|
77
|
+
import {defineConfig} from 'eslint/config';
|
|
78
|
+
|
|
79
|
+
export default defineConfig([
|
|
80
|
+
baseConfig,
|
|
81
|
+
{languageOptions: {parserOptions: {projectService: true}}},
|
|
82
|
+
]);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Node.js
|
|
86
|
+
|
|
87
|
+
This config extends from the base config. You'll need to install the Node.js ESLint plugin:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pnpm add -D eslint-plugin-n
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For `eslint-plugin-n` to work correctly, **you must specify the `engines` field in your `package.json`** so it knows your target Node.js version:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{"engines": {"node": ">= 20"}}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`eslint.config.js`
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
import {nodeConfig} from '@monholm/eslint-config/node';
|
|
103
|
+
import {defineConfig} from 'eslint/config';
|
|
104
|
+
|
|
105
|
+
export default defineConfig([
|
|
106
|
+
nodeConfig,
|
|
107
|
+
{languageOptions: {parserOptions: {projectService: true}}},
|
|
108
|
+
]);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### React
|
|
112
|
+
|
|
113
|
+
This config extends from the base config. You'll need to install the React ESLint plugins:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pnpm add -D eslint-plugin-react eslint-plugin-react-hooks
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`eslint.config.js`
|
|
120
|
+
|
|
121
|
+
```js
|
|
122
|
+
import {reactConfig} from '@monholm/eslint-config/react';
|
|
123
|
+
import {defineConfig} from 'eslint/config';
|
|
124
|
+
|
|
125
|
+
export default defineConfig([
|
|
126
|
+
reactConfig,
|
|
127
|
+
{languageOptions: {parserOptions: {projectService: true}}},
|
|
128
|
+
]);
|
|
129
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/configs/base.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,kCAQtB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import eslint from '@eslint/js';
|
|
2
|
+
import tseslint from 'typescript-eslint';
|
|
3
|
+
import importPlugin from 'eslint-plugin-import';
|
|
4
|
+
import stylisticEslint from '@stylistic/eslint-plugin';
|
|
5
|
+
import { defineConfig } from 'eslint/config';
|
|
6
|
+
import { eslintRules } from '../rules/eslint.js';
|
|
7
|
+
import { tsEslintRules } from '../rules/tsEslint.js';
|
|
8
|
+
import { importRules } from '../rules/import.js';
|
|
9
|
+
import { stylisticEslintRules } from '../rules/stylisticEslint.js';
|
|
10
|
+
export const baseConfig = defineConfig(eslint.configs.recommended, tseslint.configs.strictTypeChecked, tseslint.configs.stylisticTypeChecked, { rules: eslintRules }, { rules: tsEslintRules }, { plugins: { import: importPlugin }, rules: importRules }, { plugins: { '@stylistic': stylisticEslint }, rules: stylisticEslintRules });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/configs/node.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,UAAU,kCAGrB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { defineConfig } from 'eslint/config';
|
|
2
|
+
import nodePlugin from 'eslint-plugin-n';
|
|
3
|
+
import { nodeRules } from '../rules/node.js';
|
|
4
|
+
import { baseConfig } from './base.js';
|
|
5
|
+
export const nodeConfig = defineConfig(baseConfig, {
|
|
6
|
+
plugins: { n: nodePlugin },
|
|
7
|
+
rules: nodeRules,
|
|
8
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/configs/react.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,WAAW,kCAIvB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { defineConfig } from 'eslint/config';
|
|
2
|
+
import reactPlugin from 'eslint-plugin-react';
|
|
3
|
+
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
|
4
|
+
import { reactRules } from '../rules/react.js';
|
|
5
|
+
import { reactHooksRules } from '../rules/reactHooks.js';
|
|
6
|
+
import { baseConfig } from './base.js';
|
|
7
|
+
export const reactConfig = defineConfig(baseConfig, { plugins: { react: reactPlugin }, rules: reactRules }, { plugins: { 'react-hooks': reactHooksPlugin }, rules: reactHooksRules });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eslint.d.ts","sourceRoot":"","sources":["../../src/rules/eslint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAkJpD,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
export const eslintRules = {
|
|
2
|
+
'arrow-body-style': ['error', 'as-needed'],
|
|
3
|
+
'default-case-last': 'error',
|
|
4
|
+
'default-case': 'error',
|
|
5
|
+
'dot-notation': 'error',
|
|
6
|
+
'func-names': 'error',
|
|
7
|
+
'grouped-accessor-pairs': ['error', 'getBeforeSet'],
|
|
8
|
+
'new-cap': 'error',
|
|
9
|
+
'no-alert': 'error',
|
|
10
|
+
'no-await-in-loop': 'error',
|
|
11
|
+
'no-bitwise': 'error',
|
|
12
|
+
'no-caller': 'error',
|
|
13
|
+
'no-continue': 'error',
|
|
14
|
+
'no-div-regex': 'error',
|
|
15
|
+
'no-duplicate-imports': 'error',
|
|
16
|
+
'no-else-return': 'error',
|
|
17
|
+
'no-eval': 'error',
|
|
18
|
+
'no-extend-native': 'error',
|
|
19
|
+
'no-extra-bind': 'error',
|
|
20
|
+
'no-extra-label': 'error',
|
|
21
|
+
'no-implicit-coercion': ['error', { allow: ['!!'] }],
|
|
22
|
+
'no-implicit-globals': 'error',
|
|
23
|
+
'no-implied-eval': 'error',
|
|
24
|
+
'no-inner-declarations': [
|
|
25
|
+
'error',
|
|
26
|
+
'both',
|
|
27
|
+
{ blockScopedFunctions: 'disallow' },
|
|
28
|
+
],
|
|
29
|
+
'no-iterator': 'error',
|
|
30
|
+
'no-label-var': 'error',
|
|
31
|
+
'no-labels': 'error',
|
|
32
|
+
'no-lone-blocks': 'error',
|
|
33
|
+
'no-lonely-if': 'error',
|
|
34
|
+
'no-multi-assign': 'error',
|
|
35
|
+
'no-multi-str': 'error',
|
|
36
|
+
'no-negated-condition': 'error',
|
|
37
|
+
'no-nested-ternary': 'error',
|
|
38
|
+
'no-new-func': 'error',
|
|
39
|
+
'no-new-wrappers': 'error',
|
|
40
|
+
'no-new': 'error',
|
|
41
|
+
'no-object-constructor': 'error',
|
|
42
|
+
'no-octal-escape': 'error',
|
|
43
|
+
'no-param-reassign': 'error',
|
|
44
|
+
'no-plusplus': 'error',
|
|
45
|
+
'no-promise-executor-return': 'error',
|
|
46
|
+
'no-proto': 'error',
|
|
47
|
+
'no-return-assign': 'error',
|
|
48
|
+
'no-script-url': 'error',
|
|
49
|
+
'no-sequences': 'error',
|
|
50
|
+
'no-throw-literal': 'error',
|
|
51
|
+
'no-undef-init': 'error',
|
|
52
|
+
'no-unmodified-loop-condition': 'error',
|
|
53
|
+
'no-unneeded-ternary': ['error', { defaultAssignment: false }],
|
|
54
|
+
'no-unreachable-loop': 'error',
|
|
55
|
+
'no-useless-call': 'error',
|
|
56
|
+
'no-useless-computed-key': 'error',
|
|
57
|
+
'no-useless-concat': 'error',
|
|
58
|
+
'no-useless-constructor': 'error',
|
|
59
|
+
'no-useless-rename': 'error',
|
|
60
|
+
'no-useless-return': 'error',
|
|
61
|
+
'no-var': 'error',
|
|
62
|
+
'no-void': ['error', { allowAsStatement: false }],
|
|
63
|
+
'no-warning-comments': 'error',
|
|
64
|
+
'object-shorthand': 'error',
|
|
65
|
+
'prefer-arrow-callback': 'error',
|
|
66
|
+
'prefer-const': ['error', { destructuring: 'all' }],
|
|
67
|
+
'prefer-exponentiation-operator': 'error',
|
|
68
|
+
'prefer-named-capture-group': 'error',
|
|
69
|
+
'prefer-numeric-literals': 'error',
|
|
70
|
+
'prefer-object-has-own': 'error',
|
|
71
|
+
'prefer-object-spread': 'error',
|
|
72
|
+
'prefer-promise-reject-errors': 'error',
|
|
73
|
+
'prefer-regex-literals': ['error', { disallowRedundantWrapping: true }],
|
|
74
|
+
'prefer-rest-params': 'error',
|
|
75
|
+
'prefer-spread': 'error',
|
|
76
|
+
'prefer-template': 'error',
|
|
77
|
+
'preserve-caught-error': ['error', { requireCatchParameter: true }],
|
|
78
|
+
'require-unicode-regexp': 'error',
|
|
79
|
+
'symbol-description': 'error',
|
|
80
|
+
curly: ['error', 'multi-line', 'consistent'],
|
|
81
|
+
eqeqeq: 'error',
|
|
82
|
+
radix: 'error',
|
|
83
|
+
strict: ['error', 'global'],
|
|
84
|
+
yoda: 'error',
|
|
85
|
+
'no-restricted-properties': [
|
|
86
|
+
'error',
|
|
87
|
+
{
|
|
88
|
+
object: 'global',
|
|
89
|
+
property: 'isFinite',
|
|
90
|
+
message: 'Please use `Number.isFinite` instead.',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
object: 'self',
|
|
94
|
+
property: 'isFinite',
|
|
95
|
+
message: 'Please use `Number.isFinite` instead.',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
object: 'window',
|
|
99
|
+
property: 'isFinite',
|
|
100
|
+
message: 'Please use `Number.isFinite` instead.',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
object: 'global',
|
|
104
|
+
property: 'isNaN',
|
|
105
|
+
message: 'Please use `Number.isNaN` instead.',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
object: 'self',
|
|
109
|
+
property: 'isNaN',
|
|
110
|
+
message: 'Please use `Number.isNaN` instead.',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
object: 'window',
|
|
114
|
+
property: 'isNaN',
|
|
115
|
+
message: 'Please use `Number.isNaN` instead.',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
'no-restricted-syntax': [
|
|
119
|
+
'error',
|
|
120
|
+
{
|
|
121
|
+
selector: 'ForInStatement',
|
|
122
|
+
message: 'for..in loops iterate over the entire prototype chain, which is most likely an error. Use `for..of` or `Object.values` instead.',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
selector: 'MemberExpression[object.name="JSON"][property.name="parse"]',
|
|
126
|
+
message: '`JSON.parse` returns `any` type. Use `jsonParse` from `@monholm/util` instead',
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
selector: 'MemberExpression[object.name="Array"][property.name="isArray"]',
|
|
130
|
+
message: '`Array.isArray` returns `any[]` type. Use `isArray` from `@monholm/util` instead',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
selector: 'Identifier[name="Omit"]',
|
|
134
|
+
message: '`Omit` is not type safe (does not show any error if omitting a key that does not exist). Use `Except` from `type-fest` instead',
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
'no-restricted-globals': [
|
|
138
|
+
'error',
|
|
139
|
+
{ name: 'isFinite', message: 'Use `Number.isFinite` instead.' },
|
|
140
|
+
{ name: 'isNaN', message: 'Use `Number.isNaN` instead.' },
|
|
141
|
+
],
|
|
142
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../src/rules/import.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAiDpD,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const importRules = {
|
|
2
|
+
'import/first': 'error',
|
|
3
|
+
'import/no-empty-named-blocks': 'error',
|
|
4
|
+
'import/no-extraneous-dependencies': [
|
|
5
|
+
'error',
|
|
6
|
+
{
|
|
7
|
+
// globs where importing from devDependencies is allowed
|
|
8
|
+
devDependencies: [
|
|
9
|
+
'test/**',
|
|
10
|
+
'tests/**',
|
|
11
|
+
'spec/**',
|
|
12
|
+
'**/__tests__/**',
|
|
13
|
+
'**/__mocks__/**',
|
|
14
|
+
'**/tests/**',
|
|
15
|
+
'**/spec/**',
|
|
16
|
+
'**/test/**',
|
|
17
|
+
'**/*{.,_}{test,spec}.{js,jsx,ts,tsx}',
|
|
18
|
+
'**/eslint.config.{js,cjs,mjs}',
|
|
19
|
+
'**/prettier.config.{js,cjs,mjs}',
|
|
20
|
+
'**/vite.config.ts',
|
|
21
|
+
'**/vitest.config.ts',
|
|
22
|
+
'**/playwright.config.ts',
|
|
23
|
+
],
|
|
24
|
+
optionalDependencies: false,
|
|
25
|
+
bundledDependencies: false,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
'import/no-mutable-exports': 'error',
|
|
29
|
+
'import/no-named-as-default': 'error',
|
|
30
|
+
'import/no-named-as-default-member': 'error',
|
|
31
|
+
'import/enforce-node-protocol-usage': ['error', 'always'],
|
|
32
|
+
'import/no-absolute-path': 'error',
|
|
33
|
+
/**
|
|
34
|
+
* `ignoreExternal` decides if files imported from node_modules are also checked.
|
|
35
|
+
* This goes against the general rule that linting shouldn't happen for dependencies,
|
|
36
|
+
* and the example shown in the `eslint-plugin-import` documentation is unlikely to occur.
|
|
37
|
+
*
|
|
38
|
+
* This has the added benefit of a quicker lint, and in the case of react-native,
|
|
39
|
+
* linting can succeed since no react-native/*.js files (written in flow) have to be parsed
|
|
40
|
+
* by `eslint-plugin-import`.
|
|
41
|
+
*/
|
|
42
|
+
'import/no-cycle': ['error', { maxDepth: '∞', ignoreExternal: true }],
|
|
43
|
+
'import/no-dynamic-require': 'error',
|
|
44
|
+
'import/no-relative-packages': 'error',
|
|
45
|
+
'import/no-self-import': 'error',
|
|
46
|
+
'import/no-useless-path-segments': 'error',
|
|
47
|
+
'import/newline-after-import': 'error',
|
|
48
|
+
'import/no-unassigned-import': 'error',
|
|
49
|
+
'import/order': 'error',
|
|
50
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/rules/node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CA2BlD,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export const nodeRules = {
|
|
2
|
+
'n/callback-return': 'error',
|
|
3
|
+
'n/exports-style': 'error',
|
|
4
|
+
'n/hashbang': 'error',
|
|
5
|
+
'n/no-deprecated-api': 'error',
|
|
6
|
+
'n/no-exports-assign': 'error',
|
|
7
|
+
'n/no-path-concat': 'error',
|
|
8
|
+
'n/no-process-exit': 'error',
|
|
9
|
+
'n/no-sync': 'error',
|
|
10
|
+
'n/no-unpublished-bin': 'error',
|
|
11
|
+
// eslint-disable-next-line @stylistic/multiline-comment-style
|
|
12
|
+
// 'n/no-unpublished-import': 'error', // Flags devDependencies as well (which duplicates import/no-extraneous-dependencies) and have no option to disable it.
|
|
13
|
+
// 'n/no-unpublished-require': 'error', // Flags devDependencies as well (which duplicates import/no-extraneous-dependencies) and have no option to disable it.
|
|
14
|
+
'n/no-unsupported-features/es-builtins': 'error',
|
|
15
|
+
'n/no-unsupported-features/es-syntax': 'error',
|
|
16
|
+
'n/no-unsupported-features/node-builtins': 'error',
|
|
17
|
+
'n/prefer-global/buffer': 'error',
|
|
18
|
+
'n/prefer-global/console': 'error',
|
|
19
|
+
'n/prefer-global/process': 'error',
|
|
20
|
+
'n/prefer-global/text-decoder': 'error',
|
|
21
|
+
'n/prefer-global/text-encoder': 'error',
|
|
22
|
+
'n/prefer-global/url': 'error',
|
|
23
|
+
'n/prefer-global/url-search-params': 'error',
|
|
24
|
+
// 'n/prefer-node-protocol': 'error', // Duplicate of import/enforce-node-protocol-usage
|
|
25
|
+
'n/prefer-promises/dns': 'error',
|
|
26
|
+
'n/prefer-promises/fs': 'error',
|
|
27
|
+
'n/process-exit-as-throw': 'error',
|
|
28
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/rules/react.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAkInD,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export const reactRules = {
|
|
2
|
+
'react/button-has-type': 'error',
|
|
3
|
+
'react/checked-requires-onchange-or-readonly': 'error',
|
|
4
|
+
'react/default-props-match-prop-types': 'error',
|
|
5
|
+
'react/display-name': 'error',
|
|
6
|
+
'react/forward-ref-uses-ref': 'error',
|
|
7
|
+
'react/hook-use-state': 'error',
|
|
8
|
+
'react/iframe-missing-sandbox': 'error',
|
|
9
|
+
'react/jsx-boolean-value': 'error',
|
|
10
|
+
'react/jsx-child-element-spacing': 'error',
|
|
11
|
+
'react/jsx-closing-tag-location': 'error',
|
|
12
|
+
'react/jsx-curly-brace-presence': [
|
|
13
|
+
'error',
|
|
14
|
+
{ props: 'never', children: 'never', propElementValues: 'always' },
|
|
15
|
+
],
|
|
16
|
+
'react/jsx-equals-spacing': 'error',
|
|
17
|
+
'react/jsx-filename-extension': [
|
|
18
|
+
'error',
|
|
19
|
+
{
|
|
20
|
+
allow: 'as-needed',
|
|
21
|
+
extensions: ['.jsx', '.tsx'],
|
|
22
|
+
ignoreFilesWithoutCode: true,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
'react/jsx-fragments': 'error',
|
|
26
|
+
'react/jsx-key': [
|
|
27
|
+
'error',
|
|
28
|
+
{
|
|
29
|
+
checkFragmentShorthand: true,
|
|
30
|
+
checkKeyMustBeforeSpread: true,
|
|
31
|
+
warnOnDuplicates: true,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
'react/jsx-no-bind': [
|
|
35
|
+
'error',
|
|
36
|
+
{
|
|
37
|
+
ignoreRefs: true,
|
|
38
|
+
allowArrowFunctions: true,
|
|
39
|
+
allowFunctions: false,
|
|
40
|
+
allowBind: false,
|
|
41
|
+
ignoreDOMComponents: true,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
'react/jsx-no-comment-textnodes': 'error',
|
|
45
|
+
'react/jsx-no-constructed-context-values': 'error',
|
|
46
|
+
'react/jsx-no-duplicate-props': 'error',
|
|
47
|
+
/*
|
|
48
|
+
* jsx-no-leaked-render isn't type aware, meaning it report errors even on boolean expressions.
|
|
49
|
+
* To use it, you have to always use `!!` or `Boolean()` to cast your expressions to booleans,
|
|
50
|
+
* which in turn will cause @typescript-eslint/no-unnecessary-type-conversion to report errors.
|
|
51
|
+
* So instead of using it, we stricten our use of strict-boolean-expressions to disallow anything besides booleans
|
|
52
|
+
* in react projects.
|
|
53
|
+
*/
|
|
54
|
+
'react/jsx-no-leaked-render': 'off',
|
|
55
|
+
'@typescript-eslint/strict-boolean-expressions': [
|
|
56
|
+
'error',
|
|
57
|
+
{
|
|
58
|
+
allowAny: false,
|
|
59
|
+
allowNullableBoolean: false,
|
|
60
|
+
allowNullableEnum: false,
|
|
61
|
+
allowNullableNumber: false,
|
|
62
|
+
allowNullableObject: false,
|
|
63
|
+
allowNullableString: false,
|
|
64
|
+
allowNumber: false,
|
|
65
|
+
allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false,
|
|
66
|
+
allowString: false,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
'react/jsx-no-script-url': 'error',
|
|
70
|
+
'react/jsx-no-target-blank': [
|
|
71
|
+
'error',
|
|
72
|
+
{ enforceDynamicLinks: 'always', warnOnSpreadAttributes: true },
|
|
73
|
+
],
|
|
74
|
+
'react/jsx-no-undef': 'error',
|
|
75
|
+
'react/jsx-no-useless-fragment': 'error',
|
|
76
|
+
'react/jsx-pascal-case': 'error',
|
|
77
|
+
'react/jsx-props-no-multi-spaces': 'error',
|
|
78
|
+
'react/jsx-props-no-spread-multi': 'error',
|
|
79
|
+
'react/jsx-tag-spacing': [
|
|
80
|
+
'error',
|
|
81
|
+
{
|
|
82
|
+
closingSlash: 'never',
|
|
83
|
+
beforeSelfClosing: 'always',
|
|
84
|
+
afterOpening: 'never',
|
|
85
|
+
beforeClosing: 'never',
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
'react/jsx-uses-vars': 'error',
|
|
89
|
+
'react/no-access-state-in-setstate': 'error',
|
|
90
|
+
'react/no-array-index-key': 'error',
|
|
91
|
+
'react/no-arrow-function-lifecycle': 'error',
|
|
92
|
+
'react/no-children-prop': 'error',
|
|
93
|
+
'react/no-danger': 'error',
|
|
94
|
+
'react/no-danger-with-children': 'error',
|
|
95
|
+
'react/no-deprecated': 'error',
|
|
96
|
+
'react/no-did-mount-set-state': ['error', 'disallow-in-func'],
|
|
97
|
+
'react/no-did-update-set-state': ['error', 'disallow-in-func'],
|
|
98
|
+
'react/no-direct-mutation-state': 'error',
|
|
99
|
+
'react/no-find-dom-node': 'error',
|
|
100
|
+
'react/no-invalid-html-attribute': 'error',
|
|
101
|
+
'react/no-is-mounted': 'error',
|
|
102
|
+
'react/no-namespace': 'error',
|
|
103
|
+
'react/no-object-type-as-default-prop': 'error',
|
|
104
|
+
'react/no-redundant-should-component-update': 'error',
|
|
105
|
+
'react/no-render-return-value': 'error',
|
|
106
|
+
'react/no-string-refs': ['error', { noTemplateLiterals: true }],
|
|
107
|
+
'react/no-this-in-sfc': 'error',
|
|
108
|
+
'react/no-typos': 'error',
|
|
109
|
+
'react/no-unescaped-entities': 'error',
|
|
110
|
+
'react/no-unknown-property': ['error', { requireDataLowercase: true }],
|
|
111
|
+
'react/no-unstable-nested-components': 'error',
|
|
112
|
+
'react/no-unused-class-component-methods': 'error',
|
|
113
|
+
'react/no-unused-prop-types': 'error',
|
|
114
|
+
'react/no-unused-state': 'error',
|
|
115
|
+
'react/no-will-update-set-state': ['error', 'disallow-in-func'],
|
|
116
|
+
'react/prefer-es6-class': 'error',
|
|
117
|
+
'react/prefer-exact-props': 'error',
|
|
118
|
+
'react/prefer-stateless-function': 'error',
|
|
119
|
+
'react/prop-types': 'error',
|
|
120
|
+
'react/require-render-return': 'error',
|
|
121
|
+
'react/self-closing-comp': 'error',
|
|
122
|
+
'react/state-in-constructor': 'error',
|
|
123
|
+
'react/void-dom-elements-no-children': 'error',
|
|
124
|
+
'react/function-component-definition': [
|
|
125
|
+
'error',
|
|
126
|
+
{
|
|
127
|
+
namedComponents: ['function-declaration', 'arrow-function'],
|
|
128
|
+
unnamedComponents: 'arrow-function',
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactHooks.d.ts","sourceRoot":"","sources":["../../src/rules/reactHooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAoBxD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const reactHooksRules = {
|
|
2
|
+
'react-hooks/rules-of-hooks': 'error',
|
|
3
|
+
'react-hooks/exhaustive-deps': 'error',
|
|
4
|
+
// React Compiler rules
|
|
5
|
+
'react-hooks/config': 'error',
|
|
6
|
+
'react-hooks/error-boundaries': 'error',
|
|
7
|
+
'react-hooks/component-hook-factories': 'error',
|
|
8
|
+
'react-hooks/gating': 'error',
|
|
9
|
+
'react-hooks/globals': 'error',
|
|
10
|
+
'react-hooks/immutability': 'error',
|
|
11
|
+
'react-hooks/preserve-manual-memoization': 'error',
|
|
12
|
+
'react-hooks/purity': 'error',
|
|
13
|
+
'react-hooks/refs': 'error',
|
|
14
|
+
'react-hooks/set-state-in-effect': 'error',
|
|
15
|
+
'react-hooks/set-state-in-render': 'error',
|
|
16
|
+
'react-hooks/static-components': 'error',
|
|
17
|
+
'react-hooks/unsupported-syntax': 'error',
|
|
18
|
+
'react-hooks/use-memo': 'error',
|
|
19
|
+
'react-hooks/incompatible-library': 'error',
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stylisticEslint.d.ts","sourceRoot":"","sources":["../../src/rules/stylisticEslint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAmB7D,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const stylisticEslintRules = {
|
|
2
|
+
/*
|
|
3
|
+
* From https://eslint.style/rules/jsx-curly-brace-presence:
|
|
4
|
+
*
|
|
5
|
+
* Note: it is highly recommended that you configure this rule with an object,
|
|
6
|
+
* and that you set "propElementValues" to "always".
|
|
7
|
+
* The ability to omit curly braces around prop values that are JSX elements is obscure,
|
|
8
|
+
* and intentionally undocumented, and should not be relied upon.
|
|
9
|
+
*/
|
|
10
|
+
'@stylistic/jsx-curly-brace-presence': [
|
|
11
|
+
'error',
|
|
12
|
+
{ props: 'never', children: 'never', propElementValues: 'always' },
|
|
13
|
+
],
|
|
14
|
+
'@stylistic/jsx-pascal-case': 'error',
|
|
15
|
+
'@stylistic/jsx-self-closing-comp': 'error',
|
|
16
|
+
'@stylistic/linebreak-style': ['error', 'unix'],
|
|
17
|
+
'@stylistic/multiline-comment-style': 'error',
|
|
18
|
+
'@stylistic/spaced-comment': 'error',
|
|
19
|
+
'@stylistic/wrap-iife': ['error', 'inside'],
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tsEslint.d.ts","sourceRoot":"","sources":["../../src/rules/tsEslint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,eAAe,CAAC;AAE1C,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAsDtD,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const tsEslintRules = {
|
|
2
|
+
'@typescript-eslint/consistent-type-exports': [
|
|
3
|
+
'error',
|
|
4
|
+
{ fixMixedExportsWithInlineTypeSpecifier: true },
|
|
5
|
+
],
|
|
6
|
+
'class-methods-use-this': 'off',
|
|
7
|
+
'@typescript-eslint/class-methods-use-this': 'error',
|
|
8
|
+
'@typescript-eslint/consistent-type-imports': [
|
|
9
|
+
'error',
|
|
10
|
+
{ fixStyle: 'inline-type-imports' },
|
|
11
|
+
],
|
|
12
|
+
'default-param-last': 'off',
|
|
13
|
+
'@typescript-eslint/default-param-last': 'error',
|
|
14
|
+
'max-params': 'off',
|
|
15
|
+
'@typescript-eslint/max-params': ['error', { max: 3 }],
|
|
16
|
+
'@typescript-eslint/method-signature-style': 'error',
|
|
17
|
+
'@typescript-eslint/no-import-type-side-effects': 'error',
|
|
18
|
+
'no-loop-func': 'off',
|
|
19
|
+
'@typescript-eslint/no-loop-func': 'error',
|
|
20
|
+
'no-shadow': 'off',
|
|
21
|
+
'@typescript-eslint/no-shadow': ['error', { hoist: 'all' }],
|
|
22
|
+
'@typescript-eslint/no-unnecessary-qualifier': 'error',
|
|
23
|
+
'@typescript-eslint/no-unsafe-type-assertion': 'error',
|
|
24
|
+
'no-unused-private-class-members': 'off',
|
|
25
|
+
'@typescript-eslint/no-unused-private-class-members': 'error',
|
|
26
|
+
'no-use-before-define': 'off',
|
|
27
|
+
'@typescript-eslint/no-use-before-define': [
|
|
28
|
+
'error',
|
|
29
|
+
{ functions: false, classes: true, variables: false },
|
|
30
|
+
],
|
|
31
|
+
'@typescript-eslint/no-useless-empty-export': 'error',
|
|
32
|
+
'prefer-destructuring': 'off',
|
|
33
|
+
'@typescript-eslint/prefer-destructuring': 'error',
|
|
34
|
+
'@typescript-eslint/promise-function-async': 'error',
|
|
35
|
+
'@typescript-eslint/require-array-sort-compare': 'error',
|
|
36
|
+
'@typescript-eslint/strict-boolean-expressions': [
|
|
37
|
+
'error',
|
|
38
|
+
{ allowString: false, allowNumber: false },
|
|
39
|
+
],
|
|
40
|
+
'@typescript-eslint/switch-exhaustiveness-check': 'error',
|
|
41
|
+
'@typescript-eslint/no-unused-vars': [
|
|
42
|
+
'error',
|
|
43
|
+
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
|
44
|
+
],
|
|
45
|
+
'@typescript-eslint/restrict-template-expressions': [
|
|
46
|
+
'error',
|
|
47
|
+
{ allowNumber: true, allowBoolean: true },
|
|
48
|
+
],
|
|
49
|
+
'@typescript-eslint/no-misused-promises': [
|
|
50
|
+
'error',
|
|
51
|
+
{ checksVoidReturn: { attributes: false } },
|
|
52
|
+
],
|
|
53
|
+
'no-unused-expressions': 'off',
|
|
54
|
+
'@typescript-eslint/no-unused-expressions': ['error', { enforceForJSX: true }],
|
|
55
|
+
};
|
package/package.json
CHANGED
|
@@ -6,30 +6,55 @@
|
|
|
6
6
|
"directory": "packages/eslint-config"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
-
"exports":
|
|
10
|
-
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/configs/base.js",
|
|
11
|
+
"./node": "./dist/configs/node.js",
|
|
12
|
+
"./react": "./dist/configs/react.js"
|
|
13
|
+
},
|
|
11
14
|
"files": [
|
|
12
15
|
"dist"
|
|
13
16
|
],
|
|
14
17
|
"peerDependencies": {
|
|
15
|
-
"eslint": ">= 9.
|
|
18
|
+
"@eslint/js": ">= 9.35.0",
|
|
19
|
+
"@stylistic/eslint-plugin": ">= 5.6.1",
|
|
20
|
+
"eslint": ">= 9.35.0",
|
|
21
|
+
"eslint-plugin-import": ">= 2.32.0",
|
|
22
|
+
"eslint-plugin-n": ">= 17.23.1",
|
|
23
|
+
"eslint-plugin-react": ">= 7.37.5",
|
|
24
|
+
"eslint-plugin-react-hooks": ">= 6.1.1",
|
|
16
25
|
"typescript": ">= 5.8.0",
|
|
17
|
-
"typescript-eslint": ">= 8.0.0"
|
|
18
|
-
|
|
26
|
+
"typescript-eslint": ">= 8.0.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependenciesMeta": {
|
|
29
|
+
"eslint-plugin-react": {
|
|
30
|
+
"optional": true
|
|
31
|
+
},
|
|
32
|
+
"eslint-plugin-react-hooks": {
|
|
33
|
+
"optional": true
|
|
34
|
+
},
|
|
35
|
+
"eslint-plugin-n": {
|
|
36
|
+
"optional": true
|
|
37
|
+
}
|
|
19
38
|
},
|
|
20
39
|
"devDependencies": {
|
|
21
|
-
"eslint": "9.39.
|
|
22
|
-
"@eslint
|
|
23
|
-
"
|
|
24
|
-
"
|
|
40
|
+
"@eslint/js": "9.39.2",
|
|
41
|
+
"@stylistic/eslint-plugin": "5.8.0",
|
|
42
|
+
"eslint": "9.39.2",
|
|
43
|
+
"eslint-plugin-import": "2.32.0",
|
|
44
|
+
"eslint-plugin-n": "17.23.2",
|
|
45
|
+
"eslint-plugin-react": "7.37.5",
|
|
46
|
+
"eslint-plugin-react-hooks": "6.1.1",
|
|
47
|
+
"prettier": "3.8.1",
|
|
25
48
|
"typescript": "5.9.3",
|
|
26
|
-
"
|
|
27
|
-
"@monholm/
|
|
49
|
+
"typescript-eslint": "8.55.0",
|
|
50
|
+
"@monholm/prettier-config": "2.0.2",
|
|
51
|
+
"@monholm/tsconfig": "2.2.0"
|
|
28
52
|
},
|
|
29
|
-
"version": "
|
|
53
|
+
"version": "3.0.0",
|
|
30
54
|
"scripts": {
|
|
31
55
|
"build": "tsc -p tsconfig.build.json",
|
|
32
56
|
"check": "tsc",
|
|
57
|
+
"lint": "eslint . --max-warnings 0",
|
|
33
58
|
"format-check": "prettier . --check --cache-location=_unused",
|
|
34
59
|
"format": "prettier . --write --cache-location=_unused"
|
|
35
60
|
}
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED