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.

DaTaobao Tech
DaTaobao Tech
DaTaobao Tech
Developing a Node.js CLI Scaffold: Tools, Package.json and Demo

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CLINode.jsCommanderinquirerpackage.jsonscaffoldingTooling
DaTaobao Tech
Written by

DaTaobao Tech

Official account of DaTaobao Technology

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.