NelsonLabs
Node.js Fundamentals/Project: CLI File Analyser

Project: CLI File Analyser

Build a CLI tool that analyses a directory of files: counts lines, words, and characters per file, and outputs a formatted summary report. Pure Node.js β€” no npm packages needed.

analyser.js β€” complete CLI tool
javascript
#!/usr/bin/env node
// Usage: node analyser.js ./src

const fs   = require("fs/promises");
const path = require("path");

async function analyseFile(filePath) {
  const content = await fs.readFile(filePath, "utf8");
  const lines   = content.split("
");
  const words   = content.split(/s+/).filter(Boolean);

  return {
    file:  path.basename(filePath),
    lines: lines.length,
    words: words.length,
    chars: content.length,
    size:  (await fs.stat(filePath)).size,
  };
}

async function analyseDirectory(dirPath) {
  const entries = await fs.readdir(dirPath, { withFileTypes: true });
  const files   = entries
    .filter(e => e.isFile() && !e.name.startsWith("."))
    .map(e => path.join(dirPath, e.name));

  const results = await Promise.all(files.map(analyseFile));
  return results;
}

function printReport(results) {
  const totals = results.reduce((acc, r) => ({
    lines: acc.lines + r.lines,
    words: acc.words + r.words,
    chars: acc.chars + r.chars,
  }), { lines: 0, words: 0, chars: 0 });

  console.log("
── File Analysis Report ─────────────────────────
");
  console.log("File".padEnd(30) + "Lines".padStart(8) + "Words".padStart(8) + "Chars".padStart(8));
  console.log("─".repeat(54));

  for (const r of results.sort((a, b) => b.lines - a.lines)) {
    console.log(
      r.file.padEnd(30) +
      String(r.lines).padStart(8) +
      String(r.words).padStart(8) +
      String(r.chars).padStart(8)
    );
  }

  console.log("─".repeat(54));
  console.log(
    "TOTAL".padEnd(30) +
    String(totals.lines).padStart(8) +
    String(totals.words).padStart(8) +
    String(totals.chars).padStart(8)
  );
  console.log(`
${results.length} files analysed.
`);
}

async function main() {
  const targetDir = process.argv[2] || ".";
  const absPath   = path.resolve(targetDir);

  try {
    await fs.access(absPath);
  } catch {
    console.error(`Error: Directory not found: ${absPath}`);
    process.exit(1);
  }

  console.log(`Analysing: ${absPath}`);
  const results = await analyseDirectory(absPath);

  if (results.length === 0) {
    console.log("No files found.");
    return;
  }

  printReport(results);
}

main().catch(err => {
  console.error("Unexpected error:", err.message);
  process.exit(1);
});