@timobechtel/style 1.13.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
@@ -46,62 +46,114 @@ curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/temp
46
46
 
47
47
  ### Typescript
48
48
 
49
- ```bash
50
- curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/tsconfig/core/tsconfig.json
51
- ```
49
+ #### Existing tsconfig
52
50
 
53
- Or manually copy to `tsconfig.json`:
51
+ For existing projects or templates, I recomment leaving the config as-is and adding this preset to the extends array.
54
52
 
55
53
  ```json
56
- {
57
- "extends": "@timobechtel/style/tsconfig/core"
54
+ {
55
+ "extends": ["@timobechtel/style/tsconfig/core"]
58
56
  }
59
57
  ```
60
58
 
59
+ #### New tsconfig
60
+
61
+ ```bash
62
+ curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/tsconfig/core/tsconfig.json
63
+ ```
64
+
65
+ #### Expo
66
+
67
+ With expo make sure to add `"moduleResolution": "bundler"` to the `compilerOptions`, otherwise certain routing types might break.
68
+
69
+ <details>
70
+ <summary>Example</summary>
71
+
72
+ Copy to `tsconfig.json`:
73
+
74
+ ```json
75
+ {
76
+ "extends": ["expo/tsconfig.base", "@timobechtel/style/tsconfig/core"],
77
+ "compilerOptions": {
78
+ "moduleResolution": "bundler", // <-- this is important
79
+ "strict": true,
80
+ "paths": {
81
+ "@/*": [
82
+ "./*"
83
+ ]
84
+ }
85
+ },
86
+ "include": [
87
+ "**/*.ts",
88
+ "**/*.tsx",
89
+ ".expo/types/**/*.ts",
90
+ "expo-env.d.ts"
91
+ ]
92
+ }
93
+ ```
94
+
95
+ </details>
96
+
61
97
  #### Or with React
62
98
 
63
99
  ```bash
64
100
  curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/tsconfig/react/tsconfig.json
65
101
  ```
66
102
 
67
- Or manually copy to `tsconfig.json`:
103
+ <details>
104
+ <summary>Or manually</summary>
68
105
 
69
- ```json
70
- {
71
- "extends": "@timobechtel/style/tsconfig/react"
72
- }
73
- ```
106
+ Copy to `tsconfig.json`:
107
+
108
+ ```json
109
+ {
110
+ "extends": "@timobechtel/style/tsconfig/react"
111
+ }
112
+ ```
113
+
114
+ </details>
74
115
 
75
116
  ### Eslint
76
117
 
77
118
  ```bash
78
- curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/eslint/core/.eslintrc.cjs
119
+ curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/eslint/core/eslint.config.js
79
120
  ```
80
121
 
122
+ Note: If your project is not ESM (no `"type": "module"` in `package.json`), rename the file to `eslint.config.mjs`.
123
+
81
124
  <details>
82
125
  <summary>Or manually</summary>
83
126
 
84
- Copy the following to a `.eslintrc.cjs`:
127
+ Copy the following to an `eslint.config.js`:
85
128
 
86
129
  ```js
87
- const { resolve } = require('node:path');
88
-
89
- const project = resolve(process.cwd(), 'tsconfig.json');
90
-
91
- module.exports = {
92
- root: true,
93
- extends: [require.resolve('@timobechtel/style/eslint/core.cjs')],
94
- parserOptions: {
95
- tsconfigRootDir: process.cwd(),
96
- },
97
- settings: {
98
- 'import/resolver': {
99
- typescript: {
100
- 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,
101
145
  },
102
146
  },
147
+ settings: {
148
+ 'import-x/resolver-next': [
149
+ createTypeScriptImportResolver({
150
+ project: path.resolve(__dirname, 'tsconfig.json'),
151
+ }),
152
+ createNodeResolver(),
153
+ ],
154
+ },
103
155
  },
104
- };
156
+ ]);
105
157
  ```
106
158
 
107
159
  </details>
@@ -109,27 +161,50 @@ curl -O https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/temp
109
161
  #### React
110
162
 
111
163
  ```bash
112
- 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
113
165
  ```
114
166
 
115
- Or manually:
167
+ <details>
168
+ <summary>Or manually</summary>
169
+
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
+ ```
183
+
184
+ Example config:
185
+ <https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/eslint/react/eslint.config.js>
186
+ </details>
187
+
188
+ #### Migration from v1.x
116
189
 
117
- Also add `require.resolve('@timobechtel/style/eslint/react.cjs')` to the `extends` array.
190
+ If you're upgrading from v1.x, you'll need to:
118
191
 
119
- Example config:
120
- <https://raw.githubusercontent.com/TimoBechtel/style/refs/heads/main/templates/eslint/react/.eslintrc.cjs>
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/`
121
196
 
122
197
  #### VSCode
123
198
 
124
- 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.
125
200
 
126
201
  Add the following to your VSCode config, e.g. `.vscode/settings.json`
127
202
 
128
203
  ```json
129
204
  {
130
205
  "editor.codeActionsOnSave": {
131
- // use eslint import/order instead
132
- "source.organizeImports": "never"
206
+ // use eslint import-x/order instead
207
+ "source.sortImports": "never"
133
208
  }
134
209
  }
135
210
  ```
@@ -139,7 +214,7 @@ Add the following to your VSCode config, e.g. `.vscode/settings.json`
139
214
  This repo also contains a [semantic-release](https://github.com/semantic-release/semantic-release) configuration.
140
215
 
141
216
  ```bash
142
- npm i -D semantic-release
217
+ npm i -D semantic-release @semantic-release/changelog @semantic-release/git
143
218
  ```
144
219
 
145
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.13.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,29 +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
- "@typescript-eslint/eslint-plugin": "^8.19.1",
28
- "@typescript-eslint/parser": "^8.19.1",
29
- "eslint-config-prettier": "^9.1.0",
30
- "eslint-define-config": "^2.1.0",
31
- "eslint-import-resolver-typescript": "^3.7.0",
32
- "eslint-plugin-import": "^2.31.0",
33
- "eslint-plugin-no-template-curly-in-string-fix": "^1.0.4",
34
- "eslint-plugin-react": "^7.37.3",
35
- "eslint-plugin-react-hooks": "^5.1.0",
36
- "eslint-plugin-unicorn": "^56.0.1"
33
+ "@timobechtel/eslint-plugin-rules": "^1.0.0",
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"
37
42
  }
38
43
  }
package/eslint/core.cjs DELETED
@@ -1,62 +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
- require.resolve('./rules/base.cjs'),
11
- require.resolve('./rules/import.cjs'),
12
- require.resolve('./rules/unicorn.cjs'),
13
- ],
14
- parser: '@typescript-eslint/parser',
15
- plugins: [],
16
- env: {
17
- es2021: true,
18
- node: true,
19
- browser: true,
20
- },
21
- // Report unused `eslint-disable` comments.
22
- reportUnusedDisableDirectives: true,
23
- // Tell ESLint not to ignore dot-files, which are ignored by default.
24
- ignorePatterns: ['!.*.js'],
25
- // Global settings used by all overrides.
26
- settings: {
27
- // Use the Node resolver by default.
28
- 'import/resolver': { node: {} },
29
- },
30
- // Global parser options.
31
- parserOptions: {
32
- ecmaVersion: 2021,
33
- sourceType: 'module',
34
- projectService: true,
35
- },
36
- overrides: [
37
- {
38
- files: ['*.ts?(x)'],
39
- extends: [
40
- 'plugin:@typescript-eslint/recommended',
41
- 'plugin:@typescript-eslint/recommended-type-checked',
42
- 'plugin:@typescript-eslint/strict',
43
- 'plugin:@typescript-eslint/strict-type-checked',
44
- 'plugin:@typescript-eslint/stylistic',
45
- 'plugin:@typescript-eslint/stylistic-type-checked',
46
- 'plugin:import/typescript',
47
- 'prettier',
48
- require.resolve('./rules/typescript.cjs'),
49
- ],
50
- },
51
- {
52
- files: ['*.test.ts?(x)', '*.spec.ts?(x)'],
53
- rules: {
54
- // ts-expect-error makes sense for tests
55
- '@typescript-eslint/ban-ts-comment': [
56
- 'off',
57
- { 'ts-expect-error': 'off' },
58
- ],
59
- },
60
- },
61
- ],
62
- });
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
- });