Skip to content

Commit 6bd962f

Browse files
authored
Fully configure @eslint-react/eslint-plugin (#3978)
1 parent 793929e commit 6bd962f

12 files changed

Lines changed: 180 additions & 59 deletions

eslint.config.js

Lines changed: 137 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import reactX from '@eslint-react/eslint-plugin';
1+
import eslintReact from '@eslint-react/eslint-plugin';
22
import markdown from '@eslint/markdown';
33
import vitest from '@vitest/eslint-plugin';
44
import jestDom from 'eslint-plugin-jest-dom';
5+
import reactDom from 'eslint-plugin-react-dom';
56
import reactHooks from 'eslint-plugin-react-hooks';
7+
import reactNamingConvention from 'eslint-plugin-react-naming-convention';
8+
import reactRsc from 'eslint-plugin-react-rsc';
9+
import reactWebApi from 'eslint-plugin-react-web-api';
610
import sonarjs from 'eslint-plugin-sonarjs';
711
import testingLibrary from 'eslint-plugin-testing-library';
812
import { defineConfig, globalIgnores } from 'eslint/config';
@@ -24,7 +28,11 @@ export default defineConfig([
2428
plugins: {
2529
// @ts-expect-error
2630
'react-hooks': reactHooks,
27-
'react-x': reactX,
31+
'@eslint-react': eslintReact,
32+
'@eslint-react/rsc': reactRsc,
33+
'@eslint-react/dom': reactDom,
34+
'@eslint-react/web-api': reactWebApi,
35+
'@eslint-react/naming-convention': reactNamingConvention,
2836
sonarjs,
2937
'@typescript-eslint': tseslint.plugin
3038
},
@@ -276,12 +284,127 @@ export default defineConfig([
276284
'react-hooks/unsupported-syntax': 1,
277285
'react-hooks/use-memo': 1,
278286

279-
// React Hooks Extra
280-
// https://eslint-react.xyz/
281-
'react-x/no-unnecessary-use-callback': 1,
282-
'react-x/no-unnecessary-use-memo': 1,
283-
'react-x/no-unnecessary-use-prefix': 1,
284-
'react-x/prefer-use-state-lazy-initialization': 1,
287+
// ESLint React
288+
// https://www.eslint-react.xyz/docs/rules/overview
289+
/*
290+
// copy all the rules from the rules table for easy pasting
291+
function getRules(id, prefix) {
292+
return (
293+
Iterator.from(
294+
document
295+
// select rules table
296+
.querySelector(`#${id} ~ *:has(table) > table`)
297+
// select all rule links
298+
.querySelectorAll('tr a')
299+
)
300+
// map link to rule declaration
301+
.map((a) => `'@eslint-react/${prefix}${a.textContent}': 1,`)
302+
);
303+
}
304+
copy(
305+
Iterator.from([
306+
getRules('x-rules', ''),
307+
getRules('rsc-rules', 'rsc/'),
308+
getRules('dom-rules', 'dom/'),
309+
getRules('web-api-rules', 'web-api/'),
310+
getRules('naming-convention-rules', 'naming-convention/'),
311+
])
312+
.flatMap((x) => x)
313+
.toArray()
314+
.join('\n')
315+
);
316+
*/
317+
'@eslint-react/component-hook-factories': 1,
318+
'@eslint-react/error-boundaries': 1,
319+
'@eslint-react/exhaustive-deps': 1,
320+
'@eslint-react/jsx-dollar': 1,
321+
'@eslint-react/jsx-key-before-spread': 1,
322+
'@eslint-react/jsx-no-comment-textnodes': 1,
323+
'@eslint-react/jsx-no-duplicate-props': 1,
324+
'@eslint-react/jsx-shorthand-boolean': 1,
325+
'@eslint-react/jsx-shorthand-fragment': 1,
326+
'@eslint-react/jsx-uses-react': 1,
327+
'@eslint-react/jsx-uses-vars': 1,
328+
'@eslint-react/no-access-state-in-setstate': 1,
329+
'@eslint-react/no-array-index-key': 0,
330+
'@eslint-react/no-children-count': 1,
331+
'@eslint-react/no-children-for-each': 1,
332+
'@eslint-react/no-children-map': 1,
333+
'@eslint-react/no-children-only': 1,
334+
'@eslint-react/no-children-prop': 1,
335+
'@eslint-react/no-children-to-array': 1,
336+
'@eslint-react/no-class-component': 1,
337+
'@eslint-react/no-clone-element': 1,
338+
'@eslint-react/no-component-will-mount': 1,
339+
'@eslint-react/no-component-will-receive-props': 1,
340+
'@eslint-react/no-component-will-update': 1,
341+
'@eslint-react/no-context-provider': 1,
342+
'@eslint-react/no-create-ref': 1,
343+
'@eslint-react/no-direct-mutation-state': 1,
344+
'@eslint-react/no-duplicate-key': 1,
345+
'@eslint-react/no-forward-ref': 1,
346+
'@eslint-react/no-implicit-key': 1,
347+
'@eslint-react/no-leaked-conditional-rendering': 1,
348+
'@eslint-react/no-missing-component-display-name': 1,
349+
'@eslint-react/no-missing-context-display-name': 1,
350+
'@eslint-react/no-missing-key': 1,
351+
'@eslint-react/no-misused-capture-owner-stack': 1,
352+
'@eslint-react/no-nested-component-definitions': 1,
353+
'@eslint-react/no-nested-lazy-component-declarations': 1,
354+
'@eslint-react/no-redundant-should-component-update': 1,
355+
'@eslint-react/no-set-state-in-component-did-mount': 1,
356+
'@eslint-react/no-set-state-in-component-did-update': 1,
357+
'@eslint-react/no-set-state-in-component-will-update': 1,
358+
'@eslint-react/no-unnecessary-use-callback': 1,
359+
'@eslint-react/no-unnecessary-use-memo': 1,
360+
'@eslint-react/no-unnecessary-use-prefix': 1,
361+
'@eslint-react/no-unsafe-component-will-mount': 1,
362+
'@eslint-react/no-unsafe-component-will-receive-props': 1,
363+
'@eslint-react/no-unsafe-component-will-update': 1,
364+
'@eslint-react/no-unstable-context-value': 1,
365+
'@eslint-react/no-unstable-default-props': 1,
366+
'@eslint-react/no-unused-class-component-members': 1,
367+
'@eslint-react/no-unused-props': 1,
368+
'@eslint-react/no-unused-state': 1,
369+
'@eslint-react/no-use-context': 1,
370+
'@eslint-react/no-useless-fragment': [1, { allowExpressions: false }],
371+
'@eslint-react/prefer-destructuring-assignment': 1,
372+
'@eslint-react/prefer-namespace-import': 1,
373+
'@eslint-react/purity': 1,
374+
'@eslint-react/refs': 1,
375+
'@eslint-react/rules-of-hooks': 1,
376+
'@eslint-react/set-state-in-effect': 0,
377+
'@eslint-react/set-state-in-render': 1,
378+
'@eslint-react/unsupported-syntax': 1,
379+
'@eslint-react/use-memo': 1,
380+
'@eslint-react/use-state': 1,
381+
'@eslint-react/rsc/function-definition': 1,
382+
'@eslint-react/dom/no-dangerously-set-innerhtml': 1,
383+
'@eslint-react/dom/no-dangerously-set-innerhtml-with-children': 1,
384+
'@eslint-react/dom/no-find-dom-node': 1,
385+
'@eslint-react/dom/no-flush-sync': 0,
386+
'@eslint-react/dom/no-hydrate': 1,
387+
'@eslint-react/dom/no-missing-button-type': 1,
388+
'@eslint-react/dom/no-missing-iframe-sandbox': 1,
389+
'@eslint-react/dom/no-namespace': 1,
390+
'@eslint-react/dom/no-render': 1,
391+
'@eslint-react/dom/no-render-return-value': 1,
392+
'@eslint-react/dom/no-script-url': 1,
393+
'@eslint-react/dom/no-string-style-prop': 1,
394+
'@eslint-react/dom/no-unknown-property': 0,
395+
'@eslint-react/dom/no-unsafe-iframe-sandbox': 1,
396+
'@eslint-react/dom/no-unsafe-target-blank': 1,
397+
'@eslint-react/dom/no-use-form-state': 1,
398+
'@eslint-react/dom/no-void-elements-with-children': 1,
399+
'@eslint-react/dom/prefer-namespace-import': 1,
400+
'@eslint-react/web-api/no-leaked-event-listener': 1,
401+
'@eslint-react/web-api/no-leaked-interval': 1,
402+
'@eslint-react/web-api/no-leaked-resize-observer': 1,
403+
'@eslint-react/web-api/no-leaked-timeout': 1,
404+
'@eslint-react/naming-convention/component-name': 1,
405+
'@eslint-react/naming-convention/context-name': 1,
406+
'@eslint-react/naming-convention/id-name': 1,
407+
'@eslint-react/naming-convention/ref-name': 1,
285408

286409
// SonarJS rules
287410
// https://github.com/SonarSource/SonarJS/blob/master/packages/jsts/src/rules/README.md#rules
@@ -290,7 +413,7 @@ export default defineConfig([
290413
copy(
291414
Iterator.from(
292415
document
293-
// selecto rules table
416+
// select rules table
294417
.querySelector('.markdown-heading:has(> a[href="#rules"]) ~ markdown-accessiblity-table')
295418
// select all rows with a rule
296419
.querySelectorAll('tr:has(a)')
@@ -438,7 +561,7 @@ copy(
438561
'sonarjs/no-identical-functions': 1,
439562
'sonarjs/no-ignored-exceptions': 1,
440563
'sonarjs/no-ignored-return': 1,
441-
'sonarjs/no-implicit-dependencies': 1,
564+
'sonarjs/no-implicit-dependencies': 0,
442565
'sonarjs/no-implicit-global': 1,
443566
'sonarjs/no-in-misuse': 1,
444567
'sonarjs/no-incomplete-assertions': 1,
@@ -654,19 +777,7 @@ copy(
654777
'@typescript-eslint/no-redeclare': 1,
655778
'@typescript-eslint/no-redundant-type-constituents': 1,
656779
'@typescript-eslint/no-require-imports': 1,
657-
'@typescript-eslint/no-restricted-imports': [
658-
1,
659-
{
660-
name: 'react',
661-
importNames: ['default'],
662-
message: 'Use named imports instead.'
663-
},
664-
{
665-
name: 'react-dom',
666-
importNames: ['default'],
667-
message: 'Use named imports instead.'
668-
}
669-
],
780+
'@typescript-eslint/no-restricted-imports': 0,
670781
'@typescript-eslint/no-restricted-types': 0,
671782
'@typescript-eslint/no-shadow': 0,
672783
'@typescript-eslint/no-this-alias': 1,
@@ -803,6 +914,9 @@ copy(
803914
rules: {
804915
'@typescript-eslint/no-floating-promises': 1,
805916

917+
'@eslint-react/component-hook-factories': 0,
918+
'@eslint-react/no-create-ref': 0,
919+
806920
// https://github.com/vitest-dev/eslint-plugin-vitest#rules
807921
'vitest/consistent-each-for': 1,
808922
'vitest/consistent-test-filename': 0,

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"typecheck": "tsc --build"
4848
},
4949
"devDependencies": {
50-
"@eslint-react/eslint-plugin": "^2.3.12",
50+
"@eslint-react/eslint-plugin": "^3.0.0-beta.42",
5151
"@eslint/markdown": "^7.5.1",
5252
"@faker-js/faker": "^10.0.0",
5353
"@tanstack/react-router": "^1.132.31",

src/Columns.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import { SelectCellFormatter } from './cellRenderers';
44

55
export const SELECT_COLUMN_KEY = 'rdg-select-column';
66

7-
function HeaderRenderer(props: RenderHeaderCellProps<unknown>) {
7+
function HeaderRenderer({ tabIndex }: RenderHeaderCellProps<unknown>) {
88
const { isIndeterminate, isRowSelected, onRowSelectionChange } = useHeaderRowSelection();
99

1010
return (
1111
<SelectCellFormatter
1212
aria-label="Select All"
13-
tabIndex={props.tabIndex}
13+
tabIndex={tabIndex}
1414
indeterminate={isIndeterminate}
1515
value={isRowSelected}
1616
onChange={(checked) => {
@@ -20,32 +20,32 @@ function HeaderRenderer(props: RenderHeaderCellProps<unknown>) {
2020
);
2121
}
2222

23-
function SelectFormatter(props: RenderCellProps<unknown>) {
23+
function SelectFormatter({ row, tabIndex }: RenderCellProps<unknown>) {
2424
const { isRowSelectionDisabled, isRowSelected, onRowSelectionChange } = useRowSelection();
2525

2626
return (
2727
<SelectCellFormatter
2828
aria-label="Select"
29-
tabIndex={props.tabIndex}
29+
tabIndex={tabIndex}
3030
disabled={isRowSelectionDisabled}
3131
value={isRowSelected}
3232
onChange={(checked, isShiftClick) => {
33-
onRowSelectionChange({ row: props.row, checked, isShiftClick });
33+
onRowSelectionChange({ row, checked, isShiftClick });
3434
}}
3535
/>
3636
);
3737
}
3838

39-
function SelectGroupFormatter(props: RenderGroupCellProps<unknown>) {
39+
function SelectGroupFormatter({ row, tabIndex }: RenderGroupCellProps<unknown>) {
4040
const { isRowSelected, onRowSelectionChange } = useRowSelection();
4141

4242
return (
4343
<SelectCellFormatter
4444
aria-label="Select Group"
45-
tabIndex={props.tabIndex}
45+
tabIndex={tabIndex}
4646
value={isRowSelected}
4747
onChange={(checked) => {
48-
onRowSelectionChange({ row: props.row, checked, isShiftClick: false });
48+
onRowSelectionChange({ row, checked, isShiftClick: false });
4949
}}
5050
/>
5151
);

src/DataGrid.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,8 +315,8 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
315315
const [columnWidthsInternal, setColumnWidthsInternal] = useState(
316316
(): ColumnWidths => columnWidthsRaw ?? new Map()
317317
);
318-
const [isColumnResizing, setColumnResizing] = useState(false);
319-
const [isDragging, setDragging] = useState(false);
318+
const [isColumnResizing, setIsColumnResizing] = useState(false);
319+
const [isDragging, setIsDragging] = useState(false);
320320
const [draggedOverRowIdx, setDraggedOverRowIdx] = useState<number | undefined>(undefined);
321321
const [scrollToPosition, setScrollToPosition] = useState<PartialPosition | null>(null);
322322
const [shouldFocusCell, setShouldFocusCell] = useState(false);
@@ -454,7 +454,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
454454
columnWidths,
455455
onColumnWidthsChange,
456456
onColumnResize,
457-
setColumnResizing
457+
setIsColumnResizing
458458
);
459459

460460
const minColIdx = isTreeGrid ? -1 : 0;
@@ -695,7 +695,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
695695
// This check is needed as double click on the resize handle triggers onPointerMove
696696
if (isColumnResizing) {
697697
onColumnWidthsChangeRaw?.(columnWidths);
698-
setColumnResizing(false);
698+
setIsColumnResizing(false);
699699
}
700700
}
701701

@@ -705,7 +705,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
705705
if (event.pointerType === 'mouse' && event.button !== 0) {
706706
return;
707707
}
708-
setDragging(true);
708+
setIsDragging(true);
709709
event.currentTarget.setPointerCapture(event.pointerId);
710710
}
711711

@@ -728,7 +728,7 @@ export function DataGrid<R, SR = unknown, K extends Key = Key>(props: DataGridPr
728728
}
729729

730730
function handleDragHandleLostPointerCapture() {
731-
setDragging(false);
731+
setIsDragging(false);
732732
if (draggedOverRowIdx === undefined) return;
733733

734734
const { rowIdx } = selectedPosition;
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { createContext, useContext } from 'react';
1+
import { createContext, use } from 'react';
22

33
import type { Maybe, Renderers } from './types';
44

55
// eslint-disable-next-line @typescript-eslint/no-explicit-any
66
export const DataGridDefaultRenderersContext = createContext<Maybe<Renderers<any, any>>>(undefined);
7+
DataGridDefaultRenderersContext.displayName = 'DataGridDefaultRenderersContext';
78

89
export function useDefaultRenderers<R, SR>(): Maybe<Renderers<R, SR>> {
9-
return useContext(DataGridDefaultRenderersContext);
10+
return use(DataGridDefaultRenderersContext);
1011
}

src/EditCell.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,12 @@ export default function EditCell<R, SR>({
115115
}
116116
}
117117

118-
addEventListener('mousedown', onWindowCaptureMouseDown, { capture: true });
119-
addEventListener('mousedown', onWindowMouseDown);
118+
window.addEventListener('mousedown', onWindowCaptureMouseDown, { capture: true });
119+
window.addEventListener('mousedown', onWindowMouseDown);
120120

121121
return () => {
122-
removeEventListener('mousedown', onWindowCaptureMouseDown, { capture: true });
123-
removeEventListener('mousedown', onWindowMouseDown);
122+
window.removeEventListener('mousedown', onWindowCaptureMouseDown, { capture: true });
123+
window.removeEventListener('mousedown', onWindowMouseDown);
124124
cancelTask();
125125
};
126126
}, [commitOnOutsideClick]);

src/hooks/useColumnWidths.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export function useColumnWidths<R, SR>(
2121
} | null>(null);
2222
const [columnsToMeasureOnResize, setColumnsToMeasureOnResize] =
2323
useState<ReadonlySet<string> | null>(null);
24-
const [prevGridWidth, setPreviousGridWidth] = useState(gridWidth);
24+
const [prevGridWidth, setPrevGridWidth] = useState(gridWidth);
2525
const columnsCanFlex: boolean = columns.length === viewportColumns.length;
2626
const ignorePreviouslyMeasuredColumnsOnGridWidthChange =
2727
// Allow columns to flex again when...
@@ -57,7 +57,7 @@ export function useColumnWidths<R, SR>(
5757
useLayoutEffect(updateMeasuredAndResizedWidths);
5858

5959
function updateMeasuredAndResizedWidths() {
60-
setPreviousGridWidth(gridWidth);
60+
setPrevGridWidth(gridWidth);
6161
if (columnsToMeasure.length === 0) return;
6262

6363
const newColumnWidths = new Map(columnWidths);

0 commit comments

Comments
 (0)