preface

At the moment, as a front-end coder, not knowing Nodejs is inexcusable. It can be said that, except for a few specific business categories, the common back-end business Nodejs can handle it.

Another common scenario for Nodejs is to build utilities, most of which are command-line programs. Today we will show you how to write a Nodejs command-line application.

The indigenous practices

When a Nodejs program is running, there are many global variables that exist in memory, one of which is called Process, which stands for process object. The Process object has an attribute called argv. The first big part of the command line program is parsing the process.argv property.

Let’s write a random Node program and print out process.argv.

$node test1. Js -- -- the name gk ['/usr/local/Cellar/node / 6.6.0 / bin/node ', '/ Users/gejiawen/code / 20160921 / test1. Js', '--name', 'gk' ]Copy the code

Argv looks like an array, where the first element is node’s execution path, the second element is the path to the current execution file, and starting with the third element, the arguments that are carried in at execution time.

So, the pattern is simple. When writing a command line program, we only need to parse the third element of the process.argv array and its subsequent arguments.

If it’s not too much trouble, you can write a lot of branches to do it. But now we have a better way.

Using the commander. Js

Commander. Js is a toolkit written by TJ that makes node command line programming much easier.

Installation and use

Installation is simple,

$ npm install commander
Copy the code

Note that the package name is commander instead of commander.js.

Then we will create a new js file called index.js with the following contents

Var program = require('commander') program.version ('0.0.1').description('a test cli program').option('-n, --name <name>', 'your name', 'GK') .option('-a, --age <age>', 'your age', '22') .option('-e, --enjoy [enjoy]') program.parse(process.argv)Copy the code

At this point, a simple command-line program is complete. We execute it with the following command,

$ node index.js -h
Copy the code

The results are as follows,

$ ./test -h

  Usage: test [options]

  a test cli program

  Options:

    -h, --help           output usage information
    -V, --version        output the version number
    -n, --name <name>    your name
    -a, --age <age>      your age
    -e, --enjoy [enjoy]
Copy the code

The first advantage of commander.js is that it provides an introductory API for parsing options and arguments. The second advantage is automatically generating help text messages.

Commonly used API

There are two types of variability on the command line in commander.js. One is called option, which means options. One is called command, which means command.

Let’s look at two examples,

Program.version ('0.0.1'). Option ('-c, --chdir <path>', 'change the working directory'). Option ('-c, --config <path>', 'set config path. defaults to ./deploy.conf') .option('-T, --no-tests', 'ignore test hook') program .command('setup') .description('run remote setup commands') .action(function() { console.log('setup'); }); program .command('exec <cmd>') .description('run the given remote command') .action(function(cmd) { console.log('exec "%s"', cmd); }); program .command('teardown <dir> [otherDirs...] ') .description('run teardown commands') .action(function(dir, otherDirs) { console.log('dir "%s"', dir); if (otherDirs) { otherDirs.forEach(function (oDir) { console.log('dir "%s"', oDir); }); }}); program .command('*') .description('deploy the given env') .action(function(env) { console.log('deploying "%s"', env); }); program.parse(process.argv);Copy the code
  • throughoptionThe options set can be passedprogram.chdirorprogram.noTestsTo access.
  • throughcommandThe command to set is usually inactionThe logic is handled in the callback.

version

Usage: version (‘ X.Y.Z ‘)

Used to set the version number of the command program,

option

User: option (‘ – n – name < name > ‘, ‘your name’ and ‘GK)

  • The first parameter is the option definition, divided into short and long definitions. with|...The connection.
    • Parameters can be used as<>or[]Modifier, the former meaning required parameter, the latter meaning optional parameter.
  • The second parameter is the option description
  • The third parameter is the default value of the optional parameter.

command

Usage:.command(‘init ‘, ‘description’)

  • commandIn principle, it can accept three parameters: the first is the command definition, the second is the command description, and the third is the command auxiliary modifier object.
  • Can be used in the first argument<>or[]Modify command parameters
  • The second parameter is optional.
    • Commander.js returns when there is no second argumentCommandObject, with a second argument, returns the prototype object.
    • When a second argument is taken and no call is displayedaction(fn), the subcommand mode will be used.
    • The subcommand mode is,./pm../pm-install../pm-searchAnd so on. These subcommands are in separate files from the main command.
  • The third parameter is generally not used, it can set whether to display using subcommand mode.

description

.description(‘command description’)

Description of the command to set

action

Usage: the action (fn)

Used to set the related callbacks for command execution. Fn can accept commands as function parameters in the same order as defined in command().

parse

Usage: the program. The parse (process. Argv)

This API is typically called last and is used to parse process.argv.

outputHelp

Usage: program. OutputHelp ()

Generally, the help information is automatically printed when no parameter is entered.

eg:

if (! process.argv.slice(2).length) { program.outputHelp(make_red); } function make_red(txt) { return colors.red(txt); //display the help text in red on the console }Copy the code

practice

Let’s build a small tool that demonstrates the power of commander.js.

The tool, called npMRc-local, generates a.npmrc file in the execution directory using the default registry, Miley, and loglevel configurations (pointing to npm.taobao.org).

First we have to create a project,

$ mkdir npmrc-local
$ git init
$ npm init
$ touch .gitignore
$ touch bin/npmrc.js
$ touch lib/index.js
Copy the code

Modify package.json file to add bin field,

{
    "bin": {
        "npmrc": "./bin/npm.js"
    }
}
Copy the code

Modify the bin/NPMRC js,

#! /usr/bin/env node require('.. /lib/index')Copy the code

Modify the lib/index, js,

#! /usr/bin/env node var fs = require('fs') var path = require('path') var readline = require('readline') var program = require('commander') var rc = require('.. /rc') var exit_bak = process.exit program.version ('0.0.1').allowunknownoption ().option('-r, --registry <registry>', 'use custom registry', rc.registry) .option('-d, --dist-url <disturl>', 'use custom dist url', rc.disturl) .option('-l, --log-level <loglevel>', 'use custom log level', rc.loglevel) program.parse(process.argv) program.registry && (rc.registry = program.registry) program.distUrl && (rc.disturl = program.distUrl) program.logLevel && (rc.loglevel = program.logLevel) if (! _exit.exited) { _main() } // Graceful exit for async STDIO function _exit(code) { var draining = 0 var streams = [process.stdout, process.stderr] function done() { if (! (draining--)) { exit_bak(code) } } _exit.exited = true streams.forEach(function (stream) { draining += 1 stream.write('', done) }) done() } function _confirm(msg, cb) { var rl = readline.createInterface({ input: process.stdin, output: process.stdout }) rl.question(msg, function (input) { rl.close() cb(/^y|yes|ok|true$/i.test(input)) }) } function _write(path, content, mode) { fs.writeFileSync(path, content, { mode: mode || 0o666 }) console.log('success!!! ') } function _generateFile(filePath) { var content = 'registry={registry}\ndisturl={disturl}\nloglevel={loglevel}\n' content = content.replace(/\{(\w+)\}/gi, function (a, b) { return rc[b] }) _write(filePath, content) } function _overwrite(filePath) { _generateFile(filePath) } function _existNpmRC(filePath) { fs.exists(filePath, function (exists) { if (exists) { _confirm('ATTENTION: .npmrc is exist, over write? [y/N] ', function (ans) { ans ? _overwrite(filePath) : console.log('bye! ') }) } else { _generateFile(filePath) } }) } function _main() { var filePath = path.resolve(process.cwd(), '.npmrc') console.log('writing path: ' + filePath) _existNpmRC(filePath) }Copy the code

After all the code work is done, modify the version field in package.json and publish NPM addUser&npm to make the toolkit available to everyone.

Because I have not updated my blog for a long time and have not pushed the package to the NPM warehouse, THIS time I stepped on the pit I met before and burst into tears. See NPM Adduser’s pit for details.

If you are not sure how to upload your own toolkit to NPM, please refer to this article.

In addition to the introduction to commander.js, this article also introduces common tools for creating command-line tools such as Chalk and Process, which is worth reading.

– EOF –

This paper links: blog.gejiawen.com/2016/09/21/…