Frontend
This document covers the frontend web distribution model, Compose dependency versioning rules, and frontend-app repository and ViewModel conventions.
For the broader system map, see architecture-overview.md.
frontend-app Web Distribution
frontend-app no longer builds or deploys its own container image. Its JS browser distribution is published as the consumable webDistElements directory artifact. The backing distribution task is selected by the resolved build type via the conventions.js.dist convention plugin: DEV uses jsBrowserDevelopmentExecutableDistribution (unminified, fast local builds), while STG/PROD use jsBrowserDistribution (production webpack with terser/DCE/scope hoisting). SCSS is compiled via sass-loader and extracted to a single entry CSS asset (mini-css-extract-plugin) unconditionally for every build type; the bundle's styles.scss is imported as a static @JsModule entry import (Main.kt) so webpack emits one stable main.css linked from index.html rather than an async CSS chunk plus a JS loader chunk.
backend-web-app resolves that artifact, stages it into build/caddy/dist, and packages it into the production Caddy image. backend-web-app:assemble pulls the frontend distribution transitively.
Frontend Compose Dependency Versioning
Frontend Compose modules currently include frontend-app and frontend-charts.
- Prefer version-catalog aliases such as
libs.compose.runtime,libs.compose.foundation,libs.compose.ui, andlibs.compose.html.corein dependency blocks. Do not use the deprecatedcompose.*dependency accessors there; keepcompose.desktop.currentOsonly for the desktop target artifact. - Keep
org.jetbrains.compose.*modules on the sharedcomposeversion ingradle/libs.versions.toml. - Keep
org.jetbrains.compose.material3:material3on the separatecompose-material3version because it does not track the rest of the JetBrains Compose modules. - Keep AndroidX Compose-adjacent families on their own version refs:
org.jetbrains.androidx.lifecycle,org.jetbrains.androidx.navigation,androidx.savedstate, andandroidx.activity:activity-compose. - Add
compose.foundationexplicitly when frontend code uses layout, focus, scrolling, pointer-input, or other foundation APIs; do not rely on those APIs arriving transitively. - Any module that uses Kotest artifacts should import
platform(libs.kotest.bom)in the relevant configuration/source set instead of versioning individual Kotest modules directly.
frontend-app Repository And ViewModel Conventions
FeedsRepositoryandStrategiesRepositoryare internal interfaces with private in-file implementations. Construct them viacompanion object { operator fun invoke(...) }and keep DI wiring inAppComponentpointing at those factories.- Prefer mocking those repository interfaces directly in
commonTestrather than introducing extra port types just forViewModeltests. - Shared instrument-search UI state should stay generic over a feature-owned mode type. Keep
instrumentsearch.ViewModeas the shared interface withdisplayValue, define per-screen enums such asFeedsViewModeandStrategiesViewModein the owning feature package, and thread them throughInstrumentSearchViewState<TViewMode>/SearchView<TViewMode>so eachViewModelkeeps exhaustive mode handling without meaningless cross-screen values. - For
ViewModelsearch/filter logic, keep helper functions private and verify behavior through exposed flows such assearchViewStateinstead of widening visibility for tests. - Prefer private top-level helpers over nested local functions when extracting non-trivial
ViewModellogic.
frontend-app Test Conventions
frontend-app is a Kotlin Multiplatform UI module, so its verification is target-specific rather than a single JVM test task:
- Shared
ViewModeltests live insrc/commonTestand use KotestFunSpec, Mokkery mocks, Turbine flow assertions, andandroidx.lifecycle.viewmodel.testing.viewModelScenario. CoroutineTestExtensioninsrc/commonTestsetsDispatchers.Mainbefore each test and resets it afterward. Specs that use it should enable Kotest's coroutine test scope (coroutineTestScope = true) so the test body runs under virtual time.viewModelScenario { ... }.use { ... }is the preferred harness forViewModellifecycle tests in common code.- Android host unit tests for
frontend-apprun throughtestDebugUnitTest; keepandroid.testOptions.unitTests.isReturnDefaultValues = truebecauseviewModelScenarioreaches AndroidBundleAPIs that otherwise throw "not mocked" errors under local unit tests. - Verify frontend-app changes with
./gradlew :frontend-app:testDebugUnitTest :frontend-app:desktopTest :frontend-app:jsTestwhen the change touches sharedViewModeltests or other code exercised by Android host tests. In CI, the root unit-test step runs./gradlew test allTests, which covers backend JVM tests plus KMPallTeststasks such as:frontend-app:allTests.
Related Documents
- architecture-overview.md — frontend delivery model in context
- testing.md — full test suite layout including frontend-app test suites
- build-system.md — Compose versioning catalog and Gradle dependency rules