@noravel/command
v1.0.5
Published
```bash npm install @noravel/command ```
Readme
Noravel Command
Installation
npm install @noravel/commandContent
- Create handler
- Create command
- Define input expectations
- Command I/O
- Create a maker command
- Help command
- Programmatically executing commands
Create handler
Create a handler file in your project.
touch noravelOpen a noravel file and add the following code:
#!/usr/bin/env node
import { Kernel } from '@noravel/command';
const kernel = new Kernel(process.argv);
kernel.run();Create command
Create a command file in your project.
touch Commands/Greeting.jsOpen a Commands/Greeting.js file and add the following code:
import { Command } from '@noravel/command';
class Greeting extends Command {
signature = 'greeting';
description = 'Sending greetings to someone';
async handle() {
console.log('Hello World');
}
}
export default Greeting;Make sure your command class exists the handle method. Then you can register your command in the noravel file.
#!/usr/bin/env node
import { Kernel } from '@noravel/command';
import Greeting from './Commands/Greeting.js';
const kernel = new Kernel(process.argv);
kernel.register([
Greeting,
]).run();Now, you can run your command like this:
./noravel greetingDefining input expectations
When writing console commands, it is common to gather input from the user through arguments or options.
Noravel Command makes it very convenient to define the input you expect from the user using the signature property on your commands.
The signature property allows you to define the name, arguments, and options for the command in a single, expressive.
Arguments
All user supplied arguments and options are wrapped in curly braces. In the following example, the command defines one required argument: user:
signature = 'greeting {user}';You may also make arguments optional or define default values for arguments:
// Optional argument
signature = 'greeting {user?}';
// Optional argument with default value
signature = 'greeting {user=Nam}';Options
Options, like arguments, are another form of user input.
Options are prefixed by two hyphens (--) when they are provided via the command line.
There are two types of options: those that receive a value and those that don't.
Options that don't receive a value serve as a boolean "switch".
Let's take a look at an example of this type of option:
signature = 'greeting {--verbose}';In this example, the --verbose switch may be specified when calling your command.
If the --verbose switch is passed, the value of the option will be true. Otherwise, the value will be false:
./noravel greeting --verboseOptions with values
Next, let's take a look at an option that expects a value. If the user must specify a value for an option, you should suffix the option name with a = sign:
signature = 'greeting {--verbose=}';In this example, the user may pass a value for the option like so. If the option is not specified when invoking the command, its value will be undefined:
./noravel greeting --verbose=DefaultYou may assign default values to options by specifying the default value after the option name. If no option value is passed by the user, the default value will be used:
signature = 'greeting {--verbose=Default}';Option shortcuts
To assign a shortcut when defining an option, you may specify it before the option name and use the | character as a delimiter to separate the shortcut from the full option name:
signature = 'greeting {--v|verbose=}';When invoking the command on your terminal, option shortcuts should be prefixed with a single hyphen and no = character should be included when specifying a value for the option:
./noravel greeting -vDefaultInput arrays
If you would like to define arguments or options to expect multiple input values, you may use the * character.
First, let's take a look at an example that specifies such an argument:
signature = 'greeting {user*}';When running this command, the user arguments may be passed in order to the command line. For example, the following command will set the value of user to an array with 1 and 2 as its values:
./noravel greeting user1 user2This * character can be combined with an optional argument definition to allow zero or more instances of an argument:
signature = 'greeting {user?*}';Option arrays
When defining an option that expects multiple input values, each option value passed to the command should be prefixed with the option name:
signature = 'greeting {--tag=*}';Such a command may be invoked by passing multiple --tag arguments:
./noravel greeting --tag=tag1 --tag=tag2Input descriptions
You may assign descriptions to input arguments and options by separating the argument name from the description using a colon. If you need a little extra room to define your command, feel free to spread the definition across multiple lines:
signature = `greeting {name : The name of the person to greet}
{--v|verbose : Increase the verbosity of messages}
{--tag=* : Tags to apply to the greeting}`;Note: If your command contains required arguments, the user will receive a thrown error when they are not provided.
Command I/O
Retrieving Input
While your command is executing, you will likely need to access the values for the arguments and options accepted by your command. To do so, you may use the getArgument and getOption methods. If an argument or option does not exist, undefined will be returned:
async handle() {
const name = this.getArgument('name');
const verbose = this.getOption('verbose');
}If you need to retrieve all of the arguments as an array, call the getArgument method without any parameters:
async handle() {
const arguments = this.getArgument();
}Options may be retrieved just as easily as arguments using the option method. To retrieve all of the options as an array, call the getOption method without any parameters:
async handle() {
const options = this.getOption();
}Prompting for input
In addition to displaying output, you may also ask the user to provide input during the execution of your command. The ask method will prompt the user with the given question, accept their input, and then return the user's input back to your command:
async handle() {
const name = this.prompt.ask('What is your name?');
}The secret method is similar to ask, but the user's input will not be visible to them as they type in the console. This method is useful when asking for sensitive information such as passwords:
async handle() {
const password = this.prompt.secret('What is your password?');
}Multiple choice questions
If you need to give the user a predefined set of choices when asking a question, you may use the choice method. You may set the array index of the default value to be returned if no option is chosen by passing the index as the third argument to the method:
async handle() {
const choice = this.prompt.choice('What is your favorite color?', ['red', 'green', 'blue'], defaultIndex);
}In addition, the choice method accepts optional fourth argument for determining whether multiple selections are permitted:
const choices = this.prompt.choice('What is your favorite color?', ['red', 'green', 'blue'], defaultIndex, true);Writing Output
To send output to the console, you may use the line, info, comment, success, warning, and error methods. Each of these methods will use appropriate ANSI colors for their purpose. For example, let's display some general information to the user.
async handle() {
this.output.info('This is an info message');
this.output.success('This is a success message');
this.output.warning('This is a warning message');
this.output.error('This is an error message');
this.output.comment('This is a comment message');
}Table
The table method makes it easy to correctly format multiple rows / columns of data. All you need to do is provide the column names and the data for the table and Noravel Command will automatically calculate the appropriate width and height of the table for you:
async handle() {
this.output.table(
['id', 'name', 'email', 'date of birth'],
[
[1, 'John Doe', '[email protected]', '2000-01-01'],
[2, 'Jane Doe', '[email protected]', '1999-01-01'],
[3, 'James Doe', '[email protected]', '2010-01-01'],
[4, 'John Smith', '[email protected]', '2000-11-01'],
[5, 'Jane Smith', '[email protected]', '2000-01-21'],
[6, 'James Smith', '[email protected]', '2001-01-01'],
]
);
}Progress Bars
For long running tasks, it can be helpful to show a progress bar that informs users how complete the task is. First, define the total number of steps the process will iterate through. Then, advance the progress bar after processing each item.
async handle() {
const bar = this.output.createProgressBar(100);
bar.start();
for (let i = 0; i < units; i++) {
// Do some work...
bar.advance();
}
bar.finish();
}Progress Bar Methods
The progress bar provides several methods to control its behavior:
async handle() {
const bar = this.output.createProgressBar(100);
// Start the progress bar with an optional message
bar.start('Processing items...');
// Advance the progress by one step
bar.advance();
// Set a different total after creation
bar.setTotal(150);
// Get the current total
const total = bar.getTotal();
// Set the progress bar size (in characters)
bar.setSize(50);
// Get the current size
const size = bar.getSize();
// Finish the progress bar with an optional message
bar.finish('Processing completed!');
}Progress Bar Formats
The progress bar supports three different display formats:
async handle() {
const bar = this.output.createProgressBar(100);
// Normal format (default): [=====> ] 50.0% 50/100
bar.setFormat('normal');
// Verbose format: [=====>] 50.0% 50/100 remaining: 25.0s. elapsed: 12.5s.
bar.setFormat('verbose');
// Minimal format: Progress: 50.0%
bar.setFormat('minimal');
bar.start();
for (let i = 0; i < 100; i++) {
bar.advance();
}
bar.finish();
}Progress Bar Styles
You can choose between different progress bar styles:
async handle() {
const bar = this.output.createProgressBar(100);
// Arrow style (default): [=====>-----]
bar.useArrowProgress();
// Shape style: [#####_____]
bar.useShapeProgress();
// Block style: [█████░░░░░]
bar.useBlockProgress();
bar.start();
for (let i = 0; i < 100; i++) {
bar.advance();
}
bar.finish();
}Constructor Options
When creating a progress bar, you can specify custom options:
async handle() {
// Create progress bar with custom size (in characters)
const bar = this.output.createProgressBar(100, 80);
// Create progress bar that uses full terminal width
const fullBar = this.output.createProgressBar(100, null);
bar.start();
for (let i = 0; i < 100; i++) {
bar.advance();
}
bar.finish();
}Create a maker command
Create a command file in your project.
touch Commands/ControllerMaker.jsOpen a Commands/ControllerMaker.js file and add the following code:
import { MakerCommand } from '@noravel/command';
class ControllerMaker extends MakerCommand {
signature = `make:controller
{name : The name of the controller}
{--m|model= : Generate a resource controller for the given model}
{--f|force : Create the class even if the controller already exists}`;
description = 'Create a new controller class';
async handle() {
console.log('Creating controller...');
}
}
export default ControllerMaker;Now let register the command in the noravel file:
#!/usr/bin/env node
import { Kernel } from '@noravel/command';
import Greeting from './Commands/Greeting.js';
import ControllerMaker from './Commands/ControllerMaker.js';
const kernel = new Kernel(process.argv);
kernel.register([
Greeting,
ControllerMaker,
]).run();Let's look at the methods available in the MakerCommand class:
class MakerCommand {
// Returns the base path of the project
public basePath(dirPath?: string): string;
// Returns the filename; if the user hasn't entered it, it will wait for user input
public async fileName(question?: string = 'What should the file be named?'): Promise<string>;
// Creates a file with the given name and path
public makeFile(fileName: string, dirPath: string): void;
// Returns the message that the file already exists
protected fileAlreadyExists(filePath: string): string;
// Returns the message that the file created successfully
protected fileCreatedSuccessfully(filePath: string): string;
// Returns the contents of a file that will be written to the file being created
protected contentFile(fileName: string): string;
}Help command
Check the available commands:
./noravel --helpOutput:
Usage:
command [arguments] [--options]
Available commands:
greeting Sending greetings to someone
make:controller Create a new controller class
Options:
-h, --help Show this help message
-v, --version Display the version of Noravel CommandYou can also view a description of a specific command by:
./noravel make:controller --helpOutput:
Description:
Create a new controller class
Usage:
make:controller [options] [--] <name>
Arguments:
name The name of the controller
Options:
-m, --model Generate a resource controller for the given model
-f, --force Create the class even if the controller already exists
-h, --help Display help for the given command
-v, --version Display the version of Noravel CommandProgrammatically executing commands
Sometimes you may wish to execute your command outside of the CLI.
For example, you may wish to execute your command from a route or controller.
You may use the call method on the Executor to accomplish this.
The call method accepts either the command's class name as its first argument, and an array of command parameters as the second argument.
import Executor from './Executor.js';
import Greeting from './Commands/Greeting.js';
await Executor.call(Greeting, [
'John Doe',
'--verbose',
'--tag=foo',
'--tag=bar',
]);