Craftsman Console
Introduction
Craftsman is the command line interface included with Formidable. It provides a number of helpful commands that can assist you while you build your application. To view a list of all available Craftsman commands, you may call the craftsman
command:
node craftsman
Every command also includes a "help" screen which displays and describes the command's available arguments and options. To view a help screen, precede the name of the command with the --help
flag:
node craftsman migrate:latest --help
Shell (REPL)
Craftsman Shell is a powerful REPL for the Formidable framework, powered by the Imba-Shell package.
Usage
Shell allows you to interact with your Formidable application on the command line. To enter the Shell environment, run the shell
Craftsman command:
node craftsman shell
In order for a class or function to be accessible in the Shell environment, it needs to be added in the context
config file. To get started, open config/context.imba
or config/context.ts
:
- Imba
- TypeScript
import { helpers } from '@formidablejs/framework'
export default {
...helpers
}
import { helpers } from '@formidablejs/framework'
export default {
...helpers,
}
Out of the box, Formidable exposes the Framework's helper function to the Shell environment. Lets add the User
repository:
- Imba
- TypeScript
import { helpers } from '@formidablejs/framework'
import { UserRepository } from '../app/Repositories/UserRepository'
export default {
UserRepository: UserRepository
...helpers
}
import { helpers } from '@formidablejs/framework'
import { UserRepository } from '../app/Repositories/UserRepository'
export default {
UserRepository: UserRepository,
...helpers
}
Now that you've added your User
repository, you can access it from the Shell environment by just calling User
in the environment. Imba-Shell will autocomplete your code.
Writing Commands
In addition to the commands provided with Craftsman, you may build your own custom commands. Commands are typically stored in the app/Console/Commands
directory; however, you are free to choose your own storage location.
Generating Commands
To create a new command, you may use the make:command
Craftsman command. This command will create a new command class in the app/Console/Commands
directory:
node craftsman make:command Hello
Command Structure
After generating your command, you should define appropriate values for the signature
, description
and props
properties of the class. These properties will be used when displaying your command on the list screen. The signature
property also allows you to define your command's input expectations. The handle
method will be called when your command is executed. You may place your command logic in this method:
- Imba
- TypeScript
import { Command } from '@formidablejs/framework'
import { PropList, string } from '@formidablejs/console'
export class Hello < Command
# The name and signature of the console command.
get signature\string
'hello {?name}'
# The console command description.
get description\string
'My command description'
# Command props.
get props\PropList
{
name: string('Your name')
}
# Execute the console command.
def handle\void
self.write "<fg:green>Hello {argument('name', 'Stranger')}</fg:green>"
self.exit!
import { Command } from '@formidablejs/framework'
import { PropList, string } from '@formidablejs/console'
export class Hello extends Command {
/**
* The name and signature of the console command.
*/
get signature(): string {
return 'hello {?name}'
}
/**
* The console command description.
*/
get description(): string {
return 'My command description'
}
/**
* Command props.
*/
get props(): PropList {
return {
name: string('Your name')
}
}
/**
* Execute the console command.
*/
handle(): void {
this.write(`<fg:green>Hello ${argument('name', 'Stranger')}</fg:green>`)
this.exit()
}
}
Defining Input Expectations
When writing console commands, it is common to gather input from the user through arguments or options. Formidable allows you to define the commands input structure in the signature
property. In the signature
property you may define the name, arguments, and options for the command in a single, expressive, route-like syntax.
Arguments
All user supplied arguments and options are wrapped in curly braces. In the following example, the command defines one required argument: name
:
- Imba
- TypeScript
# The name and signature of the console command.
get signature\string
'hello {name}'
/**
* The name and signature of the console command.
*/
get signature(): string {
return 'hello {name}'
}
You may also make arguments
optional:
`hello {?name}`
If you want to define a default value, you can use the props
property:
- Imba
- TypeScript
# Command props.
get props\PropList
{
name: string!.default('Donald')
}
/**
* Command props.
*/
get props(): PropList {
return {
name: string().default('Donald')
}
}
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 "flag". Let's take a look at an example of this type of option:
- Imba
- TypeScript
# The name and signature of the console command.
get signature\string
'hello {name} {--time}'
/**
* The name and signature of the console command.
*/
get signature(): string {
return 'hello {name} {--time}'
}
In this example, the --time
switch may be specified when calling the command. If the --time
flag is passed, the value will of the option will be true
. Otherwise, the value will be false
:
node craftsman hello Luna --time
Options 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 set the option type to string
:
- Imba
- TypeScript
# The name and signature of the console command.
get signature\string
'hello {name} {--time}'
# Command props.
get props\PropList
{
name: string!.default('Donald')
time: string!
}
/**
* The name and signature of the console command.
*/
get signature(): string {
return 'hello {name} {--time}'
}
/**
* Command props.
*/
get props(): PropList {
{
name: string().default('Donald')
time: string()
}
}
When invoking the command, you may give the --time
flag a value:
node craftsman hello Luna --time=19:05
Option Alias
To assign an alias when defining an option, you may specify it in the props
property:
- Imba
- TypeScript
# Command props.
get props\PropList
{
time: string!.alias('t')
}
/**
* Command props.
*/
get props(): PropList {
return {
time: string().alias('t')
}
}
When invoking the command, you may use -t
instead of --time
:
node craftsman hello Luna -t=19:05
Input Descriptions
You may assign descriptions to input arguments and options in the props
property:
- Imba
- TypeScript
# Command props.
get props\PropList
{
time: string('Current time')
}
/**
* Command props.
*/
get props(): PropList {
return {
time: string('Current time')
}
}
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 argument and option methods. If an argument
or option
does not exist, null will be returned:
- Imba
- TypeScript
# Execute the console command.
def handle\void
const name\string = self.argument('name')
/**
* Execute the console command.
*/
handle(): void {
const name: string = this.argument('name')
}
Options may be retrieved just as easily as arguments using the option
method:
- Imba
- TypeScript
# Execute the console command.
def handle\void
const time\string = self.option('time')
/**
* Execute the console command.
*/
handle(): void {
const time: string = this.option('time')
}
You may also pass a default value as the second parameter if the value is null
:
- Imba
- TypeScript
# Execute the console command.
def handle\void
const name\string = self.argument('name', 'Donald')
const time\string = self.option('time', '19:05')
/**
* Execute the console command.
*
* @returns {mixed}
*/
handle(): void {
const name: string = this.argument('name', 'Donald')
const time: string = this.option('time', '19:05')
}
Writing Output
To send output to the console, you may use the message
, info
, succces,
write
, line
, 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. Typically, the info method will display in the console as green colored text:
- Imba
- TypeScript
# Execute the console command.
def handle\void
# ...
self.success('The command was successful')
/**
* Execute the console command.
*/
handle(): void {
// ...
this.success('The command was successful')
}
To display an error message, use the error
method. Error message text is typically displayed in red:
- Imba
- TypeScript
self.error('Something went wrong!')
this.error('Something went wrong!')
You may use the write
method to display plain, uncolored text:
- Imba
- TypeScript
self.write('Display this on the screen')
this.write('Display this on the screen')
Custom Styling
You may custom style your output, for example, to display a text in blue
and red
, use:
- Imba
- TypeScript
self.write('This is <fg:blue>blue</fg:blue> and this has a <bg:red>red</bg:red> background')
this.write('This is <fg:blue>blue</fg:blue> and this has a <bg:red>red</bg:red> background')
Tag | Type | Color |
---|---|---|
bg:black | Background | Black |
bg:blue | Background | Blue |
bg:cyan | Background | Cyan |
bg:green | Background | Green |
bg:magenta | Background | Magenta |
bg:red | Background | Red |
bg:white | Background | White |
bg:yellow | Background | Yellow |
fg:black | Color | Black |
fg:blue | Color | Blue |
fg:cyan | Color | Cyan |
fg:green | Color | Green |
fg:magenta | Color | Magenta |
fg:red | Color | Red |
fg:white | Color | White |
fg:yellow | Color | Yellow |
dim | Style | null |
u | Style | null |
underline | Style | null |
Tables
The table
method makes it easy to correctly format multiple rows / columns of data. All you need to do is provide the an array of objects for the table and Formidable will automatically calculate the appropriate width and height of the table for you:
- Imba
- TypeScript
self.table([
{ name: "Donald" }
{ name: "Luna" }
])
this.table([
{ name: "Donald" }
{ name: "Luna" }
])
Columns
The column
method makes it easy to correctly format rows of data. All you need to do is provide the data for the column and Formidable will automatically calculate the appropriate width of the column for you:
- Imba
- TypeScript
self.column('Name', 'Donald')
this.column('Name', 'Donald')
Registering Commands
All of your console commands are registered within your application's app/Console/Kernel.imba
file or app/Console/Kernel.ts
file, which is your application's "console kernel". You can register a command by adding it under the registered
property:
- Imba
- TypeScript
import { ConsoleKernel } from '@formidablejs/framework'
import { Hello } from './Commands/Hello'
export class Kernel < ConsoleKernel
get registered
[
Hello
]
import { ConsoleKernel } from '@formidablejs/framework'
import { Hello } from './Commands/Hello'
export class Kernel extends ConsoleKernel {
get registered(): Array<object> {
return [
Hello
]
}
}