/** * Controleert of de dependencies in package.json op de laatste versie staan. * * Vergelijkt de versie in package.json met "latest" in het npm-register. Werkt * zonder node_modules en zonder npm install, dus geschikt als snelle check in * de testsuite. * * Gebruik: node docker/check-updates.mjs [map ...] * node docker/check-updates.mjs frontend backend * * Exit 0 = alles actueel (of alleen waarschuwingen) * Exit 1 = achterstand gevonden én --strict meegegeven */ import { readFile } from "node:fs/promises"; import { join } from "node:path"; const args = process.argv.slice(2); const strict = args.includes("--strict"); const mappen = args.filter((a) => !a.startsWith("--")); const doelen = mappen.length ? mappen : ["frontend", "backend"]; /** "^1.2.3" → [1,2,3] */ function ontleed(versie) { const schoon = String(versie).replace(/^[\^~>=<\s]*/, "").split("-")[0]; const delen = schoon.split(".").map((n) => parseInt(n, 10) || 0); return [delen[0] ?? 0, delen[1] ?? 0, delen[2] ?? 0]; } /** Vergelijkt twee versies: -1 kleiner, 0 gelijk, 1 groter */ function vergelijk(a, b) { const va = ontleed(a), vb = ontleed(b); for (let i = 0; i < 3; i++) { if (va[i] !== vb[i]) return va[i] < vb[i] ? -1 : 1; } return 0; } /** Welk deel van het versienummer verschilt */ function soort(huidig, laatste) { const va = ontleed(huidig), vb = ontleed(laatste); if (va[0] !== vb[0]) return "major"; if (va[1] !== vb[1]) return "minor"; return "patch"; } async function laatsteVersie(naam) { const url = `https://registry.npmjs.org/${naam.replace("/", "%2F")}/latest`; const res = await fetch(url, { signal: AbortSignal.timeout(10000) }); if (!res.ok) throw new Error(`register gaf ${res.status}`); return (await res.json()).version; } /** * Bewuste uitzonderingen: dependencies die om een reden achterblijven. * Staan in docker/update-exceptions.json en laten de check niet falen. */ async function leesUitzonderingen() { try { const ruw = await readFile(join(process.cwd(), "docker", "update-exceptions.json"), "utf8"); return JSON.parse(ruw); } catch { return {}; } } const uitzonderingen = await leesUitzonderingen(); let achterstand = 0; let mislukt = 0; for (const map of doelen) { const pad = join(process.cwd(), map, "package.json"); let pkg; try { pkg = JSON.parse(await readFile(pad, "utf8")); } catch { console.log(`\n${map}/package.json niet gevonden — overgeslagen`); continue; } const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) }; const namen = Object.keys(deps).sort(); console.log(`\n${map}/package.json (${namen.length} dependencies)`); const resultaten = await Promise.all(namen.map(async (naam) => { const huidig = deps[naam]; // Alleen echte versienummers vergelijken (geen git/file/workspace ranges) if (!/^[\^~]?\d/.test(huidig)) return { naam, huidig, status: "overgeslagen" }; try { const laatste = await laatsteVersie(naam); return vergelijk(huidig, laatste) < 0 ? { naam, huidig, laatste, status: "verouderd", soort: soort(huidig, laatste) } : { naam, huidig, laatste, status: "actueel" }; } catch (err) { return { naam, huidig, status: "onbekend", fout: err.message }; } })); const verouderd = resultaten.filter((r) => r.status === "verouderd" && !(uitzonderingen[map]?.[r.naam])); const vastgezet = resultaten.filter((r) => r.status === "verouderd" && uitzonderingen[map]?.[r.naam]); const onbekend = resultaten.filter((r) => r.status === "onbekend"); for (const r of verouderd) { console.log(` [${r.soort.padEnd(5)}] ${r.naam.padEnd(24)} ${r.huidig.padEnd(12)} → ${r.laatste}`); } for (const r of vastgezet) { console.log(` [vast ] ${r.naam.padEnd(24)} ${r.huidig.padEnd(12)} → ${String(r.laatste).padEnd(10)} ${uitzonderingen[map][r.naam]}`); } for (const r of onbekend) { console.log(` [? ] ${r.naam.padEnd(24)} kon niet worden opgezocht (${r.fout})`); } if (!verouderd.length && !vastgezet.length && !onbekend.length) { console.log(" alles actueel"); } else if (!verouderd.length && !onbekend.length) { console.log(" actueel op de vastgezette versies na"); } achterstand += verouderd.length; mislukt += onbekend.length; } console.log(""); if (achterstand === 0) { console.log(mislukt ? `Geen achterstand gevonden (${mislukt} niet opgezocht).` : "Alle dependencies staan op de laatste versie."); } else { console.log(`${achterstand} dependency(s) niet op de laatste versie.`); console.log("Bijwerken: pas package.json aan en herschrijf de lockfile:"); console.log(" docker run --rm -v ${PWD}/:/w -w /w node:22-alpine npm install --package-lock-only"); } process.exit(achterstand > 0 && strict ? 1 : 0);