import fs from 'fs';
import path from 'path';
/**
* ファイルの行数をカウントするスクリプト。
* 物理行数と、空行・コメントを除いた実効行数を計測。
* Rust (.rs), JavaScript (.js, .mjs), TypeScript (.ts) に対応。
*/
const WARN_FILE_LINES = 200;
const TARGET_EXTENSIONS = ['.js', '.mjs', '.ts', '.rs'];
function countLines(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
const lines = content.split('\n');
let physicalLines = lines.length;
let logicalLines = 0;
let inComment = false;
for (let line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
// ブロックコメント対応
if (inComment) {
if (trimmed.includes('*/')) {
inComment = false;
// */ 以降にコードがあるか(簡易)
if (trimmed.split('*/')[1].trim()) logicalLines++;
}
continue;
}
if (trimmed.startsWith('//')) continue;
if (trimmed.startsWith('/*')) {
if (!trimmed.includes('*/')) {
inComment = true;
} else {
// /* */ が一行で完結し、かつ前後にコードがあるか(実運用上は稀だが考慮)
const contentWithoutComment = trimmed.replace(/\/\*.*?\*\//g, '').trim();
if (contentWithoutComment) logicalLines++;
}
continue;
}
logicalLines++;
}
console.log(`\nLines Info: ${filePath}`);
console.log(` Physical: ${physicalLines}`);
console.log(` Logical : ${logicalLines} ${logicalLines > WARN_FILE_LINES ? '[WARN!]' : '[OK]'}`);
return { physicalLines, logicalLines };
}
function walkDir(dir) {
const files = fs.readdirSync(dir);
files.forEach(file => {
const fullPath = path.join(dir, file);
if (fs.statSync(fullPath).isDirectory()) {
if (file !== 'node_modules' && file !== 'target' && file !== '.git') {
walkDir(fullPath);
}
} else if (TARGET_EXTENSIONS.includes(path.extname(file))) {
countLines(fullPath);
}
});
}
const args = process.argv.slice(2);
if (args.length > 0) {
args.forEach(arg => {
if (fs.existsSync(arg)) {
if (fs.statSync(arg).isDirectory()) {
walkDir(arg);
} else {
countLines(arg);
}
} else {
console.error(`File or directory not found: ${arg}`);
}
});
} else {
walkDir('.');
}