About the front scaffolding

I wanted to write this article for a long time, but I've been delayed by other things (maybe because I'm too lazy). If I don't write it down, I'll be thrown out of the sky.

The emergence of Nodejs has deepened the concept of front-end engineering and is approaching the regular army. First, it brings powerful construction tools such as Gulp and webpack, and then there are perfect scaffolds such as Vue CLI and create react app, which provide a complete project architecture, so that we can pay more attention to the business without spending a lot of time on the project infrastructure.

However, these ready-made scaffolds may not be able to meet our business needs or best practices. At this time, we can develop a scaffold ourselves. Of course, this is actually very simple. It can be done by using the ready-made wheels on npm. Here's a record. It's only a memo to attract jade.



In a project in the first half of the year, we need to customize a scaffold to help our partners improve development efficiency, unify the quality of code output, and solve some problems in use. Of course, it is also for installation.
Before using scaffold construction, we encountered these problems:

  • When creating each project, you need to go to Git warehouse to pull the project template or copy the previous project. There are two problems in doing so

    • After pulling a project from Git, because some people have push permission, if they push the modifications in the private project to the template warehouse by mistake, the project template on Git may be damaged
    • The latest project template cannot be obtained from the previous project copy, which leads to some problems that have been repaired in the latest template, but still exist in the new project
  • The project template needs to fill in some configuration information, which is easy for developers to forget to fill in

So let's solve these problems with the following ideas:

  • Pull the latest template from Git, and finally kill the Git warehouse information and cut off the association with the remote warehouse
  • During initialization, the user is forced to enter the configuration information through question and answer, and then the configuration file is generated according to the configuration information, which is a bit similar to vueCli initialization project.

Of course, in addition to the above requirements, we can also do some additional work:

  • After the pull is completed, automatically install the project dependency and open the editor
  • Provide help information and common commands
  • Publish to npm, and all personnel can directly install and use it globally
  • ...


Urgent as a law

Implementation of executable modules

First, we need to create a project called yncms template cli. The project structure is as follows:

- commands  // This folder is used to place custom commands
- utils
- index.js  // Project entrance
- readme.md

In order to test, let's start with index JS put some content:

#!/usr/bin/env node
// The above content must be added to the file header. Specify the running environment as node
console.log('hello cli');

For general nodejs projects, we directly use node index JS is OK, but this is a scaffold. It must not be like this. We need to publish the project to npm, and users can install it globally, and then we can directly use our customized commands, such as yncms template.

Therefore, we need to make changes to our project. First, we need to make changes in packge Add the following content to JSON:

 "bin": {
    "yncms-template": "index.js"

In this way, yncms template can be defined as a command, but it can only be used in the project at this time, not as a global command. Here, we need to use npm link to link it to the global command. After successful execution, it will be displayed in your global node_ The corresponding files can be found in the modules directory. Then enter the command to test. If the following contents appear, the first step has been more than half successful:

PS E:\WorkSpace\yncms-template-cli> yncms-template
hello cli

However, at present, this command can only be used on our own computer. If you want others to install and use it, you need to publish it to npm. The general process is as follows:

  1. Register an npm account. If you already have an account, you can skip this step
  2. To log in using npm login, you need to enter username, password and email
  3. Publish using npm public

This step is relatively simple and not much to say, but please pay attention to the following points:

  • If nrm is used, you need to switch the source to the official npm source first
  • package. There are several fields in JSON that need to be improved:

    • Name is the published package name, which cannot be duplicated with npm existing packages
    • The version of each release must be higher than the online version
    • Home page, bugs and repository can also be added, corresponding to the following pages

  • In readme MD adds the introduction and use method of scaffold, which is convenient for others to use. If you need to add a logo to the document to show the number of downloads of the scaffold, you can generate it here.

After publishing successfully, you need to wait for a while before you can search in the npm warehouse.


Create command

Since it's a scaffold, you can't just let it output a paragraph of text. We also need to define some commands. Users input these commands and parameters on the command line, and the scaffold will make corresponding operations. There is no need for us to analyze these input commands and parameters. There are ready-made commanders that can be used, which can fully meet our needs.

Help (- - help)

After installing the commander, we will index JS is changed as follows:

#!/usr/bin/env node
const commander = require('commander');
// When parsing command line input with commander, it must be written at the end of all contents

At this time, although we do not define any command, the commander defines a help command -- help (abbreviated as - h):

PS E:\WorkSpace\yncms-template-cli> yncms-template -h
Usage: index [options]

  -h, --help  output usage information

version (- - version)

Next, we create a query version of the command parameters in index JS add the following content:

// View version number

In this way, we can view the version number on the command line:

PS E:\WorkSpace\yncms-template-cli> yncms-template -V
PS E:\WorkSpace\yncms-template-cli> yncms-template --version

The default parameter is uppercase V. if you need to change it to lowercase, you can change the above content as follows:

// View version number
    .option('-v,--version', 'View version number');
PS E:\WorkSpace\yncms-template-cli> yncms-template -h
Usage: index [options]
  -V, --version  output the version number
  -h, --help     output usage information

init subcommand

Next, let's define an init command, such as yncms template init test.
In index JS, add the following contents:

    .command('init <name>') // Define the init subcommand, < name > is a required parameter and can be received in the function of action. If you need to set non required parameters, you can use brackets
    .option('-d, --dev', 'Get development Edition') // Configuration parameters, used in short and full letters, split
    .description('Create project') // Command description
    .action(function (name, option) { // The command executes the operation, and the parameters correspond to the parameters set above
        // All the operations we need to perform are completed here

Now test:

PS E:\WorkSpace\yncms-template-cli> yncms-template init test -d

For the specific usage of commander, please check the official documents.

In this way, even if a user-defined command prototype is completed, there are still several things to do:

  • The specific operations of the init command are described in a separate section below.
  • To facilitate maintenance, split the command action into the commands folder


Pull item

Above, we defined the init command, but it did not achieve the purpose of initializing the project. Next, we will implement it.

Generally speaking, there are two processing methods for project templates:

  • The advantage of putting the project template and the scaffold together is that after the user installs the scaffold, the template is local and the initialization will be faster; The disadvantage is that updating the project template is troublesome because it is coupled with the scaffold
  • The advantage of placing the project in a separate GIT warehouse is that the template update is relatively simple, because it is independent of each other. You only need to maintain the template's own warehouse. In addition, you can control the pull permission, because if it is a private project, people without permission cannot pull successfully; The disadvantage is that you have to go to GIT to pull every initialization, which may be slower, but the impact is small, so it is recommended to choose this method

First, we use download git repo to encapsulate a clone method for pulling items from GIT.

// utils/clone.js
const download = require('download-git-repo');
const symbols = require('log-symbols');  // Icon for output
const ora = require('ora'); // Used to output loading
const chalk = require('chalk'); // Used to change the text color
module.exports = function (remote, name, option) {
    const downSpinner = ora('Downloading template...').start();
    return new Promise((resolve, reject) => {
        download(remote, name, option, err => {
            if (err) {
                console.log(symbols.error, chalk.red(err));
            downSpinner.succeed(chalk.green('Template downloaded successfully!'));
// commands/init.js
const shell = require('shelljs');
const symbols = require('log-symbols');
const clone = require('../utils/clone.js');
const remote = 'https://gitee.com/letwrong/cli-demo.git';
let branch = 'master';

const initAction = async (name, option) => {
    // 0. Check whether the console can run 'git',
    if (!shell.which('git')) {
        console.log(symbols.error, 'I'm sorry, git Command not available!');
    // 1. Verify whether the input name is legal
    if (fs.existsSync(name)) {
        console.log(symbols.warning,`Project folder already exists ${name}!`);
    if (name.match(/[^A-Za-z0-9\u4e00-\u9fa5_-]/g)) {
        console.log(symbols.error, 'Illegal character in project name!');
    // 2. Get the option and determine the template type (Branch)
    if (option.dev) branch = 'develop';
    // 4. Download template
    await clone(`direct:${remote}#${branch}`, name, { clone: true });

module.exports = initAction;

Test it, and you can successfully pull the project without accident.

The items pulled here are associated with the remote warehouse. We need to delete them (because our project is managed by svn, we can delete the. Git folder directly. If Git is used, we can initialize git init). Clean up some redundant files:

// commands/init.js
// 5. Clean up documents
const deleteDir = ['.git', '.gitignore', 'README.md', 'docs']; // Files to be cleaned
const pwd = shell.pwd();
deleteDir.map(item => shell.rm('-rf', pwd + `/${name}/${item}`));


Some personalization

In the above process, we realized the basic function of a scaffold, which is roughly divided into three processes (pull template - > create project - > close out cleaning), and also solved the first problem encountered in my project above. Next, let's look at how to solve the second problem.

The solution is to force developers to enter the corresponding configuration through the command line when creating the project, and then write the configuration file automatically, so as to effectively avoid the embarrassment of forgetting to fill in. Of course, this way can also realize the dynamic initialization of the project according to the user's input, so as to achieve the purpose of personalization.

Here we can directly use the ready-made wheel inquirer. The effect is the same as that of VueCli's project. It supports many types, which are powerful and simple. See the official documents for specific usage. Here, I directly the code and add the following before step 4 (download template):

// init.js
const inquirer = require('inquirer');
// Define questions to ask
const questions = [
    type: 'input',
    message: 'Please enter the template name:',
    name: 'name',
    validate(val) {
      if (!val) return 'Template name cannot be empty!';
      if (val.match(/[^A-Za-z0-9\u4e00-\u9fa5_-]/g)) return 'The template name contains illegal characters, please re-enter';
      return true;
    type: 'input',
    message: 'Please enter template keywords(;(split):',
    name: 'keywords'
    type: 'input',
    message: 'Please enter the profile of the template:',
    name: 'description'
    type: 'list',
    message: 'Please select template type:',
    choices: ['Responsive', 'Desktop end', 'Mobile terminal'],
    name: 'type'
    type: 'list',
    message: 'Please select template classification:',
    choices: ['Whole station', 'Single page', 'special'],
    name: 'category'
    type: 'input',
    message: 'Please enter template style:',
    name: 'style'
    type: 'input',
    message: 'Please enter the template color system:',
    name: 'color'
    type: 'input',
    message: 'Please enter your name:',
    name: 'author'
// Get the content entered by the user through the inquirer
const answers = await inquirer.prompt(questions);
// Print the user's configuration and confirm whether it is correct
let confirm = await inquirer.prompt([
        type: 'confirm',
        message: 'Confirm creation?',
        default: 'Y',
        name: 'isConfirm'
if (!confirm.isConfirm) return false;

After obtaining the configuration entered by the user, you can write the configuration file or do personalized processing. This is too simple for me to repeat here.


add a beautiful thing to a contrasting beautiful thing

Here, a scaffold that fully meets the needs is completed, but as a aspiring programmer, we can do something more about the interface and ease of use:

  • Adding loding animation to asynchronous operations, you can use ora directly
const installSpinner = ora('Installing dependencies...').start();
if (shell.exec('npm install').code !== 0) {
    console.log(symbols.warning, chalk.yellow('Automatic installation failed, please install manually!'));
    installSpinner.fail(); // Installation failed
installSpinner.succeed(chalk.green('Dependency installation succeeded!'));
  • When the operation succeeds or fails, the icon prompt is given, and log symbols is used
  • You can add some color to the text. Similarly, use the ready-made wheel Chalk
  • When installing dependencies or taking a long time, the user may switch the terminal to the background. At this time, after our operation is completed, we can use node notifier to send a system notification to give the user a prompt.
    title: 'YNCMS-template-cli',
    icon: path.join(__dirname, 'coulson.png'),
    message: ' Congratulations, the project was created successfully!'
  • When creating a project, we may need to execute some shell commands, which can be completed by using shelljs. For example, we need to open vscode and exit the terminal after the project is created
// 8. Open the editor
if (shell.which('code')) shell.exec('code ./');

Guangzhou packaging design companyhttp://www.maiqicn.com Computer embroidery factory ttp://www.szhdn.com


Here, you will find that developing a scaffold is actually very simple. It can be done with ready-made wheels. I don't know which Daniel said that playing NodeJS is playing with wheels.

In addition to the above methods, we can also directly create it through the famous Yeoman, but I don't think it's necessary. After all, it's not difficult.

A good scaffold should be able to solve the problems encountered in the work and improve the development efficiency.

Tags: Front-end

Posted by mrtechguy on Fri, 13 May 2022 19:36:19 +0300