a reactive (signals based) hypermedia web framework (wip) stormlightlabs.github.io/volt/
hypermedia frontend signals
at main 4.0 kB view raw
1import { echo } from "$console/echo.js"; 2import { findMonorepoRoot, getLibSrcPath, getLibTestPath } from "$utils/paths.js"; 3import { readdir, readFile, stat } from "node:fs/promises"; 4import path from "node:path"; 5 6type FileStats = { path: string; lines: number; totalLines: number }; 7type DirectoryStats = { totalLines: number; codeLines: number; files: FileStats[] }; 8type Lines = { lines: number; totalLines: number }; 9 10/** 11 * Count lines of code in a file, excluding doc comments 12 */ 13async function countLines(filePath: string): Promise<Lines> { 14 const content = await readFile(filePath, "utf8"); 15 const lines = content.split("\n"); 16 17 let codeLines = 0; 18 let inDocComment = false; 19 20 for (const line of lines) { 21 const trimmed = line.trim(); 22 23 if (trimmed.startsWith("/**")) { 24 inDocComment = true; 25 continue; 26 } 27 28 if (inDocComment) { 29 if (trimmed.endsWith("*/")) { 30 inDocComment = false; 31 } 32 continue; 33 } 34 35 if (trimmed === "" || trimmed.startsWith("//")) { 36 continue; 37 } 38 39 codeLines++; 40 } 41 42 return { lines: codeLines, totalLines: lines.length }; 43} 44 45/** 46 * Recursively walk a directory and collect TypeScript files 47 */ 48async function walkDirectory(dir: string, files: string[] = []): Promise<string[]> { 49 const entries = await readdir(dir); 50 51 for (const entry of entries) { 52 const fullPath = path.join(dir, entry); 53 const stats = await stat(fullPath); 54 55 if (stats.isDirectory()) { 56 if (entry !== "node_modules" && entry !== "dist" && entry !== ".git") { 57 await walkDirectory(fullPath, files); 58 } 59 } else if (stats.isFile() && entry.endsWith(".ts")) { 60 files.push(fullPath); 61 } 62 } 63 64 return files; 65} 66 67/** 68 * Collect stats for a directory 69 */ 70async function collectStats(directory: string, baseDir: string): Promise<DirectoryStats> { 71 const files = await walkDirectory(directory); 72 const fileStats: FileStats[] = []; 73 let totalLines = 0; 74 let codeLines = 0; 75 76 for (const file of files) { 77 const { lines, totalLines: total } = await countLines(file); 78 const relativePath = path.relative(baseDir, file); 79 80 fileStats.push({ path: relativePath, lines, totalLines: total }); 81 82 codeLines += lines; 83 totalLines += total; 84 } 85 86 return { totalLines, codeLines, files: fileStats }; 87} 88 89/** 90 * Stats command implementation 91 */ 92export async function statsCommand(includeFull: boolean): Promise<void> { 93 const monorepoRoot = await findMonorepoRoot(); 94 const srcDir = await getLibSrcPath(); 95 const srcStats = await collectStats(srcDir, monorepoRoot); 96 97 echo.title("\nVolt.js Code Statistics\n"); 98 99 echo.label("Source Code (src/):"); 100 echo.text(` Files: ${srcStats.files.length}`); 101 echo.text(` Total Lines: ${srcStats.totalLines}`); 102 echo.ok(` Code Lines: ${srcStats.codeLines}`); 103 echo.text(` Doc/Comments: ${srcStats.totalLines - srcStats.codeLines}`); 104 105 let totalCode = srcStats.codeLines; 106 let totalTotal = srcStats.totalLines; 107 let totalFileCount = srcStats.files.length; 108 109 if (includeFull) { 110 const testDir = await getLibTestPath(); 111 const testStats = await collectStats(testDir, monorepoRoot); 112 113 echo.label("\nTest Code (test/):"); 114 echo.text(` Files: ${testStats.files.length}`); 115 echo.text(` Total Lines: ${testStats.totalLines}`); 116 echo.ok(` Code Lines: ${testStats.codeLines}`); 117 echo.text(` Doc/Comments: ${testStats.totalLines - testStats.codeLines}`); 118 119 totalCode += testStats.codeLines; 120 totalTotal += testStats.totalLines; 121 totalFileCount += testStats.files.length; 122 } 123 124 echo.title("\nTotal:"); 125 echo.text(` Files: ${totalFileCount}`); 126 echo.text(` Total Lines: ${totalTotal}`); 127 echo.success(` Code Lines: ${totalCode}`); 128 echo.text(` Doc/Comments: ${totalTotal - totalCode}`); 129 130 if (process.env.VERBOSE) { 131 echo.warn("\n\nFile Breakdown:"); 132 for (const file of srcStats.files) { 133 echo.text(` ${file.path}: ${file.lines} lines`); 134 } 135 } 136 137 echo.text(); 138}