import fs from 'node:fs'; import path from 'node:path'; import crypto from 'node:crypto'; const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..'); const required = [ '09_DEVELOPER_HANDOFF_v1.0.md', 'DECISION_LOG_v9.0.md', 'DEFINITION_OF_DONE_v1.0.md', 'OPEN_ISSUE_REGISTER_v1.0.csv', 'tokens/design-tokens_v1.0.json', 'tokens/semantic-tokens_v1.0.css', 'components/COMPONENT_CONTRACTS_v1.0.md', 'localization/ROUTE_MANIFEST_v1.0.json', 'localization/locales/en/homepage.json', 'localization/locales/fr/homepage.json', 'localization/locales/ar/homepage.json', 'interaction/theme-bootstrap_v1.0.js', 'qa/QA_MATRIX_v1.0.csv', 'qa/ACCESSIBILITY_CHECKLIST_v1.0.csv', 'qa/PERFORMANCE_BUDGETS_v1.0.json', ]; const errors = []; let checks = 0; for (const file of required) { checks++; if (!fs.existsSync(path.join(root, file))) errors.push(`Missing ${file}`); } const readJson = (f) => JSON.parse(fs.readFileSync(path.join(root, f), 'utf8')); for (const file of [ 'tokens/design-tokens_v1.0.json', 'localization/ROUTE_MANIFEST_v1.0.json', 'qa/PERFORMANCE_BUDGETS_v1.0.json', 'qa/visual-regression-scenarios_v1.0.json', ]) { try { readJson(file); checks++; } catch (e) { errors.push(`${file}: ${e.message}`); } } function shape(value) { if (Array.isArray(value)) return value.map(shape); if (value && typeof value === 'object') return Object.fromEntries( Object.keys(value) .sort() .map((k) => [k, shape(value[k])]), ); return typeof value; } const locales = ['en', 'fr', 'ar'].map((l) => readJson(`localization/locales/${l}/homepage.json`)); const base = JSON.stringify(shape(locales[0])); for (let i = 1; i < locales.length; i++) { checks++; if (JSON.stringify(shape(locales[i])) !== base) errors.push(`Locale shape mismatch: ${['en', 'fr', 'ar'][i]}`); } const routes = readJson('localization/ROUTE_MANIFEST_v1.0.json'); for (const route of routes.routes) { for (const locale of routes.locales) { checks++; if (!(locale in route.slugs)) errors.push(`Route ${route.id} missing ${locale}`); } } const issueCsv = fs.readFileSync(path.join(root, 'OPEN_ISSUE_REGISTER_v1.0.csv'), 'utf8'); checks++; if (/,(Critical|High),[^\n]*,,/.test(issueCsv)) errors.push('Critical/High issue missing ownership data'); const css = fs.readFileSync(path.join(root, 'tokens/semantic-tokens_v1.0.css'), 'utf8'); checks++; if (!css.includes('--color-action-conversion')) errors.push('Conversion semantic token missing'); if (errors.length) { console.error(JSON.stringify({ status: 'FAILED', checks, errors }, null, 2)); process.exit(1); } console.log( JSON.stringify({ status: 'PASSED', checks, files: fs.readdirSync(root).length }, null, 2), );