diff --git a/scripts/count-locs.js b/scripts/count-locs.js new file mode 100755 index 000000000..2e12afac8 --- /dev/null +++ b/scripts/count-locs.js @@ -0,0 +1,128 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +const PACKAGES_DIR = path.join(__dirname, '..', 'packages'); +const EXCLUDE_DIRS = ['node_modules', 'dist', 'build', '.next', 'coverage', '.git']; +const EXCLUDE_PACKAGES = ['bruno-toml', 'bruno-tests', 'bruno-docs']; +const CODE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx', '.css', '.scss', '.json', '.md']; + +function countLinesInFile(filePath) { + try { + const content = fs.readFileSync(filePath, 'utf-8'); + return content.split('\n').length; + } catch (error) { + return 0; + } +} + +function shouldExcludeDir(dirName) { + return EXCLUDE_DIRS.includes(dirName) || dirName.startsWith('.'); +} + +function isCodeFile(fileName) { + return CODE_EXTENSIONS.some(ext => fileName.endsWith(ext)); +} + +function countLinesInDirectory(dirPath) { + let totalLines = 0; + let fileCount = 0; + + function walkDir(currentPath) { + const items = fs.readdirSync(currentPath); + + for (const item of items) { + const itemPath = path.join(currentPath, item); + const stat = fs.statSync(itemPath); + + if (stat.isDirectory()) { + if (!shouldExcludeDir(item)) { + walkDir(itemPath); + } + } else if (stat.isFile() && isCodeFile(item)) { + const lines = countLinesInFile(itemPath); + totalLines += lines; + fileCount++; + } + } + } + + walkDir(dirPath); + return { totalLines, fileCount }; +} + +function getPackages() { + const packages = []; + const items = fs.readdirSync(PACKAGES_DIR); + + for (const item of items) { + const itemPath = path.join(PACKAGES_DIR, item); + const stat = fs.statSync(itemPath); + + if (stat.isDirectory() && !shouldExcludeDir(item) && !EXCLUDE_PACKAGES.includes(item)) { + packages.push({ + name: item, + path: itemPath + }); + } + } + + return packages; +} + +function formatNumber(num) { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); +} + +function printTable(data) { + // Calculate column widths + const nameWidth = Math.max(20, ...data.map(d => d.name.length)); + const locWidth = 12; + const filesWidth = 12; + + // Header + console.log('\n┌' + '─'.repeat(nameWidth + 2) + '┬' + '─'.repeat(locWidth + 2) + '┬' + '─'.repeat(filesWidth + 2) + '┐'); + console.log(`│ ${'Package'.padEnd(nameWidth)} │ ${'LOC'.padStart(locWidth)} │ ${'Files'.padStart(filesWidth)} │`); + console.log('├' + '─'.repeat(nameWidth + 2) + '┼' + '─'.repeat(locWidth + 2) + '┼' + '─'.repeat(filesWidth + 2) + '┤'); + + // Data rows + let totalLOC = 0; + let totalFiles = 0; + + for (const row of data) { + console.log(`│ ${row.name.padEnd(nameWidth)} │ ${formatNumber(row.loc).padStart(locWidth)} │ ${formatNumber(row.files).padStart(filesWidth)} │`); + totalLOC += row.loc; + totalFiles += row.files; + } + + // Footer + console.log('├' + '─'.repeat(nameWidth + 2) + '┼' + '─'.repeat(locWidth + 2) + '┼' + '─'.repeat(filesWidth + 2) + '┤'); + console.log(`│ ${'TOTAL'.padEnd(nameWidth)} │ ${formatNumber(totalLOC).padStart(locWidth)} │ ${formatNumber(totalFiles).padStart(filesWidth)} │`); + console.log('└' + '─'.repeat(nameWidth + 2) + '┴' + '─'.repeat(locWidth + 2) + '┴' + '─'.repeat(filesWidth + 2) + '┘\n'); +} + +function main() { + console.log('Counting lines of code in Bruno packages...\n'); + + const packages = getPackages(); + const results = []; + + for (const pkg of packages) { + process.stdout.write(`Analyzing ${pkg.name}...`); + const { totalLines, fileCount } = countLinesInDirectory(pkg.path); + results.push({ + name: pkg.name, + loc: totalLines, + files: fileCount + }); + process.stdout.write(' Done\n'); + } + + // Sort by LOC descending + results.sort((a, b) => b.loc - a.loc); + + printTable(results); +} + +main(); \ No newline at end of file