Developing a Node.js CLI Scaffold: Tools, Package.json and Demo
This guide walks you through building a custom Node.js CLI scaffold using commander, chalk, inquirer, ora, and download‑git‑repo, explains essential package.json fields like bin and engines, and provides a complete demo that prompts users, downloads templates, and initializes projects ready for global installation.
This article introduces the essential tools for building a CLI scaffolding project— commander , chalk , inquirer , ora and others—along with important package.json fields, and demonstrates a complete example.
Why a custom scaffold? While many projects start with npm init X , existing scaffolds may not meet specific business needs, so a custom CLI is required.
Core capabilities a scaffold should provide:
Interactive prompting
Template downloading
Template writing
Post‑processing (git init, dependency installation, etc.)
Tool overview
commander.js – command‑line parsing and sub‑command handling.
chalk – styling terminal output.
inquirer.js – interactive question prompts.
ora – spinner / loading indicator.
download-git-repo – fetch remote git templates.
commander example
import { Command } from 'commander';
const program = new Command();
program
.name('test-string-utils')
.description('CLI to some JavaScript string utilities by 禾汐')
.version('0.8.0');
program.command('split')
.description('Split a string into substrings and display as an array')
.argument('
', 'string to split')
.option('--first', 'display just the first substring')
.option('-s, --separator
', 'separator character', ',')
.action((str, options) => {
const limit = options.first ? 1 : undefined;
console.log(str.split(options.separator, limit));
});
program.parse();chalk usage
import chalk from 'chalk';
const log = console.log;
log(chalk.blue('Hello world!'));
log(chalk.red('Error'), chalk.underline.bgBlue('important'));
log(chalk.rgb(123,45,67).underline('Styled text'));
log(chalk.hex('#DEADED').bold('Bold gray!'));inquirer example
import inquirer from 'inquirer';
const promptList = [{
type: 'input',
name: 'name',
message: '设置一个用户名:',
default: 'test_user'
}, {
type: 'input',
name: 'phone',
message: '请输入手机号:',
validate: val => val.match(/\d{11}/) ? true : '请输入11位数字'
}];
inquirer.prompt(promptList).then(answers => console.log(answers));ora spinner
import ora from 'ora';
import chalk from 'chalk';
const spinner = ora(`Loading ${chalk.red('模板')}`).start();
setTimeout(() => spinner.succeed('模板下载成功!'), 3000);
setTimeout(() => spinner.fail('模板下载失败!'), 3000);download‑git‑repo usage
import download from 'download-git-repo';
import rimraf from 'rimraf';
import path from 'path';
const dir = path.join(process.cwd(), 'template');
rimraf.sync(dir);
download('[email protected]:repo.git#master', dir, { clone: true }, err => {
console.log(err ? 'Error' : 'Success');
});package.json essentials
{
"name": "ls",
"version": "1.0.0",
"type": "module",
"scripts": { "test": "echo \"Error: no test specified\" && exit 1" },
"dependencies": {
"chalk": "^5.0.1",
"commander": "^9.4.0",
"download-git-repo": "^3.0.2",
"inquirer": "^9.1.0",
"ora": "^6.1.2",
"rimraf": "^3.0.2"
},
"engines": { "node": ">=6.0.0", "npm": ">=3.0.0" },
"bin": { "test-cli": "./bin.js" }
}The bin field maps a command name to a script file, enabling global installation (e.g., npm install -g . ) and usage like test-cli -v .
Demo CLI implementation
#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import download from 'download-git-repo';
import ora from 'ora';
import inquirer from 'inquirer';
import { exec } from 'child_process';
import { Command } from 'commander';
import checkDir from './checkDir.js';
const commander = new Command();
const promptTypeList = [{
type: 'list',
name: 'type',
message: '请选择拉取的模版类型:',
choices: [
{ name: '天猫优品的业务脚手架', value: { url: '[email protected]:dinamic/miniapp-tracker.git#master', gitName: 'web', val: '天猫优品的业务脚手架' } },
{ name: '小程序', value: { url: '[email protected]:dinamic/miniapp-tracker.git#master', gitName: 'miniapp', val: 'miniapp' } }
]
}];
commander.version('1.1.1', '-v, --version')
.command('init
')
.alias('i')
.description('输入项目名称,初始化项目模版')
.action(async (projectName) => {
const dir = path.join(process.cwd(), projectName);
await checkDir(dir, projectName);
const { url, val } = (await inquirer.prompt(promptTypeList)).type;
const spinner = ora(`Loading ${chalk.red(val)} 仓库到项目中`).start();
download(url, dir, { clone: true }, err => err ? spinner.fail('模板下载失败!') : spinner.succeed('模板下载成功!'));
});
commander.parse(process.argv);After creating the package, publish it with tnpm login , tnpm publish or npm publish . Users can then run youpin-cli init demo-cli to generate a project.
Conclusion
The article provides a beginner‑level guide to CLI scaffold development, covering required libraries, key package.json fields, and a full example that can be adapted for various front‑end projects.
DaTaobao Tech
Official account of DaTaobao Technology
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.