How to Build, Optimize, and Publish a Full‑Featured CLI Scaffolding Tool for Frontend Projects
This tutorial walks you through creating a 100‑line Node.js CLI scaffolding tool that supports global and local installation, interactive project and template selection, command‑line arguments, loading animations, overwrite prompts, dynamic template fetching via GitHub API, and finally publishing the package to npm for frontend engineers.
This article is part of a series on front‑end engineering and demonstrates how to build a complete CLI scaffolding tool from scratch using Node.js and npm.
Core functionality of a typical CLI includes global installation ( npm i xxx-cli -g ), local execution via npx xxx-cli , passing arguments, and interactive prompts.
Project initialization : create a folder, run npm init -y , and add a bin field in package.json that points to ./bin/cli.js . The cli.js file starts with a shebang #!/usr/bin/env node and logs a test message.
mkdir wuyou-cli
cd wuyou-cli
npm init -y #!/usr/bin/env node
console.log('wuyou-cli~~~~')Install commander to parse commands and define a create command. Add inquirer for interactive prompts to ask for the project name and template selection.
const program = require('commander')
const inquirer = require('inquirer')
program.version(`v${require('../package.json').version}`)
program.command('create')
.description('Create template')
.action(async () => {
const { name } = await inquirer.prompt({
type: 'input',
name: 'name',
message: 'Enter project name:'
})
console.log('Project name:', name)
})
program.parse(process.argv)Define the template list in bin/templates.js and load it in the CLI. Use download-git-repo to fetch a remote Git repository into the newly created folder.
downloadGitRepo('https://github.com:user/repo', dest, err => {
if (err) console.log('Download failed', err)
else console.log('Download succeeded')
})Enhance user experience with ora to show a loading spinner while downloading.
const ora = require('ora')
const loading = ora('Downloading template...')
loading.start()
downloadGitRepo(..., err => {
loading.stop()
if (err) console.error('Failed')
else console.log('Success')
})Add a --help handler so users can view all available commands, and support passing the project name and template directly via command‑line options ( -t, --template ).
program.command('create [projectName]')
.option('-t, --templateBefore downloading, check if the target folder already exists with fs-extra . If it does, prompt the user with a confirm question to overwrite or abort.
const fs = require('fs-extra')
if (fs.existsSync(dest)) {
const { force } = await inquirer.prompt({
type: 'confirm',
name: 'force',
message: 'Folder exists. Overwrite?'
})
force ? fs.removeSync(dest) : process.exit(1)
}After a successful creation, print guidance such as cd <projectName> , npm i , and npm start to help users get started quickly.
To make the template list dynamic, implement bin/api.js that calls the GitHub API ( https://api.github.com/users/:username/repos ) and returns an array of { name, value } objects. Load this list at runtime with a loading spinner.
function getGitReposList(username) {
return new Promise((resolve, reject) => {
https.request(`https://api.github.com/users/${username}/repos`, { headers: { 'User-Agent': username } }, res => {
let data = ''
res.on('data', chunk => data += chunk)
res.on('end', () => resolve(JSON.parse(data).map(item => ({ name: item.name, value: `https://github.com/${username}/${item.name}` }))))
}).end()
})
}Finally, update package.json to include the bin folder in the files array, write a README.md with installation and usage instructions, and publish the package to npm using npm publish . Verify both global installation and npx usage to ensure the CLI works as expected.
The complete workflow covers project setup, command definition, interactive prompts, template downloading, loading feedback, overwrite handling, dynamic template fetching, publishing, and verification, providing a reusable scaffold for front‑end developers.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
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.