Jiseoup/showmycode-private-demoPrivate
EN|KO
  • 코드
  • 커밋
  • 풀 리퀘스트
← 목록으로

Merge pull request #3 from Jiseoup/feat/project-stats-cli

feat: add project statistics CLI tool

JiseoupJISUB LIM · 2026년 6월 13일bee4a89

변경된 파일5개+170 -0

변경된 파일

+170 -0 · 5개

@@ -0,0 +1,41 @@
+function formatReport(stats) {
+ const lines = [];
+
+ lines.push("=== Project Statistics ===");
+ lines.push("");
+ lines.push(`Total files: ${stats.totalFiles}`);
+ lines.push(`Total lines: ${stats.totalLines.toLocaleString()}`);
+ lines.push(`Total size: ${formatSize(stats.totalSize)}`);
+
+ lines.push("");
+ lines.push("--- By Extension ---");
+
+ const sortedExts = Object.entries(stats.extensions).sort(
+ ([, a], [, b]) => b.lines - a.lines,
+ );
+
+ for (const [ext, data] of sortedExts) {
+ lines.push(` ${ext.padEnd(12)} ${String(data.files).padStart(4)} files ${String(data.lines.toLocaleString()).padStart(8)} lines`);
+ }
+
+ lines.push("");
+ lines.push("--- By Directory ---");
+
+ const sortedDirs = Object.entries(stats.directories).sort(
+ ([, a], [, b]) => b.size - a.size,
+ );
+
+ for (const [dir, data] of sortedDirs) {
+ lines.push(` ${dir.padEnd(30)} ${String(data.files).padStart(4)} files ${formatSize(data.size).padStart(10)}`);
+ }
+
+ return lines.join("\n");
+}
+
+function formatSize(bytes) {
+ if (bytes < 1024) return `${bytes} B`;
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
+}
+
+module.exports = { formatReport };
@@ -0,0 +1,27 @@
+const { resolve } = require("path");
+const { scanDirectory } = require("./scanner");
+const { formatReport } = require("./formatter");
+const { writeReport } = require("./writer");
+
+const targetDir = process.argv[2] || ".";
+const outputPath = process.argv[3] || null;
+
+async function main() {
+ const absolutePath = resolve(targetDir);
+ console.log(`Scanning: ${absolutePath}\n`);
+
+ const stats = await scanDirectory(absolutePath);
+ const report = formatReport(stats);
+
+ console.log(report);
+
+ if (outputPath) {
+ writeReport(outputPath, stats);
+ console.log(`\nReport saved to ${outputPath}`);
+ }
+}
+
+main().catch((err) => {
+ console.error("Error:", err.message);
+ process.exit(1);
+});
@@ -0,0 +1,66 @@
+const fs = require("fs");
+const path = require("path");
+
+const IGNORED_DIRS = ["node_modules", ".git", "dist", "build", "coverage"];
+
+function scanDirectory(dirPath) {
+ const stats = {
+ totalFiles: 0,
+ totalLines: 0,
+ totalSize: 0,
+ extensions: {},
+ directories: {},
+ };
+
+ walk(dirPath, dirPath, stats);
+ return stats;
+}
+
+function walk(basePath, currentPath, stats) {
+ const entries = fs.readdirSync(currentPath, { withFileTypes: true });
+
+ for (const entry of entries) {
+ const fullPath = path.join(currentPath, entry.name);
+
+ if (entry.isDirectory()) {
+ if (IGNORED_DIRS.includes(entry.name)) continue;
+ walk(basePath, fullPath, stats);
+ continue;
+ }
+
+ if (!entry.isFile()) continue;
+
+ const ext = path.extname(entry.name) || "(no ext)";
+ const relativePath = path.relative(basePath, currentPath);
+ const dir = relativePath || "(root)";
+ const fileStat = fs.statSync(fullPath);
+ const lines = countLines(fullPath);
+
+ stats.totalFiles++;
+ stats.totalLines += lines;
+ stats.totalSize += fileStat.size;
+
+ if (!stats.extensions[ext]) {
+ stats.extensions[ext] = { files: 0, lines: 0 };
+ }
+ stats.extensions[ext].files++;
+ stats.extensions[ext].lines += lines;
+
+ if (!stats.directories[dir]) {
+ stats.directories[dir] = { files: 0, size: 0 };
+ }
+ stats.directories[dir].files++;
+ stats.directories[dir].size += fileStat.size;
+ }
+}
+
+function countLines(filePath) {
+ try {
+ const content = fs.readFileSync(filePath, "utf-8");
+ return content.split("\n").length;
+ } catch {
+ return 0;
+ }
+}
+
+module.exports = { scanDirectory };
@@ -0,0 +1,25 @@
+const fs = require("fs");
+const path = require("path");
+
+function writeReport(outputPath, stats) {
+ const dir = path.dirname(outputPath);
+
+ if (!fs.existsSync(dir)) {
+ fs.mkdirSync(dir, { recursive: true });
+ }
+
+ const report = {
+ generatedAt: new Date().toISOString(),
+ summary: {
+ totalFiles: stats.totalFiles,
+ totalLines: stats.totalLines,
+ totalSize: stats.totalSize,
+ },
+ extensions: stats.extensions,
+ directories: stats.directories,
+ };
+
+ fs.writeFileSync(outputPath, JSON.stringify(report, null, 2));
+}
+
+module.exports = { writeReport };
@@ -0,0 +1,11 @@
+{
+ "name": "showmycode-private-demo",
+ "version": "1.0.0",
+ "description": "A simple CLI tool for generating project statistics",
+ "main": "src/index.js",
+ "scripts": {
+ "start": "node src/index.js"
+ },
+ "keywords": ["cli", "stats", "project"],
+ "license": "MIT"
+}