#!/usr/bin/env node /** * Full-site Lighthouse accessibility audit (live URL). * Usage: node scripts/a11y/audit-site-lighthouse.mjs [--url=https://mim4u.org] */ import { spawnSync } from 'node:child_process' import { mkdirSync, readFileSync, writeFileSync } from 'node:fs' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' const __dirname = dirname(fileURLToPath(import.meta.url)) const repoRoot = join(__dirname, '../..') const outDir = join(repoRoot, 'reports/status') const urlArg = process.argv.find((a) => a.startsWith('--url=')) const url = urlArg ? urlArg.split('=')[1] : 'https://mim4u.org' const lhOut = join(outDir, 'lighthouse-site-a11y-latest.json') mkdirSync(outDir, { recursive: true }) const chromePath = process.env.CHROME_PATH || '/usr/bin/google-chrome' const result = spawnSync( 'npx', [ '--yes', 'lighthouse', url, '--only-categories=accessibility,performance,best-practices,seo', '--skip-audits=full-page-screenshot,screenshot-thumbnails', '--output=json', `--output-path=${lhOut}`, '--chrome-flags=--headless --no-sandbox', ], { cwd: repoRoot, env: { ...process.env, CHROME_PATH: chromePath }, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], } ) if (result.status !== 0 && !readFileSync(lhOut, 'utf8').includes('"categories"')) { console.error((result.stderr || result.stdout || 'Lighthouse failed').trim()) process.exit(1) } const raw = JSON.parse(readFileSync(lhOut, 'utf8')) const scores = Object.fromEntries( Object.entries(raw.categories).map(([k, v]) => [k, Math.round(v.score * 100)]) ) const failing = raw.categories.accessibility.auditRefs .filter((ref) => { const audit = raw.audits[ref.id] return audit && audit.score !== null && audit.score < 1 }) .map((ref) => ({ id: ref.id, title: raw.audits[ref.id].title })) const report = { schemaVersion: '1.0.0', assessmentDate: new Date().toISOString().slice(0, 10), url, scores, lcpMs: raw.audits['largest-contentful-paint']?.numericValue ? Math.round(raw.audits['largest-contentful-paint'].numericValue) : undefined, accessibilityFailingAudits: failing, reportPath: lhOut, } const outPath = join(outDir, 'mim4u-site-lighthouse-latest.json') writeFileSync(outPath, `${JSON.stringify(report, null, 2)}\n`) console.log('Lighthouse scores:', scores) if (failing.length) { failing.forEach((f) => console.log(` a11y FAIL: ${f.id} — ${f.title}`)) } console.log(`Report: ${outPath}`) process.exit(scores.accessibility >= 95 ? 0 : 1)