Building a command line tool - auttam/easycli GitHub Wiki
Building a command-line tool for an npm package requires the following -
- An npm package
- A script that serves as a program for the command-line tool. These types of scripts contain hashbang for Node.js scripts and are generally kept in the
binfolder of an npm package. - An entry in the
binkey of apackage.jsonthat maps a command name to the program script. This entry specifies by what command the program will be invoked from the command line.
Building a command-line tool with EasyCli
As mentioned above, building a CLI tool requires a script containing the code that represents the program of the CLI. In EasyCli, this program is represented by a JavaScirpt Class which inherits from base Program class and contains methods that represent the commands that the CLI can accept.
Creating an executable script with a program
To create an executable script, create a file index.js with following contents -
#!/usr/bin/env node
const Program = require('@auttam/easycli').Program
// a class representing the cli program
class SampleProgram extends Program {}
// runs the program
Program.run(new SampleProgram())
The above examples show an executable script that has 3 types of code
- hashbang for Node.js scripts,
- a class for the sample program and,
- a statement that creates an instance of the program class and runs it
default program options --help and --version
A program created with EasyCli accepts 2 options by default - -h or --help and -v or --version. (see Program Settings page to know how to disable default options)
Getting CLI help -
node ./index.js -h
Sample Program v1.0.0
Usage: sample-program
Other usage:
sample-program --help, -h To view help
sample-program --version, -v To view help
Getting CLI version info -
node ./index.js -v
Sample Program v1.0.0
See CLI Configuration page to know how to change auto-generated name and version.
Program methods
The Program methods are the class methods that are automatically called when the CLI is invoked from the command-line.
EasyCli supports 2 types of programs -
- a program that performs a single main operation and,
- a program that performs multiple operations each identified and invoked by a command of the program
Based on how a CLI program is configured to operate, there can be a single main() method performing a single main operation of the program or one or more command methods denoting different operations the program supports.
The main() method
This the default method that is called when the program is running in a single operation mode. By default it is main() but can be changed using Program Settings.
Following is an example of using main method:
#!/usr/bin/env node
const Program = require('@auttam/easycli').Program
// a class representing the CLI program
class SampleProgram extends Program {
// main method
main(){
console.log('hello-world')
}
}
// runs the program
Program.run(new SampleProgram())
Running the program now will print 'hello-world' to the
node ./index
#expected output
hello-world
Command methods
A program can be enabled to perform different operations based on the invoked commands from the command-line interface. Commands, the program accepts, can be configured using the command configuration object, a command decorator or by defining a method that has a name ending with 'Command' suffix.
The following example shows how to configure a command by using a command method name convention.
Create or update index.js file with the following content -
#!/usr/bin/env node
const Program = require('@auttam/easycli').Program
// setting to enable program commands
Program.settings({
enableCommands: true
})
// represents the CLI program
class DemoCommands extends Program {
// method called when 'test' command is requested
testCommand() {
console.log('This is a test command');
}
}
// Running the program
Program.run(new DemoCommands())
Test the command by running the above script as following -
node ./index test
#expected output
This is a test command
Note: When running in command-mode, the first non-option argument supplied from the command-line is treated as the command name of the CLI program.
Asynchronous methods
The program methods and the program event callbacks are executed asynchronously.
#!/usr/bin/env node
const Program = require('@auttam/easycli').Program
Program.settings({
enableCommands: true
})
// a class representing the CLI program
class SampleProgram extends Program {
// async process
testProcess(value) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(value)
}, 1000)
})
}
// command using async/await
async test1Command() {
var value = await this.testProcess(1)
console.log(value)
}
// command returning a promise
test2Command() {
return this.testProcess(2).then(value => console.log(value))
}
// method called on program exit
onExit(err) {
console.log('Existing', err)
}
}
// runs the program
Program.run(new SampleProgram())
Command-line Arguments
Command-line arguments supplied to the CLI are parsed as parameters and options and passed to the program methods.
In the following 2 examples, arg1 & arg2 will be parsed as the parameters whereas --arg3 will be parsed as an option-
# Example 1
node ./hello-world arg1 arg2 --arg3
# Example 2
hello-world arg1 arg2 --arg3
Note: When commands are enabled the first argument supplied to the CLI is treated as the command name.
In the example 3, arg0 will be interpreted as the command name argument-
# Example 3
node ./hello-world arg0 arg1 arg2 --arg3
Method Parameters
EasyCli maps the parameter arguments (non-command, non-option arguments) directly to the program methods. There are two ways how it gets the mapping information - 1. from the method signature itself and 2. from the program and command configurations.
Following is a simple no-configuration example showing how command-line arguments are passed as the method parameters-
#!/usr/bin/env node
const Program = require('@auttam/easycli').Program
// a class representing the CLI program
class SampleProgram extends Program {
// main method
main(param1, param2){
console.log('param1:', param1)
console.log('param2:', param2)
}
}
// runs the program
Program.run(new SampleProgram())
Assuming the above code is saved as index.js invoke the script as follwoing-
node ./index value1 value2
#expected output
param1: value1
param2: value2
Special parameters $params and $options
A program method (i.e. the main or the command methods) may also have two special parameters called $params and $optionsthat contain both known and unknown arguments supplied from the CLI.
Known arguments are the ones that are identified using internal (auto-generated) or external configuration. Whereas unknown are the unidentified arguments.
main(param1, param2, $params, $options) {
console.log($params.param1) // same as param1
console.log($params.param2) // same as param2
console.log($params.$unknown) // array of unknown params
}
Both $params, $options contains identified arguments as their own properties. All unidentified arguments are stored as $unknown property. Apart from $unknown these objects also provide $has and $get (options only) methods-
main(param1, param2, $params, $options) {
console.log($params.param1) // same as param1
console.log($params.param2) // same as param2
console.log($params.$unknown) // array of unknown params
// checks if param1 and param2 are supplied
console.log($params.$has['param1', 'param2'])
// checks if option1 and option2 are supplied
// note: it checks both known and unknown list
console.log($options.$has['option1', 'option2'])
// returns the value of option1 only if it is supplied
console.log($options.$get('option1'))
}
Autogenerated Configuration
EasyCli autogenerates a configuration from the program class and the methods to map the command-line arguments to the program methods and their parameters. This configuration can be customized later to provide more information on program, commands, parameters and options.
onInvalidCommand, onExit, onProgramOption methods
Apart from program methods (i.e. the main or the command methods), a program class may also contain 3 program event callback methods.
Program class members
| Property Name | Description |
|---|---|
| config | program configuration object |
| Method Name | Description |
|---|---|
| showHelp(commandName?:string) | prints program or command help |
| showVersion() | print program version info |
| static run(target) | runs the instance of a program |
| static settings(settingObj) | program settings |