@timobechtel/style 1.14.0 → 2.0.1

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 CHANGED
@@ -10,7 +10,7 @@ Highly opinionated configuration files for typescript projects. Inspired by [@ve
10
10
  ## Usage
11
11
 
12
12
  ```bash
13
- npm i -D @timobechtel/style prettier "eslint@^8.57.1" typescript
13
+ npm i -D @timobechtel/style prettier "eslint@^9" typescript
14
14
  ```
15
15
 
16
16
  ### Prettier
@@ -94,7 +94,6 @@ With expo make sure to add `"moduleResolution": "bundler"` to the `compilerOptio
94
94
 
95
95
  </details>
96
96
 
97
-
98
97
  #### Or with React
99
98
 
100
99
  ```bash
@@ -117,51 +116,44 @@ curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/temp
117
116
  ### Eslint
118
117
 
119
118
  ```bash
120
- curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/eslint/core/.eslintrc.cjs
121
- ```
122
-
123
- #### Fix Parsing errors for config files
124
-
125
- You may get a `Parsing error: <FILE> was not found by the project service.` for config files like .eslintrc.cjs when not included in the tsconfig.
126
-
127
- To fix, either add to tsconfig or add them to the eslint config:
128
-
129
- ```diff
130
- //...
131
- parserOptions: {
132
- + projectService: {
133
- + allowDefaultProject: ['.eslintrc.cjs'],
134
- + },
135
- //...
136
- },
137
- //...
119
+ curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/eslint/core/eslint.config.js
138
120
  ```
139
121
 
122
+ Note: If your project is not ESM (no `"type": "module"` in `package.json`), rename the file to `eslint.config.mjs`.
140
123
 
141
124
  <details>
142
125
  <summary>Or manually</summary>
143
126
 
144
- Copy the following to a `.eslintrc.cjs`:
127
+ Copy the following to an `eslint.config.js`:
145
128
 
146
129
  ```js
147
- const { resolve } = require('node:path');
148
-
149
- const project = resolve(process.cwd(), 'tsconfig.json');
150
-
151
- module.exports = {
152
- root: true,
153
- extends: [require.resolve('@timobechtel/style/eslint/core.cjs')],
154
- parserOptions: {
155
- tsconfigRootDir: process.cwd(),
156
- },
157
- settings: {
158
- 'import/resolver': {
159
- typescript: {
160
- project,
130
+ import path from 'node:path';
131
+ import { fileURLToPath } from 'node:url';
132
+ import { defineConfig } from 'eslint/config';
133
+ import styleCore from '@timobechtel/style/eslint/core.js';
134
+ import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript';
135
+ import { createNodeResolver } from 'eslint-plugin-import-x';
136
+
137
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
138
+
139
+ export default defineConfig([
140
+ ...styleCore,
141
+ {
142
+ languageOptions: {
143
+ parserOptions: {
144
+ tsconfigRootDir: __dirname,
161
145
  },
162
146
  },
147
+ settings: {
148
+ 'import-x/resolver-next': [
149
+ createTypeScriptImportResolver({
150
+ project: path.resolve(__dirname, 'tsconfig.json'),
151
+ }),
152
+ createNodeResolver(),
153
+ ],
154
+ },
163
155
  },
164
- };
156
+ ]);
165
157
  ```
166
158
 
167
159
  </details>
@@ -169,28 +161,49 @@ To fix, either add to tsconfig or add them to the eslint config:
169
161
  #### React
170
162
 
171
163
  ```bash
172
- curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/eslint/react/.eslintrc.cjs
164
+ curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/eslint/react/eslint.config.js
173
165
  ```
174
166
 
175
167
  <details>
176
168
  <summary>Or manually</summary>
177
169
 
178
- Also add `require.resolve('@timobechtel/style/eslint/react.cjs')` to the `extends` array.
170
+ Also spread `styleReact` from `@timobechtel/style/eslint/react.js`:
171
+
172
+ ```js
173
+ import styleCore from '@timobechtel/style/eslint/core.js';
174
+ import styleReact from '@timobechtel/style/eslint/react.js';
175
+ import { defineConfig } from 'eslint/config';
176
+
177
+ export default defineConfig([
178
+ ...styleCore,
179
+ ...styleReact,
180
+ // ... your config
181
+ ]);
182
+ ```
179
183
 
180
184
  Example config:
181
- <https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/eslint/react/.eslintrc.cjs>
185
+ <https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/eslint/react/eslint.config.js>
182
186
  </details>
183
187
 
188
+ #### Migration from v1.x
189
+
190
+ If you're upgrading from v1.x, you'll need to:
191
+
192
+ 1. Upgrade to ESLint v9+
193
+ 2. Replace `.eslintrc.cjs` with `eslint.config.js`
194
+ 3. Update imports to use `.js` extension (e.g., `@timobechtel/style/eslint/core.js`)
195
+ 4. Note: Import plugin rules now use `import-x/` prefix instead of `import/`
196
+
184
197
  #### VSCode
185
198
 
186
- Note: You should disable `source.organizeImports` in your VSCode config, as this collides with the `import/order` rule.
199
+ Note: You should disable `source.organizeImports` in your VSCode config, as this collides with the `import-x/order` rule.
187
200
 
188
201
  Add the following to your VSCode config, e.g. `.vscode/settings.json`
189
202
 
190
203
  ```json
191
204
  {
192
205
  "editor.codeActionsOnSave": {
193
- // use eslint import/order instead
206
+ // use eslint import-x/order instead
194
207
  "source.sortImports": "never"
195
208
  }
196
209
  }
@@ -201,7 +214,7 @@ Add the following to your VSCode config, e.g. `.vscode/settings.json`
201
214
  This repo also contains a [semantic-release](https://github.com/semantic-release/semantic-release) configuration.
202
215
 
203
216
  ```bash
204
- npm i -D semantic-release
217
+ npm i -D semantic-release @semantic-release/changelog @semantic-release/git
205
218
  ```
206
219
 
207
220
  ```bash
package/eslint/core.js ADDED
@@ -0,0 +1,86 @@
1
+ import js from '@eslint/js';
2
+ import timobechtelRulesPlugin from '@timobechtel/eslint-plugin-rules';
3
+ import prettierConfig from 'eslint-config-prettier/flat';
4
+ import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript';
5
+ import { createNodeResolver, importX } from 'eslint-plugin-import-x';
6
+ import unicornPlugin from 'eslint-plugin-unicorn';
7
+ import { defineConfig } from 'eslint/config';
8
+ import globals from 'globals';
9
+ import tseslint from 'typescript-eslint';
10
+
11
+ import baseRules from './rules/base.js';
12
+ import importRules from './rules/import.js';
13
+ import typescriptRules from './rules/typescript.js';
14
+ import unicornRules from './rules/unicorn.js';
15
+
16
+ const mergeRules = (configs) =>
17
+ Object.assign({}, ...configs.map((config) => config?.rules ?? {}));
18
+
19
+ export default defineConfig([
20
+ js.configs.recommended,
21
+ importX.flatConfigs.recommended,
22
+ importX.flatConfigs.typescript,
23
+ timobechtelRulesPlugin.configs['flat/all'],
24
+
25
+ {
26
+ languageOptions: {
27
+ globals: {
28
+ ...globals.browser,
29
+ ...globals.node,
30
+ ...globals.es2021,
31
+ },
32
+ ecmaVersion: 2021,
33
+ sourceType: 'module',
34
+ },
35
+ plugins: {
36
+ unicorn: unicornPlugin,
37
+ },
38
+ rules: {
39
+ ...mergeRules(baseRules),
40
+ ...mergeRules(importRules),
41
+ ...mergeRules(unicornRules),
42
+ },
43
+ linterOptions: {
44
+ reportUnusedDisableDirectives: true,
45
+ },
46
+ settings: {
47
+ 'import-x/resolver-next': [
48
+ createTypeScriptImportResolver(),
49
+ createNodeResolver(),
50
+ ],
51
+ },
52
+ },
53
+
54
+ ...tseslint.configs.recommended,
55
+ ...tseslint.configs.recommendedTypeChecked,
56
+ ...tseslint.configs.strict,
57
+ ...tseslint.configs.strictTypeChecked,
58
+ ...tseslint.configs.stylistic,
59
+ ...tseslint.configs.stylisticTypeChecked,
60
+ {
61
+ languageOptions: {
62
+ parserOptions: {
63
+ projectService: true,
64
+ },
65
+ },
66
+ },
67
+ {
68
+ files: ['**/*.js'],
69
+ extends: [tseslint.configs.disableTypeChecked],
70
+ },
71
+ {
72
+ files: ['**/*.ts', '**/*.tsx'],
73
+ rules: {
74
+ ...mergeRules(typescriptRules),
75
+ },
76
+ },
77
+
78
+ {
79
+ files: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx'],
80
+ rules: {
81
+ '@typescript-eslint/ban-ts-comment': 'off',
82
+ },
83
+ },
84
+
85
+ prettierConfig,
86
+ ]);
@@ -0,0 +1,32 @@
1
+ import prettierConfig from 'eslint-config-prettier/flat';
2
+ import reactPlugin from 'eslint-plugin-react';
3
+ import reactHooksPlugin from 'eslint-plugin-react-hooks';
4
+ import { defineConfig } from 'eslint/config';
5
+ import reactRules from './rules/react.js';
6
+
7
+ const mergeRules = (configs) =>
8
+ Object.assign({}, ...configs.map((config) => config?.rules ?? {}));
9
+
10
+ export default defineConfig([
11
+ reactPlugin.configs.flat.recommended,
12
+ reactPlugin.configs.flat['jsx-runtime'],
13
+
14
+ {
15
+ plugins: {
16
+ react: reactPlugin,
17
+ 'react-hooks': reactHooksPlugin,
18
+ },
19
+ settings: {
20
+ react: {
21
+ version: 'detect',
22
+ },
23
+ linkComponents: ['Link'],
24
+ },
25
+ rules: {
26
+ ...reactHooksPlugin.configs.flat.recommended.rules,
27
+ ...mergeRules(reactRules),
28
+ },
29
+ },
30
+
31
+ prettierConfig,
32
+ ]);
@@ -0,0 +1,52 @@
1
+ import { defineConfig } from 'eslint/config';
2
+
3
+ export default defineConfig({
4
+ rules: {
5
+ 'prefer-arrow-callback': [
6
+ 'warn',
7
+ {
8
+ allowNamedFunctions: true,
9
+ allowUnboundThis: true,
10
+ },
11
+ ],
12
+ 'no-console': [
13
+ 'error',
14
+ {
15
+ allow: ['warn', 'error', 'clear', 'info'],
16
+ },
17
+ ],
18
+ 'max-depth': ['warn', 4],
19
+ 'no-restricted-globals': ['error', 'event', 'name'],
20
+ curly: ['warn', 'multi-line'],
21
+ 'default-case-last': 'error',
22
+ eqeqeq: 'error',
23
+ 'no-alert': 'error',
24
+ 'no-useless-rename': 'warn',
25
+ 'no-var': 'error',
26
+ 'object-shorthand': 'warn',
27
+ 'prefer-const': 'warn',
28
+ 'prefer-rest-params': 'error',
29
+ 'prefer-spread': 'error',
30
+ 'prefer-template': 'warn',
31
+ 'no-promise-executor-return': 'error',
32
+ 'no-unreachable-loop': 'error',
33
+ 'new-cap': ['error', { capIsNew: false }],
34
+ 'new-parens': 'warn',
35
+ 'no-lonely-if': 'warn',
36
+ 'no-unneeded-ternary': 'error',
37
+ 'prefer-object-spread': 'warn',
38
+ 'no-label-var': 'error',
39
+ 'no-undef-init': 'warn',
40
+ 'no-unused-vars': [
41
+ 'error',
42
+ {
43
+ args: 'after-used',
44
+ argsIgnorePattern: '^_',
45
+ ignoreRestSiblings: false,
46
+ vars: 'all',
47
+ varsIgnorePattern: '^_',
48
+ },
49
+ ],
50
+ 'no-constant-binary-expression': 'error',
51
+ },
52
+ });
@@ -1,12 +1,10 @@
1
- // @ts-check
2
- const { defineConfig } = require('eslint-define-config');
1
+ import { defineConfig } from 'eslint/config';
3
2
 
4
- module.exports = defineConfig({
3
+ export default defineConfig({
5
4
  rules: {
6
5
  'no-restricted-syntax': [
7
6
  'error',
8
7
  {
9
- // catches common mistakes when writing comments in CSS
10
8
  selector:
11
9
  'TemplateElement[value.cooked=/\\s+\\u002F\\u002F\\s*/], Literal[value=/\\s+\\u002F\\u002F\\s*/]',
12
10
  message: 'Invalid comment syntax. Use `/* */` instead of `//`.',
@@ -0,0 +1,28 @@
1
+ import { defineConfig } from 'eslint/config';
2
+
3
+ export default defineConfig({
4
+ rules: {
5
+ 'import-x/first': 'error',
6
+ 'import-x/newline-after-import': 'warn',
7
+ 'import-x/no-self-import': 'error',
8
+ 'import-x/no-useless-path-segments': ['error'],
9
+ 'import-x/order': [
10
+ 'warn',
11
+ {
12
+ groups: [
13
+ 'builtin',
14
+ 'external',
15
+ 'internal',
16
+ 'parent',
17
+ 'sibling',
18
+ 'index',
19
+ ],
20
+ },
21
+ ],
22
+ 'import-x/no-default-export': 'off',
23
+ 'import-x/default': 'off',
24
+ 'import-x/export': 'off',
25
+ 'import-x/namespace': 'off',
26
+ 'import-x/no-unresolved': 'off',
27
+ },
28
+ });
@@ -0,0 +1,40 @@
1
+ import { defineConfig } from 'eslint/config';
2
+
3
+ export default defineConfig({
4
+ rules: {
5
+ 'react/prop-types': 'off',
6
+ 'react/react-in-jsx-scope': 'off',
7
+ 'react/button-has-type': 'error',
8
+ 'react/function-component-definition': 'warn',
9
+ 'react/hook-use-state': 'warn',
10
+ 'react/jsx-boolean-value': 'warn',
11
+ 'react/jsx-curly-brace-presence': [
12
+ 'warn',
13
+ {
14
+ props: 'never',
15
+ children: 'ignore',
16
+ propElementValues: 'always',
17
+ },
18
+ ],
19
+ 'react/jsx-fragments': 'warn',
20
+ 'react/jsx-no-leaked-render': 'error',
21
+ 'react/jsx-no-target-blank': [
22
+ 'error',
23
+ {
24
+ allowReferrer: true,
25
+ },
26
+ ],
27
+ 'react/jsx-no-useless-fragment': ['warn', { allowExpressions: true }],
28
+ 'react/jsx-pascal-case': 'warn',
29
+ 'react/jsx-sort-props': [
30
+ 'warn',
31
+ {
32
+ callbacksLast: true,
33
+ },
34
+ ],
35
+ 'react/no-unstable-nested-components': 'error',
36
+ 'react/self-closing-comp': 'warn',
37
+ 'react-hooks/exhaustive-deps': 'error',
38
+ 'react/destructuring-assignment': ['warn', 'always'],
39
+ },
40
+ });
@@ -0,0 +1,63 @@
1
+ import { defineConfig } from 'eslint/config';
2
+ import baseRules from './base.js';
3
+
4
+ const noUnusedVarsConfig = baseRules[0]?.rules?.['no-unused-vars'];
5
+
6
+ export default defineConfig({
7
+ rules: {
8
+ '@typescript-eslint/consistent-type-exports': [
9
+ 'error',
10
+ { fixMixedExportsWithInlineTypeSpecifier: true },
11
+ ],
12
+ '@typescript-eslint/consistent-type-imports': [
13
+ 'error',
14
+ {
15
+ prefer: 'type-imports',
16
+ fixStyle: 'inline-type-imports',
17
+ },
18
+ ],
19
+ '@typescript-eslint/naming-convention': [
20
+ 'error',
21
+ {
22
+ format: ['PascalCase'],
23
+ selector: ['typeLike', 'enumMember'],
24
+ },
25
+ {
26
+ custom: {
27
+ match: false,
28
+ regex: '^I[A-Z]|^(Interface|Props|State)$',
29
+ },
30
+ format: ['PascalCase'],
31
+ selector: 'interface',
32
+ },
33
+ ],
34
+ '@typescript-eslint/no-redundant-type-constituents': 'error',
35
+ '@typescript-eslint/prefer-regexp-exec': 'warn',
36
+ '@typescript-eslint/require-array-sort-compare': [
37
+ 'error',
38
+ { ignoreStringArrays: true },
39
+ ],
40
+ '@typescript-eslint/switch-exhaustiveness-check': 'error',
41
+ 'default-param-last': 'off',
42
+ '@typescript-eslint/default-param-last': 'error',
43
+ 'no-loop-func': 'off',
44
+ '@typescript-eslint/no-loop-func': 'error',
45
+ 'no-unused-vars': 'off',
46
+ '@typescript-eslint/no-unused-vars': noUnusedVarsConfig,
47
+ 'import-x/default': 'off',
48
+ 'import-x/export': 'off',
49
+ 'import-x/namespace': 'off',
50
+ 'import-x/no-unresolved': 'off',
51
+ '@typescript-eslint/prefer-nullish-coalescing': 'off',
52
+ '@typescript-eslint/no-misused-promises': [
53
+ 'error',
54
+ {
55
+ checksVoidReturn: false,
56
+ },
57
+ ],
58
+ '@typescript-eslint/consistent-type-definitions': 'off',
59
+ '@typescript-eslint/method-signature-style': 'off',
60
+ '@typescript-eslint/consistent-indexed-object-style': 'off',
61
+ '@typescript-eslint/no-import-type-side-effects': 'error',
62
+ },
63
+ });
@@ -0,0 +1,71 @@
1
+ import { defineConfig } from 'eslint/config';
2
+
3
+ export default defineConfig({
4
+ rules: {
5
+ 'unicorn/consistent-destructuring': 'warn',
6
+ 'unicorn/consistent-function-scoping': [
7
+ 'warn',
8
+ {
9
+ checkArrowFunctions: false,
10
+ },
11
+ ],
12
+ 'unicorn/error-message': 'error',
13
+ 'unicorn/escape-case': 'warn',
14
+ 'unicorn/no-empty-file': 'error',
15
+ 'unicorn/no-for-loop': 'warn',
16
+ 'unicorn/no-instanceof-array': 'error',
17
+ 'unicorn/no-invalid-remove-event-listener': 'error',
18
+ 'unicorn/no-object-as-default-parameter': 'error',
19
+ 'unicorn/no-thenable': 'error',
20
+ 'unicorn/no-unnecessary-await': 'error',
21
+ 'unicorn/no-unreadable-iife': 'warn',
22
+ 'unicorn/no-useless-fallback-in-spread': 'warn',
23
+ 'unicorn/no-useless-length-check': 'warn',
24
+ 'unicorn/no-useless-promise-resolve-reject': 'error',
25
+ 'unicorn/no-useless-spread': 'error',
26
+ 'unicorn/no-zero-fractions': 'warn',
27
+ 'unicorn/numeric-separators-style': 'error',
28
+ 'unicorn/prefer-add-event-listener': 'error',
29
+ 'unicorn/prefer-array-find': 'error',
30
+ 'unicorn/prefer-array-flat': 'error',
31
+ 'unicorn/prefer-array-flat-map': 'error',
32
+ 'unicorn/prefer-array-index-of': 'error',
33
+ 'unicorn/prefer-array-some': 'error',
34
+ 'unicorn/prefer-at': 'error',
35
+ 'unicorn/prefer-blob-reading-methods': 'error',
36
+ 'unicorn/prefer-date-now': 'error',
37
+ 'unicorn/prefer-default-parameters': 'warn',
38
+ 'unicorn/prefer-dom-node-append': 'error',
39
+ 'unicorn/prefer-dom-node-dataset': 'error',
40
+ 'unicorn/prefer-dom-node-remove': 'error',
41
+ 'unicorn/prefer-event-target': 'warn',
42
+ 'unicorn/prefer-export-from': [
43
+ 'warn',
44
+ {
45
+ ignoreUsedVariables: true,
46
+ },
47
+ ],
48
+ 'unicorn/prefer-includes': 'error',
49
+ 'unicorn/prefer-keyboard-event-key': 'error',
50
+ 'unicorn/prefer-logical-operator-over-ternary': 'error',
51
+ 'unicorn/prefer-math-trunc': 'error',
52
+ 'unicorn/prefer-modern-dom-apis': 'error',
53
+ 'unicorn/prefer-modern-math-apis': 'error',
54
+ 'unicorn/prefer-negative-index': 'error',
55
+ 'unicorn/prefer-node-protocol': 'error',
56
+ 'unicorn/prefer-number-properties': ['error', { checkInfinity: false }],
57
+ 'unicorn/prefer-object-from-entries': 'error',
58
+ 'unicorn/prefer-reflect-apply': 'error',
59
+ 'unicorn/prefer-regexp-test': 'error',
60
+ 'unicorn/prefer-set-has': 'error',
61
+ 'unicorn/prefer-set-size': 'error',
62
+ 'unicorn/prefer-spread': 'error',
63
+ 'unicorn/prefer-string-replace-all': 'error',
64
+ 'unicorn/prefer-string-slice': 'error',
65
+ 'unicorn/prefer-string-starts-ends-with': 'error',
66
+ 'unicorn/prefer-ternary': ['warn', 'only-single-line'],
67
+ 'unicorn/prefer-top-level-await': 'error',
68
+ 'unicorn/require-array-join-separator': 'error',
69
+ 'unicorn/switch-case-braces': 'error',
70
+ },
71
+ });
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@timobechtel/style",
3
- "version": "1.14.0",
3
+ "version": "2.0.1",
4
4
  "type": "module",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/TimoBechtel/style"
8
+ },
5
9
  "files": [
6
10
  "bin",
7
11
  "eslint",
@@ -10,30 +14,30 @@
10
14
  "tsconfig"
11
15
  ],
12
16
  "devDependencies": {
13
- "eslint": "^8.57.1",
14
- "prettier": "^3.4.2",
15
- "semantic-release": "^24.2.1",
16
- "typescript": "^5.7.2"
17
+ "@semantic-release/npm": "^13.1.4",
18
+ "eslint": "^9.39.3",
19
+ "prettier": "^3.8.1",
20
+ "semantic-release": "^25.0.3",
21
+ "typescript": "^5.9.3"
17
22
  },
18
23
  "peerDependencies": {
19
- "eslint": "^8.57.1",
20
- "prettier": "^3.4.2",
21
- "semantic-release": "^24.2.1",
22
- "typescript": "^5.7.2"
24
+ "eslint": "^9.39.3",
25
+ "prettier": "^3.8.1",
26
+ "semantic-release": "^25.0.3",
27
+ "typescript": "^5.9.3"
23
28
  },
24
29
  "dependencies": {
30
+ "@eslint/js": "^9.39.3",
25
31
  "@semantic-release/changelog": "^6.0.3",
26
32
  "@semantic-release/git": "^10.0.1",
27
33
  "@timobechtel/eslint-plugin-rules": "^1.0.0",
28
- "@typescript-eslint/eslint-plugin": "^8.19.1",
29
- "@typescript-eslint/parser": "^8.19.1",
30
- "eslint-config-prettier": "^9.1.0",
31
- "eslint-define-config": "^2.1.0",
32
- "eslint-import-resolver-typescript": "^3.7.0",
33
- "eslint-plugin-import": "^2.31.0",
34
- "eslint-plugin-no-template-curly-in-string-fix": "^1.0.4",
35
- "eslint-plugin-react": "^7.37.3",
36
- "eslint-plugin-react-hooks": "^5.1.0",
37
- "eslint-plugin-unicorn": "^56.0.1"
34
+ "eslint-config-prettier": "^10.1.8",
35
+ "eslint-import-resolver-typescript": "^4.4.4",
36
+ "eslint-plugin-import-x": "^4.16.1",
37
+ "eslint-plugin-react": "^7.37.5",
38
+ "eslint-plugin-react-hooks": "^7.0.1",
39
+ "eslint-plugin-unicorn": "^63.0.0",
40
+ "globals": "^17.3.0",
41
+ "typescript-eslint": "^8.56.0"
38
42
  }
39
43
  }
package/eslint/core.cjs DELETED
@@ -1,63 +0,0 @@
1
- // @ts-check
2
- const { defineConfig } = require('eslint-define-config');
3
-
4
- module.exports = defineConfig({
5
- extends: [
6
- 'eslint:recommended',
7
- 'plugin:import/recommended',
8
- 'prettier',
9
- 'plugin:no-template-curly-in-string-fix/recommended',
10
- 'plugin:@timobechtel/rules/all',
11
- require.resolve('./rules/base.cjs'),
12
- require.resolve('./rules/import.cjs'),
13
- require.resolve('./rules/unicorn.cjs'),
14
- ],
15
- parser: '@typescript-eslint/parser',
16
- plugins: [],
17
- env: {
18
- es2021: true,
19
- node: true,
20
- browser: true,
21
- },
22
- // Report unused `eslint-disable` comments.
23
- reportUnusedDisableDirectives: true,
24
- // Tell ESLint not to ignore dot-files, which are ignored by default.
25
- ignorePatterns: ['!.*.js'],
26
- // Global settings used by all overrides.
27
- settings: {
28
- // Use the Node resolver by default.
29
- 'import/resolver': { node: {} },
30
- },
31
- // Global parser options.
32
- parserOptions: {
33
- ecmaVersion: 2021,
34
- sourceType: 'module',
35
- projectService: true,
36
- },
37
- overrides: [
38
- {
39
- files: ['*.ts?(x)'],
40
- extends: [
41
- 'plugin:@typescript-eslint/recommended',
42
- 'plugin:@typescript-eslint/recommended-type-checked',
43
- 'plugin:@typescript-eslint/strict',
44
- 'plugin:@typescript-eslint/strict-type-checked',
45
- 'plugin:@typescript-eslint/stylistic',
46
- 'plugin:@typescript-eslint/stylistic-type-checked',
47
- 'plugin:import/typescript',
48
- 'prettier',
49
- require.resolve('./rules/typescript.cjs'),
50
- ],
51
- },
52
- {
53
- files: ['*.test.ts?(x)', '*.spec.ts?(x)'],
54
- rules: {
55
- // ts-expect-error makes sense for tests
56
- '@typescript-eslint/ban-ts-comment': [
57
- 'off',
58
- { 'ts-expect-error': 'off' },
59
- ],
60
- },
61
- },
62
- ],
63
- });
package/eslint/react.cjs DELETED
@@ -1,21 +0,0 @@
1
- // @ts-check
2
- const { defineConfig } = require('eslint-define-config');
3
-
4
- module.exports = defineConfig({
5
- extends: [
6
- 'plugin:react/recommended',
7
- 'plugin:react-hooks/recommended',
8
- 'plugin:import/react',
9
- 'prettier',
10
- require.resolve('./rules/react.cjs'),
11
- ],
12
- settings: {
13
- react: {
14
- version: 'detect',
15
- },
16
- linkComponents: [
17
- // Components used as alternatives to <a> for linking, eg. <Link to={ url } />
18
- 'Link',
19
- ],
20
- },
21
- });
@@ -1,84 +0,0 @@
1
- // @ts-check
2
- const { defineConfig } = require('eslint-define-config');
3
-
4
- module.exports = defineConfig({
5
- rules: {
6
- // prefer arrow functions for callbacks
7
- 'prefer-arrow-callback': [
8
- 'warn',
9
- {
10
- allowNamedFunctions: true,
11
- allowUnboundThis: true,
12
- },
13
- ],
14
- // console logs other than error and warn are only needed for debugging, so they should be removed before merging
15
- 'no-console': [
16
- 'error',
17
- {
18
- allow: [
19
- 'warn',
20
- 'error',
21
- 'clear',
22
- // sometimes we really need to log something, even in production, so info is allowed in edge cases
23
- 'info',
24
- ],
25
- },
26
- ],
27
- 'max-depth': ['warn', 4],
28
- // some globals, like "name" might be similar to the name of a variable, so we prevent them from being used
29
- 'no-restricted-globals': ['error', 'event', 'name'],
30
- // Require curly braces for multiline blocks.
31
- curly: ['warn', 'multi-line'],
32
- // Require default clauses in switch statements to be last (if used).
33
- 'default-case-last': 'error',
34
- // Require triple equals (`===` and `!==`).
35
- eqeqeq: 'error',
36
- // disallow the use of `alert()`
37
- 'no-alert': 'error',
38
- // Disallow renaming import, export, and destructured assignments to the same name.
39
- 'no-useless-rename': 'warn',
40
- // Require `let` or `const` instead of `var`.
41
- 'no-var': 'error',
42
- // Require object literal shorthand syntax.
43
- 'object-shorthand': 'warn',
44
- // Require default to `const` instead of `let`.
45
- 'prefer-const': 'warn',
46
- // Require rest parameters instead of `arguments`.
47
- 'prefer-rest-params': 'error',
48
- // Require spread syntax instead of `.apply()`.
49
- 'prefer-spread': 'error',
50
- // Require template literals instead of string concatenation.
51
- 'prefer-template': 'warn',
52
- // Disallow returning values from Promise executor functions.
53
- 'no-promise-executor-return': 'error',
54
- // Disallow loops with a body that allows only one iteration.
55
- 'no-unreachable-loop': 'error',
56
- // Require a capital letter for constructors.
57
- 'new-cap': ['error', { capIsNew: false }],
58
- // Disallow the omission of parentheses when invoking a constructor with no arguments.
59
- 'new-parens': 'warn',
60
- // Disallow if as the only statement in an else block.
61
- 'no-lonely-if': 'warn',
62
- // Disallow ternary operators when simpler alternatives exist.
63
- 'no-unneeded-ternary': 'error',
64
- // Require use of an object spread over Object.assign.
65
- 'prefer-object-spread': 'warn',
66
- // Disallow labels that share a name with a variable.
67
- 'no-label-var': 'error',
68
- // Disallow initializing variables to `undefined`.
69
- 'no-undef-init': 'warn',
70
- // Disallow unused variables.
71
- 'no-unused-vars': [
72
- 'error',
73
- {
74
- args: 'after-used',
75
- argsIgnorePattern: '^_',
76
- ignoreRestSiblings: false,
77
- vars: 'all',
78
- varsIgnorePattern: '^_',
79
- },
80
- ],
81
- // Disallow expressions where the operation doesn't affect the value
82
- 'no-constant-binary-expression': 'error',
83
- },
84
- });
@@ -1,39 +0,0 @@
1
- // @ts-check
2
- const { defineConfig } = require('eslint-define-config');
3
-
4
- module.exports = defineConfig({
5
- rules: {
6
- // Disallow non-import statements appearing before import statements.
7
- 'import/first': 'error',
8
- // Require a newline after the last import/require.
9
- 'import/newline-after-import': 'warn',
10
- // Disallow a module from importing itself.
11
- 'import/no-self-import': 'error',
12
- // Ensures that there are no useless path segments.
13
- 'import/no-useless-path-segments': ['error'],
14
- // Enforce a module import order convention.
15
- 'import/order': [
16
- 'warn',
17
- {
18
- groups: [
19
- 'builtin', // Node.js built-in modules
20
- 'external', // Packages
21
- 'internal', // Aliased modules
22
- 'parent', // Relative parent
23
- 'sibling', // Relative sibling
24
- 'index', // Relative index
25
- ],
26
- },
27
- ],
28
- // allow default exports
29
- 'import/no-default-export': 'off',
30
- /**
31
- * These are enabled by `import/recommended`, but are better handled by
32
- * TypeScript and @typescript-eslint.
33
- */
34
- 'import/default': 'off',
35
- 'import/export': 'off',
36
- 'import/namespace': 'off',
37
- 'import/no-unresolved': 'off',
38
- },
39
- });
@@ -1,90 +0,0 @@
1
- // @ts-check
2
- const { defineConfig } = require('eslint-define-config');
3
-
4
- module.exports = defineConfig({
5
- rules: {
6
- // We should prefer TypeScript over prop-types.
7
- 'react/prop-types': 'off',
8
- // Disable requiring React to be imported, as this is no longer required.
9
- 'react/react-in-jsx-scope': 'off',
10
- /**
11
- * Require an explicit type when using button elements. Prevents common mistakes where `type="button"` is omitted on `<button>` elements.
12
- */
13
- 'react/button-has-type': 'error',
14
- /**
15
- * Require consistent function type for function components.
16
- */
17
- 'react/function-component-definition': 'warn',
18
- /**
19
- * Require destructuring and symmetric naming of `useState` hook value and setter variables.
20
- */
21
- 'react/hook-use-state': 'warn',
22
- /**
23
- * Require consistent boolean attributes notation in JSX.
24
- */
25
- 'react/jsx-boolean-value': 'warn',
26
- /**
27
- * Disallow unnecessary curly braces in JSX props and children.
28
- */
29
- 'react/jsx-curly-brace-presence': [
30
- 'warn',
31
- {
32
- props: 'never',
33
- children: 'ignore',
34
- propElementValues: 'always',
35
- },
36
- ],
37
- /**
38
- * Require using shorthand form for React fragments, unless required.
39
- */
40
- 'react/jsx-fragments': 'warn',
41
- /**
42
- * Prevent problematic leaked values from being rendered.
43
- */
44
- 'react/jsx-no-leaked-render': 'error',
45
- /**
46
- * Prevents usage of unsafe `target='_blank'`.
47
- *
48
- * This rule is a part of `react/recommended`, but modified to
49
- * enable allowReferrer.
50
- */
51
- 'react/jsx-no-target-blank': [
52
- 'error',
53
- {
54
- allowReferrer: true,
55
- },
56
- ],
57
- /**
58
- * Disallow empty React fragments.
59
- */
60
- 'react/jsx-no-useless-fragment': ['warn', { allowExpressions: true }],
61
- /**
62
- * Require the use of PascalCase for user-defined JSX components.
63
- */
64
- 'react/jsx-pascal-case': 'warn',
65
- /**
66
- * Require props to be sorted alphabetically.
67
- */
68
- 'react/jsx-sort-props': [
69
- 'warn',
70
- {
71
- // list callbacks after all other props
72
- callbacksLast: true,
73
- },
74
- ],
75
- /**
76
- * Disallow creating unstable components inside components.
77
- */
78
- 'react/no-unstable-nested-components': 'error',
79
- /**
80
- * Disallow closing tags for components without children.
81
- */
82
- 'react/self-closing-comp': 'warn',
83
- /**
84
- * Enforce exhaustive dependencies in `useEffect` and `useCallback` hooks.
85
- */
86
- 'react-hooks/exhaustive-deps': 'error',
87
- // prefer destructuring props
88
- 'react/destructuring-assignment': ['warn', 'always'],
89
- },
90
- });
@@ -1,108 +0,0 @@
1
- const { defineConfig } = require('eslint-define-config');
2
- const noUnusedVarsConfig = require('./base.cjs').rules['no-unused-vars'];
3
-
4
- // @ts-check
5
- module.exports = defineConfig({
6
- rules: {
7
- // Require consistent usage of type exports.
8
- '@typescript-eslint/consistent-type-exports': [
9
- 'error',
10
- { fixMixedExportsWithInlineTypeSpecifier: true },
11
- ],
12
- // Require consistent usage of type imports.
13
- '@typescript-eslint/consistent-type-imports': [
14
- 'error',
15
- {
16
- prefer: 'type-imports',
17
- fixStyle: 'inline-type-imports',
18
- },
19
- ],
20
- /**
21
- * Require using function property types in method signatures.
22
- * These have enhanced typechecking, whereas method signatures do not.
23
- */
24
- '@typescript-eslint/method-signature-style': 'error',
25
- /**
26
- * Require consistent naming conventions.
27
- * Improves IntelliSense suggestions and avoids name collisions.
28
- */
29
- '@typescript-eslint/naming-convention': [
30
- 'error',
31
- // Anything type-like should be written in PascalCase.
32
- {
33
- format: ['PascalCase'],
34
- selector: ['typeLike', 'enumMember'],
35
- },
36
- // Interfaces cannot be prefixed with `I`, or have restricted names.
37
- {
38
- custom: {
39
- match: false,
40
- regex: '^I[A-Z]|^(Interface|Props|State)$',
41
- },
42
- format: ['PascalCase'],
43
- selector: 'interface',
44
- },
45
- ],
46
- // Disallow members of unions and intersections that do nothing or override type information.
47
- '@typescript-eslint/no-redundant-type-constituents': 'error',
48
- // Require using `RegExp.exec()` over `String.match()` for consistency.
49
- '@typescript-eslint/prefer-regexp-exec': 'warn',
50
- //
51
- '@typescript-eslint/require-array-sort-compare': [
52
- 'error',
53
- { ignoreStringArrays: true },
54
- ],
55
- /**
56
- * Require exhaustive checks when using union types in switch statements.
57
- * This ensures cases are considered when items are later added to a union.
58
- */
59
- '@typescript-eslint/switch-exhaustiveness-check': 'error',
60
- // Require default parameters to be last.
61
- 'default-param-last': 'off',
62
- '@typescript-eslint/default-param-last': 'error',
63
- // Disallow creation of functions within loops.
64
- 'no-loop-func': 'off',
65
- '@typescript-eslint/no-loop-func': 'error',
66
- // Disallow unused variables.
67
- 'no-unused-vars': 'off',
68
- '@typescript-eslint/no-unused-vars': noUnusedVarsConfig,
69
-
70
- /**
71
- * These are enabled by `import/recommended`, but are better handled by
72
- * TypeScript and @typescript-eslint.
73
- */
74
- 'import/default': 'off',
75
- 'import/export': 'off',
76
- 'import/namespace': 'off',
77
- 'import/no-unresolved': 'off',
78
-
79
- // This is disabled as I feel that checking empty strings is a valid use
80
- // of `||` over `??`.
81
- '@typescript-eslint/prefer-nullish-coalescing': 'off',
82
-
83
- // Disallow Promises in places not designed to handle them.
84
- '@typescript-eslint/no-misused-promises': [
85
- 'error',
86
- {
87
- // Disabled as I feel that passing a async callback to a function expecting
88
- // a void callback is a valid use case.
89
- // e.g. fn.on('event', async () => {})
90
- // Strictly requiring to return 'undefined' does not have a functional benefit,
91
- // but makes the code more verbose.
92
- checksVoidReturn: false,
93
- },
94
- ],
95
- // While I'd prefer using types over interfaces, there are a lot of cases
96
- // where interfaces are needed, e.g. merging declarations and this rule is too strict.
97
- '@typescript-eslint/consistent-type-definitions': 'off',
98
- // while using property signatures provides better typechecking,
99
- // method signatures provide better hinting in the editor.
100
- // (different color for methods vs properties)
101
- '@typescript-eslint/method-signature-style': 'off',
102
- // There are cases where using the index signature is more descriptive
103
- // as the index can be named
104
- '@typescript-eslint/consistent-indexed-object-style': 'off',
105
- // Enforce using a top-level type qualifier for imports when only types are imported using the inline type qualifier
106
- '@typescript-eslint/no-import-type-side-effects': 'error',
107
- },
108
- });
@@ -1,129 +0,0 @@
1
- // @ts-check
2
- const { defineConfig } = require('eslint-define-config');
3
-
4
- module.exports = defineConfig({
5
- plugins: ['unicorn'],
6
- rules: {
7
- // Use destructured variables over properties.
8
- 'unicorn/consistent-destructuring': 'warn',
9
- // Move function definitions to the highest possible scope.
10
- 'unicorn/consistent-function-scoping': [
11
- 'warn',
12
- {
13
- checkArrowFunctions: false,
14
- },
15
- ],
16
- // Enforce passing a message value when creating a built-in error.
17
- 'unicorn/error-message': 'error',
18
- // Require escape sequences to use uppercase values.
19
- 'unicorn/escape-case': 'warn',
20
- // Disallow empty files.
21
- 'unicorn/no-empty-file': 'error',
22
- // Do not use a for loop that can be replaced with a for-of loop.
23
- 'unicorn/no-for-loop': 'warn',
24
- // Require Array.isArray() instead of instanceof Array.
25
- 'unicorn/no-instanceof-array': 'error',
26
- // Prevent calling EventTarget#removeEventListener() with the result of an expression.
27
- 'unicorn/no-invalid-remove-event-listener': 'error',
28
- // Disallow the use of objects as default parameters.
29
- 'unicorn/no-object-as-default-parameter': 'error',
30
- // Disallow then property.
31
- 'unicorn/no-thenable': 'error',
32
- // Disallow awaiting non-promise values.
33
- 'unicorn/no-unnecessary-await': 'error',
34
- // Disallow unreadable IIFEs.
35
- 'unicorn/no-unreadable-iife': 'warn',
36
- // Disallow useless fallback when spreading in object literals.
37
- 'unicorn/no-useless-fallback-in-spread': 'warn',
38
- // Disallow useless array length check.
39
- 'unicorn/no-useless-length-check': 'warn',
40
- // Disallow returning/yielding Promise.resolve/reject() in async functions or promise callbacks
41
- 'unicorn/no-useless-promise-resolve-reject': 'error',
42
- // Disallow unnecessary spread.
43
- 'unicorn/no-useless-spread': 'error',
44
- // Disallow number literals with zero fractions or dangling dots.
45
- 'unicorn/no-zero-fractions': 'warn',
46
- // Enforce the style of numeric separators by correctly grouping digits.
47
- 'unicorn/numeric-separators-style': 'error',
48
- // Prefer .addEventListener() and .removeEventListener() over on-functions.
49
- 'unicorn/prefer-add-event-listener': 'error',
50
- // Prefer .find(…) and .findLast(…) over the first or last element from .filter(…).
51
- 'unicorn/prefer-array-find': 'error',
52
- // Prefer Array#flat() over legacy techniques to flatten arrays.
53
- 'unicorn/prefer-array-flat': 'error',
54
- // Prefer .flatMap(…) over .map(…).flat().
55
- 'unicorn/prefer-array-flat-map': 'error',
56
- // Prefer Array#{indexOf,lastIndexOf}() over Array#{findIndex,findLastIndex}() when looking for the index of an item.
57
- 'unicorn/prefer-array-index-of': 'error',
58
- // Prefer .some(…) over .filter(…).length check and .{find,findLast}(…).
59
- 'unicorn/prefer-array-some': 'error',
60
- // Prefer .at() method for index access and String#charAt().
61
- 'unicorn/prefer-at': 'error',
62
- // Prefer Blob#arrayBuffer() over FileReader#readAsArrayBuffer(…) and Blob#text() over FileReader#readAsText(…).
63
- 'unicorn/prefer-blob-reading-methods': 'error',
64
- // Prefer Date.now() to get the number of milliseconds since the Unix Epoch.
65
- 'unicorn/prefer-date-now': 'error',
66
- // Prefer default parameters over reassignment.
67
- 'unicorn/prefer-default-parameters': 'warn',
68
- // Prefer Node#append() over Node#appendChild().
69
- 'unicorn/prefer-dom-node-append': 'error',
70
- // Prefer using .dataset on DOM elements over calling attribute methods.
71
- 'unicorn/prefer-dom-node-dataset': 'error',
72
- // Prefer childNode.remove() over parentNode.removeChild(childNode).
73
- 'unicorn/prefer-dom-node-remove': 'error',
74
- // Prefer EventTarget over EventEmitter.
75
- 'unicorn/prefer-event-target': 'warn',
76
- // Prefer export…from when re-exporting.
77
- 'unicorn/prefer-export-from': [
78
- 'warn',
79
- {
80
- ignoreUsedVariables: true,
81
- },
82
- ],
83
- // Prefer .includes() over .indexOf() and Array#some() when checking for existence or non-existence.
84
- 'unicorn/prefer-includes': 'error',
85
- // Prefer KeyboardEvent#key over KeyboardEvent#keyCode.
86
- 'unicorn/prefer-keyboard-event-key': 'error',
87
- // Prefer using a logical operator over a ternary.
88
- 'unicorn/prefer-logical-operator-over-ternary': 'error',
89
- // Enforce the use of Math.trunc instead of bitwise operators.
90
- 'unicorn/prefer-math-trunc': 'error',
91
- // Prefer .before() over .insertBefore(), .replaceWith() over .replaceChild(), prefer one of .before(), .after(), .append() or .prepend() over insertAdjacentText() and insertAdjacentElement().
92
- 'unicorn/prefer-modern-dom-apis': 'error',
93
- // Prefer modern Math APIs over legacy patterns.
94
- 'unicorn/prefer-modern-math-apis': 'error',
95
- // Prefer negative index over .length - index when possible.
96
- 'unicorn/prefer-negative-index': 'error',
97
- // Require using the `node:` protocol when importing Node.js built-in modules.
98
- 'unicorn/prefer-node-protocol': 'error',
99
- // Prefer Number static properties over global ones.
100
- 'unicorn/prefer-number-properties': ['error', { checkInfinity: false }],
101
- // Prefer using Object.fromEntries(…) to transform a list of key-value pairs into an object.
102
- 'unicorn/prefer-object-from-entries': 'error',
103
- // Prefer Reflect.apply() over Function#apply().
104
- 'unicorn/prefer-reflect-apply': 'error',
105
- // using RegExp.test() is faster than string.match()
106
- // note: you should not use the global flag /g with RegExp.test() though!
107
- 'unicorn/prefer-regexp-test': 'error',
108
- // Prefer Set#has() over Array#includes() when checking for existence or non-existence.
109
- 'unicorn/prefer-set-has': 'error',
110
- // Prefer using Set#size instead of Array#length.
111
- 'unicorn/prefer-set-size': 'error',
112
- // Prefer the spread operator over Array.from().
113
- 'unicorn/prefer-spread': 'error',
114
- // Prefer String#replaceAll() over regex searches with the global flag.
115
- 'unicorn/prefer-string-replace-all': 'error',
116
- // Prefer String#slice() over String#substr() and String#substring().
117
- 'unicorn/prefer-string-slice': 'error',
118
- // Prefer String#startsWith() & String#endsWith() over RegExp#test().
119
- 'unicorn/prefer-string-starts-ends-with': 'error',
120
- // Prefer ternary expressions over simple if-else statements.
121
- 'unicorn/prefer-ternary': ['warn', 'only-single-line'],
122
- // Prefer top-level await over top-level promises and async function calls.
123
- 'unicorn/prefer-top-level-await': 'error',
124
- // Enforce using the separator argument with Array#join().
125
- 'unicorn/require-array-join-separator': 'error',
126
- // Enforce consistent brace style for case clauses.
127
- 'unicorn/switch-case-braces': 'error',
128
- },
129
- });