Skip to content

refactor: feature-first architecture, tests, CI hardening, bugfixes#2

Merged
kWAYTV merged 9 commits into
mainfrom
agent/screaming-architecture-65b8
Jun 10, 2026
Merged

refactor: feature-first architecture, tests, CI hardening, bugfixes#2
kWAYTV merged 9 commits into
mainfrom
agent/screaming-architecture-65b8

Conversation

@kWAYTV

@kWAYTV kWAYTV commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

What

Reorganizes the source into feature-first ("screaming") modules, adds a Vitest test suite, hardens CI, and fixes two real bugs found during the audit.

Architecture

Technical-layer folders (components/, hooks/, preferences/, steam-ui/) are dissolved into feature modules:

Module Responsibility
src/trackers/ catalog, URL transforms, preferences storage + hook
src/tracker-menu/ menu injected into Steam profile sidebars
src/popup/ toolbar popup app + components
src/steam/ profile URL parsing, match patterns
src/i18n/ locales, stored preference, runtime + hook
src/entrypoints/ thin WXT wiring only

Storage keys now live with their domains, install/update lifecycle branching moved into the background entrypoint, pure i18n helpers extracted to i18n/messages.ts, and all filenames are kebab-case (the useFilenamingConvention lint override is removed).

Bugfixes

  • Lost preference writes: setTrackerPreference is a read-modify-write of the whole record; two rapid popup toggles could clobber each other. Writes are now serialized through a queue. Regression test included (verified failing without the fix).
  • Listener leak: the content script's browser.storage.onChanged listener was never removed when the script context was invalidated (extension update/reload). Now cleaned up via ctx.onInvalidated.
  • Hardening: dropdown/trigger/empty-state DOM is built with createElement/textContent instead of interpolating translated strings into innerHTML.

Tests (new)

Vitest + WXT testing plugin (fakeBrowser) + happy-dom — 55 tests across 8 files: profile URL parsing, tracker URL transforms, catalog invariants, preference storage (incl. concurrency regression), locale normalization, i18n substitutions, and the injected menu's rendering/a11y/keyboard navigation. bun run test.

CI

  • Quality job: lint → locale check → typecheck → test
  • Build matrix: chrome + firefox
  • Concurrency cancels superseded PR runs
  • Release workflow lints and tests before zipping
  • check-locales now also validates $n placeholder consistency per key

Cleanup

  • Dead code: resolveLocale, GITHUB_ISSUES_URL, unused popupSubtitle key in all 7 locales
  • wxt.svg (was shipping inside the extension bundle) and react.svg template leftovers removed
  • json-sort script dropped its monorepo (packages/*, apps/*) discovery leftover

Verification

  • ultracite check, tsc --noEmit, check:locales, vitest run (55/55), chrome + firefox builds all pass
  • Manually tested the built extension in Chrome: popup toggles/tabs/language and the injected Trackers menu on a live Steam profile
Open in Web Open in Cursor 

Replace technical-layer folders (components/, hooks/, preferences/,
steam-ui/) with feature modules that reflect what the extension does:

- src/trackers/    tracker catalog, URL building, preferences + hook
- src/tracker-menu/ menu injected into Steam profile sidebars
- src/popup/       toolbar popup app and components
- src/steam/       Steam profile URL parsing and match patterns
- src/i18n/        locales, stored preference, runtime + hook

Entrypoints are now thin wiring only. Storage keys live with their
domains (TRACKER_PREFERENCES_KEY, LOCALE_KEY), install/update lifecycle
branching moved to the background entrypoint, pure message helpers
extracted to i18n/messages.ts, and all filenames are kebab-case so the
useFilenamingConvention override is no longer needed.
setTrackerPreference does a read-modify-write of the whole preferences
record. Two rapid toggles in the popup could both read the same
snapshot and the second write would silently revert the first one.
All writes now go through a queue so each read sees the previous
write's result.
browser.storage.onChanged listeners are not cleaned up by WXT's
content script context. After an extension update or reload the
orphaned listener kept firing against an invalidated context. Register
a named handler and remove it via ctx.onInvalidated.
Construct trigger, direct link, and empty state with createElement and
textContent instead of interpolating translated strings into innerHTML.
Hardens against markup injection from locale files and deduplicates the
count_link_label markup.
- resolveLocale was never called; locale resolution is handled by
  initI18n falling back to browser.i18n for the system setting
- GITHUB_ISSUES_URL constant was unreferenced
- popupSubtitle message key was unused in all seven locales
- wxt.svg shipped in the extension bundle and react.svg were template
  leftovers
Vitest with WXT's testing plugin (fakeBrowser for storage-backed code)
and happy-dom for DOM assertions. Covers Steam profile URL parsing,
tracker URL transforms, catalog invariants, preference storage
(including a regression test for the concurrent-write race), locale
storage normalization, i18n substitutions, and the injected menu's
rendering, a11y attributes, and keyboard navigation.

Run with bun run test / bun run test:watch.
Split CI into a quality job (lint, locale check, typecheck, test) and
a build matrix (chrome, firefox). Cancel superseded runs on PR
branches. Release workflow now lints and tests before zipping.
json-sort carried monorepo package discovery (packages/*, apps/*) from
a template; this repo has a single package.json. check-locales now also
verifies $n substitution tokens match the base locale per key.
@kWAYTV kWAYTV marked this pull request as ready for review June 10, 2026 01:17
@kWAYTV kWAYTV merged commit fcc7f97 into main Jun 10, 2026
3 checks passed
@kWAYTV kWAYTV deleted the agent/screaming-architecture-65b8 branch June 10, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants