UNPKG

@wearesage/schema

Version:

A flexible schema definition and validation system for TypeScript with multi-database support

164 lines (133 loc) 5.56 kB
#!/usr/bin/env node const fs = require('fs'); const path = require('path'); function getCoverageStats() { const coveragePath = path.join(process.cwd(), 'coverage/coverage-final.json'); if (!fs.existsSync(coveragePath)) { return { overall: { statements: 0, branches: 0, functions: 0, lines: 0 }, bySuite: {} }; } const coverage = JSON.parse(fs.readFileSync(coveragePath, 'utf8')); let totalStatements = 0, hitStatements = 0; let totalBranches = 0, hitBranches = 0; let totalFunctions = 0, hitFunctions = 0; let totalLines = 0, hitLines = 0; const bySuite = {}; for (const [file, data] of Object.entries(coverage)) { // Determine suite from file path const relativePath = path.relative(process.cwd(), file); const suite = relativePath.split(path.sep)[0] || 'root'; if (!bySuite[suite]) { bySuite[suite] = { statements: 0, branches: 0, functions: 0, lines: 0, files: 0 }; } bySuite[suite].files++; let fileStatements = 0, fileHitStatements = 0; let fileBranches = 0, fileHitBranches = 0; let fileFunctions = 0, fileHitFunctions = 0; let fileLines = 0, fileHitLines = 0; if (data.s) { fileStatements = Object.keys(data.s).length; fileHitStatements = Object.values(data.s).filter(x => x > 0).length; totalStatements += fileStatements; hitStatements += fileHitStatements; } if (data.b) { for (const branches of Object.values(data.b)) { fileBranches += branches.length; fileHitBranches += branches.filter(x => x > 0).length; } totalBranches += fileBranches; hitBranches += fileHitBranches; } if (data.f) { fileFunctions = Object.keys(data.f).length; fileHitFunctions = Object.values(data.f).filter(x => x > 0).length; totalFunctions += fileFunctions; hitFunctions += fileHitFunctions; } // Use statementMap for line coverage if l is not available if (data.statementMap) { const lineNumbers = new Set(); Object.values(data.statementMap).forEach(stmt => { lineNumbers.add(stmt.start.line); }); fileLines = lineNumbers.size; totalLines += fileLines; // Count hit lines by matching with statement hits const hitLineNumbers = new Set(); Object.entries(data.s || {}).forEach(([index, hits]) => { if (hits > 0 && data.statementMap[index]) { hitLineNumbers.add(data.statementMap[index].start.line); } }); fileHitLines = hitLineNumbers.size; hitLines += fileHitLines; } // Add to suite totals bySuite[suite].statements += fileStatements > 0 ? Math.round((fileHitStatements / fileStatements) * 100) : 0; bySuite[suite].branches += fileBranches > 0 ? Math.round((fileHitBranches / fileBranches) * 100) : 0; bySuite[suite].functions += fileFunctions > 0 ? Math.round((fileHitFunctions / fileFunctions) * 100) : 0; bySuite[suite].lines += fileLines > 0 ? Math.round((fileHitLines / fileLines) * 100) : 0; } // Average by suite Object.keys(bySuite).forEach(suite => { const s = bySuite[suite]; s.statements = Math.round(s.statements / s.files); s.branches = Math.round(s.branches / s.files); s.functions = Math.round(s.functions / s.files); s.lines = Math.round(s.lines / s.files); }); return { overall: { statements: Math.round((hitStatements / totalStatements) * 100) || 0, branches: Math.round((hitBranches / totalBranches) * 100) || 0, functions: Math.round((hitFunctions / totalFunctions) * 100) || 0, lines: Math.round((hitLines / totalLines) * 100) || 0 }, bySuite }; } function generateCoverageBadges(stats) { const badges = []; for (const [type, percent] of Object.entries(stats)) { const color = percent >= 90 ? 'brightgreen' : percent >= 80 ? 'yellow' : percent >= 70 ? 'orange' : 'red'; badges.push(`![${type}](https://img.shields.io/badge/${type}-${percent}%25-${color})`); } return badges.join(' '); } function injectCoverage() { const readmePath = path.join(process.cwd(), 'README.md'); let readme = ''; if (fs.existsSync(readmePath)) { readme = fs.readFileSync(readmePath, 'utf8'); } const { overall, bySuite } = getCoverageStats(); const badges = generateCoverageBadges(overall); let coverageSection = `# @wearesage/schema > A TypeScript decorator-based schema definition system that works across multiple database adapters (PostgreSQL, MongoDB, Neo4j, Redis, SQLite). Define your entities once with decorators, then use them with any supported database through a unified repository interface. ## Coverage ${badges} ### Overall | Metric | Percentage | |--------|-----------| | Statements | ${overall.statements}% | | Branches | ${overall.branches}% | | Functions | ${overall.functions}% | | Lines | ${overall.lines}% | ### By Module | Module | Statements | Branches | Functions | Lines | |---------|-----------|----------|-----------|-------| `; // Add suite-specific coverage Object.entries(bySuite).forEach(([suite, stats]) => { coverageSection += `| ${suite} | ${stats.statements}% | ${stats.branches}% | ${stats.functions}% | ${stats.lines}% |\n`; }); coverageSection += '\n'; // Replace entire README since we're including the title readme = coverageSection; fs.writeFileSync(readmePath, readme); console.log('✅ Coverage injected into README.md'); } if (require.main === module) { injectCoverage(); } module.exports = { injectCoverage, getCoverageStats };