@gaia-harness/lint 3.0.4 → 3.1.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.
- package/README.md +27 -4
- package/dist/configs/guardrails.d.ts.map +1 -1
- package/dist/configs/react-doctor.d.ts +61 -0
- package/dist/configs/react-doctor.d.ts.map +1 -0
- package/dist/configs/react-router.d.ts +4 -1
- package/dist/configs/react-router.d.ts.map +1 -1
- package/dist/configs/react.d.ts.map +1 -1
- package/dist/configs/shadcn.d.ts.map +1 -1
- package/dist/configs/style-hygiene.d.ts.map +1 -1
- package/dist/index.js +321 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -67,17 +67,17 @@ new source root: no per-config override blocks needed.
|
|
|
67
67
|
| Property | Shape | Includes | Required? |
|
|
68
68
|
| ---------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
|
|
69
69
|
| `base` | `Linter.Config[]` | JS recommended, TypeScript (typescript-eslint), `import-x`, unused-disable-directive reporting at error, `eslint-comments` (every disable states a reason), `prefer-arrow-functions`, lodash/underscore guard, `no-restricted-imports` (bans bare `@conform-to/zod`) | required |
|
|
70
|
-
| `react` | `Linter.Config[]` | `eslint-plugin-react`, `react-hooks`, `jsx-a11y`, `react-refresh` (Fast Refresh-safe exports), `react-you-might-not-need-an-effect` (Effect misuse), nine `@eslint-react` rules (Effect cleanup, React 19 forms), GAIA-specific React rules | required for React apps |
|
|
71
|
-
| `reactRouter` | `Linter.Config[]` | Relaxations for React Router framework mode, route-module exports allowed by `react-refresh`, plus `no-imperative-query-fetch` (custom). Spread **after** `react`. See [Router-specific rules](#router-specific-rules) | React Router only |
|
|
70
|
+
| `react` | `Linter.Config[]` | `eslint-plugin-react`, `react-hooks`, `jsx-a11y`, `react-refresh` (Fast Refresh-safe exports), `react-you-might-not-need-an-effect` (Effect misuse), nine `@eslint-react` rules (Effect cleanup, React 19 forms), 77 curated `react-doctor` rules (SSR and hydration, HTML validity, accessibility, render and Effect correctness, performance, motion, component size and complexity), GAIA-specific React rules. See [react-doctor rules](#react-doctor-rules) | required for React apps |
|
|
71
|
+
| `reactRouter` | `Linter.Config[]` | Relaxations for React Router framework mode, route-module exports allowed by `react-refresh`, plus a TanStack Query block (`no-imperative-query-fetch` (custom), `@tanstack/eslint-plugin-query` recommended, five `react-doctor` Query rules) and 25 curated `react-doctor` router rules with the capability settings they need. Spread **after** `react`. See [Router-specific rules](#router-specific-rules) | React Router only |
|
|
72
72
|
| `styleHygiene` | `Linter.Config[]` | `canonical`, `perfectionist`, `unicorn`, `unused-imports`, `check-file` | required |
|
|
73
|
-
| `guardrails` | `Linter.Config[]` | `regexp` (recommended, ReDoS included), `no-enum` (custom), `no-switch` (custom), `no-jsx-iife` (custom), `no-null-render` (custom), `no-zod-enum` (custom), `cn-conditional` (custom), `no-relative-import-paths`, `sonarjs`, `import-x`, `prefer-arrow-functions` | required |
|
|
73
|
+
| `guardrails` | `Linter.Config[]` | `regexp` (recommended, ReDoS included), `no-enum` (custom), `no-switch` (custom), `no-jsx-iife` (custom), `no-null-render` (custom), `no-zod-enum` (custom), `cn-conditional` (custom), `no-relative-import-paths`, `sonarjs`, `import-x`, `prefer-arrow-functions`, 14 curated `react-doctor` security and defensive-JS rules | required |
|
|
74
74
|
| `i18n` | `Linter.Config[]` | `i18next/no-literal-string` on raw JSX text under the source dir. See [i18n and Zod](#i18n-and-zod) | optional |
|
|
75
75
|
| `zod` | `Linter.Config[]` | `eslint-plugin-zod` recommended, minus `consistent-import`. See [i18n and Zod](#i18n-and-zod) | optional |
|
|
76
76
|
| `testing` | `Linter.Config[]` | Vitest + jest-dom config scoped to `*.test.*`, `*.stories.*` and `test/` | optional |
|
|
77
77
|
| `storybook` | `Linter.Config[]` | `eslint-plugin-storybook` scoped to `*.stories.*` and `.storybook/main.*` | optional |
|
|
78
78
|
| `playwright` | `Linter.Config[]` | `eslint-plugin-playwright` scoped to `.playwright/` | optional |
|
|
79
79
|
| `prettier` | `Linter.Config[]` | `eslint-config-prettier`, must be **last** to disable formatting rules | required if using Prettier |
|
|
80
|
-
| `shadcn` | `(opts?) => Linter.Config[]` | `@shadcn/lint` token rules plus the vendored-ui exemption; see [shadcn factory](#shadcn-factory). Spread **last**, after `prettier`, with the full GAIA composition before it | optional |
|
|
80
|
+
| `shadcn` | `(opts?) => Linter.Config[]` | `@shadcn/lint` token rules, four curated `react-doctor` composition rules, plus the vendored-ui exemption; see [shadcn factory](#shadcn-factory). Spread **last**, after `prettier`, with the full GAIA composition before it | optional |
|
|
81
81
|
| `betterTailwind` | `(opts) => Linter.Config[]` | `eslint-plugin-better-tailwindcss` factory; takes `entryPoint` (path to Tailwind entry CSS) and optional `ignore` (class names to skip) | optional |
|
|
82
82
|
| `ignores` | `Iterable<Linter.Config> & ((opts?) => Linter.Config[])` | `includeIgnoreFile` helper plus GAIA defaults. Spread directly for defaults (`...lint.ignores`) or call with options to override (`...lint.ignores({extra: ['.gaia/**']})`). | recommended |
|
|
83
83
|
|
|
@@ -287,6 +287,29 @@ The `/.react-router/**` glob stays in the `ignores` defaults. It names a
|
|
|
287
287
|
directory that only exists in a React Router project, so it costs other
|
|
288
288
|
projects nothing.
|
|
289
289
|
|
|
290
|
+
## react-doctor rules
|
|
291
|
+
|
|
292
|
+
125 rules from [`eslint-plugin-react-doctor`](https://www.react.doctor/docs/configuration/eslint-and-oxlint-plugins), chosen one by one; no preset is spread. The presets turn back on rules this config turns off on purpose and carry hundreds of rules for libraries it does not cover. The plugin is pinned to an exact version because its rule set changes between releases, and ESLint fails a config that names a rule the installed plugin no longer ships. Every curated id and severity lives in `src/configs/react-doctor.ts`, grouped with the reason for each group, and a snapshot test pins the full list.
|
|
293
|
+
|
|
294
|
+
| Block | Groups |
|
|
295
|
+
| --- | --- |
|
|
296
|
+
| `react` (77) | SSR and hydration, HTML validity, accessibility, render and state purity, Effect lifecycle, performance (including images and scripts), motion and font, component and hook size and complexity |
|
|
297
|
+
| `reactRouter` (30) | 24 framework-mode rules (loaders, actions, middleware, sessions, cookies, search params, navigation, error UI, the server entry); five Query rules in the Query block; `no-unguarded-browser-global-at-module-scope` on source files only, not tests or stories |
|
|
298
|
+
| `guardrails` (14) | auth tokens in web storage, secrets in client code, dynamic `RegExp` input, path containment, `window.open` without `noopener`, throwing and NaN-producing parses, and similar |
|
|
299
|
+
| `shadcn()` (4) | icon Button label, DialogContent title, InputGroup controls, TabsTrigger inside TabsList |
|
|
300
|
+
|
|
301
|
+
The vendored shadcn ui files skip the four shadcn rules (they check how GAIA code composes the primitives) and the three motion and font rules (registry classes).
|
|
302
|
+
|
|
303
|
+
`no-giant-component` reports a component over 300 lines. `no-high-complexity-react-function` reports a component or `use*` hook over 15 cyclomatic or cognitive complexity. Both thresholds are fixed upstream. They stand in for the sonarjs size and complexity rules, which stay off because they do not model components and hooks.
|
|
304
|
+
|
|
305
|
+
`reactRouter` also sets `settings['react-doctor'].capabilities`. The react-doctor CLI detects these per project; the standalone plugin reads them from settings, and without `react-router-framework` the router rules never recognize a route module's named exports. Once the list is set, a rule runs only when every capability it requires is listed verbatim, so an override that adds a react-doctor rule may need to extend the list.
|
|
306
|
+
|
|
307
|
+
### TanStack Query
|
|
308
|
+
|
|
309
|
+
`reactRouter`'s Query block also enables `@tanstack/eslint-plugin-query`'s recommended rules, read from its flat preset, beside `no-imperative-query-fetch`. They match TanStack Query imports, so a project without Query sees nothing. react-doctor's `query-no-rest-destructuring`, `query-stable-query-client` and `query-no-void-query-fn` stay off because the official plugin owns them. TanStack's property-order rules need `onMutate` before `onError` and `queryFn` before the page-param getters, which alphabetical order breaks. So in the object passed straight to those calls, `perfectionist/sort-objects` keeps natural order but sorts those late-binding keys into a last group: `onError` and `onSettled` for `useMutation`; `getNextPageParam` and `getPreviousPageParam` for `useInfiniteQuery`, `useSuspenseInfiniteQuery` and `infiniteQueryOptions`. Its autofix lands on the order TanStack requires. `no-void-query-fn` needs type information.
|
|
310
|
+
|
|
311
|
+
`react` also turns `react/jsx-key` back on (error, with `checkFragmentShorthand` and `warnOnDuplicates`): Airbnb's non-strict React config turns it off, and nothing else here catches a list rendered without `key`.
|
|
312
|
+
|
|
290
313
|
## i18n and Zod
|
|
291
314
|
|
|
292
315
|
Both are opt-in arrays: spread them only when the project uses the library.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/configs/guardrails.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/configs/guardrails.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,QAAQ,CAAC;AA0SnC,eAAO,MAAM,eAAe,GAAI,WAAW,MAAM,KAAG,MAAM,CAAC,MAAM,EAYhE,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { ESLint, Linter } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* The one `eslint-plugin-react-doctor` registration every block that names a
|
|
4
|
+
* `react-doctor/*` rule spreads into its `plugins`. Flat config accepts the
|
|
5
|
+
* same plugin object in several blocks, so `react`, `reactRouter`, `shadcn()`
|
|
6
|
+
* and `guardrails` each stay self-contained without a redefinition error.
|
|
7
|
+
*
|
|
8
|
+
* Curated rules only, never a preset: the presets re-enable rules GAIA turns
|
|
9
|
+
* off on purpose and carry hundreds of rules for libraries GAIA does not use.
|
|
10
|
+
* The rules take no options, carry no autofix and need no type information,
|
|
11
|
+
* so the same rule ids port unchanged to Oxlint's `oxlint-plugin-react-doctor`.
|
|
12
|
+
*
|
|
13
|
+
* The package is pinned to an exact version. The rule set churns between
|
|
14
|
+
* releases, and ESLint fails a config that names a rule the installed plugin
|
|
15
|
+
* no longer ships, so a bump is a deliberate change with its tests. Every
|
|
16
|
+
* curated rule id lives in this file, grouped by the block that applies it,
|
|
17
|
+
* so a bump is checked against one list.
|
|
18
|
+
*/
|
|
19
|
+
export declare const REACT_DOCTOR_PLUGINS: Record<string, ESLint.Plugin>;
|
|
20
|
+
/**
|
|
21
|
+
* `react`: motion that ignores reduced-motion and pixel font sizes that ignore
|
|
22
|
+
* user font scaling. Off in vendored shadcn ui (see `shadcn.ts`).
|
|
23
|
+
*/
|
|
24
|
+
export declare const MOTION_FONT_RULES: Linter.RulesRecord;
|
|
25
|
+
export declare const REACT_DOCTOR_REACT_RULES: Linter.RulesRecord;
|
|
26
|
+
/**
|
|
27
|
+
* `reactRouter`: framework-mode mistakes in loaders, actions, middleware,
|
|
28
|
+
* sessions, cookies, search params, navigation and the server entry.
|
|
29
|
+
*/
|
|
30
|
+
export declare const REACT_DOCTOR_REACT_ROUTER_RULES: Linter.RulesRecord;
|
|
31
|
+
/**
|
|
32
|
+
* `reactRouter`, in the Query block: TanStack Query misuse the official
|
|
33
|
+
* plugin does not check. Its `no-rest-destructuring`, `stable-query-client`
|
|
34
|
+
* and `no-void-query-fn` own those cases, so react-doctor's copies stay off.
|
|
35
|
+
*/
|
|
36
|
+
export declare const REACT_DOCTOR_QUERY_RULES: Linter.RulesRecord;
|
|
37
|
+
/** `reactRouter`: source modules only; a module-scope browser global crashes SSR. */
|
|
38
|
+
export declare const REACT_DOCTOR_MODULE_SCOPE_RULES: Linter.RulesRecord;
|
|
39
|
+
/** `guardrails`: security and defensive-JS classes sonarjs and regexp miss. */
|
|
40
|
+
export declare const REACT_DOCTOR_GUARDRAIL_RULES: Linter.RulesRecord;
|
|
41
|
+
/**
|
|
42
|
+
* `shadcn()`: composition that breaks a shadcn primitive's accessible name,
|
|
43
|
+
* keyboard model or structure. Off in vendored ui, which defines the
|
|
44
|
+
* primitives these check.
|
|
45
|
+
*/
|
|
46
|
+
export declare const REACT_DOCTOR_SHADCN_RULES: Linter.RulesRecord;
|
|
47
|
+
/**
|
|
48
|
+
* The capabilities `reactRouter` declares in `settings['react-doctor']`.
|
|
49
|
+
* react-doctor's CLI detects them per project; the standalone plugin reads
|
|
50
|
+
* them from settings. Without `react-router-framework` the react-router rules
|
|
51
|
+
* never recognize a route module's named `loader`, `action` or
|
|
52
|
+
* `ErrorBoundary` exports and report nothing. Once the list is set, a rule
|
|
53
|
+
* runs only when every capability it `requires` appears verbatim (a version
|
|
54
|
+
* entry such as `react-router:7.9` is a floor, not a range), so this names
|
|
55
|
+
* each capability the curated rules require that a React Router
|
|
56
|
+
* framework-mode project on React 19 and Tailwind has. `shadcn` and
|
|
57
|
+
* `tanstack-query` are listed too: their rules match the components and hooks
|
|
58
|
+
* they check by import, so they stay silent in a project without them.
|
|
59
|
+
*/
|
|
60
|
+
export declare const REACT_DOCTOR_CAPABILITIES: string[];
|
|
61
|
+
//# sourceMappingURL=react-doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-doctor.d.ts","sourceRoot":"","sources":["../../src/configs/react-doctor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,MAAM,EAAE,MAAM,EAAC,MAAM,QAAQ,CAAC;AAE3C;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAE9D,CAAC;AA+GF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,WAItC,CAAC;AAaF,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,WAS7C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAAC,WAyBpD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,WAM7C,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAAC,WAEpD,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC,WAejD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,WAK9C,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,UAerC,CAAC"}
|
|
@@ -21,7 +21,10 @@ import type { Linter } from 'eslint';
|
|
|
21
21
|
* (`createFileRoute(...)({component: Named})`) and never produces that shape,
|
|
22
22
|
* so a TanStack project wants the rule left on.
|
|
23
23
|
*
|
|
24
|
-
* It also carries
|
|
24
|
+
* It also carries ADDITIVE rules on every TypeScript file under the source
|
|
25
|
+
* dir: the curated react-doctor React Router and Query rules with the
|
|
26
|
+
* capability settings they need, the `@tanstack/eslint-plugin-query`
|
|
27
|
+
* recommended rules, and `no-imperative-query-fetch`. That last one sits on every
|
|
25
28
|
* TypeScript file under the source dir. It bans `ensureQueryData`,
|
|
26
29
|
* `fetchQuery`, `prefetchQuery` and their infinite variants, so a route has
|
|
27
30
|
* one fetch idiom: `queryClient.query` awaited in a clientLoader, read with
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-router.d.ts","sourceRoot":"","sources":["../../src/configs/react-router.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"react-router.d.ts","sourceRoot":"","sources":["../../src/configs/react-router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAS,MAAM,EAAC,MAAM,QAAQ,CAAC;AA4C3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,MAAM,KAAG,MAAM,CAAC,MAAM,EAsEjE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/configs/react.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAS,MAAM,EAAC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/configs/react.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAS,MAAM,EAAC,MAAM,QAAQ,CAAC;AAM3C;;;;;;;;;GASG;AACH;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B;;;CAGjC,CAAC;AAEX,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAgIhC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadcn.d.ts","sourceRoot":"","sources":["../../src/configs/shadcn.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAS,MAAM,EAAC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"shadcn.d.ts","sourceRoot":"","sources":["../../src/configs/shadcn.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAS,MAAM,EAAC,MAAM,QAAQ,CAAC;AAQ3C,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAgMF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,OAAO,qBAAqB,KAC3B,MAAM,CAAC,MAAM,EAsEf,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style-hygiene.d.ts","sourceRoot":"","sources":["../../src/configs/style-hygiene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"style-hygiene.d.ts","sourceRoot":"","sources":["../../src/configs/style-hygiene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,QAAQ,CAAC;AAyVnC,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,KAAG,MAAM,CAAC,MAAM,EAMlE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -703,6 +703,189 @@ var plugin6 = {
|
|
|
703
703
|
};
|
|
704
704
|
var no_zod_enum_default = plugin6;
|
|
705
705
|
|
|
706
|
+
// src/configs/react-doctor.ts
|
|
707
|
+
import reactDoctor from "eslint-plugin-react-doctor";
|
|
708
|
+
var REACT_DOCTOR_PLUGINS = {
|
|
709
|
+
"react-doctor": reactDoctor
|
|
710
|
+
};
|
|
711
|
+
var SSR_RULES = {
|
|
712
|
+
"react-doctor/no-hydration-branch-on-browser-global": "error",
|
|
713
|
+
"react-doctor/no-locale-format-in-render": "warn",
|
|
714
|
+
"react-doctor/no-match-media-in-state-initializer": "warn",
|
|
715
|
+
"react-doctor/no-unguarded-browser-global-in-render-or-hook-init": "error"
|
|
716
|
+
};
|
|
717
|
+
var HTML_RULES = {
|
|
718
|
+
"react-doctor/form-control-requires-name": "warn",
|
|
719
|
+
"react-doctor/html-no-invalid-paragraph-child": "warn",
|
|
720
|
+
"react-doctor/html-no-invalid-table-nesting": "warn",
|
|
721
|
+
"react-doctor/html-no-nested-form": "warn",
|
|
722
|
+
"react-doctor/html-no-nested-interactive": "warn",
|
|
723
|
+
"react-doctor/no-inert-sticky-position": "warn"
|
|
724
|
+
};
|
|
725
|
+
var A11Y_RULES = {
|
|
726
|
+
"react-doctor/data-table-requires-accessible-name": "warn",
|
|
727
|
+
"react-doctor/details-requires-summary": "warn",
|
|
728
|
+
"react-doctor/dialog-has-accessible-name": "warn",
|
|
729
|
+
"react-doctor/empty-table-header": "warn",
|
|
730
|
+
"react-doctor/fieldset-requires-legend": "warn",
|
|
731
|
+
"react-doctor/html-label-has-single-control": "warn",
|
|
732
|
+
"react-doctor/no-aria-invalid-without-description": "warn",
|
|
733
|
+
"react-doctor/no-assertive-status": "warn",
|
|
734
|
+
"react-doctor/no-autoplay-without-muted": "warn",
|
|
735
|
+
"react-doctor/no-blocked-paste": "error",
|
|
736
|
+
"react-doctor/no-disabled-zoom": "error",
|
|
737
|
+
"react-doctor/no-duplicate-static-id-reference": "error",
|
|
738
|
+
"react-doctor/no-focusable-content-in-aria-hidden": "warn",
|
|
739
|
+
"react-doctor/no-hover-only-reveal": "warn",
|
|
740
|
+
"react-doctor/no-invalid-progress-range": "error",
|
|
741
|
+
"react-doctor/no-multiple-main-landmarks": "warn",
|
|
742
|
+
"react-doctor/no-multiple-unlabeled-navigation-landmarks": "warn",
|
|
743
|
+
"react-doctor/no-placeholder-only-field": "warn",
|
|
744
|
+
"react-doctor/no-presentation-role-conflict": "warn",
|
|
745
|
+
"react-doctor/no-responsive-hidden-accessible-name": "warn",
|
|
746
|
+
"react-doctor/no-skipped-heading-level": "warn",
|
|
747
|
+
"react-doctor/no-uninformative-aria-label": "warn",
|
|
748
|
+
"react-doctor/radio-input-missing-name": "warn",
|
|
749
|
+
"react-doctor/role-button-requires-complete-keyboard-activation": "warn"
|
|
750
|
+
};
|
|
751
|
+
var RENDER_STATE_RULES = {
|
|
752
|
+
"react-doctor/no-call-component-as-function": "warn",
|
|
753
|
+
"react-doctor/no-controlled-input-value-without-state-update": "warn",
|
|
754
|
+
"react-doctor/no-create-context-in-render": "error",
|
|
755
|
+
"react-doctor/no-create-ref-in-function-component": "warn",
|
|
756
|
+
"react-doctor/no-default-props": "warn",
|
|
757
|
+
"react-doctor/no-impure-call-at-module-scope": "warn",
|
|
758
|
+
"react-doctor/no-indeterminate-attribute": "warn",
|
|
759
|
+
"react-doctor/no-mutate-queried-dom-node-in-component": "warn",
|
|
760
|
+
"react-doctor/no-mutating-reducer-state": "error",
|
|
761
|
+
"react-doctor/no-nondeterministic-id-value-in-render-body": "warn",
|
|
762
|
+
"react-doctor/no-prop-callback-in-render": "error",
|
|
763
|
+
"react-doctor/no-side-effect-in-state-updater-function": "warn",
|
|
764
|
+
"react-doctor/no-uncontrolled-input": "warn",
|
|
765
|
+
"react-doctor/rerender-functional-setstate": "warn",
|
|
766
|
+
"react-doctor/waapi-animation-in-render": "error"
|
|
767
|
+
};
|
|
768
|
+
var EFFECT_RULES = {
|
|
769
|
+
"react-doctor/debounce-no-cleanup": "warn",
|
|
770
|
+
"react-doctor/effect-raf-loop-needs-cancel": "warn",
|
|
771
|
+
"react-doctor/no-create-object-url-without-revoke": "warn",
|
|
772
|
+
"react-doctor/no-fetch-in-effect": "warn",
|
|
773
|
+
"react-doctor/no-fetch-response-used-without-status-check": "warn",
|
|
774
|
+
"react-doctor/no-loading-flag-reset-outside-finally": "warn",
|
|
775
|
+
"react-doctor/no-promise-then-side-effect-in-effect-without-catch": "warn",
|
|
776
|
+
"react-doctor/no-set-state-after-await-in-effect": "warn",
|
|
777
|
+
"react-doctor/no-stale-timer-ref": "warn",
|
|
778
|
+
"react-doctor/no-unbounded-animation-frame-loop": "warn",
|
|
779
|
+
"react-doctor/no-unowned-async-error-clear": "warn",
|
|
780
|
+
"react-doctor/pointer-capture-needs-cancel-handler": "warn"
|
|
781
|
+
};
|
|
782
|
+
var PERFORMANCE_RULES = {
|
|
783
|
+
"react-doctor/async-parallel": "warn",
|
|
784
|
+
"react-doctor/js-hoist-intl": "warn",
|
|
785
|
+
"react-doctor/no-eager-new-in-use-state-initializer": "warn",
|
|
786
|
+
"react-doctor/no-img-lazy-with-high-fetchpriority": "warn",
|
|
787
|
+
"react-doctor/no-img-without-dimensions": "warn",
|
|
788
|
+
"react-doctor/no-mixed-srcset-descriptors": "warn",
|
|
789
|
+
"react-doctor/no-spread-accumulator-in-reduce": "warn",
|
|
790
|
+
"react-doctor/no-srcset-without-sizes": "warn",
|
|
791
|
+
"react-doctor/rendering-script-defer-async": "warn",
|
|
792
|
+
"react-doctor/rerender-lazy-ref-init": "warn",
|
|
793
|
+
"react-doctor/rerender-lazy-state-init": "warn"
|
|
794
|
+
};
|
|
795
|
+
var MOTION_FONT_RULES = {
|
|
796
|
+
"react-doctor/no-arbitrary-px-font-size": "warn",
|
|
797
|
+
"react-doctor/no-smooth-scroll-without-reduced-motion": "warn",
|
|
798
|
+
"react-doctor/no-ungated-tailwind-animation": "warn"
|
|
799
|
+
};
|
|
800
|
+
var SIZE_RULES = {
|
|
801
|
+
"react-doctor/no-giant-component": "warn",
|
|
802
|
+
"react-doctor/no-high-complexity-react-function": "warn"
|
|
803
|
+
};
|
|
804
|
+
var REACT_DOCTOR_REACT_RULES = {
|
|
805
|
+
...SSR_RULES,
|
|
806
|
+
...HTML_RULES,
|
|
807
|
+
...A11Y_RULES,
|
|
808
|
+
...RENDER_STATE_RULES,
|
|
809
|
+
...EFFECT_RULES,
|
|
810
|
+
...PERFORMANCE_RULES,
|
|
811
|
+
...MOTION_FONT_RULES,
|
|
812
|
+
...SIZE_RULES
|
|
813
|
+
};
|
|
814
|
+
var REACT_DOCTOR_REACT_ROUTER_RULES = {
|
|
815
|
+
"react-doctor/react-router-csp-nonce-consistency": "error",
|
|
816
|
+
"react-doctor/react-router-guard-aborted-handle-error": "warn",
|
|
817
|
+
"react-doctor/react-router-internal-route-anchor": "warn",
|
|
818
|
+
"react-doctor/react-router-loader-fetch-forwards-signal": "warn",
|
|
819
|
+
"react-doctor/react-router-loader-parallel-fetch": "warn",
|
|
820
|
+
"react-doctor/react-router-no-catch-middleware-next": "warn",
|
|
821
|
+
"react-doctor/react-router-no-client-module-in-server-render": "error",
|
|
822
|
+
"react-doctor/react-router-no-loader-request-body": "error",
|
|
823
|
+
"react-doctor/react-router-no-middleware-response-body-consumption": "warn",
|
|
824
|
+
"react-doctor/react-router-no-multiple-blockers": "error",
|
|
825
|
+
"react-doctor/react-router-no-multiple-middleware-next": "error",
|
|
826
|
+
"react-doctor/react-router-no-multiple-set-search-params-in-tick": "warn",
|
|
827
|
+
"react-doctor/react-router-no-navigate-in-render": "error",
|
|
828
|
+
"react-doctor/react-router-no-redirect-in-try-catch": "error",
|
|
829
|
+
"react-doctor/react-router-no-route-module-environment-suffix": "error",
|
|
830
|
+
"react-doctor/react-router-no-session-mutation-in-loader": "error",
|
|
831
|
+
"react-doctor/react-router-no-static-cookie-expires": "error",
|
|
832
|
+
"react-doctor/react-router-no-unsynchronized-search-params-mutation": "error",
|
|
833
|
+
"react-doctor/react-router-no-use-loader-data-in-error-ui": "error",
|
|
834
|
+
"react-doctor/react-router-require-root-error-boundary": "error",
|
|
835
|
+
"react-doctor/react-router-resource-link-requires-reload": "error",
|
|
836
|
+
"react-doctor/react-router-return-navigation-promise-in-transition": "warn",
|
|
837
|
+
"react-doctor/react-router-server-middleware-return-response": "error",
|
|
838
|
+
"react-doctor/react-router-session-mutation-requires-commit": "error"
|
|
839
|
+
};
|
|
840
|
+
var REACT_DOCTOR_QUERY_RULES = {
|
|
841
|
+
"react-doctor/query-destructure-result": "warn",
|
|
842
|
+
"react-doctor/query-mutation-missing-invalidation": "warn",
|
|
843
|
+
"react-doctor/query-no-mutation-in-effect-as-read": "warn",
|
|
844
|
+
"react-doctor/query-no-query-in-effect": "warn",
|
|
845
|
+
"react-doctor/query-no-usequery-for-mutation": "warn"
|
|
846
|
+
};
|
|
847
|
+
var REACT_DOCTOR_MODULE_SCOPE_RULES = {
|
|
848
|
+
"react-doctor/no-unguarded-browser-global-at-module-scope": "warn"
|
|
849
|
+
};
|
|
850
|
+
var REACT_DOCTOR_GUARDRAIL_RULES = {
|
|
851
|
+
"react-doctor/auth-token-in-web-storage": "warn",
|
|
852
|
+
"react-doctor/no-document-write": "warn",
|
|
853
|
+
"react-doctor/no-fill-map-element-as-key": "warn",
|
|
854
|
+
"react-doctor/no-non-literal-selector-query-without-try-catch": "warn",
|
|
855
|
+
"react-doctor/no-non-null-assertion-on-maybe-undefined-result": "warn",
|
|
856
|
+
"react-doctor/no-nullish-coalescing-arithmetic-precedence": "warn",
|
|
857
|
+
"react-doctor/no-path-prefix-containment": "warn",
|
|
858
|
+
"react-doctor/no-secrets-in-client-code": "warn",
|
|
859
|
+
"react-doctor/no-sync-xhr": "warn",
|
|
860
|
+
"react-doctor/no-unescaped-dynamic-string-in-regexp": "warn",
|
|
861
|
+
"react-doctor/no-unguarded-numeric-input-parse": "warn",
|
|
862
|
+
"react-doctor/no-unguarded-throwing-parse-call": "warn",
|
|
863
|
+
"react-doctor/no-whole-object-default-losing-per-key-defaults": "warn",
|
|
864
|
+
"react-doctor/window-open-without-noopener": "warn"
|
|
865
|
+
};
|
|
866
|
+
var REACT_DOCTOR_SHADCN_RULES = {
|
|
867
|
+
"react-doctor/shadcn-dialog-content-requires-title": "warn",
|
|
868
|
+
"react-doctor/shadcn-icon-button-requires-label": "warn",
|
|
869
|
+
"react-doctor/shadcn-input-group-no-raw-controls": "warn",
|
|
870
|
+
"react-doctor/shadcn-tabs-trigger-requires-list": "warn"
|
|
871
|
+
};
|
|
872
|
+
var REACT_DOCTOR_CAPABILITIES = [
|
|
873
|
+
"react",
|
|
874
|
+
"react:19",
|
|
875
|
+
"react-router",
|
|
876
|
+
"react-router:6.4",
|
|
877
|
+
"react-router:6.7",
|
|
878
|
+
"react-router:7",
|
|
879
|
+
"react-router:7.8",
|
|
880
|
+
"react-router:7.9",
|
|
881
|
+
"react-router:7.10",
|
|
882
|
+
"react-router-framework",
|
|
883
|
+
"shadcn",
|
|
884
|
+
"ssr",
|
|
885
|
+
"tailwind",
|
|
886
|
+
"tanstack-query"
|
|
887
|
+
];
|
|
888
|
+
|
|
706
889
|
// src/configs/guardrails.ts
|
|
707
890
|
var buildSonarConfig = (sourceDir) => [
|
|
708
891
|
sonarjs.configs.recommended,
|
|
@@ -747,6 +930,14 @@ var buildSonarConfig = (sourceDir) => [
|
|
|
747
930
|
var regexpConfig = [
|
|
748
931
|
{ ...regexp.configs["flat/recommended"], name: "regexp" }
|
|
749
932
|
];
|
|
933
|
+
var reactDoctorGuardrailsConfig = [
|
|
934
|
+
{
|
|
935
|
+
files: ["**/*.ts?(x)", "**/*.js?(x)"],
|
|
936
|
+
name: "react-doctor/guardrails",
|
|
937
|
+
plugins: REACT_DOCTOR_PLUGINS,
|
|
938
|
+
rules: REACT_DOCTOR_GUARDRAIL_RULES
|
|
939
|
+
}
|
|
940
|
+
];
|
|
750
941
|
var cnConditionalConfig = [
|
|
751
942
|
{
|
|
752
943
|
files: ["**/*.ts?(x)", "**/*.js?(x)"],
|
|
@@ -900,6 +1091,7 @@ var buildNoRelativeImportPathsConfig = (sourceDir) => [
|
|
|
900
1091
|
var buildGuardrails = (sourceDir) => [
|
|
901
1092
|
...buildSonarConfig(sourceDir),
|
|
902
1093
|
...regexpConfig,
|
|
1094
|
+
...reactDoctorGuardrailsConfig,
|
|
903
1095
|
...cnConditionalConfig,
|
|
904
1096
|
...noEnumConfig,
|
|
905
1097
|
...noJsxIifeConfig,
|
|
@@ -1054,6 +1246,12 @@ var react = [
|
|
|
1054
1246
|
"react/jsx-curly-brace-presence": "error",
|
|
1055
1247
|
"react/jsx-filename-extension": "off",
|
|
1056
1248
|
"react/jsx-fragments": "error",
|
|
1249
|
+
// airbnb-extended's non-strict react config turns this off, and nothing
|
|
1250
|
+
// else in the bundle catches a list rendered without `key`.
|
|
1251
|
+
"react/jsx-key": [
|
|
1252
|
+
"error",
|
|
1253
|
+
{ checkFragmentShorthand: true, warnOnDuplicates: true }
|
|
1254
|
+
],
|
|
1057
1255
|
// off by default because it doesn't handle props onXyz function names correctly
|
|
1058
1256
|
// turn this on from time to time to check for misnamed handlers elsewhere
|
|
1059
1257
|
"react/jsx-handler-names": ["off", { checkLocalVariables: true }],
|
|
@@ -1127,6 +1325,17 @@ var react = [
|
|
|
1127
1325
|
"@eslint-react/web-api-no-leaked-timeout": "error"
|
|
1128
1326
|
}
|
|
1129
1327
|
},
|
|
1328
|
+
// Curated react-doctor rules (see `react-doctor.ts`, which groups them with
|
|
1329
|
+
// the reason for each group): SSR and hydration hazards, invalid HTML,
|
|
1330
|
+
// accessibility jsx-a11y misses, side effects in render and state updates,
|
|
1331
|
+
// Effect lifecycle and teardown, render-path performance, reduced motion
|
|
1332
|
+
// and font scaling, and component and hook size and complexity.
|
|
1333
|
+
{
|
|
1334
|
+
files: ["**/*.ts?(x)", "**/*.js?(x)"],
|
|
1335
|
+
name: "react-doctor/react",
|
|
1336
|
+
plugins: REACT_DOCTOR_PLUGINS,
|
|
1337
|
+
rules: REACT_DOCTOR_REACT_RULES
|
|
1338
|
+
},
|
|
1130
1339
|
{
|
|
1131
1340
|
files: ["**/*.test.ts?(x)", "**/*.stories.ts?(x)"],
|
|
1132
1341
|
name: "react/test-files",
|
|
@@ -1136,6 +1345,9 @@ var react = [
|
|
|
1136
1345
|
}
|
|
1137
1346
|
];
|
|
1138
1347
|
|
|
1348
|
+
// src/configs/react-router.ts
|
|
1349
|
+
import tanstackQuery from "@tanstack/eslint-plugin-query";
|
|
1350
|
+
|
|
1139
1351
|
// src/plugins/no-imperative-query-fetch.ts
|
|
1140
1352
|
var BANNED_METHODS = /* @__PURE__ */ new Map([
|
|
1141
1353
|
["ensureInfiniteQueryData", "infiniteQuery"],
|
|
@@ -1195,6 +1407,12 @@ var plugin7 = {
|
|
|
1195
1407
|
var no_imperative_query_fetch_default = plugin7;
|
|
1196
1408
|
|
|
1197
1409
|
// src/configs/react-router.ts
|
|
1410
|
+
var TANSTACK_QUERY_RECOMMENDED_RULES = Object.assign(
|
|
1411
|
+
{},
|
|
1412
|
+
...tanstackQuery.configs["flat/recommended"].map(
|
|
1413
|
+
(config) => config.rules
|
|
1414
|
+
)
|
|
1415
|
+
);
|
|
1198
1416
|
var ROUTE_MODULE_EXPORTS = [
|
|
1199
1417
|
"action",
|
|
1200
1418
|
"clientAction",
|
|
@@ -1230,13 +1448,56 @@ var buildReactRouter = (sourceDir) => [
|
|
|
1230
1448
|
]
|
|
1231
1449
|
}
|
|
1232
1450
|
},
|
|
1451
|
+
// TanStack Query, one block: `no-imperative-query-fetch`, the official
|
|
1452
|
+
// `@tanstack/eslint-plugin-query` recommended rules, and the react-doctor
|
|
1453
|
+
// Query rules the official plugin does not cover (`react-doctor.ts`). The
|
|
1454
|
+
// official and react-doctor rules match hooks and clients imported from
|
|
1455
|
+
// TanStack Query, so they stay silent in a project without Query.
|
|
1456
|
+
// `no-imperative-query-fetch` matches by method name.
|
|
1233
1457
|
{
|
|
1234
1458
|
files: [`${sourceDir}/**/*.ts?(x)`],
|
|
1235
1459
|
name: "react-router/no-imperative-query-fetch",
|
|
1236
|
-
plugins: {
|
|
1460
|
+
plugins: {
|
|
1461
|
+
"@tanstack/query": tanstackQuery,
|
|
1462
|
+
"no-imperative-query-fetch": no_imperative_query_fetch_default,
|
|
1463
|
+
...REACT_DOCTOR_PLUGINS
|
|
1464
|
+
},
|
|
1237
1465
|
rules: {
|
|
1466
|
+
...TANSTACK_QUERY_RECOMMENDED_RULES,
|
|
1467
|
+
...REACT_DOCTOR_QUERY_RULES,
|
|
1238
1468
|
"no-imperative-query-fetch/no-imperative-query-fetch": "error"
|
|
1239
1469
|
}
|
|
1470
|
+
},
|
|
1471
|
+
// react-doctor's CLI detects a project's capabilities and hands them to its
|
|
1472
|
+
// rules; the standalone plugin reads them from these settings instead (see
|
|
1473
|
+
// `REACT_DOCTOR_CAPABILITIES`). Route detection matches `app/routes/`,
|
|
1474
|
+
// `app/root.*` and `app/entry.*`.
|
|
1475
|
+
{
|
|
1476
|
+
files: [`${sourceDir}/**/*.ts?(x)`],
|
|
1477
|
+
name: "react-doctor/react-router-settings",
|
|
1478
|
+
settings: {
|
|
1479
|
+
"react-doctor": { capabilities: REACT_DOCTOR_CAPABILITIES }
|
|
1480
|
+
}
|
|
1481
|
+
},
|
|
1482
|
+
// Curated react-doctor rules (see `react-doctor.ts`) for React Router
|
|
1483
|
+
// framework-mode mistakes that throw or misbehave at runtime: loaders,
|
|
1484
|
+
// actions, middleware, sessions, cookies, search params, navigation, error
|
|
1485
|
+
// UI and the server entry.
|
|
1486
|
+
{
|
|
1487
|
+
files: [`${sourceDir}/**/*.ts?(x)`],
|
|
1488
|
+
name: "react-doctor/react-router",
|
|
1489
|
+
plugins: REACT_DOCTOR_PLUGINS,
|
|
1490
|
+
rules: REACT_DOCTOR_REACT_ROUTER_RULES
|
|
1491
|
+
},
|
|
1492
|
+
// A browser global read at module scope crashes the server render. Source
|
|
1493
|
+
// modules only: tests, stories and the helpers in `tests/` folders run in a
|
|
1494
|
+
// browser or a DOM environment, never through SSR.
|
|
1495
|
+
{
|
|
1496
|
+
files: [`${sourceDir}/**/*.ts?(x)`],
|
|
1497
|
+
ignores: ["**/*.test.ts?(x)", "**/*.stories.ts?(x)", "**/tests/**"],
|
|
1498
|
+
name: "react-doctor/react-router-module-scope",
|
|
1499
|
+
plugins: REACT_DOCTOR_PLUGINS,
|
|
1500
|
+
rules: REACT_DOCTOR_MODULE_SCOPE_RULES
|
|
1240
1501
|
}
|
|
1241
1502
|
];
|
|
1242
1503
|
|
|
@@ -1361,6 +1622,18 @@ var VENDORED_UI_RULES_OFF = {
|
|
|
1361
1622
|
"react-you-might-not-need-an-effect/no-external-store-subscription": "off",
|
|
1362
1623
|
"react-you-might-not-need-an-effect/no-initialize-state": "off",
|
|
1363
1624
|
"react-you-might-not-need-an-effect/no-pass-data-to-parent": "off",
|
|
1625
|
+
// The react-doctor shadcn rules check how GAIA code composes the primitives,
|
|
1626
|
+
// not the primitives themselves. toast.tsx: ToastClose's default
|
|
1627
|
+
// `render={<Button size="icon-sm" />}` reads as an unlabeled icon Button,
|
|
1628
|
+
// but the aria-label sits on the ToastPrimitive.Close that renders it.
|
|
1629
|
+
...Object.fromEntries(
|
|
1630
|
+
Object.keys(REACT_DOCTOR_SHADCN_RULES).map((rule) => [rule, "off"])
|
|
1631
|
+
),
|
|
1632
|
+
// Registry animation, scroll and font classes are shadcn's (toast.tsx
|
|
1633
|
+
// animates without a reduced-motion gate).
|
|
1634
|
+
...Object.fromEntries(
|
|
1635
|
+
Object.keys(MOTION_FONT_RULES).map((rule) => [rule, "off"])
|
|
1636
|
+
),
|
|
1364
1637
|
// sidebar.tsx: SidebarRail's <button> carries no `type`.
|
|
1365
1638
|
"react/button-has-type": "off",
|
|
1366
1639
|
// carousel.tsx, chart.tsx, toggle-group.tsx pass an inline object to a
|
|
@@ -1435,6 +1708,16 @@ var buildShadcn = (sourceDir, opts) => {
|
|
|
1435
1708
|
"shadcn/require-static-classes": "error"
|
|
1436
1709
|
}
|
|
1437
1710
|
},
|
|
1711
|
+
// Curated react-doctor rules (see `react-doctor.ts`) for shadcn
|
|
1712
|
+
// composition that breaks a primitive: an icon-only Button with no label,
|
|
1713
|
+
// a DialogContent with no title, a raw control in an InputGroup, a
|
|
1714
|
+
// TabsTrigger outside TabsList.
|
|
1715
|
+
{
|
|
1716
|
+
files: [`${sourceDir}/**/*.${SOURCE_EXTENSIONS}`],
|
|
1717
|
+
name: "react-doctor/shadcn",
|
|
1718
|
+
plugins: REACT_DOCTOR_PLUGINS,
|
|
1719
|
+
rules: REACT_DOCTOR_SHADCN_RULES
|
|
1720
|
+
},
|
|
1438
1721
|
{
|
|
1439
1722
|
files: uiFiles,
|
|
1440
1723
|
name: "shadcn/vendored-ui",
|
|
@@ -1562,6 +1845,43 @@ var perfectionistConfig = [
|
|
|
1562
1845
|
type: "natural"
|
|
1563
1846
|
}
|
|
1564
1847
|
],
|
|
1848
|
+
// TanStack Query reads option order for type inference: the
|
|
1849
|
+
// `@tanstack/query/*-property-order` rules need `onMutate` before
|
|
1850
|
+
// `onError` and `onSettled`, and `queryFn` before the page-param
|
|
1851
|
+
// getters. Plain natural order puts those keys first, so in the object
|
|
1852
|
+
// passed straight to the TanStack call they sort into a last group
|
|
1853
|
+
// instead, natural order everywhere else in it. Every other object
|
|
1854
|
+
// keeps `recommended-natural`'s sort.
|
|
1855
|
+
"perfectionist/sort-objects": [
|
|
1856
|
+
"error",
|
|
1857
|
+
{
|
|
1858
|
+
customGroups: [
|
|
1859
|
+
{
|
|
1860
|
+
elementNamePattern: "^(onError|onSettled)$",
|
|
1861
|
+
groupName: "after-on-mutate"
|
|
1862
|
+
}
|
|
1863
|
+
],
|
|
1864
|
+
groups: ["unknown", "after-on-mutate"],
|
|
1865
|
+
order: "asc",
|
|
1866
|
+
type: "natural",
|
|
1867
|
+
useConfigurationIf: { callingFunctionNamePattern: "^useMutation$" }
|
|
1868
|
+
},
|
|
1869
|
+
{
|
|
1870
|
+
customGroups: [
|
|
1871
|
+
{
|
|
1872
|
+
elementNamePattern: "^(getNextPageParam|getPreviousPageParam)$",
|
|
1873
|
+
groupName: "after-query-fn"
|
|
1874
|
+
}
|
|
1875
|
+
],
|
|
1876
|
+
groups: ["unknown", "after-query-fn"],
|
|
1877
|
+
order: "asc",
|
|
1878
|
+
type: "natural",
|
|
1879
|
+
useConfigurationIf: {
|
|
1880
|
+
callingFunctionNamePattern: "^(infiniteQueryOptions|useInfiniteQuery|useSuspenseInfiniteQuery)$"
|
|
1881
|
+
}
|
|
1882
|
+
},
|
|
1883
|
+
{ order: "asc", type: "natural" }
|
|
1884
|
+
],
|
|
1565
1885
|
"perfectionist/sort-jsx-props": [
|
|
1566
1886
|
"error",
|
|
1567
1887
|
{
|