mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 14:25:02 +00:00
98 lines
2.9 KiB
TypeScript
98 lines
2.9 KiB
TypeScript
#!/usr/bin/env tsx
|
|
|
|
import { readdirSync, readFileSync, statSync } from 'fs'
|
|
import { join, extname } from 'path'
|
|
|
|
interface DocMetrics {
|
|
file: string
|
|
totalFunctions: number
|
|
documentedFunctions: number
|
|
coverage: number
|
|
missingDocs: string[]
|
|
}
|
|
|
|
function analyzeJSDocCoverage(): DocMetrics[] {
|
|
const results: DocMetrics[] = []
|
|
const srcDir = 'src'
|
|
|
|
function walkDir(dir: string) {
|
|
try {
|
|
const files = readdirSync(dir)
|
|
|
|
for (const file of files) {
|
|
const fullPath = join(dir, file)
|
|
const stat = statSync(fullPath)
|
|
|
|
if (stat.isDirectory() && !['node_modules', '.next', 'dist', 'build'].includes(file)) {
|
|
walkDir(fullPath)
|
|
} else if (['.ts', '.tsx'].includes(extname(file))) {
|
|
analyzeFile(fullPath, results)
|
|
}
|
|
}
|
|
} catch (e) {
|
|
// Skip inaccessible directories
|
|
}
|
|
}
|
|
|
|
walkDir(srcDir)
|
|
return results
|
|
}
|
|
|
|
function analyzeFile(filePath: string, results: DocMetrics[]): void {
|
|
try {
|
|
const content = readFileSync(filePath, 'utf8')
|
|
|
|
// Find all exported functions, constants, and types
|
|
const exportPattern = /(?:export\s+)?(async\s+)?(?:function|const|interface|type|class)\s+(\w+)/g
|
|
const jsdocPattern = /\/\*\*[\s\S]*?\*\//g
|
|
|
|
const jsdocs = new Set()
|
|
let docMatch
|
|
while ((docMatch = jsdocPattern.exec(content)) !== null) {
|
|
// Get the next function name after this JSDoc
|
|
const afterDoc = content.substring(docMatch.index + docMatch[0].length)
|
|
const nextFunc = afterDoc.match(/(?:async\s+)?(?:function|const|interface|type|class)\s+(\w+)/)?.[1]
|
|
if (nextFunc) {
|
|
jsdocs.add(nextFunc)
|
|
}
|
|
}
|
|
|
|
const functions: string[] = []
|
|
let match
|
|
while ((match = exportPattern.exec(content)) !== null) {
|
|
functions.push(match[2])
|
|
}
|
|
|
|
const metrics: DocMetrics = {
|
|
file: filePath,
|
|
totalFunctions: functions.length,
|
|
documentedFunctions: Array.from(jsdocs).filter(doc => functions.includes(doc as string)).length,
|
|
coverage: functions.length > 0 ? (Array.from(jsdocs).filter(doc => functions.includes(doc as string)).length / functions.length) * 100 : 100,
|
|
missingDocs: functions.filter(f => !jsdocs.has(f))
|
|
}
|
|
|
|
// Only report files with missing documentation
|
|
if (metrics.missingDocs.length > 0) {
|
|
results.push(metrics)
|
|
}
|
|
} catch (e) {
|
|
// Skip files that can't be analyzed
|
|
}
|
|
}
|
|
|
|
// Main execution
|
|
const results = analyzeJSDocCoverage()
|
|
|
|
const summary = {
|
|
totalFiles: results.length,
|
|
filesWithGaps: results.filter(r => r.coverage < 100).length,
|
|
averageCoverage: results.length > 0
|
|
? (results.reduce((sum, r) => sum + r.coverage, 0) / results.length)
|
|
: 100,
|
|
totalMissingDocs: results.reduce((sum, r) => sum + r.missingDocs.length, 0),
|
|
details: results,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
|
|
console.log(JSON.stringify(summary, null, 2))
|