104 lines
2.8 KiB
JavaScript
104 lines
2.8 KiB
JavaScript
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import YAML from "yaml";
|
|
|
|
const CWD = process.cwd();
|
|
const ANNO_DIR = path.join(CWD, "src", "annotations");
|
|
const PUBLIC_DIR = path.join(CWD, "public");
|
|
|
|
async function exists(p) {
|
|
try { await fs.access(p); return true; } catch { return false; }
|
|
}
|
|
|
|
async function walk(dir) {
|
|
const out = [];
|
|
const ents = await fs.readdir(dir, { withFileTypes: true });
|
|
for (const e of ents) {
|
|
const p = path.join(dir, e.name);
|
|
if (e.isDirectory()) out.push(...(await walk(p)));
|
|
else out.push(p);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function parseDoc(raw, fileAbs) {
|
|
if (/\.json$/i.test(fileAbs)) return JSON.parse(raw);
|
|
return YAML.parse(raw);
|
|
}
|
|
|
|
function isPlainObject(x) {
|
|
return !!x && typeof x === "object" && !Array.isArray(x);
|
|
}
|
|
|
|
function toPublicPathFromUrl(urlPath) {
|
|
// "/media/..." -> "public/media/..."
|
|
const clean = String(urlPath || "").split("?")[0].split("#")[0];
|
|
if (!clean.startsWith("/media/")) return null;
|
|
return path.join(PUBLIC_DIR, clean.replace(/^\/+/, ""));
|
|
}
|
|
|
|
async function main() {
|
|
if (!(await exists(ANNO_DIR))) {
|
|
console.log("✅ annotations-media: aucun src/annotations — rien à vérifier.");
|
|
process.exit(0);
|
|
}
|
|
|
|
const files = (await walk(ANNO_DIR)).filter((p) => /\.(ya?ml|json)$/i.test(p));
|
|
let checked = 0;
|
|
let missing = 0;
|
|
const notes = [];
|
|
|
|
// Optim: éviter de vérifier 100 fois le même fichier media
|
|
const seenMedia = new Set(); // src string
|
|
|
|
for (const f of files) {
|
|
const rel = path.relative(CWD, f).replace(/\\/g, "/");
|
|
const raw = await fs.readFile(f, "utf8");
|
|
|
|
let doc;
|
|
try { doc = parseDoc(raw, f); }
|
|
catch (e) {
|
|
missing++;
|
|
notes.push(`- PARSE FAIL: ${rel} (${String(e?.message ?? e)})`);
|
|
continue;
|
|
}
|
|
|
|
if (!isPlainObject(doc) || doc.schema !== 1 || !isPlainObject(doc.paras)) continue;
|
|
|
|
for (const [paraId, entry] of Object.entries(doc.paras)) {
|
|
const media = entry?.media;
|
|
if (!Array.isArray(media)) continue;
|
|
|
|
for (const m of media) {
|
|
const src = String(m?.src || "");
|
|
if (!src.startsWith("/media/")) continue; // externes ok, ou autres conventions futures
|
|
|
|
// dédupe
|
|
if (seenMedia.has(src)) continue;
|
|
seenMedia.add(src);
|
|
|
|
checked++;
|
|
const p = toPublicPathFromUrl(src);
|
|
if (!p) continue;
|
|
|
|
if (!(await exists(p))) {
|
|
missing++;
|
|
notes.push(`- MISSING MEDIA: ${src} (from ${rel} para ${paraId})`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (missing > 0) {
|
|
console.error(`FAIL: annotations media missing (checked=${checked} missing=${missing})`);
|
|
for (const n of notes) console.error(n);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`✅ annotations-media OK: checked=${checked}`);
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error("FAIL: check-annotations-media crashed:", e);
|
|
process.exit(1);
|
|
}); |