@ucm-kit/adapter-typescript 0.1.0 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 speckaina
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,20 +1,85 @@
1
1
  # @ucm-kit/adapter-typescript
2
2
 
3
- Adaptateur optionnel pour les repositories TypeScript qui consomment des
4
- contrats UCM. Il ajoute deux capacités sans faire dépendre `@ucm-kit/core` du
5
- compilateur TypeScript :
6
-
7
- - `ucm check` le découvre automatiquement lorsqu'il est installé dans le
8
- repository et compare les props, les BOOLEAN lues et les compositions ;
9
- - `ucm-typescript` génère les unions d'enums dans
10
- `src/generated/contracts/` (ou dans le dossier donné à `--out`).
3
+ Optional adapter for TypeScript repositories that consume UCM contracts. It adds
4
+ two capabilities without making [`@ucm-kit/core`](https://www.npmjs.com/package/@ucm-kit/core)
5
+ depend on the TypeScript compiler, a 23 MB dependency no other consumer should
6
+ pay for.
11
7
 
12
8
  ```sh
13
- npm install --save-dev @ucm-kit/adapter-typescript@0.1.0 @ucm-kit/cli@0.1.7
9
+ npm install --save-dev @ucm-kit/adapter-typescript@0.1.3 @ucm-kit/cli@0.1.10
14
10
  npx ucm-typescript
15
11
  npx --no-install ucm check
16
12
  ```
17
13
 
18
- La parité exige un `tsconfig.json` à la racine. Les valeurs d'enum réellement
19
- traitées par le comportement du composant restent hors de la garantie statique :
20
- le contrat décrit les vues disponibles, pas la logique qui en choisit une.
14
+ ## Static parity
15
+
16
+ `ucm check` discovers this package when it is installed in the repository, and
17
+ compares each contract to the component that implements it. Six gaps are
18
+ reported:
19
+
20
+ | Gap | What it means |
21
+ |---|---|
22
+ | Missing prop | The contract declares a prop the component's public API does not expose |
23
+ | Incompatible type | The prop exists, with a type the contract does not allow |
24
+ | Value the code does not offer | The contract publishes an enum value the declared union leaves out |
25
+ | Unused boolean | A `BOOLEAN` prop is declared but never read by the component |
26
+ | Unused enum | An enum prop is declared but never read by the component |
27
+ | Wrong composition cardinality | A declared dependency is rendered a different number of times than the contract says |
28
+
29
+ Parity requires a `tsconfig.json` at the root. Props are read with the
30
+ TypeScript type checker; compositions are counted by their occurrences in JSX.
31
+
32
+ Every one of these **warns without blocking**. They accuse the code, not the
33
+ contract, and fixing them is a developer's job, not a re-export.
34
+
35
+ ## The convention composition counting assumes
36
+
37
+ **Counting is static: a dependency rendered by a loop is not counted.** Keep
38
+ every occurrence explicit in the source, and neutralise in place the one a
39
+ given view does not show, rather than removing it. Without that, a list built
40
+ with `.map()` reports a cardinality gap the contract did not intend.
41
+
42
+ This is a convention, not a mechanism: there is no exception list, no
43
+ annotation, and no per-component opt-out. The warning itself says what it does
44
+ not see, and it blocks nothing.
45
+
46
+ ## What it does not measure
47
+
48
+ Enum values are compared by the **declared union only**. A union smaller than
49
+ the contract is reported; a union that accepts more is not, because accepting
50
+ more contradicts nothing. On a widened type such as `string`, no value is
51
+ resolved and nothing is reported.
52
+
53
+ What the component does with a value stays outside the static guarantee. A
54
+ `switch` with a `default`, a partial mapping table, a value forwarded to a
55
+ child, a table held in another file and a business rule that substitutes one
56
+ value for another are all legitimate; telling them apart from an oversight
57
+ would require the contract to describe behaviour. A contract describes the
58
+ views that exist; it says nothing about the logic that picks one.
59
+
60
+ A prop relayed through `{...rest}` without being read is reported as unused.
61
+ Following a spread would mean knowing the child's contract, and the warning
62
+ blocks nothing.
63
+
64
+ Nothing here executes a render. Parity is a comparison of surfaces.
65
+
66
+ None of this exists for a repository without this adapter, so the **absence of
67
+ these messages means nothing**: it is a capability of one adapter, never a
68
+ guarantee of the format.
69
+
70
+ ## Generated types
71
+
72
+ ```sh
73
+ npx ucm-typescript # writes to src/generated/contracts/
74
+ npx ucm-typescript --out <dir>
75
+ ```
76
+
77
+ It derives the enum unions from the contracts, so that a component's props can
78
+ be typed against the contract rather than against a hand-copied list.
79
+
80
+ ## Status
81
+
82
+ **0.x, the public surface is not frozen.** Pin an exact version, without `^`.
83
+
84
+ - [Repository and issues](https://github.com/Vassili-g/UCM-Exporter)
85
+ - [MIT licensed](https://github.com/Vassili-g/UCM-Exporter/blob/main/LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ucm-kit/adapter-typescript",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Adaptateur TypeScript UCM : parité statique des composants et génération des types dérivés des contrats.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -20,7 +20,8 @@
20
20
  },
21
21
  "files": [
22
22
  "src",
23
- "README.md"
23
+ "README.md",
24
+ "LICENSE"
24
25
  ],
25
26
  "exports": {
26
27
  ".": {
@@ -34,7 +35,7 @@
34
35
  "test": "node scripts/run-tests.cjs"
35
36
  },
36
37
  "dependencies": {
37
- "@ucm-kit/core": "0.1.11",
38
+ "@ucm-kit/core": "0.1.14",
38
39
  "typescript": ">=5.5.0 <6"
39
40
  }
40
41
  }
package/src/cli.mjs CHANGED
File without changes
package/src/index.d.mts CHANGED
@@ -18,7 +18,9 @@ export function ecartsDeParite(
18
18
  fonctionAbsente: string | null;
19
19
  manquantes: string[];
20
20
  typesIncorrects: Array<{ prop: string; attendu: string; recu: string }>;
21
+ valeursNonImplementees: Array<{ prop: string; valeurs: string[] }>;
21
22
  booleensNonUtilises: string[];
23
+ enumsSansEffet: Array<{ prop: string; valeurs: string[] }>;
22
24
  compositionsIncorrectes: Array<{ component: string; attendu: number; rendu: number }>;
23
25
  };
24
26
  export function typesDuContrat(contrat: unknown): null | {
package/src/parite.mjs CHANGED
@@ -285,9 +285,18 @@ export function lireApiPublique(fichiers, racine) {
285
285
  const estBoolean = significatifs.length > 0 && significatifs.every(
286
286
  (item) => Boolean(item.flags & (ts.TypeFlags.Boolean | ts.TypeFlags.BooleanLiteral)),
287
287
  );
288
+ // Le texte écrit dans le source ne dit rien d'un alias : `Ton | undefined`
289
+ // masque autant une union complète qu'une union amputée. La résolution,
290
+ // elle, rend les littéraux même dérivés d'un `as const`, et ne rend rien
291
+ // d'un type élargi — donc silence plutôt que faux positif.
292
+ const valeurs = significatifs.length > 0
293
+ && significatifs.every((item) => item.isStringLiteral())
294
+ ? significatifs.map((item) => item.value)
295
+ : null;
288
296
  return [membre.getName(), {
289
297
  type: estBoolean ? "boolean" : "autre",
290
298
  typescript: verificateur.typeToString(typeMembre),
299
+ valeurs,
291
300
  utilisee: consommees.has(membre.getName()),
292
301
  }];
293
302
  }));
@@ -312,7 +321,9 @@ export function ecartsDeParite(contrat, releve, nomInterface, options = {}) {
312
321
  fonctionAbsente: null,
313
322
  manquantes: [],
314
323
  typesIncorrects: [],
324
+ valeursNonImplementees: [],
315
325
  booleensNonUtilises: [],
326
+ enumsSansEffet: [],
316
327
  compositionsIncorrectes: [],
317
328
  };
318
329
  if (!releve) {
@@ -360,8 +371,35 @@ export function ecartsDeParite(contrat, releve, nomInterface, options = {}) {
360
371
  ))
361
372
  .map(([nom]) => nom)
362
373
  .sort();
374
+ const enums = declarees.filter(([nom, prop]) => (
375
+ nom in props && prop?.type === "enum" && Array.isArray(prop.values)
376
+ ));
377
+ // Une union plus LARGE que le contrat ne se rapporte pas : accepter plus ne
378
+ // contredit rien, et le dire ferait du contrat un plafond.
379
+ const valeursNonImplementees = enums
380
+ .filter(([nom]) => Array.isArray(props[nom].valeurs))
381
+ .map(([nom, prop]) => ({
382
+ prop: nom,
383
+ valeurs: prop.values.filter((valeur) => !props[nom].valeurs.includes(valeur)),
384
+ }))
385
+ .filter(({ valeurs }) => valeurs.length > 0)
386
+ .sort((gauche, droite) => gauche.prop.localeCompare(droite.prop));
387
+ // Le même signal que `booleensNonUtilises`, sur le même relevé : le taire ici
388
+ // quand on le dit là serait une incohérence, pas une décision.
389
+ const enumsSansEffet = enums
390
+ .filter(([nom]) => props[nom].utilisee !== true)
391
+ .map(([nom, prop]) => ({ prop: nom, valeurs: [...prop.values] }))
392
+ .sort((gauche, droite) => gauche.prop.localeCompare(droite.prop));
363
393
 
364
- return { ...vide, manquantes, typesIncorrects, booleensNonUtilises, compositionsIncorrectes };
394
+ return {
395
+ ...vide,
396
+ manquantes,
397
+ typesIncorrects,
398
+ valeursNonImplementees,
399
+ booleensNonUtilises,
400
+ enumsSansEffet,
401
+ compositionsIncorrectes,
402
+ };
365
403
  }
366
404
 
367
405
  /** Surface que `@ucm-kit/core/lecteurs` attend d'un adaptateur. */