You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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)
Mechanical renames: rename each useRef variable to end with Ref (e.g. optionMap → optionMapRef, drillDirection → drillDirectionRef). Update all usages within the same file.
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:
no-context-provider (13 files): Replace <Ctx.Provider value={...}> with <Ctx value={...}> — ESLint suggestions available for all 14 violations
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.
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).
Derivable state → useMemo: State computed from props/other state inside an effect. Replace useState + useEffect(setState) with useMemo.
Sync effects → event handlers: Effects that sync external state. Move setState to event handlers instead.
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)
Context
The
eslint.config.mjshas 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.mjsExisting PRs (already in-flight)
@eslint-react/exhaustive-deps@eslint-react/no-unnecessary-use-prefix@eslint-react/use-state@eslint-react/no-array-index-keyPR Sequence (remaining 9 rules → 8 PRs)
PR 1:
@eslint-react/no-nested-component-definitionseslint.config.mjsonly)"off"linechore/eslint-no-nested-component-definitionsSimply delete the rule's
"off"entry fromeslint.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-fnchore/eslint-no-void-query-fnAffected files:
client/src/app/queries/sboms.tsclient/src/app/queries/vulnerabilities.tsFix query functions that can return
void/undefined. These are cases wherequeryFnreturnsundefinedwhen a required parameter is missing (e.g.if (!id) return undefined). The fix is to use TanStack Query'senabledoption to prevent the query from running, or return a proper sentinel value.PR 3:
@tanstack/query/exhaustive-depsqueryKeyarrays)sboms.ts,vulnerabilities.ts)chore/eslint-tanstack-exhaustive-depsAffected files:
client/src/app/queries/advisories.tsclient/src/app/queries/sbom-groups.tsclient/src/app/queries/sboms.tsclient/src/app/queries/vulnerabilities.tsESLint suggestions are available for all 4 violations — add missing variables referenced in
queryFnto thequeryKeyarray.PR 4:
@eslint-react/naming-convention-ref-nameRefsuffix)sbom-groups-provider.tsx)chore/eslint-naming-convention-ref-nameAffected files:
client/src/app/components/DrilldownSelect/DrilldownSelect.tsxclient/src/app/components/FilterPanel/AsyncMultiselectFilterControl.tsxclient/src/app/components/FilterToolbar/AsyncMultiselectFilterControl.tsxclient/src/app/components/FilterToolbar/AutocompleteLabelFilterControl.tsxclient/src/app/pages/sbom-groups/sbom-groups-provider.tsxclient/src/app/pages/sbom-groups/sbom-groups-table.tsxMechanical renames: rename each
useRefvariable to end withRef(e.g.optionMap→optionMapRef,drillDirection→drillDirectionRef). Update all usages within the same file.PR 5:
@eslint-react/no-context-provider+@eslint-react/no-use-context(combined)chore/eslint-react19-context-apiWhy combined: Both rules are React 19 Context API modernization. They share
PageDrawerContext.tsxand a reviewer naturally understands both together — "we're adopting the React 19 context API."Two mechanical transformations:
<Ctx.Provider value={...}>with<Ctx value={...}>— ESLint suggestions available for all 14 violationsuseContext(Ctx)withuse(Ctx)and update imports — ESLint suggestions available for all 59 violationsDespite 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):
PageDrawerContext.tsx— both rules apply heresbom-groups-provider.tsx— overlaps with PR Fixerror TS2307: Cannot find module '@trustification-ui/common'#4 and existing PR chore(eslint): enable @eslint-react/use-state rule #1059Full list of affected files for
no-context-provider(13):client/src/app/components/NotificationsContext/NotificationsProvider.tsxclient/src/app/components/PageDrawerContext.tsxclient/src/app/components/ReadOnlyContext/ReadOnlyProvider.tsxclient/src/app/layout/default-layout.test.tsxclient/src/app/pages/advisory-details/csaf-provider.tsxclient/src/app/pages/advisory-list/advisory-provider.tsxclient/src/app/pages/license-list/license-provider.tsxclient/src/app/pages/model-list/model-provider.tsxclient/src/app/pages/package-list/package-provider.tsxclient/src/app/pages/sbom-groups/sbom-groups-provider.tsxclient/src/app/pages/sbom-list/sbom-provider.tsxclient/src/app/pages/search/components/SearchMenu.test.tsxclient/src/app/pages/vulnerability-list/vulnerability-provider.tsxFull list of affected files for
no-use-context(48):client/src/app/components/ErrorFallback.tsxclient/src/app/components/Notifications.tsxclient/src/app/components/PageDrawerContext.tsxclient/src/app/components/ReadOnlyButton.tsxclient/src/app/components/ReadOnlyContext/ReadOnlyContext.test.tsxclient/src/app/hooks/domain-controls/useDownload.tsclient/src/app/hooks/useNotifyErrorCallback.tsclient/src/app/layout/default-layout.tsxclient/src/app/pages/advisory-details/advisory-details.tsxclient/src/app/pages/advisory-details/components/CsafTreeChart.tsxclient/src/app/pages/advisory-details/csaf-overview.tsxclient/src/app/pages/advisory-details/csaf-product-tree.tsxclient/src/app/pages/advisory-details/csaf-relationship-tree.tsxclient/src/app/pages/advisory-details/csaf-source.tsxclient/src/app/pages/advisory-details/csaf-vulnerabilities.tsxclient/src/app/pages/advisory-list/advisory-table.tsxclient/src/app/pages/advisory-list/advisory-toolbar.tsxclient/src/app/pages/advisory-list/components/AdvisoryEditLabelsForm.tsxclient/src/app/pages/advisory-upload/advisory-upload.tsxclient/src/app/pages/importer-list/components/importer-form.tsxclient/src/app/pages/importer-list/importer-list.tsxclient/src/app/pages/license-list/license-table.tsxclient/src/app/pages/license-list/license-toolbar.tsxclient/src/app/pages/model-list/model-table.tsxclient/src/app/pages/model-list/model-toolbar.tsxclient/src/app/pages/package-list/package-table.tsxclient/src/app/pages/package-list/package-toolbar.tsxclient/src/app/pages/sbom-details/sbom-details.tsxclient/src/app/pages/sbom-groups/components/group-form/useGroupFormData.tsclient/src/app/pages/sbom-groups/sbom-groups-table.tsxclient/src/app/pages/sbom-groups/sbom-groups-toolbar.tsxclient/src/app/pages/sbom-groups/sbom-groups.tsxclient/src/app/pages/sbom-list/components/SBOMEditLabelsForm.tsxclient/src/app/pages/sbom-list/components/add-to-group-form/useAddToGroupFormData.tsclient/src/app/pages/sbom-list/sbom-table.tsxclient/src/app/pages/sbom-list/sbom-toolbar.tsxclient/src/app/pages/sbom-upload/sbom-upload.tsxclient/src/app/pages/search/components/SearchMenu.tsxclient/src/app/pages/search/search.tsxclient/src/app/pages/vulnerability-list/vulnerability-table.tsxclient/src/app/pages/vulnerability-list/vulnerability-toolbar.tsxPR 6:
@typescript-eslint/no-unused-varschore/eslint-no-unused-varsFix categories:
_or remove if last parameter_Affected files (22):
client/src/app/Constants.tsclient/src/app/axios-config/apiInit.tsclient/src/app/components/FilterToolbar/DateRangeFilter.tsxclient/src/app/components/HookFormPFFields/HookFormPFSelect.tsxclient/src/app/components/VulnerabilityGallery.tsxclient/src/app/hooks/table-controls/expansion/useExpansionState.tsclient/src/app/hooks/table-controls/filtering/helpers.tsclient/src/app/pages/advisory-list/advisory-table.tsxclient/src/app/pages/advisory-list/components/AdvisoryEditLabelsForm.tsxclient/src/app/pages/importer-list/components/importer-form.tsxclient/src/app/pages/importer-list/importer-list.tsxclient/src/app/pages/sbom-groups/components/group-form/useGroupFormData.tsclient/src/app/pages/sbom-groups/components/sbom-group-select/utils.tsclient/src/app/pages/sbom-groups/sbom-groups-table.tsxclient/src/app/pages/sbom-list/components/SBOMEditLabelsForm.tsxclient/src/app/pages/sbom-list/components/add-to-group-form/useAddToGroupFormData.tsclient/src/app/pages/sbom-list/sbom-table.tsxclient/src/app/pages/sbom-list/sbom-toolbar.tsxclient/src/app/pages/sbom-scan/components/VulnerabilityTable.tsxclient/src/app/pages/sbom-scan/sbom-scan.tsxclient/src/app/queries/advisories.tsclient/src/app/queries/importers.tsSome "unused" variables may be needed for type inference. Each case needs brief inspection.
PR 7:
@tanstack/query/prefer-query-optionschore/eslint-prefer-query-optionsAffected files (all in
client/src/app/queries/):advisories.tsimporters.tslicenses.tsmodels.tspackages.tssbom-groups.tssboms-analysis.tssboms.tstrustifyInfo.tsvulnerabilities.tsThe pattern: Extract inline
useQuery({...})options intoqueryOptions({...})factory functions. The codebase already has examples of this pattern (e.g.sbomByIdQueryOptionsinsboms.ts). Follow the existing naming convention:<entity><Action>QueryOptions/<entity><Action>MutationOptions.Note: For mutations that use
useQueryClient()inside the hook for cache invalidation inonSuccess, thequeryClientmust 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-effectchore/eslint-set-state-in-effectAffected files (14):
client/src/app/components/FilterPanel/AsyncMultiselectFilterControl.tsxclient/src/app/components/FilterPanel/AutocompleteLabelFilterControl.tsxclient/src/app/components/FilterPanel/CheckboxFilterControl.tsxclient/src/app/components/FilterPanel/DateRangeFilter.tsxclient/src/app/components/FilterPanel/SearchFilterControl.tsxclient/src/app/components/FilterToolbar/AsyncMultiselectFilterControl.tsxclient/src/app/components/FilterToolbar/AutocompleteLabelFilterControl.tsxclient/src/app/components/FilterToolbar/DateRangeFilter.tsxclient/src/app/components/FilterToolbar/SearchFilterControl.tsxclient/src/app/hooks/table-controls/filtering/useFilterState.tsclient/src/app/hooks/useMultiFileUpload.tsclient/src/app/components/UploadFiles.tsxclient/src/app/pages/advisory-details/components/CsafTreeChart.tsxclient/src/app/pages/search/components/SearchMenu.tsxRefactoring strategies (per violation):
useMemo: State computed from props/other state inside an effect. ReplaceuseState+useEffect(setState)withuseMemo.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:
Dependency Graph
Parallel lanes:
PRs in different lanes can run in parallel with each other.
File overlap analysis
Between existing PRs and new PRs
set-state-in-effectoverlaps with PR chore(eslint): enable @eslint-react/no-array-index-key rule #1060 on:UploadFiles.tsxno-use-contextoverlaps with PR chore(eslint): enable @eslint-react/no-array-index-key rule #1060 on:package-table.tsxno-context-provideroverlaps with PR chore(eslint): enable @eslint-react/use-state rule #1059 on:sbom-groups-provider.tsxnaming-convention-ref-nameoverlaps with PR chore(eslint): enable @eslint-react/use-state rule #1059 on:sbom-groups-provider.tsxBetween new PRs (cross-rule overlaps)
no-use-context∩no-context-provider:PageDrawerContext.tsx(combined in PR 5)no-use-context∩no-unused-vars: 10 shared files (PR 5 before PR 6)set-state-in-effect∩naming-convention-ref-name: 3 FilterPanel/FilterToolbar files (PR 4 before PR 8)Verification (per PR)
npm run lint— must pass with zero errorsnpm test— all unit tests must passnpm run start:dev— app must start without errorsSummary Table
no-nested-component-definitionsquery/no-void-query-fnquery/exhaustive-depsnaming-convention-ref-nameno-context-provider+no-use-contextno-unused-varsprefer-query-optionsset-state-in-effect