Aller au contenu principal

Structurer un projet multi-modules

Pour une bibliothèque de composants réutilisable ou publiée, gardez les composants et les stories dans des modules séparés.

Le plugin dev.cstories.gradle ajoute cstories-runtime et l'infrastructure du catalogue au module auquel il est appliqué. Si ce module est la bibliothèque publiée de composants, les consommateurs peuvent être obligés de résoudre les dépendances runtime de CStories même lorsqu'ils n'utilisent que les composants.

Un setup mono-module convient uniquement à un design system appartenant à une application ou à un module qui n'est pas publié comme bibliothèque réutilisable.

Quand un seul module est acceptable

Si votre module de design system non publié cible déjà jvm() (ou que vous êtes prêt à l'ajouter), vous pouvez appliquer le plugin CStories directement dessus. Un catalogue uniquement jvm() n'a besoin d'aucune cible wasmJs, donc il ne force jamais Gradle à résoudre les dépendances commonMain pour une plateforme que votre module ne supporte pas par ailleurs :

// lib/build.gradle.kts
plugins {
kotlin("multiplatform") version "2.2.0"
id("org.jetbrains.compose") version "1.8.2"
id("org.jetbrains.kotlin.plugin.compose") version "2.2.0"
id("dev.cstories.gradle") version "1.1.5"
}

kotlin {
jvm()
}

Structure recommandée pour les bibliothèques publiées

Le module de composants ne doit pas appliquer dev.cstories.gradle. Il peut uniquement appliquer dev.cstories.gradle.components lorsqu'il a besoin de références de composants générées.

Le module de stories est le seul module qui applique dev.cstories.gradle. Ainsi, cstories-runtime, les tâches du catalogue et les dépendances spécifiques à CStories restent en dehors de la bibliothèque publiée de composants.

Pourquoi séparer composants et stories

Si vous voulez aussi (ou seulement) le catalogue web, et que votre module de design system cible déjà d'autres plateformes et embarque des dépendances qui ne sont pas publiées pour wasmJs (une bibliothèque d'icônes privée, un SDK spécifique à une plateforme, ...), appliquer le plugin directement sur ce module avec wasmJs déclaré force Gradle à résoudre toutes ses dépendances commonMain pour wasmJs aussi — le build peut alors échouer avec des erreurs du type :

Could not resolve com.example:some-native-only-lib:1.0.0.
Required by:
project :lib

La structure recommandée reflète le principe central de CStories (@CStory ne vit jamais sur le composant du design system lui-même) au niveau du module : garder un module de stories séparé qui dépend de :lib comme une dépendance normale, et qui est le seul endroit où wasmJs et le plugin CStories sont appliqués. :lib lui-même reste complètement inchangé — aucune nouvelle cible, aucune nouvelle contrainte de résolution de dépendances.

:lib // votre design system, inchangé — jvm, ios, android, peu importe ce qu'il cible déjà
:lib:stories // nouveau module — dépend de :lib, applique le plugin CStories, cible wasmJs (et/ou jvm)

Mise en place

settings.gradle.kts :

include(":lib", ":lib:stories")

lib/stories/build.gradle.kts :

import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl

plugins {
kotlin("multiplatform") version "2.2.0"
id("org.jetbrains.compose") version "1.8.2"
id("org.jetbrains.kotlin.plugin.compose") version "2.2.0"
id("dev.cstories.gradle") version "1.1.5"
}

@OptIn(ExperimentalWasmDsl::class)
kotlin {
jvm() // optionnel — ajoutez-le pour un catalogue desktop en plus du catalogue web
wasmJs {
browser()
binaries.executable()
}

sourceSets {
commonMain.dependencies {
implementation(project(":lib"))
}
}
}

Les stories vivent dans lib/stories/src/commonMain, en important les composants depuis :lib et en les démontrant. :lib ne dépend jamais de CStories, et :lib:stories n'a jamais besoin de résoudre les dépendances de :lib non publiées pour wasmJs, pour aucune autre cible que wasmJs.

Le plugin se charge du reste pour la ou les cibles déclarées : il câble cstories-annotations, cstories-runtime, et la dépendance KSP cstories-processor, et génère le ou les points d'entrée du catalogue. Ces dépendances restent dans le module de stories et ne sont pas nécessaires aux consommateurs de :lib.

Et ensuite ?

Si votre composant et sa story vivent dans des modules différents, le référencer de façon sûre depuis la story nécessite une étape supplémentaire — voir Référencer un composant dans une story.