Skip to content

chore(eslint): enable all disabled rules incrementally #1128

Description

@carlosthe19916

Context

The eslint.config.mjs has 13 rules disabled with a TODO comment to enable them incrementally. 4 rules are already addressed by open PRs (#1054, #1058, #1059, #1060). This issue tracks enabling the remaining 9 rules through 8 PRs, ordered to avoid merge conflicts and create a smooth review experience.

Goal: Enable all 13 disabled ESLint rules, one PR per rule (or tightly coupled group), starting with the easiest and ending with the most complex.

Current disabled rules in eslint.config.mjs

rules: {
  "@eslint-react/set-state-in-effect": "off",
  "@eslint-react/no-use-context": "off",
  "@eslint-react/no-context-provider": "off",
  "@eslint-react/naming-convention-ref-name": "off",
  "@eslint-react/no-array-index-key": "off",
  "@eslint-react/use-state": "off",
  "@eslint-react/no-unnecessary-use-prefix": "off",
  "@typescript-eslint/no-unused-vars": "off",
  "@eslint-react/exhaustive-deps": "off",
  "@eslint-react/no-nested-component-definitions": "off",
  "@tanstack/query/prefer-query-options": "off",
  "@tanstack/query/exhaustive-deps": "off",
  "@tanstack/query/no-void-query-fn": "off",
},

Existing PRs (already in-flight)

PR Rule Files changed
#1054 @eslint-react/exhaustive-deps 4
#1058 @eslint-react/no-unnecessary-use-prefix 9
#1059 @eslint-react/use-state 5
#1060 @eslint-react/no-array-index-key 9

PR Sequence (remaining 9 rules → 8 PRs)

PR 1: @eslint-react/no-nested-component-definitions

  • Violations: 0 | Files: 1 (eslint.config.mjs only)
  • Complexity: None — just remove the "off" line
  • Dependencies: None — start immediately
  • Branch: chore/eslint-no-nested-component-definitions

Simply delete the rule's "off" entry from eslint.config.mjs. Zero violations means zero risk. This prevents future regressions and establishes the PR pattern for the series.


PR 2: @tanstack/query/no-void-query-fn

  • Violations: 2 | Files: 3 (2 source + config)
  • Complexity: Trivial
  • Dependencies: None — start immediately, parallel with PR 1
  • Branch: chore/eslint-no-void-query-fn

Affected files:

  • client/src/app/queries/sboms.ts
  • client/src/app/queries/vulnerabilities.ts

Fix query functions that can return void/undefined. These are cases where queryFn returns undefined when a required parameter is missing (e.g. if (!id) return undefined). The fix is to use TanStack Query's enabled option to prevent the query from running, or return a proper sentinel value.


PR 3: @tanstack/query/exhaustive-deps

  • Violations: 4 | Files: 5 (4 source + config)
  • Complexity: Moderate (add missing variables to queryKey arrays)
  • Dependencies: After PR 2 merges (both touch sboms.ts, vulnerabilities.ts)
  • Branch: chore/eslint-tanstack-exhaustive-deps

Affected files:

  • client/src/app/queries/advisories.ts
  • client/src/app/queries/sbom-groups.ts
  • client/src/app/queries/sboms.ts
  • client/src/app/queries/vulnerabilities.ts

ESLint suggestions are available for all 4 violations — add missing variables referenced in queryFn to the queryKey array.


PR 4: @eslint-react/naming-convention-ref-name

  • Violations: 7 | Files: 7 (6 source + config)
  • Complexity: Trivial (rename variables to end with Ref suffix)
  • Dependencies: After existing PR chore(eslint): enable @eslint-react/use-state rule #1059 merges (both touch sbom-groups-provider.tsx)
  • Branch: chore/eslint-naming-convention-ref-name

Affected files:

  • client/src/app/components/DrilldownSelect/DrilldownSelect.tsx
  • client/src/app/components/FilterPanel/AsyncMultiselectFilterControl.tsx
  • client/src/app/components/FilterToolbar/AsyncMultiselectFilterControl.tsx
  • client/src/app/components/FilterToolbar/AutocompleteLabelFilterControl.tsx
  • client/src/app/pages/sbom-groups/sbom-groups-provider.tsx
  • client/src/app/pages/sbom-groups/sbom-groups-table.tsx

Mechanical renames: rename each useRef variable to end with Ref (e.g. optionMapoptionMapRef, drillDirectiondrillDirectionRef). Update all usages within the same file.


PR 5: @eslint-react/no-context-provider + @eslint-react/no-use-context (combined)

Why combined: Both rules are React 19 Context API modernization. They share PageDrawerContext.tsx and a reviewer naturally understands both together — "we're adopting the React 19 context API."

Two mechanical transformations:

  1. no-context-provider (13 files): Replace <Ctx.Provider value={...}> with <Ctx value={...}> — ESLint suggestions available for all 14 violations
  2. no-use-context (48 files): Replace useContext(Ctx) with use(Ctx) and update imports — ESLint suggestions available for all 59 violations

Despite the high file count, every change is a simple find-and-replace with no judgment calls. The PR is large but easy to review because the pattern is identical across all files.

Key files (overlap points):

Full list of affected files for no-context-provider (13):

  • client/src/app/components/NotificationsContext/NotificationsProvider.tsx
  • client/src/app/components/PageDrawerContext.tsx
  • client/src/app/components/ReadOnlyContext/ReadOnlyProvider.tsx
  • client/src/app/layout/default-layout.test.tsx
  • client/src/app/pages/advisory-details/csaf-provider.tsx
  • client/src/app/pages/advisory-list/advisory-provider.tsx
  • client/src/app/pages/license-list/license-provider.tsx
  • client/src/app/pages/model-list/model-provider.tsx
  • client/src/app/pages/package-list/package-provider.tsx
  • client/src/app/pages/sbom-groups/sbom-groups-provider.tsx
  • client/src/app/pages/sbom-list/sbom-provider.tsx
  • client/src/app/pages/search/components/SearchMenu.test.tsx
  • client/src/app/pages/vulnerability-list/vulnerability-provider.tsx

Full list of affected files for no-use-context (48):

  • client/src/app/components/ErrorFallback.tsx
  • client/src/app/components/Notifications.tsx
  • client/src/app/components/PageDrawerContext.tsx
  • client/src/app/components/ReadOnlyButton.tsx
  • client/src/app/components/ReadOnlyContext/ReadOnlyContext.test.tsx
  • client/src/app/hooks/domain-controls/useDownload.ts
  • client/src/app/hooks/useNotifyErrorCallback.ts
  • client/src/app/layout/default-layout.tsx
  • client/src/app/pages/advisory-details/advisory-details.tsx
  • client/src/app/pages/advisory-details/components/CsafTreeChart.tsx
  • client/src/app/pages/advisory-details/csaf-overview.tsx
  • client/src/app/pages/advisory-details/csaf-product-tree.tsx
  • client/src/app/pages/advisory-details/csaf-relationship-tree.tsx
  • client/src/app/pages/advisory-details/csaf-source.tsx
  • client/src/app/pages/advisory-details/csaf-vulnerabilities.tsx
  • client/src/app/pages/advisory-list/advisory-table.tsx
  • client/src/app/pages/advisory-list/advisory-toolbar.tsx
  • client/src/app/pages/advisory-list/components/AdvisoryEditLabelsForm.tsx
  • client/src/app/pages/advisory-upload/advisory-upload.tsx
  • client/src/app/pages/importer-list/components/importer-form.tsx
  • client/src/app/pages/importer-list/importer-list.tsx
  • client/src/app/pages/license-list/license-table.tsx
  • client/src/app/pages/license-list/license-toolbar.tsx
  • client/src/app/pages/model-list/model-table.tsx
  • client/src/app/pages/model-list/model-toolbar.tsx
  • client/src/app/pages/package-list/package-table.tsx
  • client/src/app/pages/package-list/package-toolbar.tsx
  • client/src/app/pages/sbom-details/sbom-details.tsx
  • client/src/app/pages/sbom-groups/components/group-form/useGroupFormData.ts
  • client/src/app/pages/sbom-groups/sbom-groups-table.tsx
  • client/src/app/pages/sbom-groups/sbom-groups-toolbar.tsx
  • client/src/app/pages/sbom-groups/sbom-groups.tsx
  • client/src/app/pages/sbom-list/components/SBOMEditLabelsForm.tsx
  • client/src/app/pages/sbom-list/components/add-to-group-form/useAddToGroupFormData.ts
  • client/src/app/pages/sbom-list/sbom-table.tsx
  • client/src/app/pages/sbom-list/sbom-toolbar.tsx
  • client/src/app/pages/sbom-upload/sbom-upload.tsx
  • client/src/app/pages/search/components/SearchMenu.tsx
  • client/src/app/pages/search/search.tsx
  • client/src/app/pages/vulnerability-list/vulnerability-table.tsx
  • client/src/app/pages/vulnerability-list/vulnerability-toolbar.tsx

PR 6: @typescript-eslint/no-unused-vars

  • Violations: 38 | Files: 23 (22 source + config)
  • Complexity: Moderate
  • Dependencies: After PR 5 merges (10 shared files)
  • Branch: chore/eslint-no-unused-vars

Fix categories:

  1. Unused imports — remove them (4 auto-fixable)
  2. Unused function parameters — prefix with _ or remove if last parameter
  3. Unused destructured variables — remove from destructuring pattern or prefix with _
  4. Unused local variables — remove declaration and associated computation

Affected files (22):

  • client/src/app/Constants.ts
  • client/src/app/axios-config/apiInit.ts
  • client/src/app/components/FilterToolbar/DateRangeFilter.tsx
  • client/src/app/components/HookFormPFFields/HookFormPFSelect.tsx
  • client/src/app/components/VulnerabilityGallery.tsx
  • client/src/app/hooks/table-controls/expansion/useExpansionState.ts
  • client/src/app/hooks/table-controls/filtering/helpers.ts
  • client/src/app/pages/advisory-list/advisory-table.tsx
  • client/src/app/pages/advisory-list/components/AdvisoryEditLabelsForm.tsx
  • client/src/app/pages/importer-list/components/importer-form.tsx
  • client/src/app/pages/importer-list/importer-list.tsx
  • client/src/app/pages/sbom-groups/components/group-form/useGroupFormData.ts
  • client/src/app/pages/sbom-groups/components/sbom-group-select/utils.ts
  • client/src/app/pages/sbom-groups/sbom-groups-table.tsx
  • client/src/app/pages/sbom-list/components/SBOMEditLabelsForm.tsx
  • client/src/app/pages/sbom-list/components/add-to-group-form/useAddToGroupFormData.ts
  • client/src/app/pages/sbom-list/sbom-table.tsx
  • client/src/app/pages/sbom-list/sbom-toolbar.tsx
  • client/src/app/pages/sbom-scan/components/VulnerabilityTable.tsx
  • client/src/app/pages/sbom-scan/sbom-scan.tsx
  • client/src/app/queries/advisories.ts
  • client/src/app/queries/importers.ts

Some "unused" variables may be needed for type inference. Each case needs brief inspection.


PR 7: @tanstack/query/prefer-query-options

  • Violations: 43 | Files: 10 (9 source + config)
  • Complexity: Moderate (systematic pattern refactor)
  • Dependencies: After PR 3 merges (both modify query files extensively)
  • Branch: chore/eslint-prefer-query-options

Affected files (all in client/src/app/queries/):

  • advisories.ts
  • importers.ts
  • licenses.ts
  • models.ts
  • packages.ts
  • sbom-groups.ts
  • sboms-analysis.ts
  • sboms.ts
  • trustifyInfo.ts
  • vulnerabilities.ts

The pattern: Extract inline useQuery({...}) options into queryOptions({...}) factory functions. The codebase already has examples of this pattern (e.g. sbomByIdQueryOptions in sboms.ts). Follow the existing naming convention: <entity><Action>QueryOptions / <entity><Action>MutationOptions.

Note: For mutations that use useQueryClient() inside the hook for cache invalidation in onSuccess, the queryClient must be passed as a parameter to the options factory, or the factory must return only the options that don't depend on the client.

Can run in parallel with PRs 4, 5, 6 (different file sets — query files vs component/page files).


PR 8: @eslint-react/set-state-in-effect

Affected files (14):

  • client/src/app/components/FilterPanel/AsyncMultiselectFilterControl.tsx
  • client/src/app/components/FilterPanel/AutocompleteLabelFilterControl.tsx
  • client/src/app/components/FilterPanel/CheckboxFilterControl.tsx
  • client/src/app/components/FilterPanel/DateRangeFilter.tsx
  • client/src/app/components/FilterPanel/SearchFilterControl.tsx
  • client/src/app/components/FilterToolbar/AsyncMultiselectFilterControl.tsx
  • client/src/app/components/FilterToolbar/AutocompleteLabelFilterControl.tsx
  • client/src/app/components/FilterToolbar/DateRangeFilter.tsx
  • client/src/app/components/FilterToolbar/SearchFilterControl.tsx
  • client/src/app/hooks/table-controls/filtering/useFilterState.ts
  • client/src/app/hooks/useMultiFileUpload.ts
  • client/src/app/components/UploadFiles.tsx
  • client/src/app/pages/advisory-details/components/CsafTreeChart.tsx
  • client/src/app/pages/search/components/SearchMenu.tsx

Refactoring strategies (per violation):

  1. Derivable state → useMemo: State computed from props/other state inside an effect. Replace useState + useEffect(setState) with useMemo.
  2. Sync effects → event handlers: Effects that sync external state. Move setState to event handlers instead.
  3. Init patterns → default value: Effects that set state once on mount. Move initial value into useState(initialValue).

This is the hardest PR. Each violation requires understanding the component's intent. Consider splitting into two sub-PRs if the review scope is too large:

  • Sub-PR A: FilterPanel + FilterToolbar (9 files, similar patterns)
  • Sub-PR B: Remaining 5 files (hooks, components, pages)

Dependency Graph

                    Existing PRs
         ┌──────────┬──────────┬──────────┐
       #1054      #1058      #1059      #1060
                               │          │
    PR 1 (0 violations)        │          │
    PR 2 (2 violations)        │          │
      │                        │          │
    PR 3 (4 violations)     PR 4 (7 violations)
      │                        │
    PR 7 (43 violations)    PR 5 (73 violations)
                               │
                            PR 6 (38 violations)
                               │
                            PR 8 (24 violations)

Parallel lanes:

  • Lane A (TanStack): PR 1 → PR 2 → PR 3 → PR 7
  • Lane B (React): PR 4 → PR 5 → PR 6 → PR 8

PRs in different lanes can run in parallel with each other.

File overlap analysis

Between existing PRs and new PRs

Between new PRs (cross-rule overlaps)

  • no-use-contextno-context-provider: PageDrawerContext.tsx (combined in PR 5)
  • no-use-contextno-unused-vars: 10 shared files (PR 5 before PR 6)
  • set-state-in-effectnaming-convention-ref-name: 3 FilterPanel/FilterToolbar files (PR 4 before PR 8)
  • TanStack rules share 4 query files (sequential PRs 2 → 3 → 7)

Verification (per PR)

  1. Run npm run lint — must pass with zero errors
  2. Run npm test — all unit tests must pass
  3. Run npm run start:dev — app must start without errors

Summary Table

Order Rule(s) Violations Files Complexity Blocked by
PR 1 no-nested-component-definitions 0 1 None
PR 2 query/no-void-query-fn 2 3 Trivial
PR 3 query/exhaustive-deps 4 5 Moderate PR 2
PR 4 naming-convention-ref-name 7 7 Trivial #1059
PR 5 no-context-provider + no-use-context 73 49 Mechanical PR 4, #1059, #1060
PR 6 no-unused-vars 38 23 Moderate PR 5
PR 7 prefer-query-options 43 10 Moderate PR 3
PR 8 set-state-in-effect 24 15 Complex PR 4, PR 5, #1060
Total 191

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-kindIndicates an issue or PR lacks a `kind/foo` label and requires one.needs-priorityIndicates an issue or PR lacks a `priority/foo` label and requires one.needs-triageIndicates an issue or PR lacks a `triage/foo` label and requires one.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions