preface

Scaffolding such as vue-CLI and React-native CLI can quickly generate an initial project by typing a simple command vue init webpack project. In actual work, we can customize a scaffold of our own to improve our work efficiency.

Why do you need scaffolding?

  • Less repetitive work, no need to copy other projects and delete irrelevant code, or create a project and file from scratch.
  • Dynamically generate project structures, configuration files, etc., based on interactions.
  • It’s easier to collaborate with multiple people, without having to pass files around.

Train of thought

To develop scaffolding, you first have to think clearly, how does scaffolding work? We can learn from vue-CLI basic ideas. Vue-cli is to put the project template on Git, and then download different templates according to user interaction during the operation, which are rendered by the template engine to generate the project. In this way, the template and scaffold can be maintained separately. Even if the template changes, only the latest template can be uploaded, and the latest project can be generated without requiring the user to update the scaffold. Then you can develop along these lines.

Third-party libraries

Let’s start by looking at which libraries we’ll use.

  • Commander. Js automatically parses commands and parameters to process user input commands.
  • Download-git-repo, which downloads and extracts a Git repository for downloading project templates.
  • Inquirer. Js, a collection of generic command-line user interfaces for interacting with users.
  • Handlebars.js, a template engine, dynamically populates files with user-submitted information.
  • Ora, if the download process is long, can be used to display the animation effect in the download.
  • Chalk, which adds color to the font on the terminal.
  • Log-symbols can display symbols such as √ or × on the terminal.

Initialize the project

Create an empty project, tentatively named okiI-cli, create a new index.js file, and execute NPM init to generate a package.json file. Finally, install the dependencies needed above.

npm install commander download-git-repo inquirer handlebars ora chalk log-symbols -S
Copy the code

Processing command line

Node.js has built-in support for command-line operations, and the bin field in package.json defines the command name and associated execution file. So now add the contents of bin to package.json:

{" name ":" okii - cli ", "version" : "1.0.0", "description" : "scaffolding tools based on node", "bin" : {" okii ":" index. Js "},... }Copy the code

Then define the init command in index.js:

#! /usr/bin/env node const program = require('commander'); The program version (1.0.0, '-v - version). The command (' init < name >'). The action ((name) = > {the console. The log (name); }); program.parse(process.argv);Copy the code

Calling version(‘1.0.0’, ‘-v, –version’) adds -v and –version to the command, and prints the version number with these options. Call command(‘init

‘) to define the init command, and name is the required parameter, which is the project name. Action () is what happens when you execute the init command, and this is where the process of building the project takes place, just printing the name for the moment. Actually, at this point, you’re ready to execute init. Let’s test this by executing it in the okiI-CLI sibling:

node ./okii-cli/index.js init HelloWorld
Copy the code

You can see that the command line tool also prints HelloWorld, so it is clear that the parameter name in action((name) => {}) is the name of the project we typed when we executed init.

Now that the command is complete, it’s time to download the template to generate the project structure.

Download the template

Download-git-repo supports downloading repositories from Github, Gitlab and Bitbucket. Please refer to the official documentation of each repository.

Since this is a company project, we have put the template repository on Gitlab, so download the template in action() :

#! /usr/bin/env node const program = require('commander'); const download = require('download-git-repo'); The program version (' 1.0.0 ', '- v, --version') .command('init <name>') .action((name) => { download('http://xxxxxx:9999:HTML5/H5Template#master', name, {clone: true}, (err) => { console.log(err ? 'Error' : 'Success') }) }); program.parse(process.argv);Copy the code

The first argument to Download () is the repository address, but it’s a little different. Actual warehouse address is http://xxxxxx:9999/HTML5/H5Template#master, you can see the port number at the back of the ‘/’ to write in the parameter ‘:’, # master representative is the branch name, different templates can be placed in different branches, You can change the branch to download different template files. ${name} = test/${name}

Command line interaction

After the user runs the init command, the cli interaction can ask the user a question, receive the user’s input, and process the problem accordingly. This is done using inquirer. Js.

const inquirer = require('inquirer'); inquirer.prompt([ { type: 'input', name: 'author', message: 'Please input author name'}]). Then ((answers) => {console.log(answers. Author); })Copy the code

As you can see from this example, the question is in prompt(), the type of the question is input, the name is the key in the answer object, the message is the question, and the answers the user typed are in answers. It’s that simple to use. You can refer to the official documentation for more parameter Settings.

The user’s input is obtained through command line interaction so that answers can be rendered into templates.

Apply colours to a drawing template

Here are some changes to the package.json file in the template of the HTML5/H5Template repository using handlebars syntax

{" name ":" {{name}} ", "version" : "1.0.0", "description" : "{{description}}", "scripts" : {" test ", "echo \" Error: no test specified\" && exit 1" }, "author": "{{author}}", "license": "ISC" }Copy the code

After downloading the template, render the user’s input into package.json

Program.version ('1.0.0', '-v, --version').command('init <name>').action((name) => {inquirer. Prompt ([{name: 'description', message: 'Please enter project description'}, {name: 'author', message: }]). Then ((answers) => {download('xxxxx#master',name,{clone: true},(err) => { const meta = { name, description: answers.description, author: answers.author } const fileName = `${name}/package.json`; const content = fs.readFileSync(fileName).toString(); const result = handlebars.compile(content)(meta); fs.writeFileSync(fileName, result); })})});Copy the code

Here we use the Node.js file module FS to write the handlebars rendered template back into the file.

Visual beautification

After the user enters an answer, the template is downloaded, and ora is used to indicate that the user is downloading.

const ora = require('ora'); // start downloading const spinner = ora(' downloading template... '); spinner.start(); // Call spinner.fail(); Spinner. Succeed ();Copy the code

Then, the chalk will add styles for the printed information, such as green for the success information and red for the failure information, which will make it easier for users to distinguish and make the display of the terminal more beautiful.

const chalk = require('chalk'); Console. log(chalk. Green (' project created successfully ')); Console. log(chalk. Red (' project creation failed '));Copy the code

In addition to coloring printed messages, log-symbols can be used to prefix messages with symbols such as √ or ×

const chalk = require('chalk'); const symbols = require('log-symbols'); Console. log(symbols.success, chalk. Green (' project created successfully ')); Console. log(symbols.error, chalk. Red (' project creation failed '));Copy the code

Complete sample

#! /usr/bin/env node const fs = require('fs'); const program = require('commander'); const download = require('download-git-repo'); const handlebars = require('handlebars'); const inquirer = require('inquirer'); const ora = require('ora'); const chalk = require('chalk'); const symbols = require('log-symbols'); The program version (1.0.0, '-v - version). The command (' init < name >'). The action (= > {if ((name)! Fs.existssync (name)){inquirer. Prompt ([{name: 'description', message: 'Please enter project description'}, {name: 'author', message: }]). Then ((answers) => {const spinner = ora(' Downloading template... '); spinner.start(); download('http://xxxxxx:9999:HTML5/H5Template#master', name, {clone: true}, (err) => { if(err){ spinner.fail(); console.log(symbols.error, chalk.red(err)); }else{ spinner.succeed(); const fileName = `${name}/package.json`; const meta = { name, description: answers.description, author: answers.author } if(fs.existsSync(fileName)){ const content = fs.readFileSync(fileName).toString(); const result = handlebars.compile(content)(meta); fs.writeFileSync(fileName, result); } console.log(symbols.success, chalk. Green (' initializing project completed ')); }})})}else{console.log(symbol. error, chalk. Red (' symbols ')); } }) program.parse(process.argv);Copy the code

The effect is as follows:


Once that’s done, you can publish the scaffold to the NPM, perform a global installation with -g, and initialize the project by executing okII init [name] on your own machine, thus completing a simple scaffold tool.

More articles:lin-xin/blog