@wistia/oxlint-config 0.3.5 → 0.4.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
@@ -23,11 +23,10 @@ Create an `oxlint.config.ts` in your project root:
23
23
 
24
24
  ```ts
25
25
  import { defineConfig } from 'oxlint';
26
- import { typescriptRules } from '@wistia/oxlint-config';
26
+ import { typescriptConfig, reactConfig } from '@wistia/oxlint-config';
27
27
 
28
28
  export default defineConfig({
29
- plugins: typescriptRules.plugins,
30
- rules: typescriptRules.rules,
29
+ extends: [typescriptConfig, reactConfig],
31
30
  });
32
31
  ```
33
32
 
@@ -35,106 +34,102 @@ export default defineConfig({
35
34
 
36
35
  ```ts
37
36
  import { defineConfig } from 'oxlint';
38
- import { baseRules, importRules, promiseRules } from '@wistia/oxlint-config';
37
+ import { javascriptConfig } from '@wistia/oxlint-config';
39
38
 
40
39
  export default defineConfig({
41
- plugins: [...baseRules.plugins, ...importRules.plugins, ...promiseRules.plugins],
42
- rules: {
43
- ...baseRules.rules,
44
- ...importRules.rules,
45
- ...promiseRules.rules,
46
- },
40
+ extends: [javascriptConfig],
47
41
  });
48
42
  ```
49
43
 
50
- ## Available rules
51
-
52
- ### Base rules
53
-
54
- | Export | Description |
55
- | ----------------- | ------------------------------------------------------------------------ |
56
- | `baseRules` | Core ESLint rules (correctness + suggestions) |
57
- | `importRules` | Import/export validation (native + eslint-plugin-import-x via jsPlugins) |
58
- | `promiseRules` | Promise handling rules |
59
- | `typescriptRules` | TypeScript-specific rules (disables superseded base rules) |
44
+ ## Available configs
60
45
 
61
- ### Feature rules
46
+ ### Base configs
62
47
 
63
- | Export | Description |
64
- | ----------------------- | ------------------------------------------------------------ |
65
- | `reactRules` | React component rules (native + @eslint-react via jsPlugins) |
66
- | `reactA11yRules` | JSX accessibility rules (jsx-a11y) |
67
- | `nodeRules` | Node.js rules (native + eslint-plugin-n via jsPlugins) |
68
- | `vitestRules` | Vitest testing rules (see note below) |
69
- | `playwrightRules` | Playwright E2E testing rules (via jsPlugins) |
70
- | `storybookRules` | Storybook story conventions (via jsPlugins) |
71
- | `testingLibraryRules` | Testing Library + jest-dom rules (via jsPlugins) |
72
- | `styledComponentsRules` | Styled Components accessibility rules (via jsPlugins) |
73
- | `filenamesRules` | Filename convention rules (via jsPlugins) |
74
- | `barrelFilesRules` | Barrel file rules (via jsPlugins) |
48
+ | Export | Description |
49
+ | ------------------ | ----------------------------------------------------------------------------- |
50
+ | `javascriptConfig` | Base config for any JS project (base + import + promise + barrel-files rules) |
51
+ | `typescriptConfig` | TypeScript projects (includes all JavaScript rules + TypeScript rules) |
75
52
 
76
- **Note on jsPlugins:** Some rule sets use oxlint's [jsPlugins](https://oxc.rs/docs/guide/usage/linter/js-plugins) feature to load ESLint plugins for rules that don't have native oxlint equivalents. These require the corresponding ESLint plugin to be installed. All jsPlugin dependencies are included in this package's `dependencies`.
53
+ ### Feature configs
77
54
 
78
- **Note on vitest:** Oxlint splits vitest-related rules across two internal plugin namespaces -- `vitest/` for vitest-specific rules and `jest/` for rules that originated in eslint-plugin-jest but apply equally to vitest. The `jest/` prefix is an oxlint naming convention; it does **not** mean Jest is required.
55
+ Feature configs wrap their rules in `overrides` with default file patterns, so they scope correctly when extended at the top level.
79
56
 
80
- ## Composing rules
57
+ | Export | Default file patterns | Description |
58
+ | ------------------------ | ----------------------------------------- | ------------------------------------- |
59
+ | `reactConfig` | all files (global) | React component + accessibility rules |
60
+ | `styledComponentsConfig` | all files (global) | Styled Components accessibility rules |
61
+ | `vitestConfig` | `**/*.{test,spec,vitest}.{ts,tsx,js,jsx}` | Vitest testing rules |
62
+ | `testingLibraryConfig` | `**/*.{test,spec,vitest}.{ts,tsx,js,jsx}` | Testing Library + jest-dom rules |
63
+ | `storybookConfig` | `**/*.stories.{ts,tsx,js,jsx}` | Storybook story conventions |
64
+ | `nodeConfig` | `**/*.{mts,mjs,cjs}` | Node.js-specific rules |
65
+ | `playwrightConfig` | `**/*.spec.{ts,tsx,js}`, `**/e2e/**/*.ts` | Playwright E2E testing rules |
81
66
 
82
- Rules are **composable** -- import what you need and spread into your config. Rule sets that include `jsPlugins` need those spread too:
67
+ **Composing configs:**
83
68
 
84
69
  ```ts
85
70
  import { defineConfig } from 'oxlint';
86
71
  import {
87
- baseRules,
88
- importRules,
89
- typescriptRules,
90
- reactRules,
91
- reactA11yRules,
92
- vitestRules,
72
+ typescriptConfig,
73
+ reactConfig,
74
+ styledComponentsConfig,
75
+ vitestConfig,
76
+ testingLibraryConfig,
77
+ storybookConfig,
78
+ nodeConfig,
93
79
  } from '@wistia/oxlint-config';
94
80
 
95
81
  export default defineConfig({
96
- plugins: [
97
- ...baseRules.plugins,
98
- ...importRules.plugins,
99
- ...typescriptRules.plugins,
100
- ...reactRules.plugins,
101
- ...reactA11yRules.plugins,
82
+ extends: [
83
+ typescriptConfig,
84
+ reactConfig,
85
+ styledComponentsConfig,
86
+ vitestConfig,
87
+ testingLibraryConfig,
88
+ storybookConfig,
89
+ nodeConfig,
102
90
  ],
103
- jsPlugins: [...(importRules.jsPlugins || []), ...(reactRules.jsPlugins || [])],
91
+ });
92
+ ```
93
+
94
+ ## Overriding rules
95
+
96
+ Add a `rules` section — your rules take precedence over the shared configs:
97
+
98
+ ```ts
99
+ export default defineConfig({
100
+ extends: [typescriptConfig, reactConfig],
104
101
  rules: {
105
- ...baseRules.rules,
106
- ...importRules.rules,
107
- ...typescriptRules.rules,
108
- ...reactRules.rules,
109
- ...reactA11yRules.rules,
102
+ 'typescript/no-explicit-any': 'off',
103
+ 'react/no-clone-element': 'off',
110
104
  },
105
+ });
106
+ ```
107
+
108
+ File-specific overrides use `overrides`. Note that `extends` is not supported inside `overrides` — use rule imports for those:
109
+
110
+ ```ts
111
+ import { defineConfig } from 'oxlint';
112
+ import { typescriptConfig, vitestRules } from '@wistia/oxlint-config';
113
+
114
+ export default defineConfig({
115
+ extends: [typescriptConfig],
111
116
  overrides: [
112
117
  {
113
- files: ['**/*.test.ts', '**/*.test.tsx'],
118
+ files: ['**/*.test.ts', '**/*.vitest.ts'],
114
119
  plugins: vitestRules.plugins,
115
120
  jsPlugins: vitestRules.jsPlugins,
116
- rules: vitestRules.rules,
121
+ rules: {
122
+ ...vitestRules.rules,
123
+ // project-specific overrides
124
+ 'jest/expect-expect': ['error', { assertFunctionNames: ['expect', 'expectValid'] }],
125
+ },
117
126
  },
118
127
  ],
119
128
  });
120
129
  ```
121
130
 
122
- ## Overriding rules
123
-
124
- Add overrides after the spread -- your rules take precedence:
125
-
126
- ```ts
127
- rules: {
128
- ...typescriptRules.rules,
129
- // override
130
- 'typescript/no-explicit-any': 'off',
131
- },
132
- ```
133
-
134
131
  ## Running oxlint
135
132
 
136
- Add a script to your `package.json`:
137
-
138
133
  ```json
139
134
  {
140
135
  "scripts": {
@@ -150,6 +145,7 @@ Add a script to your `package.json`:
150
145
  3. Person/team adding new rules handles upgrading consumers and fixing violations
151
146
  4. Rules should always be set to `error`, never `warn`
152
147
  5. Add short description of rule and link to rule documentation in code comments
148
+ 6. Never delete a rule — set it to `off` with a comment explaining why
153
149
 
154
150
  ## ESLint parity
155
151
 
package/configs/react.mjs CHANGED
@@ -4,6 +4,7 @@ import { reactA11yRules } from '../rules/react-a11y.mjs';
4
4
 
5
5
  export default defineConfig({
6
6
  plugins: [...reactRules.plugins, ...reactA11yRules.plugins],
7
+ jsPlugins: [...(reactRules.jsPlugins || [])],
7
8
  rules: {
8
9
  ...reactRules.rules,
9
10
  ...reactA11yRules.rules,
@@ -6,6 +6,10 @@ import { typescriptRules } from '../rules/typescript.mjs';
6
6
  import { barrelFilesRules } from '../rules/barrel-files.mjs';
7
7
 
8
8
  export default defineConfig({
9
+ options: {
10
+ typeAware: true,
11
+ typeCheck: true,
12
+ },
9
13
  plugins: [
10
14
  ...baseRules.plugins,
11
15
  ...importRules.plugins,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@wistia/oxlint-config",
3
- "version": "0.3.5",
3
+ "version": "0.4.1",
4
4
  "description": "Wistia's Oxlint configurations",
5
- "packageManager": "yarn@4.13.0",
5
+ "packageManager": "yarn@4.14.1",
6
6
  "type": "module",
7
7
  "files": [
8
8
  "configs",
@@ -34,29 +34,33 @@
34
34
  "validate": "vitest run test/validate-configs.test.mjs"
35
35
  },
36
36
  "peerDependencies": {
37
- "oxlint": ">= 1.0.0"
37
+ "oxlint": ">= 1.0.0",
38
+ "oxlint-tsgolint": ">= 1.0.0"
38
39
  },
39
40
  "dependencies": {
40
- "@vitest/eslint-plugin": "^1.6.14",
41
+ "@eslint-react/eslint-plugin": "^4.2.3",
42
+ "@vitest/eslint-plugin": "^1.6.16",
41
43
  "eslint-plugin-barrel-files": "^3.0.1",
42
44
  "eslint-plugin-import-x": "^4.16.2",
43
45
  "eslint-plugin-jest-dom": "^5.5.0",
44
46
  "eslint-plugin-n": "^17.24.0",
45
47
  "eslint-plugin-no-only-tests": "^3.3.0",
46
- "eslint-plugin-playwright": "^2.10.1",
47
- "eslint-plugin-storybook": "^10.3.4",
48
+ "eslint-plugin-playwright": "^2.10.2",
49
+ "eslint-plugin-storybook": "^10.3.5",
48
50
  "eslint-plugin-styled-components": "^0.0.0",
49
51
  "eslint-plugin-styled-components-a11y": "^2.2.1",
50
- "eslint-plugin-testing-library": "^7.16.2"
52
+ "eslint-plugin-testing-library": "^7.16.2",
53
+ "typescript": "^6.0.2"
51
54
  },
52
55
  "devDependencies": {
53
56
  "@changesets/changelog-github": "^0.6.0",
54
- "@changesets/cli": "^2.30.0",
55
- "eslint": "^10.2.0",
56
- "oxfmt": "^0.43.0",
57
- "oxlint": "^1.58.0",
58
- "storybook": "^10.3.4",
59
- "vitest": "^4.1.2"
57
+ "@changesets/cli": "^2.31.0",
58
+ "eslint": "^10.2.1",
59
+ "oxfmt": "^0.46.0",
60
+ "oxlint": "^1.61.0",
61
+ "oxlint-tsgolint": "^0.21.1",
62
+ "storybook": "^10.3.5",
63
+ "vitest": "^4.1.5"
60
64
  },
61
65
  "engines": {
62
66
  "node": "^20.19.0 || ^22.13.0 || >=24 || >=26"
package/rules/react.mjs CHANGED
@@ -1,5 +1,8 @@
1
+ // Native oxlint react rules + @eslint-react/eslint-plugin via jsPlugins for rules
2
+ // without native equivalents.
1
3
  export const reactRules = {
2
4
  plugins: ['react'],
5
+ jsPlugins: [{ name: '@eslint-react', specifier: '@eslint-react/eslint-plugin' }],
3
6
  rules: {
4
7
  // -- React Core --
5
8
 
@@ -232,5 +235,379 @@ export const reactRules = {
232
235
  // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md
233
236
  // decision: stylistic choice best left for formatter
234
237
  'react/jsx-handler-names': 'off',
238
+
239
+ // -- Rules via jsPlugins (@eslint-react/eslint-plugin) --
240
+ // These rules have no native oxlint equivalent.
241
+
242
+ // --- Core React rules ---
243
+
244
+ // Validates higher order functions defining nested components or hooks
245
+ // https://eslint-react.xyz/docs/rules/component-hook-factories
246
+ '@eslint-react/component-hook-factories': 'error',
247
+
248
+ // Validates usage of Error Boundaries instead of try/catch for child errors
249
+ // https://eslint-react.xyz/docs/rules/error-boundaries
250
+ '@eslint-react/error-boundaries': 'error',
251
+
252
+ // Verify the list of the dependencies for Hooks like useEffect and similar
253
+ // https://eslint-react.xyz/docs/rules/exhaustive-deps
254
+ '@eslint-react/exhaustive-deps': 'error',
255
+
256
+ // Validates against mutating props, state, and other immutable values
257
+ // https://eslint-react.xyz/docs/rules/immutability
258
+ '@eslint-react/immutability': 'error',
259
+
260
+ // Prevent accidental JS comments from being injected into JSX as text
261
+ // https://eslint-react.xyz/docs/rules/jsx-no-comment-textnodes
262
+ '@eslint-react/jsx-no-comment-textnodes': 'error',
263
+
264
+ // Prevents unintentional '$' sign before expression in JSX
265
+ // https://eslint-react.xyz/docs/rules/jsx-no-leaked-dollar
266
+ '@eslint-react/jsx-no-leaked-dollar': 'error',
267
+
268
+ // Enforce key prop comes before spread to avoid overriding
269
+ // https://eslint-react.xyz/docs/rules/jsx-no-key-after-spread
270
+ '@eslint-react/jsx-no-key-after-spread': 'error',
271
+
272
+ // Prevent passing of children as props
273
+ // https://eslint-react.xyz/docs/rules/jsx-no-children-prop
274
+ '@eslint-react/jsx-no-children-prop': 'error',
275
+
276
+ // Prevent children prop used alongside children in JSX
277
+ // https://eslint-react.xyz/docs/rules/jsx-no-children-prop-with-children
278
+ '@eslint-react/jsx-no-children-prop-with-children': 'error',
279
+
280
+ // Disallow unnecessary fragments
281
+ // https://eslint-react.xyz/docs/rules/jsx-no-useless-fragment
282
+ '@eslint-react/jsx-no-useless-fragment': 'error',
283
+
284
+ // Prevent leaked semicolons in JSX
285
+ // https://eslint-react.xyz/docs/rules/jsx-no-leaked-semicolon
286
+ '@eslint-react/jsx-no-leaked-semicolon': 'error',
287
+
288
+ // Disallow namespace in JSX
289
+ // https://eslint-react.xyz/docs/rules/jsx-no-namespace
290
+ '@eslint-react/jsx-no-namespace': 'error',
291
+
292
+ // Prevent using this.state within a this.setState
293
+ // https://eslint-react.xyz/docs/rules/no-access-state-in-setstate
294
+ '@eslint-react/no-access-state-in-setstate': 'error',
295
+
296
+ // Prevent usage of Array index in keys
297
+ // https://eslint-react.xyz/docs/rules/no-array-index-key
298
+ '@eslint-react/no-array-index-key': 'error',
299
+
300
+ // Disallow usage of Children.count
301
+ // https://eslint-react.xyz/docs/rules/no-children-count
302
+ '@eslint-react/no-children-count': 'error',
303
+
304
+ // Disallow usage of Children.forEach
305
+ // https://eslint-react.xyz/docs/rules/no-children-for-each
306
+ '@eslint-react/no-children-for-each': 'error',
307
+
308
+ // Disallow usage of Children.map
309
+ // https://eslint-react.xyz/docs/rules/no-children-map
310
+ '@eslint-react/no-children-map': 'error',
311
+
312
+ // Disallow usage of Children.only
313
+ // https://eslint-react.xyz/docs/rules/no-children-only
314
+ '@eslint-react/no-children-only': 'error',
315
+
316
+ // Disallow usage of Children.toArray
317
+ // https://eslint-react.xyz/docs/rules/no-children-to-array
318
+ '@eslint-react/no-children-to-array': 'error',
319
+
320
+ // Disallow class components (except for error boundaries)
321
+ // https://eslint-react.xyz/docs/rules/no-class-component
322
+ '@eslint-react/no-class-component': 'error',
323
+
324
+ // Disallow usage of cloneElement
325
+ // https://eslint-react.xyz/docs/rules/no-clone-element
326
+ '@eslint-react/no-clone-element': 'error',
327
+
328
+ // Disallow usage of componentWillMount
329
+ // https://eslint-react.xyz/docs/rules/no-component-will-mount
330
+ '@eslint-react/no-component-will-mount': 'error',
331
+
332
+ // Disallow usage of componentWillReceiveProps
333
+ // https://eslint-react.xyz/docs/rules/no-component-will-receive-props
334
+ '@eslint-react/no-component-will-receive-props': 'error',
335
+
336
+ // Disallow usage of componentWillUpdate
337
+ // https://eslint-react.xyz/docs/rules/no-component-will-update
338
+ '@eslint-react/no-component-will-update': 'error',
339
+
340
+ // Disallow usage of legacy Context.Provider (React 19+)
341
+ // https://eslint-react.xyz/docs/rules/no-context-provider
342
+ '@eslint-react/no-context-provider': 'error',
343
+
344
+ // Disallow usage of createRef (prefer useRef)
345
+ // https://eslint-react.xyz/docs/rules/no-create-ref
346
+ '@eslint-react/no-create-ref': 'error',
347
+
348
+ // Prevent direct mutation of this.state
349
+ // https://eslint-react.xyz/docs/rules/no-direct-mutation-state
350
+ '@eslint-react/no-direct-mutation-state': 'error',
351
+
352
+ // Disallow duplicate keys in JSX arrays
353
+ // https://eslint-react.xyz/docs/rules/no-duplicate-key
354
+ '@eslint-react/no-duplicate-key': 'error',
355
+
356
+ // Disallow usage of forwardRef (React 19+ passes ref as prop)
357
+ // https://eslint-react.xyz/docs/rules/no-forward-ref
358
+ '@eslint-react/no-forward-ref': 'error',
359
+
360
+ // Prevent implicitly passing the children prop
361
+ // https://eslint-react.xyz/docs/rules/no-implicit-children
362
+ '@eslint-react/no-implicit-children': 'error',
363
+
364
+ // Prevent implicitly passing the key prop
365
+ // https://eslint-react.xyz/docs/rules/no-implicit-key
366
+ '@eslint-react/no-implicit-key': 'error',
367
+
368
+ // Prevent implicitly passing the ref prop
369
+ // https://eslint-react.xyz/docs/rules/no-implicit-ref
370
+ '@eslint-react/no-implicit-ref': 'error',
371
+
372
+ // Prevent problematic leaked values from being rendered
373
+ // https://eslint-react.xyz/docs/rules/no-leaked-conditional-rendering
374
+ '@eslint-react/no-leaked-conditional-rendering': 'error',
375
+
376
+ // Enforce that components have a displayName for DevTools
377
+ // https://eslint-react.xyz/docs/rules/no-missing-component-display-name
378
+ '@eslint-react/no-missing-component-display-name': 'off',
379
+
380
+ // Enforce that contexts have a displayName for DevTools
381
+ // https://eslint-react.xyz/docs/rules/no-missing-context-display-name
382
+ '@eslint-react/no-missing-context-display-name': 'off',
383
+
384
+ // Enforce that every JSX element in a list has a key prop
385
+ // https://eslint-react.xyz/docs/rules/no-missing-key
386
+ '@eslint-react/no-missing-key': 'error',
387
+
388
+ // Disallow misusing captureOwnerStack
389
+ // https://eslint-react.xyz/docs/rules/no-misused-capture-owner-stack
390
+ '@eslint-react/no-misused-capture-owner-stack': 'error',
391
+
392
+ // Prevent creating unstable components inside components
393
+ // https://eslint-react.xyz/docs/rules/no-nested-component-definitions
394
+ '@eslint-react/no-nested-component-definitions': 'error',
395
+
396
+ // Prevent creating lazy components inside components
397
+ // https://eslint-react.xyz/docs/rules/no-nested-lazy-component-declarations
398
+ '@eslint-react/no-nested-lazy-component-declarations': 'error',
399
+
400
+ // Prevent usage of shouldComponentUpdate when extending React.PureComponent
401
+ // https://eslint-react.xyz/docs/rules/no-redundant-should-component-update
402
+ '@eslint-react/no-redundant-should-component-update': 'error',
403
+
404
+ // Prevent usage of setState in componentDidMount
405
+ // https://eslint-react.xyz/docs/rules/no-set-state-in-component-did-mount
406
+ '@eslint-react/no-set-state-in-component-did-mount': 'error',
407
+
408
+ // Prevent usage of setState in componentDidUpdate
409
+ // https://eslint-react.xyz/docs/rules/no-set-state-in-component-did-update
410
+ '@eslint-react/no-set-state-in-component-did-update': 'error',
411
+
412
+ // Prevent usage of setState in componentWillUpdate
413
+ // https://eslint-react.xyz/docs/rules/no-set-state-in-component-will-update
414
+ '@eslint-react/no-set-state-in-component-will-update': 'error',
415
+
416
+ // Disallow unnecessary useCallback hooks
417
+ // https://eslint-react.xyz/docs/rules/no-unnecessary-use-callback
418
+ '@eslint-react/no-unnecessary-use-callback': 'error',
419
+
420
+ // Disallow unnecessary useMemo hooks
421
+ // https://eslint-react.xyz/docs/rules/no-unnecessary-use-memo
422
+ '@eslint-react/no-unnecessary-use-memo': 'error',
423
+
424
+ // Disallow unnecessary "use" prefix on custom hooks
425
+ // https://eslint-react.xyz/docs/rules/no-unnecessary-use-prefix
426
+ '@eslint-react/no-unnecessary-use-prefix': 'error',
427
+
428
+ // Disallow usage of UNSAFE_componentWillMount
429
+ // https://eslint-react.xyz/docs/rules/no-unsafe-component-will-mount
430
+ '@eslint-react/no-unsafe-component-will-mount': 'error',
431
+
432
+ // Disallow usage of UNSAFE_componentWillReceiveProps
433
+ // https://eslint-react.xyz/docs/rules/no-unsafe-component-will-receive-props
434
+ '@eslint-react/no-unsafe-component-will-receive-props': 'error',
435
+
436
+ // Disallow usage of UNSAFE_componentWillUpdate
437
+ // https://eslint-react.xyz/docs/rules/no-unsafe-component-will-update
438
+ '@eslint-react/no-unsafe-component-will-update': 'error',
439
+
440
+ // Prevent non-stable values used as context values
441
+ // https://eslint-react.xyz/docs/rules/no-unstable-context-value
442
+ '@eslint-react/no-unstable-context-value': 'error',
443
+
444
+ // Disallow referential-type variables as default props
445
+ // https://eslint-react.xyz/docs/rules/no-unstable-default-props
446
+ '@eslint-react/no-unstable-default-props': 'error',
447
+
448
+ // Prevent declaring unused methods of component class
449
+ // https://eslint-react.xyz/docs/rules/no-unused-class-component-members
450
+ '@eslint-react/no-unused-class-component-members': 'error',
451
+
452
+ // Warn about component props that are defined but never used
453
+ // https://eslint-react.xyz/docs/rules/no-unused-props
454
+ '@eslint-react/no-unused-props': 'error',
455
+
456
+ // Prevent unused state values
457
+ // https://eslint-react.xyz/docs/rules/no-unused-state
458
+ '@eslint-react/no-unused-state': 'error',
459
+
460
+ // Prefer using use() over useContext() (React 19+)
461
+ // https://eslint-react.xyz/docs/rules/no-use-context
462
+ '@eslint-react/no-use-context': 'error',
463
+
464
+ // Enforce consistent usage of destructuring assignment of props, state, and context
465
+ // decision: best left up to the implementer
466
+ // https://eslint-react.xyz/docs/rules/prefer-destructuring-assignment
467
+ '@eslint-react/prefer-destructuring-assignment': 'off',
468
+
469
+ // Enforce importing React via a namespace import
470
+ // https://eslint-react.xyz/docs/rules/prefer-namespace-import
471
+ '@eslint-react/prefer-namespace-import': 'off',
472
+
473
+ // Validates that components/hooks are pure
474
+ // https://eslint-react.xyz/docs/rules/purity
475
+ '@eslint-react/purity': 'error',
476
+
477
+ // Validates correct usage of refs, not reading/writing during render
478
+ // https://eslint-react.xyz/docs/rules/refs
479
+ '@eslint-react/refs': 'error',
480
+
481
+ // Enforce Rules of Hooks
482
+ // https://eslint-react.xyz/docs/rules/rules-of-hooks
483
+ '@eslint-react/rules-of-hooks': 'error',
484
+
485
+ // Validates against calling setState synchronously in an effect
486
+ // https://eslint-react.xyz/docs/rules/set-state-in-effect
487
+ '@eslint-react/set-state-in-effect': 'error',
488
+
489
+ // Validates against setting state during render
490
+ // https://eslint-react.xyz/docs/rules/set-state-in-render
491
+ '@eslint-react/set-state-in-render': 'error',
492
+
493
+ // Validates against syntax that React does not support
494
+ // https://eslint-react.xyz/docs/rules/unsupported-syntax
495
+ '@eslint-react/unsupported-syntax': 'error',
496
+
497
+ // Validates usage of useMemo with a return value
498
+ // https://eslint-react.xyz/docs/rules/use-memo
499
+ '@eslint-react/use-memo': 'error',
500
+
501
+ // Ensure destructuring and symmetric naming of useState hook value and setter
502
+ // https://eslint-react.xyz/docs/rules/use-state
503
+ '@eslint-react/use-state': 'error',
504
+
505
+ // --- React DOM rules ---
506
+
507
+ // Warn on usage of dangerouslySetInnerHTML
508
+ // https://eslint-react.xyz/docs/rules/dom-no-dangerously-set-innerhtml
509
+ '@eslint-react/dom-no-dangerously-set-innerhtml': 'error',
510
+
511
+ // Prevent problem with children and dangerouslySetInnerHTML
512
+ // https://eslint-react.xyz/docs/rules/dom-no-dangerously-set-innerhtml-with-children
513
+ '@eslint-react/dom-no-dangerously-set-innerhtml-with-children': 'error',
514
+
515
+ // Warn against using findDOMNode()
516
+ // https://eslint-react.xyz/docs/rules/dom-no-find-dom-node
517
+ '@eslint-react/dom-no-find-dom-node': 'error',
518
+
519
+ // Disallow usage of flushSync
520
+ // https://eslint-react.xyz/docs/rules/dom-no-flush-sync
521
+ '@eslint-react/dom-no-flush-sync': 'error',
522
+
523
+ // Disallow usage of ReactDOM.hydrate (use hydrateRoot instead)
524
+ // https://eslint-react.xyz/docs/rules/dom-no-hydrate
525
+ '@eslint-react/dom-no-hydrate': 'error',
526
+
527
+ // Enforce that buttons have an explicit type attribute
528
+ // https://eslint-react.xyz/docs/rules/dom-no-missing-button-type
529
+ '@eslint-react/dom-no-missing-button-type': 'error',
530
+
531
+ // Enforce sandbox attribute on iframe elements
532
+ // https://eslint-react.xyz/docs/rules/dom-no-missing-iframe-sandbox
533
+ '@eslint-react/dom-no-missing-iframe-sandbox': 'error',
534
+
535
+ // Disallow usage of ReactDOM.render (use createRoot instead)
536
+ // https://eslint-react.xyz/docs/rules/dom-no-render
537
+ '@eslint-react/dom-no-render': 'error',
538
+
539
+ // Disallow using ReactDOM.render return value
540
+ // https://eslint-react.xyz/docs/rules/dom-no-render-return-value
541
+ '@eslint-react/dom-no-render-return-value': 'error',
542
+
543
+ // Prevent usage of javascript: URLs
544
+ // https://eslint-react.xyz/docs/rules/dom-no-script-url
545
+ '@eslint-react/dom-no-script-url': 'error',
546
+
547
+ // Require style prop value be an object
548
+ // https://eslint-react.xyz/docs/rules/dom-no-string-style-prop
549
+ '@eslint-react/dom-no-string-style-prop': 'error',
550
+
551
+ // Prevent usage of unknown DOM property
552
+ // https://eslint-react.xyz/docs/rules/dom-no-unknown-property
553
+ '@eslint-react/dom-no-unknown-property': 'error',
554
+
555
+ // Enforce safe iframe sandbox attribute values
556
+ // https://eslint-react.xyz/docs/rules/dom-no-unsafe-iframe-sandbox
557
+ '@eslint-react/dom-no-unsafe-iframe-sandbox': 'error',
558
+
559
+ // Disallow target="_blank" without rel="noreferrer"
560
+ // https://eslint-react.xyz/docs/rules/dom-no-unsafe-target-blank
561
+ '@eslint-react/dom-no-unsafe-target-blank': 'error',
562
+
563
+ // Disallow usage of deprecated useFormState (use useActionState instead)
564
+ // https://eslint-react.xyz/docs/rules/dom-no-use-form-state
565
+ '@eslint-react/dom-no-use-form-state': 'error',
566
+
567
+ // Prevent void DOM elements from receiving children
568
+ // https://eslint-react.xyz/docs/rules/dom-no-void-elements-with-children
569
+ '@eslint-react/dom-no-void-elements-with-children': 'error',
570
+
571
+ // Enforce importing React DOM via a namespace import
572
+ // https://eslint-react.xyz/docs/rules/dom-prefer-namespace-import
573
+ '@eslint-react/dom-prefer-namespace-import': 'off',
574
+
575
+ // --- RSC rules ---
576
+
577
+ // Enforce correct function definition for React Server Components
578
+ // https://eslint-react.xyz/docs/rules/rsc-function-definition
579
+ '@eslint-react/rsc-function-definition': 'error',
580
+
581
+ // --- Naming convention rules ---
582
+
583
+ // Enforce consistent naming for React context
584
+ // https://eslint-react.xyz/docs/rules/naming-convention-context-name
585
+ '@eslint-react/naming-convention-context-name': 'error',
586
+
587
+ // Enforce consistent naming for React component identifiers
588
+ // https://eslint-react.xyz/docs/rules/naming-convention-id-name
589
+ '@eslint-react/naming-convention-id-name': 'error',
590
+
591
+ // Enforce consistent naming for refs
592
+ // https://eslint-react.xyz/docs/rules/naming-convention-ref-name
593
+ '@eslint-react/naming-convention-ref-name': 'error',
594
+
595
+ // --- Web API rules ---
596
+
597
+ // Prevent leaked event listeners
598
+ // https://eslint-react.xyz/docs/rules/web-api-no-leaked-event-listener
599
+ '@eslint-react/web-api-no-leaked-event-listener': 'error',
600
+
601
+ // Prevent leaked setInterval calls
602
+ // https://eslint-react.xyz/docs/rules/web-api-no-leaked-interval
603
+ '@eslint-react/web-api-no-leaked-interval': 'error',
604
+
605
+ // Prevent leaked ResizeObserver instances
606
+ // https://eslint-react.xyz/docs/rules/web-api-no-leaked-resize-observer
607
+ '@eslint-react/web-api-no-leaked-resize-observer': 'error',
608
+
609
+ // Prevent leaked setTimeout calls
610
+ // https://eslint-react.xyz/docs/rules/web-api-no-leaked-timeout
611
+ '@eslint-react/web-api-no-leaked-timeout': 'error',
235
612
  },
236
613
  };