Spaces:
Sleeping
Sleeping
| import fs from 'node:fs'; | |
| import path from 'node:path'; | |
| import { loadConfig, DIRS } from './lib/config.js'; | |
| import { compareWeeks } from './lib/week.js'; | |
| /** | |
| * Emits the weekly Markdown summary posted as a GitHub issue comment, | |
| * matching the open-scans pattern: one comment per week, table per | |
| * domain, deltas against the previous week, links to the Pages report. | |
| * | |
| * Reads the same data/ summaries the HTML reports use. One truth. | |
| * | |
| * Usage: node src/issue-comment.js [--pages-base https://USER.github.io/REPO] | |
| * Output: docs/comment.md (also printed to stdout) | |
| */ | |
| const args = Object.fromEntries( | |
| process.argv.slice(2).join(' ').split('--').filter(Boolean).map((s) => { | |
| const [k, ...v] = s.trim().split(' '); | |
| return [k, v.join(' ') || true]; | |
| }) | |
| ); | |
| const pagesBase = (args['pages-base'] ?? process.env.PAGES_BASE_URL ?? '').replace(/\/$/, ''); | |
| const config = loadConfig(); | |
| let out = `## Weekly scan summary\n\n`; | |
| let latestWeek = null; | |
| for (const target of config.targets) { | |
| const domainDir = path.join(DIRS.data, target.key); | |
| if (!fs.existsSync(domainDir)) continue; | |
| const weeks = fs | |
| .readdirSync(domainDir) | |
| .filter((w) => /^\d{4}-W\d{2}$/.test(w) && fs.existsSync(path.join(domainDir, w, 'summary.json'))) | |
| .sort(compareWeeks); | |
| if (weeks.length === 0) continue; | |
| const week = weeks[weeks.length - 1]; | |
| latestWeek = latestWeek && compareWeeks(latestWeek, week) > 0 ? latestWeek : week; | |
| const curr = JSON.parse(fs.readFileSync(path.join(domainDir, week, 'summary.json'), 'utf8')); | |
| const prev = | |
| weeks.length > 1 | |
| ? JSON.parse(fs.readFileSync(path.join(domainDir, weeks[weeks.length - 2], 'summary.json'), 'utf8')) | |
| : null; | |
| const d = (a, b) => (prev ? ` (${a - b >= 0 ? '+' : ''}${a - b})` : ''); | |
| const reportLink = pagesBase ? `${pagesBase}/reports/${target.key}/${week}/` : `docs/reports/${target.key}/${week}/`; | |
| out += `### ${target.domain}: ${week}\n\n`; | |
| out += `| Metric | This week | Notes |\n|---|---:|---|\n`; | |
| out += `| Pages audited (unique) | ${curr.pagesAudited ?? curr.pagesScanned}${prev ? d(curr.pagesAudited ?? curr.pagesScanned, prev.pagesAudited ?? prev.pagesScanned) : ''} | scanned by axe and/or Alfa |\n`; | |
| out += `| Median axe violations / page | ${curr.axe.medianViolations ?? 'n/a'}${prev && curr.axe.medianViolations != null && prev.axe.medianViolations != null ? d(curr.axe.medianViolations, prev.axe.medianViolations) : ''} | ${curr.axe.pagesWithViolations} pages affected |\n`; | |
| out += `| Median Alfa failures / page | ${curr.alfa.medianFailures ?? 'n/a'}${prev && curr.alfa.medianFailures != null && prev.alfa.medianFailures != null ? d(curr.alfa.medianFailures, prev.alfa.medianFailures) : ''} | ${curr.alfa.pagesWithFailures} pages affected |\n`; | |
| if (curr.sustainability) { | |
| out += `| Median page weight | ${Math.round(curr.sustainability.medianBytes / 1024)} KB${prev?.sustainability ? d(Math.round(curr.sustainability.medianBytes / 1024), Math.round(prev.sustainability.medianBytes / 1024)) : ''} | est. ${curr.sustainability.meanCo2g} g CO₂/page (SWD v4) |\n`; | |
| } | |
| const topAxe = Object.entries(curr.axe.rules) | |
| .sort(([, a], [, b]) => b.pages - a.pages) | |
| .slice(0, 5); | |
| if (topAxe.length) { | |
| out += `\nTop axe rules by pages affected: ${topAxe.map(([id, r]) => `\`${id}\` (${r.pages})`).join(', ')}.\n`; | |
| } | |
| out += `\n[Full report](${reportLink})\n\n`; | |
| } | |
| if (!latestWeek) out += `No scan data found yet.\n`; | |
| out += `\n---\n*Generated by [vital-scans](https://github.com/${process.env.GITHUB_REPOSITORY ?? 'mgifford/vital-core'}). Automated checks cover roughly a third of accessibility barriers; manual testing with assistive technology remains essential.*\n`; | |
| fs.mkdirSync(DIRS.docs, { recursive: true }); | |
| fs.writeFileSync(path.join(DIRS.docs, 'comment.md'), out); | |
| console.log(out); | |