From 801a12d17806f27edee312f15a9e6439a08f7625 Mon Sep 17 00:00:00 2001 From: Adam Setch Date: Sat, 24 Oct 2026 09:48:12 -0400 Subject: [PATCH] refactor(renderer): import store modules directly in utils --- ARCHITECTURE.md | 3 ++- src/renderer/utils/core/storage.ts | 5 +++-- src/renderer/utils/notifications/filters/reason.ts | 2 +- .../utils/notifications/filters/reviewRequestType.ts | 2 +- src/renderer/utils/notifications/filters/search.ts | 2 +- src/renderer/utils/notifications/filters/state.ts | 2 +- src/renderer/utils/notifications/filters/userType.ts | 2 +- src/renderer/utils/notifications/group.ts | 2 +- src/renderer/utils/notifications/notifications.ts | 3 ++- src/renderer/utils/notifications/remove.ts | 2 +- src/renderer/utils/system/comms.ts | 2 +- src/renderer/utils/system/tray.ts | 2 +- vite.config.mts | 9 +++++++++ 13 files changed, 25 insertions(+), 13 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f5e89f569..eff531ba8 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -42,6 +42,7 @@ The renderer is layered. **Runtime imports flow in one direction only** — a mo - `utils/` is split by concern: `api`, `auth`, `core`, `forges`, `notifications`, `system`, `ui`. - `types.ts`, `constants.ts`, and `icons/` are the leaf modules: shared types, shared values, and presentational icons. `types.ts` imports types from `utils/auth/types` and generated GitHub GraphQL types — those imports are type-only, so the direction rule does not apply. `icons/` sits below `utils`, so both `utils` and `components` may import it (a forge adapter declares its brand mark from here). - `stores/` holds client state (Zustand): persisted `accounts` and `settings`, session `filters` and notification action failures. +- `stores/index.ts` is the public store facade for `hooks`, `components`, and `routes`. Lower layers (`utils`) import the specific store module instead (for example `stores/useFiltersStore`): the barrel re-exports every store and its dependencies, which would pull the account/auth/forge graph into leaf utilities and create cycles. - `utils` and `stores` are **peers** — they may reference each other. Neither may import `hooks`, `components`, or `routes`. - `components/` is grouped by feature (`notifications`, `filters`, `settings`, `login`, `fields`, `avatars`, `layout`, `metrics`, `primitives`). - The composition root (`App.tsx`, `index.tsx`) is the only place allowed to wire every layer together. @@ -78,4 +79,4 @@ Multi-forge support is adapter-based. Each forge implements the `ForgeAdapter` i ## Enforcement -The one-way module dependency direction is checked automatically: `vite.config.mts` defines per-layer `no-restricted-imports` overrides for the renderer, and `pnpm check` fails on a violation. The enforced rules are: `utils` and `stores` must not import `hooks`, `components`, or `routes`; `hooks` must not import `components` or `routes`; `components` must not import `routes`; and the leaf layers stay leaves — `icons` must not import any implementation layer, and the leaf modules `types`/`constants` must not import implementation layers either (their type-only imports are permitted). Any future exception must be argued and documented here rather than silently allow-listed. +The one-way module dependency direction is checked automatically: `vite.config.mts` defines per-layer `no-restricted-imports` overrides for the renderer, and `pnpm check` fails on a violation. The enforced rules are: `utils` and `stores` must not import `hooks`, `components`, or `routes`; `utils` must not import the `stores` barrel (it imports the specific store module); `hooks` must not import `components` or `routes`; `components` must not import `routes`; and the leaf layers stay leaves — `icons` must not import any implementation layer, and the leaf modules `types`/`constants` must not import implementation layers either (their type-only imports are permitted). Any future exception must be argued and documented here rather than silently allow-listed. diff --git a/src/renderer/utils/core/storage.ts b/src/renderer/utils/core/storage.ts index f3ee39285..989b30c2c 100644 --- a/src/renderer/utils/core/storage.ts +++ b/src/renderer/utils/core/storage.ts @@ -1,7 +1,8 @@ import { Constants } from '../../constants'; -import { DEFAULT_SETTINGS_STATE, useAccountsStore, useSettingsStore } from '../../stores'; -import { sanitizeAccounts } from '../../stores/useAccountsStore'; +import { DEFAULT_SETTINGS_STATE } from '../../stores/defaults'; +import useAccountsStore, { sanitizeAccounts } from '../../stores/useAccountsStore'; +import useSettingsStore from '../../stores/useSettingsStore'; import { rendererLogError, rendererLogInfo, toError } from './logger'; diff --git a/src/renderer/utils/notifications/filters/reason.ts b/src/renderer/utils/notifications/filters/reason.ts index 406f2dc4c..2c4ea969a 100644 --- a/src/renderer/utils/notifications/filters/reason.ts +++ b/src/renderer/utils/notifications/filters/reason.ts @@ -1,4 +1,4 @@ -import { useFiltersStore } from '../../../stores'; +import useFiltersStore from '../../../stores/useFiltersStore'; import type { AccountNotifications, diff --git a/src/renderer/utils/notifications/filters/reviewRequestType.ts b/src/renderer/utils/notifications/filters/reviewRequestType.ts index 63b457827..12ceed5df 100644 --- a/src/renderer/utils/notifications/filters/reviewRequestType.ts +++ b/src/renderer/utils/notifications/filters/reviewRequestType.ts @@ -1,4 +1,4 @@ -import { useFiltersStore } from '../../../stores'; +import useFiltersStore from '../../../stores/useFiltersStore'; import type { AccountNotifications, diff --git a/src/renderer/utils/notifications/filters/search.ts b/src/renderer/utils/notifications/filters/search.ts index a21052f51..75ebca15d 100644 --- a/src/renderer/utils/notifications/filters/search.ts +++ b/src/renderer/utils/notifications/filters/search.ts @@ -1,4 +1,4 @@ -import { useFiltersStore } from '../../../stores'; +import useFiltersStore from '../../../stores/useFiltersStore'; import type { RawGitifyNotification } from '../../../types'; diff --git a/src/renderer/utils/notifications/filters/state.ts b/src/renderer/utils/notifications/filters/state.ts index 4deeef736..46e11bde5 100644 --- a/src/renderer/utils/notifications/filters/state.ts +++ b/src/renderer/utils/notifications/filters/state.ts @@ -1,4 +1,4 @@ -import { useFiltersStore } from '../../../stores'; +import useFiltersStore from '../../../stores/useFiltersStore'; import type { AccountNotifications, diff --git a/src/renderer/utils/notifications/filters/userType.ts b/src/renderer/utils/notifications/filters/userType.ts index bb13d6942..5f34fae4d 100644 --- a/src/renderer/utils/notifications/filters/userType.ts +++ b/src/renderer/utils/notifications/filters/userType.ts @@ -1,4 +1,4 @@ -import { useFiltersStore } from '../../../stores'; +import useFiltersStore from '../../../stores/useFiltersStore'; import type { AccountNotifications, diff --git a/src/renderer/utils/notifications/group.ts b/src/renderer/utils/notifications/group.ts index b4f04d9d2..9f5ebeb45 100644 --- a/src/renderer/utils/notifications/group.ts +++ b/src/renderer/utils/notifications/group.ts @@ -1,4 +1,4 @@ -import { useSettingsStore } from '../../stores'; +import useSettingsStore from '../../stores/useSettingsStore'; import type { GitifyNotification } from '../../types'; diff --git a/src/renderer/utils/notifications/notifications.ts b/src/renderer/utils/notifications/notifications.ts index 2ab200b76..9e04f01c9 100644 --- a/src/renderer/utils/notifications/notifications.ts +++ b/src/renderer/utils/notifications/notifications.ts @@ -1,4 +1,5 @@ -import { useAccountsStore, useSettingsStore } from '../../stores'; +import useAccountsStore from '../../stores/useAccountsStore'; +import useSettingsStore from '../../stores/useSettingsStore'; import type { Account, diff --git a/src/renderer/utils/notifications/remove.ts b/src/renderer/utils/notifications/remove.ts index 2e3e7dde2..484f1d9f3 100644 --- a/src/renderer/utils/notifications/remove.ts +++ b/src/renderer/utils/notifications/remove.ts @@ -1,4 +1,4 @@ -import { useSettingsStore } from '../../stores'; +import useSettingsStore from '../../stores/useSettingsStore'; import type { Account, AccountNotifications, GitifyNotification } from '../../types'; diff --git a/src/renderer/utils/system/comms.ts b/src/renderer/utils/system/comms.ts index a7ef8639d..1cf807994 100644 --- a/src/renderer/utils/system/comms.ts +++ b/src/renderer/utils/system/comms.ts @@ -4,7 +4,7 @@ import type { TrayIconAppearance, } from '../../../shared/events'; -import { useSettingsStore } from '../../stores'; +import useSettingsStore from '../../stores/useSettingsStore'; import { type Link, OpenPreference } from '../../types'; diff --git a/src/renderer/utils/system/tray.ts b/src/renderer/utils/system/tray.ts index 4b592f4ee..bec5f4101 100644 --- a/src/renderer/utils/system/tray.ts +++ b/src/renderer/utils/system/tray.ts @@ -1,4 +1,4 @@ -import { useSettingsStore } from '../../stores'; +import useSettingsStore from '../../stores/useSettingsStore'; import { updateTrayColor, updateTrayTitle } from './comms'; diff --git a/vite.config.mts b/vite.config.mts index f3d68ca61..80643fddf 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -140,6 +140,13 @@ const forgeSpecificBoundaryPattern = rendererBoundaryPattern( 'Renderer boundary: shared code must use the forge adapter instead of importing a forge-specific module (see ARCHITECTURE.md).', ); +const storeBarrelBoundaryPattern = { + group: ['**/stores', '**/stores/index', '**/stores/index.ts'], + allowTypeImports: true, + message: + 'Renderer boundary: import the specific store module instead of the `stores` barrel (see ARCHITECTURE.md).', +}; + export default defineConfig({ plugins: [ // only run the checker plugin in dev (not during `vite build`) @@ -290,6 +297,7 @@ export default defineConfig({ ), processBoundaryPattern, forgeSpecificBoundaryPattern, + storeBarrelBoundaryPattern, ], }, ], @@ -311,6 +319,7 @@ export default defineConfig({ 'Renderer boundary: `utils` may not import from hooks, components, or routes (see ARCHITECTURE.md).', ), processBoundaryPattern, + storeBarrelBoundaryPattern, ], }, ],