@starterlib/cli 0.10.2 → 0.10.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/matrice-compatibilite.md
CHANGED
|
@@ -90,5 +90,6 @@ partout d'un coup.
|
|
|
90
90
|
`transloco` entrera ici quand le premier depot de SPA l'emploiera ; il declare
|
|
91
91
|
une plage de pairs tres large (`>=16`), qui ne vaut pas compatibilite testee
|
|
92
92
|
avec la 22. `orval` y est depuis l'ADR web 0008, a la version relevee le
|
|
93
|
-
2026-09-25 :
|
|
94
|
-
|
|
93
|
+
2026-09-25 : sa compatibilite avec la 22 est prouvee depuis le lot 175 (2026-09-28), par les
|
|
94
|
+
clients engendres de la coquille, du temoin, de catalog et d'identity, construits et essayes
|
|
95
|
+
contre Angular 22.2.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@starterlib/cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Chaine de verification et de construction des SPA du socle frontal starter : starter-web verify, construire, sprite, manifeste, precompresser. Jamais en dependance de production.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -128,13 +128,18 @@ export function ecrireLeManifeste(projet) {
|
|
|
128
128
|
`${prefixe}/i18n/${hacherLeCatalogue(join(projet.sortie, 'i18n'), nom, langue)}`;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if (
|
|
131
|
+
// Le sprite annonce est celui de la SOURCE, nomme par son empreinte (lot 194) : jamais le premier
|
|
132
|
+
// fichier venu d'un dossier ou deux sprites haches pouvaient coexister (le temoin y avait porte
|
|
133
|
+
// son sprite metier et une copie de celui du socle, et le manifeste pouvait annoncer l'autre).
|
|
134
|
+
const spriteSource = join(projet.racine, 'src', 'icones', 'sprite.svg');
|
|
135
|
+
if (!existsSync(spriteSource)) {
|
|
136
|
+
throw new Error('sprite metier absent : src/icones/sprite.svg.');
|
|
137
|
+
}
|
|
138
|
+
const sprite = `sprite-${empreinteCourte(readFileSync(spriteSource))}.svg`;
|
|
139
|
+
if (!existsSync(join(projet.sortie, 'icones', sprite))) {
|
|
136
140
|
throw new Error(
|
|
137
|
-
|
|
141
|
+
`${projet.sortieRelative}/icones/${sprite} absent (le sprite de src/icones/sprite.svg) : ` +
|
|
142
|
+
"starter-web sprite, puis construire, d'abord.",
|
|
138
143
|
);
|
|
139
144
|
}
|
|
140
145
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* Une ancienne empreinte est retiree : deux sprites livres, et le manifeste pourrait annoncer le
|
|
14
14
|
* mauvais.
|
|
15
15
|
*/
|
|
16
|
+
import { createHash } from 'node:crypto';
|
|
16
17
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
17
18
|
import { join } from 'node:path';
|
|
18
19
|
import { empreinteCourte } from '../commun.mjs';
|
|
@@ -47,6 +48,27 @@ function reglages(projet) {
|
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
/**
|
|
52
|
+
* L'hote livre le sprite du socle tel que `@starterlib/ui` l'a publie (lot 194) : le
|
|
53
|
+
* `catalogue.json` publie a cote en porte l'empreinte SHA-256. Un sprite recopie a la main, tronque
|
|
54
|
+
* ou d'une autre version que son catalogue arrete la construction, au lieu de livrer des icones que
|
|
55
|
+
* le catalogue ne decrit pas.
|
|
56
|
+
*/
|
|
57
|
+
function verifierLeSpriteDuSocle(source, contenu) {
|
|
58
|
+
const chemin = join(source, '..', 'catalogue.json');
|
|
59
|
+
if (!existsSync(chemin)) {
|
|
60
|
+
throw new Error(`catalogue du sprite du socle absent : ${chemin}.`);
|
|
61
|
+
}
|
|
62
|
+
const annonce = JSON.parse(readFileSync(chemin, 'utf8')).sprite ?? {};
|
|
63
|
+
const lue = createHash('sha256').update(contenu).digest('hex');
|
|
64
|
+
if (annonce.sha256 !== lue) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`sprite du socle altere : empreinte ${empreinteCourte(contenu)}, le catalogue de ` +
|
|
67
|
+
`@starterlib/ui annonce ${String(annonce.empreinte)} (${chemin}).`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
50
72
|
/** Pose le sprite hache et sa constante ; rend l'adresse servie. */
|
|
51
73
|
export function poserLeSprite(projet) {
|
|
52
74
|
const { source, dossier, adresse, genere, constante } = reglages(projet);
|
|
@@ -56,6 +78,7 @@ export function poserLeSprite(projet) {
|
|
|
56
78
|
);
|
|
57
79
|
}
|
|
58
80
|
const contenu = readFileSync(source);
|
|
81
|
+
if (projet.role === 'hote') verifierLeSpriteDuSocle(source, contenu);
|
|
59
82
|
const fichier = `sprite-${empreinteCourte(contenu)}.svg`;
|
|
60
83
|
mkdirSync(dossier, { recursive: true });
|
|
61
84
|
for (const existant of readdirSync(dossier)) {
|
|
@@ -34,16 +34,22 @@ export function typesDeCles(perimetres, catalogues) {
|
|
|
34
34
|
for (const perimetre of perimetres) {
|
|
35
35
|
const cles = Object.keys(catalogues[perimetre]?.fr ?? {}).sort();
|
|
36
36
|
lignes.push('', `/** Les cles du perimetre ${perimetre}. */`);
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
//
|
|
37
|
+
const union = cles.length === 0 ? 'never' : cles.map((cle) => `'${cle}'`).join(' | ');
|
|
38
|
+
const enLigne = `export type ${nomDuType(perimetre)} = ${union};`;
|
|
39
|
+
// La forme que Prettier (largeur 100) donne, dans ses trois cas : une ligne si elle tient ;
|
|
40
|
+
// sinon l'union sur sa propre ligne indentee, si elle y tient (0.10.3 : la 0.10.2 sautait ce
|
|
41
|
+
// cas, et un petit catalogue, le squelette de new-spa.sh pour un nom de cinq lettres, sortait
|
|
42
|
+
// en echec de `prettier --check`) ; sinon une cle par ligne. Le fichier engendre passe ainsi
|
|
43
|
+
// `prettier --check` sans etre ecarte (test/i18n.test.mjs le compare a Prettier).
|
|
42
44
|
if (enLigne.length <= 100) {
|
|
43
45
|
lignes.push(enLigne);
|
|
44
46
|
continue;
|
|
45
47
|
}
|
|
46
48
|
lignes.push(`export type ${nomDuType(perimetre)} =`);
|
|
49
|
+
if (` ${union};`.length <= 100 || cles.length === 1) {
|
|
50
|
+
lignes.push(` ${union};`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
47
53
|
cles.forEach((cle, indice) => {
|
|
48
54
|
lignes.push(` | '${cle}'${indice === cles.length - 1 ? ';' : ''}`);
|
|
49
55
|
});
|
package/src/etapes/outillage.mjs
CHANGED
|
@@ -100,17 +100,52 @@ export const test = {
|
|
|
100
100
|
executer: (projet) => deleguer(projet, ['exec', 'ng', 'test', '--no-watch'], 'ng test conforme.'),
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
+
/** Ou vit le client engendre (ADR web 0008). */
|
|
104
|
+
export const DOSSIER_DU_CLIENT_ENGENDRE = join('src', 'app', 'donnees', 'genere');
|
|
105
|
+
|
|
106
|
+
/** Un motif du contrat, tel qu'orval le recopie en commentaire du client engendre. */
|
|
107
|
+
const MOTIF_ENGENDRE = /@pattern\s+(\S.*?)\s*$/gm;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Les motifs du client engendre qui ne compilent pas en ECMA-262 (lot 175, report du lot 81) : un
|
|
111
|
+
* motif Java (`\p{Cntrl}`, quantificateur possessif, groupe atomique) recopie par le contrat. Il
|
|
112
|
+
* refuserait chaque saisie legitime, ou ne compilerait pas, le jour ou un formulaire ou un schema
|
|
113
|
+
* de validation le reprendrait. Lu avec le drapeau `u`, celui d'un motif HTML.
|
|
114
|
+
*/
|
|
115
|
+
export function motifsNonPortables(racine) {
|
|
116
|
+
const defauts = [];
|
|
117
|
+
for (const chemin of fichiers(join(racine, DOSSIER_DU_CLIENT_ENGENDRE))) {
|
|
118
|
+
if (!chemin.endsWith('.ts')) continue;
|
|
119
|
+
for (const [, motif] of readFileSync(chemin, 'utf8').matchAll(MOTIF_ENGENDRE)) {
|
|
120
|
+
try {
|
|
121
|
+
new RegExp(motif, 'u');
|
|
122
|
+
} catch (erreur) {
|
|
123
|
+
defauts.push(
|
|
124
|
+
`${relatif(racine, chemin)} : motif ${motif} hors ECMA-262 (${erreur instanceof Error ? erreur.message : String(erreur)}) ; le corriger dans le contrat du service.`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return defauts;
|
|
130
|
+
}
|
|
131
|
+
|
|
103
132
|
/**
|
|
104
|
-
* `contrat` : le client d'API engendre depuis le contrat du service, sans difference (lot 175)
|
|
105
|
-
* Tant que la SPA n'a pas de script `contrat:check`,
|
|
133
|
+
* `contrat` : le client d'API engendre depuis le contrat du service, sans difference (lot 175), et
|
|
134
|
+
* sans motif qu'un navigateur ne sait pas lire. Tant que la SPA n'a pas de script `contrat:check`,
|
|
135
|
+
* l'etape est sautee, et le dit.
|
|
106
136
|
*/
|
|
107
137
|
export const contrat = {
|
|
108
138
|
nom: 'contrat',
|
|
109
139
|
roles: ['remote', 'hote'],
|
|
110
140
|
executer(projet) {
|
|
111
141
|
if (projet.paquet.scripts?.['contrat:check'] === undefined) {
|
|
112
|
-
return { sautee: 'aucun script contrat:check
|
|
142
|
+
return { sautee: 'aucun script contrat:check : pas de client engendre (ADR web 0008).' };
|
|
113
143
|
}
|
|
114
|
-
|
|
144
|
+
const resultat = deleguer(projet, ['run', 'contrat:check'], 'client engendre sans difference.');
|
|
145
|
+
if (resultat.defauts.length > 0) return resultat;
|
|
146
|
+
const motifs = motifsNonPortables(projet.racine);
|
|
147
|
+
return motifs.length === 0
|
|
148
|
+
? { defauts: [], resume: 'client engendre sans difference, motifs ECMA-262.' }
|
|
149
|
+
: { defauts: motifs };
|
|
115
150
|
},
|
|
116
151
|
};
|
package/src/verifier.mjs
CHANGED
|
@@ -75,12 +75,14 @@ export async function verifier(
|
|
|
75
75
|
|
|
76
76
|
for (const [indice, etape] of retenues.entries()) {
|
|
77
77
|
journal.info(`[${String(indice + 1)}/${String(retenues.length)}] ${etape.nom}`);
|
|
78
|
-
if (etape.preparation === 'sprite' && !spritePose) {
|
|
79
|
-
journal.info(` sprite : ${poserLeSprite(projet)}`);
|
|
80
|
-
spritePose = true;
|
|
81
|
-
}
|
|
82
78
|
let resultat;
|
|
83
79
|
try {
|
|
80
|
+
// Un sprite qui ne se pose pas (absent, altere) est un defaut de l'etape qui le lit, nomme
|
|
81
|
+
// comme les autres, et non une commande qui ne peut pas s'executer.
|
|
82
|
+
if (etape.preparation === 'sprite' && !spritePose) {
|
|
83
|
+
journal.info(` sprite : ${poserLeSprite(projet)}`);
|
|
84
|
+
spritePose = true;
|
|
85
|
+
}
|
|
84
86
|
resultat = await etape.executer(projet, contexte);
|
|
85
87
|
} catch (erreur) {
|
|
86
88
|
resultat = { defauts: [erreur instanceof Error ? erreur.message : String(erreur)] };
|