feat(outils): sélection open source, sommaire d'ancres et garde-fous (B6-M4)
28 outils vérifiés en 4 rubriques (open_source.selection), affichés en premier sous un sommaire d'ancres ; grille 2 colonnes dès 1024 px. Le test contrôle la sélection (champs, https, liste noire) et les doublons d'URL. Co-Authored-By: Claude Sonnet <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0136n5xMLHuaBDLcPJYCDjRW
This commit is contained in:
co-authored by
Claude Sonnet
parent
dfbac7dd12
commit
43ae065e26
@@ -117,6 +117,46 @@ verifie(
|
||||
verifie(Array.isArray(outils.open_source?.rubriques) && outils.open_source.rubriques.every((r) => r.liens?.length > 0), 'open_source.rubriques : une rubrique vide doit disparaître')
|
||||
verifie(!!outils.contribution?.texte && !!outils.contribution?.lien && !!outils.contribution?.cta, 'contribution incomplète')
|
||||
|
||||
// ── Open source : sélection (B6-M4) ───────────────────────────
|
||||
// Chaque lien de la sélection est vérifié par une personne (licence, date) ; la liste noire
|
||||
// piraterie s'applique déjà à toute chaîne d'outils.json (voir plus haut), sélection comprise.
|
||||
const sel = outils.open_source?.selection
|
||||
verifie(!!sel && typeof sel.intention === 'string' && sel.intention, 'open_source.selection : intention manquante')
|
||||
verifie(Array.isArray(sel?.rubriques) && sel.rubriques.length >= 1, 'open_source.selection : au moins une rubrique attendue')
|
||||
const idsRub = new Set()
|
||||
for (const r of sel?.rubriques ?? []) {
|
||||
const rub = `sélection « ${r.id ?? r.titre ?? '?'} »`
|
||||
verifie(typeof r.id === 'string' && r.id && !idsRub.has(r.id), `${rub} : id manquant ou en double`)
|
||||
idsRub.add(r.id)
|
||||
verifie(typeof r.titre === 'string' && r.titre, `${rub} : titre manquant`)
|
||||
verifie(Array.isArray(r.liens) && r.liens.length > 0, `${rub} : rubrique vide`)
|
||||
for (const l of r.liens ?? []) {
|
||||
const nom = `${rub} › ${l.nom ?? '?'}`
|
||||
verifie(typeof l.nom === 'string' && l.nom, `${nom} : nom manquant`)
|
||||
verifie(typeof l.url === 'string' && l.url.startsWith('https://'), `${nom} : url https manquante (${l.url})`)
|
||||
verifie(typeof l.desc === 'string' && l.desc, `${nom} : desc manquante`)
|
||||
verifie(typeof l.licence === 'string' && l.licence, `${nom} : licence manquante`)
|
||||
verifie(DATE.test(l.verifie_le ?? ''), `${nom} : verifie_le « ${l.verifie_le} » n'est pas AAAA-MM-JJ`)
|
||||
const bas = (l.url ?? '').toLowerCase()
|
||||
const hit = LISTE_NOIRE.find((d) => bas.includes(d))
|
||||
verifie(!hit, `${nom} : domaine de la liste noire « ${hit} »`)
|
||||
}
|
||||
}
|
||||
|
||||
// Aucun doublon d'URL dans tout open_source (sélection + recommandés + rubriques fmhy), à la barre finale et à la casse près.
|
||||
const norm = (u) => u.toLowerCase().replace(/^https?:\/\//, '').replace(/^www\./, '').replace(/\/+$/, '')
|
||||
const vues = new Map()
|
||||
const urlsOS = [
|
||||
...(sel?.rubriques ?? []).flatMap((r) => (r.liens ?? []).map((l) => [`sélection › ${l.nom}`, l.url])),
|
||||
...(outils.open_source?.recommandes ?? []).filter((o) => o.url).map((o) => [`recommandé › ${o.titre}`, o.url]),
|
||||
...(outils.open_source?.rubriques ?? []).flatMap((r) => (r.liens ?? []).map((l) => [`fmhy › ${l.nom}`, l.url])),
|
||||
]
|
||||
for (const [qui, url] of urlsOS) {
|
||||
const k = norm(url)
|
||||
verifie(!vues.has(k), `open_source : URL en double « ${url} » (${vues.get(k)} et ${qui})`)
|
||||
vues.set(k, qui)
|
||||
}
|
||||
|
||||
if (echecs.length) {
|
||||
for (const e of echecs) console.log(`FAIL ${e}`)
|
||||
console.log(`\n${n - echecs.length}/${n} vérifications OK — ${echecs.length} échec(s)`)
|
||||
|
||||
Reference in New Issue
Block a user