@mikey-pro/eslint-config-react 7.5.3 → 8.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.
Files changed (2) hide show
  1. package/index.js +251 -64
  2. package/package.json +35 -4
package/index.js CHANGED
@@ -1,69 +1,256 @@
1
- const baseConfig = require('@mikey-pro/eslint-config');
1
+ import baseConfig from '@mikey-pro/eslint-config';
2
+ import reactAppConfig from 'eslint-config-react-app-bump';
3
+ import jestDomPlugin from 'eslint-plugin-jest-dom';
4
+ import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
5
+ import reactPlugin from 'eslint-plugin-react';
6
+ import reactHooksPlugin from 'eslint-plugin-react-hooks';
7
+ import reactPerformancePlugin from 'eslint-plugin-react-perf';
8
+ import reactRefreshPlugin from 'eslint-plugin-react-refresh';
9
+ import testingLibraryPlugin from 'eslint-plugin-testing-library';
2
10
 
3
- module.exports = {
11
+ const reactConfig = [
4
12
  ...baseConfig,
5
- extends: [...baseConfig.extends, 'react-app-bump'],
6
- parserOptions: {
7
- ...baseConfig.parserOptions,
8
- babelOptions: {
9
- ...baseConfig.parserOptions.babelOptions,
10
- presets: [
11
- ...baseConfig.parserOptions.babelOptions.presets,
12
- [
13
- '@babel/preset-react',
14
- {
15
- runtime: 'automatic',
16
- },
17
- ],
18
- ],
13
+ {
14
+ plugins: {
15
+ react: reactPlugin,
16
+ 'react-hooks': reactHooksPlugin,
17
+ 'jsx-a11y': jsxA11yPlugin,
18
+ 'react-perf': reactPerformancePlugin,
19
+ 'testing-library': testingLibraryPlugin,
20
+ 'jest-dom': jestDomPlugin,
21
+ 'react-refresh': reactRefreshPlugin,
19
22
  },
20
- },
21
- rules: {
22
- ...baseConfig.rules,
23
- 'react/display-name': ['warn', { ignoreTranspilerName: false }],
24
- 'react/function-component-definition': [
25
- 'off',
26
- { namedComponents: 'arrow-function' },
27
- ],
28
- 'react/jsx-curly-spacing': 'warn',
29
- 'react/jsx-key': ['warn', { checkFragmentShorthand: true }],
30
- 'react/jsx-no-bind': [
31
- 'warn',
32
- {
33
- allowArrowFunctions: true,
34
- allowFunctions: true,
35
- ignoreRefs: true,
36
- },
37
- ],
38
- 'react/jsx-no-comment-textnodes': 'warn',
39
- 'react/jsx-no-duplicate-props': 'warn',
40
- 'react/jsx-no-target-blank': 'warn',
41
- 'react/jsx-no-undef': 'warn',
42
- 'react/jsx-tag-spacing': ['warn', { beforeSelfClosing: 'always' }],
43
- 'react/jsx-uses-vars': 'warn',
44
- 'react/no-danger': 'warn',
45
- 'react/no-deprecated': 'warn',
46
- 'react/no-did-mount-set-state': 'warn',
47
- 'react/no-did-update-set-state': 'warn',
48
- 'react/no-find-dom-node': 'warn',
49
- 'react/no-is-mounted': 'warn',
50
- 'react/no-string-refs': 'warn',
51
- 'react/prefer-es6-class': 'warn',
52
- 'react/prefer-stateless-function': 'warn',
53
- 'react/require-render-return': 'warn',
54
- 'react/self-closing-comp': [
55
- 'error',
56
- {
57
- component: true,
58
- html: false,
23
+ languageOptions: {
24
+ parserOptions: {
25
+ ...baseConfig.languageOptions.parserOptions,
26
+ ecmaVersion: 'latest',
27
+ sourceType: 'module',
28
+ ecmaFeatures: {
29
+ jsx: true,
30
+ },
31
+ babelOptions: {
32
+ ...baseConfig.languageOptions.parserOptions.babelOptions,
33
+ presets: [
34
+ ...baseConfig.languageOptions.parserOptions.babelOptions.presets,
35
+ [
36
+ '@babel/preset-react',
37
+ {
38
+ runtime: 'automatic',
39
+ },
40
+ ],
41
+ ],
42
+ },
59
43
  },
60
- ],
61
- 'react/state-in-constructor': 'off',
62
- },
63
- settings: {
64
- ...baseConfig.settings,
65
- react: {
66
- version: 'detect',
67
44
  },
68
- },
69
- };
45
+ rules: {
46
+ ...reactAppConfig.rules,
47
+ ...baseConfig.rules,
48
+
49
+ // Hooks
50
+ 'react-hooks/rules-of-hooks': 'error',
51
+ 'react-hooks/exhaustive-deps': [
52
+ 'error',
53
+ {
54
+ additionalHooks: '(useAsync|useAsyncCallback|useMemoizedCallback)',
55
+ enableDangerousAutofixThisMayCauseInfiniteLoops: false,
56
+ },
57
+ ],
58
+
59
+ // Accessibility
60
+ 'jsx-a11y/alt-text': 'error',
61
+ 'jsx-a11y/anchor-has-content': 'error',
62
+ 'jsx-a11y/aria-props': 'error',
63
+ 'jsx-a11y/aria-role': 'error',
64
+ 'jsx-a11y/role-has-required-aria-props': 'error',
65
+
66
+ // Modern React
67
+ 'react/hook-use-state': 'warn',
68
+ 'react/jsx-no-leaked-render': 'warn',
69
+ 'react/jsx-no-useless-fragment': 'warn',
70
+ 'react/no-array-index-key': 'warn',
71
+
72
+ // Performance
73
+ 'react-perf/jsx-no-new-object-as-prop': 'warn',
74
+ 'react-perf/jsx-no-new-array-as-prop': 'warn',
75
+ 'react-perf/jsx-no-new-function-as-prop': 'warn',
76
+
77
+ // Modern Patterns
78
+ 'react/prefer-stateless-function': 'error',
79
+ 'react/no-unused-prop-types': 'warn',
80
+ 'react/no-unstable-nested-components': ['error', {
81
+ allowAsProps: true,
82
+ customValidators: ['memo', 'Suspense'],
83
+ }],
84
+ 'react/jsx-no-constructed-context-values': ['error', {
85
+ allowObjectLiteral: false,
86
+ checkFragmentShorthand: true,
87
+ }],
88
+
89
+ // Existing rules...
90
+ 'react/display-name': ['warn', { ignoreTranspilerName: false }],
91
+ 'react/function-component-definition': [
92
+ 'off',
93
+ { namedComponents: 'arrow-function' },
94
+ ],
95
+ 'react/jsx-curly-spacing': 'warn',
96
+ 'react/jsx-key': ['warn', { checkFragmentShorthand: true }],
97
+ 'react/jsx-no-bind': [
98
+ 'error',
99
+ {
100
+ allowArrowFunctions: true,
101
+ allowFunctions: false,
102
+ allowBind: false,
103
+ },
104
+ ],
105
+ 'react/jsx-no-comment-textnodes': 'warn',
106
+ 'react/jsx-no-duplicate-props': 'warn',
107
+ 'react/jsx-no-target-blank': 'warn',
108
+ 'react/jsx-no-undef': 'warn',
109
+ 'react/jsx-tag-spacing': ['warn', { beforeSelfClosing: 'always' }],
110
+ 'react/jsx-uses-vars': 'warn',
111
+ 'react/no-danger': 'warn',
112
+ 'react/no-deprecated': 'warn',
113
+ 'react/no-did-mount-set-state': 'warn',
114
+ 'react/no-did-update-set-state': 'warn',
115
+ 'react/no-find-dom-node': 'warn',
116
+ 'react/no-is-mounted': 'warn',
117
+ 'react/no-string-refs': 'warn',
118
+ 'react/prefer-es6-class': 'warn',
119
+ 'react/prefer-stateless-function': 'warn',
120
+ 'react/require-render-return': 'warn',
121
+ 'react/self-closing-comp': [
122
+ 'error',
123
+ {
124
+ component: true,
125
+ html: false,
126
+ },
127
+ ],
128
+ 'react/state-in-constructor': 'off',
129
+
130
+ // Testing Library
131
+ 'testing-library/await-async-queries': 'error',
132
+ 'testing-library/no-await-sync-queries': 'error',
133
+ 'testing-library/prefer-screen-queries': 'warn',
134
+ 'testing-library/no-node-access': 'error',
135
+ 'testing-library/prefer-presence-queries': 'error',
136
+ 'testing-library/prefer-user-event': 'error',
137
+ 'testing-library/prefer-query-by-disappearance': 'error',
138
+ 'testing-library/prefer-find-by': 'error',
139
+
140
+ // Testing Best Practices
141
+ 'jest-dom/prefer-checked': 'error',
142
+ 'jest-dom/prefer-in-document': 'error',
143
+ 'jest-dom/prefer-to-have-attribute': 'error',
144
+ 'jest-dom/prefer-enabled-disabled': 'error',
145
+ 'jest-dom/prefer-required': 'error',
146
+
147
+ // Additional Performance
148
+ 'react/no-object-type-as-default-prop': 'warn',
149
+ 'react-perf/jsx-no-jsx-as-prop': ['error', {
150
+ allowDestructuredProps: true,
151
+ }],
152
+
153
+ // Advanced React Performance
154
+ 'react/jsx-handler-names': ['warn', {
155
+ eventHandlerPrefix: 'handle',
156
+ eventHandlerPropPrefix: 'on',
157
+ checkLocalVariables: true,
158
+ }],
159
+ 'react/jsx-no-script-url': 'error',
160
+ 'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }],
161
+
162
+ // React 18+ Features
163
+ 'react/prefer-read-only-props': 'warn',
164
+ 'react/no-unused-class-component-methods': 'error',
165
+ 'react/no-arrow-function-lifecycle': 'error',
166
+
167
+ // Testing Best Practices
168
+ 'testing-library/no-container': 'error',
169
+ 'testing-library/no-node-access': ['error', { allowContainerFirstChild: true }],
170
+ 'testing-library/prefer-explicit-assert': 'warn',
171
+ 'jest-dom/prefer-focus': 'error',
172
+
173
+ // React 18.2+ Features
174
+ 'react/hook-use-state': ['error', {
175
+ allowDestructuredState: true,
176
+ allowStatefulImports: false
177
+ }],
178
+ 'react/no-unstable-default-props': ['error', {
179
+ forbidAllDefaults: true
180
+ }],
181
+ 'react/prefer-exact-props': 'error',
182
+
183
+ // Enhanced Testing
184
+ 'testing-library/no-render-in-lifecycle': 'error',
185
+ 'testing-library/prefer-wait-for': 'error',
186
+ 'testing-library/no-wait-for-empty-callback': 'error',
187
+
188
+ // Performance Guards
189
+ 'react/forbid-dom-props': ['error', { forbid: ['style'] }],
190
+ 'react-perf/jsx-no-new-function-as-prop': ['error', {
191
+ allowInlineArrows: true,
192
+ allowMethods: true
193
+ }],
194
+ 'react/jsx-no-constructed-context-values': ['error', {
195
+ allowObjectLiteral: false
196
+ }],
197
+
198
+ // Strict Mode Optimizations
199
+ 'react/jsx-no-leaked-render': ['error', {
200
+ validStrategies: ['ternary', 'coalescing', 'suspense']
201
+ }],
202
+
203
+ // Advanced Testing
204
+ 'testing-library/prefer-user-event': ['error', {
205
+ allowedMethods: ['type', 'click', 'keyboard', 'upload']
206
+ }],
207
+ 'testing-library/render-result-naming-convention': ['error', {
208
+ prefix: 'render'
209
+ }],
210
+
211
+ // Concurrent Mode
212
+ 'react/no-unstable-nested-components': ['error', {
213
+ allowAsProps: true,
214
+ customValidators: ['memo', 'Suspense']
215
+ }],
216
+ 'react/jsx-no-constructed-context-values': ['error', {
217
+ allowObjectLiteral: false,
218
+ checkFragmentShorthand: true
219
+ }],
220
+
221
+ // Error Boundaries
222
+ 'react/no-unstable-default-props': ['error', {
223
+ forbidAllDefaults: true
224
+ }],
225
+ 'react/iframe-missing-sandbox': 'error',
226
+ 'react/jsx-no-leaked-render': ['error', {
227
+ validStrategies: ['ternary', 'coalescing', 'suspense']
228
+ }],
229
+
230
+ // Testing
231
+ 'testing-library/no-global-regexp-flag-in-query': 'error',
232
+ 'testing-library/prefer-query-matchers': 'error',
233
+ 'testing-library/no-manual-cleanup': 'error'
234
+ },
235
+ settings: {
236
+ ...baseConfig.settings,
237
+ react: {
238
+ version: 'detect',
239
+ formComponents: ['Form'],
240
+ linkComponents: ['Link', 'NavLink', 'RouterLink'],
241
+ 'import/resolver': {
242
+ typescript: {
243
+ alwaysTryTypes: true,
244
+ project: ['./tsconfig.json', './tsconfig.*.json']
245
+ }
246
+ },
247
+ 'jsx-runtime': 'automatic'
248
+ }
249
+ }
250
+ }
251
+ ];
252
+
253
+ export default reactConfig;
254
+ if (typeof module !== 'undefined' && module.exports) {
255
+ module.exports = reactConfig;
256
+ }
package/package.json CHANGED
@@ -1,11 +1,39 @@
1
1
  {
2
2
  "name": "@mikey-pro/eslint-config-react",
3
- "version": "7.5.3",
3
+ "version": "8.0.0",
4
4
  "description": "Mikey Pro ESLint React configuration",
5
+ "type": "module",
5
6
  "main": "index.js",
6
7
  "dependencies": {
7
- "@babel/preset-react": "^7.24.6",
8
- "eslint-config-react-app-bump": "^1.0.25"
8
+ "@babel/preset-react": "^7.26.3",
9
+ "@mikey-pro/eslint-config": "workspace:^",
10
+ "eslint-config-react-app-bump": "^1.0.25",
11
+ "eslint-plugin-jest-dom": "^5.1.0",
12
+ "eslint-plugin-jsx-a11y": "^6.8.0",
13
+ "eslint-plugin-react": "^7.33.2",
14
+ "eslint-plugin-react-hooks": "^4.6.0",
15
+ "eslint-plugin-react-perf": "^3.3.2",
16
+ "eslint-plugin-react-refresh": "^0.4.5",
17
+ "eslint-plugin-testing-library": "^6.2.0"
18
+ },
19
+ "peerDependencies": {
20
+ "@babel/core": "^7.24.0",
21
+ "@mikey-pro/eslint-config": "workspace:^",
22
+ "@types/react": "^18.2.0",
23
+ "@types/react-dom": "^18.2.0",
24
+ "eslint": "^8.57.0",
25
+ "prettier": "^3.2.0",
26
+ "react": "^18.2.0",
27
+ "react-dom": "^18.2.0",
28
+ "typescript": ">=5.0.0"
29
+ },
30
+ "peerDependenciesMeta": {
31
+ "@types/react": {
32
+ "optional": true
33
+ },
34
+ "@types/react-dom": {
35
+ "optional": true
36
+ }
9
37
  },
10
38
  "files": [
11
39
  "index.js",
@@ -38,5 +66,8 @@
38
66
  "author": "Mikl Wolfe <wolfe@mikl.io> (https://mikl.io)",
39
67
  "browserslist": [
40
68
  "defaults"
41
- ]
69
+ ],
70
+ "engines": {
71
+ "node": ">=18.0.0"
72
+ }
42
73
  }