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>', 'string to split')
.option('--first', 'display just the first substring')
.option('-s, --separator <char>', '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 <projectName>')
.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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
