Building CLI Tools With Node.js: From Script to npm Package
The best CLI tools are ones that scratch your own itch — a script you'd write anyway, packaged for others.
Project Setup
mkdir my-cli && cd my-cli
npm init -y
npm install commander chalk ora
npm install -D typescript @types/node tsx
// package.json
{
"name": "my-cli",
"bin": { "my-cli": "./dist/index.js" },
"scripts": {
"build": "tsc",
"dev": "tsx src/index.ts"
}
}
Main Entry Point
#!/usr/bin/env node
import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
const program = new Command();
program
.name('my-cli')
.description('A useful CLI tool')
.version('1.0.0');
program
.command('generate <type>')
.description('Generate a new component')
.option('-d, --dir <directory>', 'output directory', './src')
.option('--no-test', 'skip generating test file')
.action(async (type, options) => {
const spinner = ora(`Generating ${type}...`).start();
try {
await generateComponent(type, options);
spinner.succeed(chalk.green(`Created ${type} successfully`));
} catch (err) {
spinner.fail(chalk.red(`Failed: ${(err as Error).message}`));
process.exit(1);
}
});
program.parse();
Interactive Prompts
import { input, select, confirm } from '@inquirer/prompts';
async function interactiveSetup() {
const name = await input({ message: 'Project name:' });
const framework = await select({
message: 'Choose framework:',
choices: [
{ value: 'nextjs', name: 'Next.js' },
{ value: 'remix', name: 'Remix' },
{ value: 'astro', name: 'Astro' },
],
});
const includeStripe = await confirm({ message: 'Include Stripe billing?' });
return { name, framework, includeStripe };
}
Publishing to npm
# Build
npm run build
# Make the entry point executable
chmod +x dist/index.js
# Test locally
npm link
my-cli generate component
# Publish
npm publish --access public
Exit Codes
// Always exit with proper codes
process.exit(0); // Success
process.exit(1); // General error
process.exit(2); // Misuse of CLI
// Handle unhandled rejections
process.on('unhandledRejection', (err) => {
console.error(chalk.red('Unhandled error:'), err);
process.exit(1);
});
CLI tools, code generators, and automation scripts are part of the developer tooling patterns in the Ship Fast Skill Pack — including a /generate skill that scaffolds components from your project's conventions.
Top comments (0)