preface

With scaffolding like vue-CLI and create-react-app, you can quickly generate an initial project by simply entering the 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, then 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": "suporka-parcel-vue"."version": "1.0.0"."description": "a vue cli which use parcel to package object"."bin": {
    "suporka-parcel-vue": "index.js"},... }Copy the code

Then define the init command in index.js:

#! /usr/bin/env node
const program = require('commander');

program.version('1.0.0'.'-v, --version')
    .command('init <name>')
    .action((name) => {
        console.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 sibling directory:

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

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([
    {
        name: 'description',
        message: 'Input the object description'
    },
    {
        name: 'author',
        message: 'Input the object author'
    }
    ]).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 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

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 templates... '); 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 nodeConst program = require(const program = require('commander'); // download template const download = require('download-git-repo'); Const inquirer = require('inquirer'); // node file module const fs = require('fs'); // Fill information to file const handlebars = require('handlebars'); // const ora = require('ora'); // const chalk = require('chalk'); // display ICONS const symbols = require('log-symbols'); Var shell = require("shelljs");

program.version('1.0.1'.'-v, --version')
  .command('init <name>')
  .action((name) => {
    if(! fs.existsSync(name)) { inquirer.prompt([ { name:'description',
          message: 'Input the object description'
        },
        {
          name: 'author',
          message: 'Input the object author'
        }
      ]).then((answers) => {
        const spinner = ora('Downloading... ');
        spinner.start();
        download('zxpsuper/suporka-parcel-vue', name, (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('The vue object has downloaded successfully! '));
            inquirer.prompt([
              {
                type: 'confirm',
                name: 'ifInstall',
                message: 'Are you want to install dependence now? ',
                default: true
              }
            ]).then((answers) => {
              if (answers.ifInstall) {
                inquirer.prompt([
                  {
                    type: 'list',
                    name: 'installWay',
                    message: 'Choose the tool to install',
                    choices: [
                      'npm'.'cnpm'
                    ]
                  }
                ]).then(ans => {
                  if (ans.installWay === 'npm') {
                    let spinner = ora('Installing... '); spinner.start(); Shell. Exec ()"cd " + name + " && npm i".function (err, stdout, stderr) {
                      if (err) {
                        spinner.fail();
                        console.log(symbols.error, chalk.red(err));
                      }
                      else {
                        spinner.succeed();
                        console.log(symbols.success, chalk.green('The object has installed dependence successfully! ')); }}); }else {
                    let spinner = ora('Installing... ');
                    spinner.start();
                    shell.exec("cd " + name + " && cnpm i".function (err, stdout, stderr) {
                      if (err) {
                        spinner.fail();
                        console.log(symbols.error, chalk.red(err));
                      }
                      else {
                        spinner.succeed();
                        console.log(symbols.success, chalk.green('The object has installed dependence successfully! ')); }})}})}else {
                console.log(symbols.success, chalk.green('You should install the dependence by yourself! ')); }})}})})}elseError: console.log(symbols.error, chalk. Red () {// Error: console.log(symbols.error, chalk.'The object has exist')); }}); program.parse(process.argv);Copy the code

Suporka-parcel -vue (suporka-parcel-vue)