Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions .github/workflows/publish-ui-kit.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
name: Publish @4sh/ui-kit to npm

# Publishes TWO packages in lockstep, from the same approved job:
# @4sh/ui-kit — the compiled components
# @4sh/ui-kit — the compiled components (library mode)
# @4sh/ui-kit-schematics — the raw sources the starter copies (FSHSP-109)
# They always carry the same version (stamped from the kit at assembly time),
# because the kit's ng-add facade requests `^<kit version>` of the companion.
# They always carry the same version (stamped from the kit at assembly time):
# the companion embeds a copy of the kit's sources, and that shared number is what
# identifies which kit a copied file came from (FSHSP-122).

# Manual trigger only: publishing is irreversible (a name+version can never be
# reused, and `npm unpublish` is limited to 72h), so it never fires on a push.
Expand Down Expand Up @@ -73,10 +74,10 @@ jobs:
- name: Build the schematics package
run: npm run schematics:build

# Les deux packages partent ensemble et portent le MÊME numéro : la façade
# de `@4sh/ui-kit` demande le compagnon en `^<version du kit>` (voir
# projects/ui-kit/schematics/index.cjs). Un décalage ici casserait
# `ng add` chez le consommateur, sans que rien ne le signale côté kit.
# Les deux packages partent ensemble et portent le MÊME numéro : c'est lui
# qui identifie de quel kit vient un fichier copié (en-tête de traçabilité
# + ui-kit.json, dont `update` se sert pour ses diffs). Un décalage ici
# rendrait cette provenance fausse, sans que rien ne le signale.
- name: Check both packages carry the same version
run: |
kit=$(node -p "require('./dist/ui-kit/package.json').version")
Expand Down Expand Up @@ -197,16 +198,19 @@ jobs:
# no notion of a transaction: the second publish can fail on its own. The
# order decides what a half-published release leaves behind.
#
# companion then kit (this order) — if the companion fails, the kit is
# never published and nothing on the registry references a missing
# package. If the kit then fails, the orphan companion version is
# inert: no released kit points at it.
# kit then companion — a companion failure would strand a published kit
# whose facade requests `^<version>` of something that does not exist,
# breaking `ng add` for everyone, with no way to unpublish after 72h.
# Since FSHSP-122 neither package references the other at install time, so
# a partial release no longer breaks anyone — it used to strand a published
# kit whose facade required a companion that did not exist, making `ng add`
# fail for everyone. What is left at stake is narrower:
#
# Both failure modes are recoverable, but only one of them is invisible to
# consumers. Do not swap these two steps.
# companion then kit (this order) — an orphan companion ships sources
# whose header announces a kit version absent from the registry.
# kit then companion — an orphan kit leaves the starter path copying the
# PREVIOUS version's sources, silently, while library mode moved on.
#
# The first is the more legible failure: the version it names simply is not
# there yet, rather than a starter quietly one release behind. Either way
# the fix is to publish the missing half, or a patch. Do not swap these.
- name: Publish @4sh/ui-kit-schematics
run: npm publish --provenance
working-directory: dist/ui-kit-schematics
Expand Down
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,41 @@ Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) et le pr
### Added
- **`@4sh/ui-kit-schematics` embarque son `LICENSE` et ses README** (EN + FR). Le package déclarait `Apache-2.0` sans en fournir le texte, alors que la licence (§4a) demande qu'une redistribution en joigne une copie — d'autant plus ici que ce package existe pour que son contenu soit recopié ailleurs. Sa page npmjs, jusque-là vide, redirige maintenant vers `@4sh/ui-kit` : le compagnon ne s'installe jamais directement.

### Fixed
- **`ui-file-upload` ne déclare plus `UiLink`** (FSHSP-124). Le composant l'avait dans ses `imports` sans jamais s'en servir — en mode `drag`, le lien est un `<span>` stylé à l'intérieur du `<label>`, un élément interactif y étant à éviter. Le build d'un projet en sources copiées remontait un `NG8113`, et `ui-file-upload` faisait recopier `ui-link` pour rien.
- **Les sources copiées n'importent plus depuis `node_modules`** (FSHSP-119). Un composant copié résolvait ses dépendances dans `node_modules/@4sh/ui-kit`, c'est-à-dire le code compilé : modifier le `ui-icon` copié n'avait aucun effet sur les composants qui l'utilisent, ce qui annulait la raison d'être du starter. Les 139 imports `@4sh/ui-kit/*` des sources sont désormais réadressés vers les copies voisines au moment de la copie.

Un import passant par un barrel est résolu jusqu'au fichier qui porte réellement le symbole, et **scindé** si ses symboles sont dispersés (`{ UiIcon, UiIconType }` → `ui-icon.ts` + `ui-icon-families.ts`). Un symbole introuvable interrompt la copie en le nommant, plutôt que d'écrire un chemin faux.

Effets de bord corrigés au passage : les dépendances copiées n'étaient utilisées par personne (copies mortes, suivies par `update` pour rien), le bundle embarquait le composant deux fois, et du code applicatif dépendait d'une `devDependency` — ce qui cassait en `npm ci --omit=dev`.

### Changed
- ⚠️ **Le parcours starter s'installe en une commande, et n'installe plus `@4sh/ui-kit`** (FSHSP-122) :

```bash
ng add @4sh/ui-kit-schematics # fondation + sélection des composants, enchaînées
```

Auparavant il en fallait deux (`ng add @4sh/ui-kit` pour la fondation, puis `ng generate @4sh/ui-kit:add` pour les composants), et le kit restait dans `node_modules` : l'auto-complétion de l'IDE proposait ses imports alors que le projet doit utiliser ses copies locales. Les deux problèmes avaient la même cause — la porte d'entrée. Au moment du `ng add`, le compagnon n'était pas encore installé, d'où une `RunSchematicTask` différée, dans laquelle un prompt interactif ne tient pas.

En entrant par le compagnon, le kit n'est plus installé du tout : plus rien à auto-importer, et le prompt fonctionne dans la foulée de la fondation. La version copiée est lue dans le compagnon lui-même, plus dans le `package.json` du consommateur.

`--skip-components` pose la fondation seule, pour choisir les composants plus tard.

**Le mode librairie est inchangé** : `npm i @4sh/ui-kit` puis `import { UiButton } from '@4sh/ui-kit/actions/ui-button'`. Seule la voie « sources copiées » change. `ng add @4sh/ui-kit` installe désormais le kit en `dependencies` (et non plus en `devDependencies`, qui n'avait de sens que pour piloter la CLI) et affiche laquelle des deux voies a été prise.

**Migration** depuis `0.2.0` : désinstaller `@4sh/ui-kit` du projet (`npm rm @4sh/ui-kit`), les sources copiées n'en dépendant plus.
- ⚠️ **L'arborescence copiée est aplatie, et les fichiers transverses sortent de `components/`** (FSHSP-121). La copie reproduisait la structure d'`ng-packagr` (`ui-checkbox/src/lib/ui-checkbox.ts`, plus un barrel `src/public-api.ts`) : `src/` et `lib/` délimitent la surface publiée d'une librairie, et une fois le fichier chez le consommateur ils n'encadrent plus rien — ils enfouissaient chaque composant de deux niveaux. Elles disparaissent, le barrel avec :

```
src/app/shared/
├── components/ui/{catégorie}/{ui-nom}/{ui-nom}.ts ← uniquement des composants
└── ui-core/{forms|motion|overlay|theming|types}/ ← directives de base, services, utilitaires, types
```

Les bases partagées atterrissaient dans `components/ui/{catégorie}/_shared/`, c'est-à-dire des services (`theme.service`) et des utilitaires (`mask-engine`) rangés sous `components/`. Elles vivent maintenant dans `ui-core/`, en conservant leur regroupement par domaine. Les fichiers propres à un composant (`date-utils`, `ui-alert.types`, `ui-toast.service`…) restent à côté de lui : ils ne servent qu'à lui.

**Mise à jour non triviale** pour un projet déjà installé en `0.2.0` : les chemins des fichiers copiés changent tous, donc `update` ne reconnaîtra pas les anciens. Déplacer les fichiers existants avant de lancer la commande, ou repartir d'un `add` propre.
- **L'en-tête de traçabilité des fichiers copiés par `ng generate @4sh/ui-kit:add` porte la mention de licence** (`Apache-2.0 — Copyright 2026 4SH.`) en plus de son origine et de sa version. Une fois copié, le fichier vit dans le dépôt du consommateur, où plus rien n'indiquait sous quels termes il est fourni. Conséquence attendue : le premier `ng generate @4sh/ui-kit:update` suivant cette version affiche un diff d'une ligne en tête de chaque fichier installé.

## [0.2.0] - 2026-08-14
Expand Down
73 changes: 50 additions & 23 deletions docs/PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,42 @@ Versionnage : voir [`VERSIONING.md`](./VERSIONING.md).
| `@4sh/ui-kit-schematics` | les **sources brutes** que le starter recopie chez le consommateur, et les schematics qui les copient | `projects/ui-kit-schematics` |

Le second existe parce que `ng-packagr` *inline* template et SCSS dans le `.mjs`
publié : les sources que `ng generate @4sh/ui-kit:add` doit copier n'existent
nulle part dans le tarball du kit (FSHSP-109).
publié : les sources que les schematics doivent copier n'existent nulle part dans
le tarball du kit (FSHSP-109).

Chacun sert **un mode de consommation**, et les deux ne se croisent pas
(FSHSP-122) :

| Mode | Commande | Ce que le consommateur installe |
|---|---|---|
| Librairie | `npm i @4sh/ui-kit` | le kit compilé |
| Starter (sources copiées) | `ng add @4sh/ui-kit-schematics` | le compagnon seul — **jamais le kit** |

Le kit est absent du parcours starter délibérément : hors de `node_modules`, aucun
import ne peut viser son code compilé au lieu des copies locales.

**Les deux portent toujours le même numéro de version, et se publient dans le
même job.** Le `ng-add` de `@4sh/ui-kit` réclame le compagnon en
`^<version du kit>` ([`projects/ui-kit/schematics/index.cjs`](../projects/ui-kit/schematics/index.cjs)) :
publier l'un sans l'autre casse `ng add` chez le consommateur. La version du
compagnon est d'ailleurs **estampillée depuis celle du kit** au moment de
l'assemblage (`scripts/schematics-package.build.mjs`) — il n'y a pas de numéro à
tenir à jour à deux endroits, et le job `verify` vérifie la parité avant toute
publication.

> ⚠️ **Ordre de publication : le compagnon d'abord, le kit ensuite.** npm ne
> connaît pas la transaction ; si la seconde publication échoue, l'ordre décide
> de ce qui reste. Un compagnon orphelin est inerte (aucun kit publié ne le
> référence) ; un kit publié sans son compagnon est cassé pour tout le monde, et
> irréparable passé 72 h. Le workflow applique cet ordre, avec le raisonnement
> en commentaire — ne pas l'inverser.
même job.** La version du compagnon est **estampillée depuis celle du kit** au
moment de l'assemblage (`scripts/schematics-package.build.mjs`) — il n'y a pas de
numéro à tenir à jour à deux endroits, et le job `verify` vérifie la parité avant
toute publication.

Ce numéro commun n'est pas cosmétique : le compagnon embarque une copie des
sources du kit, et c'est lui qui identifie **de quel kit** vient un fichier copié.
Il est inscrit dans l'en-tête de traçabilité de chaque fichier et dans le
`ui-kit.json` du consommateur, dont `update` se sert pour calculer ses diffs.

> ⚠️ **Ordre de publication : le compagnon d'abord, le kit ensuite.** Le workflow
> applique cet ordre — ne pas l'inverser sans relire ce qui suit.
>
> Depuis FSHSP-122, aucun des deux packages ne référence l'autre à l'installation :
> une publication partielle ne casse donc plus personne, là où un kit publié sans
> son compagnon rendait auparavant `ng add` inopérant pour tout le monde. Ce qui
> reste en jeu est plus étroit : un compagnon publié seul livre des sources dont
> l'en-tête annonce une version de kit absente du registre, et un kit publié seul
> laisse le parcours starter sur les sources de la version précédente. L'ordre
> actuel privilégie le second cas, moins déroutant — mais dans les deux
> situations, la réponse est de publier le manquant, ou un correctif.

---

Expand Down Expand Up @@ -314,16 +332,25 @@ Contenu et résolution des imports strictement identiques à une vraie
publication. Pour du développement en parallèle :
`npm install ../starter-angular/dist/ui-kit`.

Pour éprouver le **starter** (`ng add`, `ng generate …:add`), il faut les deux
packages, le kit ne sachant que déléguer :
Pour éprouver le **starter**, le compagnon suffit — il porte les sources *et* les
schematics, et le parcours n'installe pas le kit :

```bash
npm run schematics:pack # → 4sh-ui-kit-schematics-<version>.tgz
npm run ui-kit:pack # → 4sh-ui-kit-<version>.tgz

# dans le projet consommateur — le compagnon d'abord, sinon `ng add` tirerait
# la version publiée sur le registre au lieu de celle qu'on veut éprouver
# dans le projet consommateur
npm install -D /chemin/vers/4sh-ui-kit-schematics-<version>.tgz
npm install -D /chemin/vers/4sh-ui-kit-<version>.tgz
ng generate @4sh/ui-kit:add
npx ng generate @4sh/ui-kit-schematics:ng-add --components ui-button ui-checkbox
```

On passe par `ng generate …:ng-add` plutôt que `ng add` : la commande publiée
`ng add @4sh/ui-kit-schematics` irait chercher le package sur le registre, pas le
tarball local. La règle exécutée est exactement la même. Et `--components` évite
le prompt interactif, ce qui rend l'essai scriptable — l'omettre le rétablit.

Vérifications qui valent la peine, une fois la commande passée :

```bash
grep '@4sh' package.json # le kit doit être ABSENT, seul le compagnon apparaît
npm install && npx ng build # les sources copiées doivent compiler telles quelles
```
15 changes: 9 additions & 6 deletions docs/VERSIONING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ Format: `MAJOR.MINOR.PATCH`
## Scope: one version number, two published packages

SemVer applies to **`projects/ui-kit/package.json`** only — that is the artifact
published as `@4sh/ui-kit`, the one a consumer's `package.json` pins a version
against.
published as `@4sh/ui-kit`, the one a library-mode consumer's `package.json` pins a
version against.

`@4sh/ui-kit-schematics` (the raw sources the starter copies, see
[`PUBLISHING.md`](./PUBLISHING.md)) is published from the same repo, at the same
Expand All @@ -17,10 +17,13 @@ rather than maintained separately. It has no version of its own to bump: the
number written in `projects/ui-kit-schematics/package.json` is a development
placeholder and is overwritten by the build.

That lockstep is not cosmetic. The kit's `ng-add` requests the companion as
`^<kit version>`, so the two moving independently would break `ng add` for
consumers. It also means the table below is read against the **kit**: a change
confined to the schematics still ships under the kit's next version.
That lockstep is not cosmetic. The companion embeds a copy of the kit's sources,
and the shared number is what identifies **which kit** a copied file came from: it
is written into every copied file's traceability header and into the consumer's
`ui-kit.json`, which `update` reads to compute its diffs. Two numbers drifting
apart would make that provenance meaningless. It also means the table below is read
against the **kit**: a change confined to the schematics still ships under the
kit's next version.

The root `package.json` (demo app + Storybook tooling) is **not** versioned in
step with it. Nothing depends on it: it is never published, `private: true`, and
Expand Down
Loading
Loading