Frontend Development 10 min read

Building a CLI Scaffolding Tool for React and Vue Projects

This article walks through the design and implementation of a Node‑based command‑line tool that generates React or Vue project scaffolds, covering initialization, script development, template downloading, local testing, npm publishing, and usage instructions.

Beike Product & Technology
Beike Product & Technology
Beike Product & Technology
Building a CLI Scaffolding Tool for React and Vue Projects

The author, a front‑end developer, explains how to create a command‑line scaffolding tool using Node.js that can generate project templates based on React or Vue. The tool is installed globally via npm and interacts with the user through prompts to collect project details.

Implementation Idea

Inspired by ant‑design‑pro‑cli, the project separates the template repository from the CLI logic. The CLI parses arguments and prompts, downloads the chosen template from GitHub, and optionally modifies files such as package.json before generating the final project.

Project Initialization

Create a directory named gframe .

Run npm init -y and edit name , version , and description in package.json .

Create bin and lib folders.

Add bin/gframe and lib/init.js files.

Install required dependencies.

1.
2├── bin
3          ├──gframe // 脚本文件
4├── lib
5          ├──init.js // 逻辑处理文件
6├── node_modules
7├── package-lock.json
8├── package.json // 项目配置文件
{
  "dependencies": {
    "bluebird": "^3.5.3", // promise库
    "chalk": "^2.4.2", // 终端样式
    "commander": "^2.19.0", // 命令行解析
    "download-git-repo": "^1.1.0", // 下载仓库
    "inquirer": "^6.2.2", // 交互
    "ora": "^3.2.0" // 小图标
  }
}

Script File bin/gframe

This file registers an init command with commander , parses arguments, validates the target directory, collects user input via inquirer , and triggers the download and rendering logic.

#!/usr/bin/env node
const fs = require('fs');
const chalk = require('chalk');
const program = require('commander');
const pkg = require('../package.json');
const { inquirerFn, downloadFn } = require('../lib/init');
program.version(pkg.version, '-v,--version');
program
  .command('init
')
  .description(pkg.description)
  .action(dirname => {
    if (fs.existsSync(dirname)) {
      return console.log(chalk.red(`dirname ${dirname} is exist`));
    }
    inquirerFn().then(answers => {
      downloadFn(answers, dirname);
    });
  });
program.arguments('
').action(cmd => {
  program.outputHelp();
  console.log(' ');
  console.log(`error: unknown option '${cmd}'`);
});
program.parse(process.argv);
if (!process.argv.slice(2).length) {
  program.outputHelp();
}

Logic File lib/init.js

The file handles two main tasks: collecting user input with inquirer and downloading/rendering the selected template using download-git-repo wrapped by bluebird promises, while showing a loading spinner via ora .

var inquirer = require('inquirer');
function inquirerFn() {
  return inquirer.prompt([
    { type: 'list', name: 'frame', message: '请选择开发用的脚手架:', choices: ['react', 'vue'] },
    { type: 'input', name: 'name', message: '请输入项目名称:' },
    { type: 'input', name: 'description', message: '请输入项目简介:' }
  ]);
}
const fs = require('fs');
const ora = require('ora');
const Promise = require('bluebird');
const download = Promise.promisify(require('download-git-repo'));
const spinner = ora('正在下载模板...');
function downloadFn(answers, dirname) {
  const { frame, name = dirname, description = dirname } = answers;
  let url = 'https://github.com:bodyno/react-starter-kit#master';
  if (frame === 'vue') {
    url = 'https://github.com:Mrminfive/vue-multiple-page#master';
  }
  spinner.start();
  download(url, dirname, { clone: false })
    .then(() => {
      spinner.stop();
      console.log(chalk.green('download template success'));
      const pkgPath = process.cwd() + `/${dirname}/package.json`;
      const content = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
      content.name = name;
      content.description = description;
      fs.writeFileSync(pkgPath, JSON.stringify(content));
    })
    .catch(err => {
      spinner.stop();
      console.log(chalk.red('download template failed'));
      console.log(err);
    });
}

Local Testing

Add a bin field to package.json mapping the command name to the script, then run npm link to create a global symlink. The CLI can now be invoked from any directory.

{
  "bin": {
    "gframe": "./bin/gframe"
  }
}

npm Publishing

Register or log in to an npm account, run npm adduser to authenticate, then publish with npm publish . If a name conflict occurs, change the name field in package.json before publishing.

Usage

Install the tool globally: npm install gframe -g . Create a new project with gframe init my-app , follow the prompts, and a ready‑to‑develop React or Vue scaffold will be generated in the current directory.

Conclusion

Beyond generating scaffolds, the CLI can be extended to reset template Git URLs, auto‑install dependencies, perform code linting, run tests, and more, offering developers a versatile tool to boost productivity.

CLIreactNode.jsVuescaffoldingnpm
Beike Product & Technology
Written by

Beike Product & Technology

As Beike's official product and technology account, we are committed to building a platform for sharing Beike's product and technology insights, targeting internet/O2O developers and product professionals. We share high-quality original articles, tech salon events, and recruitment information weekly. Welcome to follow us.

0 followers
Reader feedback

How this landed with the community

login 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.