#!/usr/bin/env node const fs = require('fs'); const path = require('path'); // Import validation from atproto lexicon package const atprotoPath = path.join(__dirname, '../../atproto/packages/lexicon'); const { Lexicons } = require(path.join(atprotoPath, 'dist/index.js')); function findJsonFiles(dir, fileList = []) { const files = fs.readdirSync(dir); files.forEach(file => { // Skip node_modules, tsp-output, and common non-lexicon directories if (file === 'node_modules' || file === 'tsp-output' || file === 'dist' || file === '.git') { return; } const filePath = path.join(dir, file); const stat = fs.statSync(filePath); if (stat.isDirectory()) { findJsonFiles(filePath, fileList); } else if (file.endsWith('.json') && !file.includes('package') && !file.includes('tsconfig')) { fileList.push(filePath); } }); return fileList; } function validateLexicons() { const packagesDir = path.join(__dirname, '..', 'packages'); const files = findJsonFiles(packagesDir); if (files.length === 0) { console.log('No lexicon files found'); return 0; } let errorCount = 0; let validCount = 0; let skippedCount = 0; const seenIds = new Set(); for (const filePath of files) { const relPath = path.relative(path.join(__dirname, '..'), filePath); try { const content = fs.readFileSync(filePath, 'utf-8'); const json = JSON.parse(content); // Skip non-lexicon JSON files (e.g., package.json, tsconfig.json) if (!json.lexicon || typeof json.lexicon !== 'number') { continue; } // Skip duplicates (same ID already seen) if (json.id && seenIds.has(json.id)) { skippedCount++; continue; } // Validate individually with a fresh Lexicons instance // This avoids duplicate ID errors when validating independent lexicons const lexicons = new Lexicons(); lexicons.add(json); if (json.id) { seenIds.add(json.id); } validCount++; console.log(`✓ ${relPath}`); } catch (error) { errorCount++; console.error(`✗ ${relPath}: ${error.message}`); } } console.log(`\nValidated ${validCount} lexicons: ${validCount} valid, ${errorCount} errors${skippedCount > 0 ? `, ${skippedCount} duplicates skipped` : ''}`); return errorCount > 0 ? 1 : 0; } try { const exitCode = validateLexicons(); process.exit(exitCode); } catch (err) { console.error('Fatal error:', err); process.exit(1); }