Scaffolding Tool (commander)

Posted by Innovative_Andy on Mon, 09 Sep 2019 10:23:25 +0200

With the continuous development of NodeJs, there are more things to do for the front end. A series of things such as Vue scaffolding, React scaffolding and so on stand out and enter people's vision. For these scaffolding tools, they just stay in the application stage, and never think about how scaffolding is realized. How the vue init webpack project name creates a project through such commands, the most important module of which is Commander today.

Commander module is compiled by TJ God abroad

Project address: Commander

Commander Basic Usage

Commander document is written in detail, follow the article to learn in detail, Commander is a Nodejs module, need to run in the Node environment, before using to confirm whether the Node environment has been installed.

Installation dependency

npm install commander --save

Options parsing

Under the Commander module, there exists an option method to define the options of the commander, which is used as the document for the options.

var program = require('commander');

program
  .option('-g, --git [type]', 'Add [marble]', 'Angie')
  .parse(process.argv);

console.log("process.argv",process.argv)
console.log("program.args",program.args)

console.log('you ordered a pizza with:');
if (program.git) console.log('  - git');
console.log('  - %s git', program.git);

The above example parses args and options from process.argv, then assigns the remaining parameters (undefined parameters) to the args attribute (program.args) of the commander object, which is an array.

Print out process.argv and program.args and check the output as follows. Run the file using the following commands:

node index -g type Aaron
process.argv ['F:\\node\\installation\\node.exe',
              'C:\\Users\\wo_99\\Desktop\\cli-dome\\index',   
              '-g',
              'type',
              'Aaron' ]
program.args [ 'Aaron' ]  

The option method can receive three parameters:

  1. Custom logos must be divided into long and short logos, separated by commas, vertical lines or spaces, followed by necessary or optional parameters, the former containing with <> and the latter containing with [].
  2. Option Description Omits Error-free: Display Flag Description when Using the -- help command
  3. Default values can be omitted: default values are used when no parameters are passed in

If we execute node index -g, the result is Angie git whose third parameter is filled in the corresponding position as the default value. In addition to the above, you can use the following commands:

//  Execution-g parameter a
//  Execution - b parameter s
node index -g a -b s
//  Execute - g and - b to pass in a parameter to - g
//  - b parameter does not know how to pass in for the time being
node index -gb a

Version options

The invoked version adds the - V and - version options to the command by default. When any of these options exists, the command prints the version number and exits.

var program = require('commander');
 
program
    .version('0.0.1')
    .parse(process.argv);
//  Execution of orders
//  node index -V

//  Output results
//  0.0.1

If you want the program to respond to the - V option instead of the - V option, you just need to pass the custom flag to the version method using the same syntax as the option method. The version flag can be named any value, but long options are required.

var program = require('commander');
program
  .version('0.0.1', '-e, --version');

Command adds command name

This method allows the use of the command line to execute a command, that is, a section:

var program = require('commander');

program
  .version('0.0.1', '-V, --version')
  .command('rm <dir>')
  .action(function (dir, cmd) {
    console.log('remove ' + dir + (cmd.recursive ? ' recursively' : ''))
  });

program.parse(process.argv);

//  Execution of orders
//  node index rm /aaa -r

//  Output results
//  Remove / AAA recursivity: console content in code

The command function receives three parameters:

  1. The name of the command must be: the parameters contained in <> or [] can be followed by the command; the last parameter of the command can be variable, adding a flag after the array as in the example; the parameters passed after the command are passed into the callback function of action and the program.args array.
  2. Command description can be omitted: If there is an action(fn) call, the subcommand program will be started, otherwise an error will be reported.
  3. Configuration options can be omitted: configurable noHelp, isDefault, etc.

When the command is executed, the options of the command are validated, and any unknown option will report an error. However, options are not validated if action-based commands do not define actions.

var program = require('commander');

program
  .version('0.0.1', '-V, --version')
  .command('rm <dir>')
  .option('-r, --recursive', 'Remove recursively')
  .action(function (dir, cmd) {
    console.log('remove ' + dir + (cmd.recursive ? ' recursively' : ''))
  });
program.parse(process.argv);
console.log(program.args)

//  Execution of orders
//  node index rm /aaa -r /ddd

//  Output results
//  remove /ddd recursively
//  [ '/aaa' ]

HelOption Help

Providing help information

var program = require('commander');
program
  .version('0.1.0')
  .helpOption('-h,--HELP')
  .option('-f, --foo', 'enable some foo')
  .option('-b, --bar', 'enable some bar')
  .option('-B, --baz', 'enable some baz');
program.parse(process.argv);

//  Execution of orders
//  node index -h or node index --HELP
/*  Output results
 *  Options:
 *    -V, --version  output the version number
 *    -f, --foo      enable some foo
 *    -b, --bar      enable some bar
 *    -B, --baz      enable some baz
 *    -h,--HELP      output usage information 
 */

Output help information and exit immediately. The optional callback cb allows post-processing of help text before it is displayed. HelOption also provides long names, - h, - - both are acceptable when HELP calls for long names after short names. The use of version is similar.

Description command description

The second parameter of command is also the description of command. When it coexists with description, it takes precedence over the description of the second parameter, and description is displayed at the top as a global description. This description can only be seen when - HELP is used.

var program = require('commander');

program
  .version('0.0.1', '-V, --version')
  .command('rm <dir>',"arg is description")
  .description("this is description")
  .option('-r, --recursive', 'Remove recursively')
  .action(function (dir, cmd) {
    console.log('remove ' + dir + (cmd.recursive ? ' recursively' : ''))
  });

program.parse(process.argv);

//  Execution of orders
//  node index -h
//  Output results
/*
this is description

Options:
  -V, --version    output the version number
  -r, --recursive  Remove recursively       
  -h, --help       output usage information 

Commands:
  rm <dir>         arg is description       
  help [cmd]       display help for [cmd] 
*/

As can be seen from the output above, the final description of the rm command is arg is description, and if the second parameter is deleted, this is description will be output.

Custom Event Listener

Used to capture option s and command s, trigger functions when they are used by thieves.

var program = require('commander');

program
  .version('0.0.1', '-V, --version')
  .command('rm <dir>',"arg is description")
  .option('-r, --recursive', 'Remove recursively')
  .option('-g, --git [type]', 'Add [marble]', 'Angie')
  .option('-a, --am',"ampm")
  .action(() => {
    console.log(123)
  });
program.on('option:am', function () {
  console.log("on:am")
});
program.on('option:recursive', function () {
  console.log("option:recursive")
});
program.on('command:rm', function () {
  console.log("command:rm")
});
program.on('option:git', function () {
  console.log("option:git")
});
program.on('command:*', function () {
  console.log(987)
  console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' '));
  process.exit(1);
});
program.on('--help', function() {
  console.log('****************');
  console.log('Examples:');
  console.log('****************');
  console.log('  $ deploy exec sequential');
  console.log('  $ deploy exec async');
});
program.parse(process.argv);

Executing command and option separately triggers the corresponding function in turn, but when exactly did command:* trigger?

  1. command and option are defined but triggered when event capture is not performed
  2. When a parameter is specified or there is no parameter, the function is triggered by an incoming parameter.
  3. No command

The above triggers the command:* corresponding event, option: closely followed by the long name of option. Only then can the event be captured.

Developing Local Modules

Create project files as follows:

├─bin
│  └─init-project.js
├─lib
│  └─install.js
└─templates
    └─dome1

After creating the project directory, install the following dependency packages:

  1. chalk
  2. commander
  3. fs-extra
  4. path
  5. through2
  6. vinyl-fs
  7. which

Command: npm install --save-dev chalk commander fs-extra through2 vinyl-fs which path

First, add #!/usr/bin/env node in the first line of init-project.js, which is used to specify the executor of the script, where Node can be used!/usr/bin/node, if the user installs Node in a non-default path, Node will not be found. So ~ It's better to select env environment variables to find the Node installation directory.

init-project.js

#! /usr/bin/env node

// Introducing dependencies
var program = require('commander');
var vfs = require('vinyl-fs');
var through = require('through2');
const chalk = require('chalk');
const fs = require('fs-extra');
const path = require('path');

// Define version number and command options
program
  .version('1.0.0')
  .option('-i --init [name]', 'init a project', 'myFirstProject')

program.parse(process.argv);

if (program.init) {
  // Get the project root directory to be built
  var projectPath = path.resolve(program.init);
  // Get the name of the project to be built
  var projectName = path.basename(projectPath);
  console.log(`Start to init a project in ${chalk.green(projectPath)}`);

  // Create folders based on the project name to be built
  fs.ensureDirSync(projectName);

  // Get the demo1 directory under the local module
  var cwd = path.join(__dirname, '../templates/demo1');

  // Read and remove all files from the node_modules directory from the demo1 directory and filter them
  vfs.src(['**/*', '!node_modules/**/*'], { cwd: cwd, dot: true }).
  pipe(through.obj(function (file, enc, callback) {
    if (!file.stat.isFile()) {
      return callback();
    }

    this.push(file);
    return callback();
  }))
    // Write the stream of files read from the demo1 directory to the folder you created earlier
    .pipe(vfs.dest(projectPath))
    .on('end', function () {
      console.log('Installing packages...')

      // Change the node working directory to the project root directory under construction
      process.chdir(projectPath);

      // Execute installation commands
      require('../lib/install');
    })
    .resume();
}

install.js

// Introducing dependencies
var which = require('which');
const chalk = require('chalk');

var childProcess = require('child_process');

// Open subprocesses to execute npm install commands
function runCmd(cmd, args, fn) {
  args = args || [];
  var runner = childProcess.spawn(cmd, args, {
    stdio: 'inherit'
  });

  runner.on('close', function (code) {
    if (fn) {
      fn(code);
    }
  })
}

// Find commands for installing dependency packages in the system
function findNpm() {
  var npms = ['tnpm', 'cnpm', 'npm'];
  for (var i = 0; i < npms.length; i++) {
    try {
      // Find the first instance of the executable specified under the environment variable
      which.sync(npms[i]);
      console.log('use npm: ' + npms[i]);
      return npms[i]
    } catch (e) {
    }
  }
  throw new Error(chalk.red('please install npm'));
}

var npm = findNpm();
runCmd(which.sync(npm), ['install'], function () {
  console.log(npm + ' install end');
})

After completing the above code, change package.json to add the following attributes:

{
  "bin": {
    "q-init": "./bin/init-project.js"
  }
}

Note: The file specified after the custom instruction must be added. js suffix file name, otherwise an error will be thrown.

Next is the test. For testing, there is no need to push the installation package into npm. NPM provides npm link command for convenience, which can realize pre-release. If there are no errors in using npm link in the project root directory, the push is successful. Now you can use q-init globally.

Using initP-h command globally, the ability to output compiled help information indicates that the project can be initialized.

Usage: init-project [options]

Options:
  -V, --version     output the version number
  -i --init [name]  init a project (default: "myFirstProject")
  -h, --help        output usage information

summary

commander plays a very important role in Vue-cli and creat-app(react). This way of creating scaffolding is different from that of vue-cli. vue-cli uses git to remotely pull the project and then complete initialization. This way, it is more convenient and flexible. Each template change does not need to upload the package again, but only needs to change G. It warehouse is good, convenient and fast.

git address: cli-dome

Topics: node.js npm git Vue React