CommanderJS
Building a Simple CLI
Step-by-step guide to create a CLI tool using commander.js and plain Node.js to count words or lines in a file, plus comparison.
What is Commander.js?
Commander.js is a powerful Node.js library for building CLI applications.
It provides:
- Command parsing
- Option handling
- Help generation
All with a clean and declarative API.
Using commander.js makes building CLIs in Node.js easy and clean.
- Handles argument parsing.
- Automatically generates help.
- Supports options and arguments out of the box.
Goal
We will build a CLI tool that:
- Accepts a file path as an argument.
- Has options to count:
- Number of words (
--words) - Number of lines (
--lines)
- Number of words (
Approach 1: Using Commander.js
1. Initialize Project
mkdir file-counter-cli
cd file-counter-cli
npm init -y
npm install commander2. Create CLI with Commander.js
#!/usr/bin/env node
const fs = require('fs');
const { Command } = require('commander');
const program = new Command();
program
.name('file-counter')
.description('CLI to count words or lines in a file')
.version('1.0.0')
.argument('<file>', 'Path to the file')
.option('-w, --words', 'Count words')
.option('-l, --lines', 'Count lines')
.action((file, options) => {
fs.readFile(file, 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err.message);
process.exit(1);
}
if (options.words) {
const wordCount = data.split(/\s+/).filter(Boolean).length;
console.log(`Word count: ${wordCount}`);
}
if (options.lines) {
const lineCount = data.split(/\r?\n/).length;
console.log(`Line count: ${lineCount}`);
}
if (!options.words && !options.lines) {
console.log('Please provide --words and/or --lines');
}
});
});
program.parse();Approach 2: Plain Node.js without Commander.js
Create Simple CLI
#!/usr/bin/env node
const fs = require('fs');
// Get arguments manually
const args = process.argv.slice(2);
const file = args.find(arg => !arg.startsWith('--'));
const options = args.filter(arg => arg.startsWith('--'));
if (!file) {
console.error('Error: No file specified.');
process.exit(1);
}
fs.readFile(file, 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err.message);
process.exit(1);
}
if (options.includes('--words')) {
const wordCount = data.split(/\s+/).filter(Boolean).length;
console.log(`Word count: ${wordCount}`);
}
if (options.includes('--lines')) {
const lineCount = data.split(/\r?\n/).length;
console.log(`Line count: ${lineCount}`);
}
if (!options.includes('--words') && !options.includes('--lines')) {
console.log('Please provide --words and/or --lines');
}
});Comparison
| Feature | Commander.js | Plain Node.js |
|---|---|---|
| Argument Parsing | Built-in, declarative | Manual, error-prone |
| Help Generation | Auto-generated | Not available by default |
| Option Handling | Supports aliases, defaults | Manual string checks |
| Code Cleanliness | Clear, readable API | Messy with manual logic |
| Recommended Use Case | Complex CLI tools | Simple or one-off scripts |
Example Usage
With Commander.js
file-counter sample.txt --words --linesWithout Commander.js
node index.js sample.txt --words --lines