> node ace
You are viewing the legacy version of AdonisJS. Visit https://adonisjs.com for newer docs. This version will receive security patches until the end of 2021.
Ace is a powerful command line tool crafted for AdonisJs.
So far, you have used various Ace commands to generate controllers, models, or to run migrations.
In this guide, we learn about Ace internals and how to create commands.
Every AdonisJs project has an ace
file in the project root, which is a regular JavaScript file but without the .js
extension.
The ace
file is used to execute project-specific commands. For reusable commands, you must bundle them as npm packages.
Run the following code to see the list of available Ace commands:
> node ace
Usage:
command [arguments] [options]
Global Options:
--env Set NODE_ENV before running the commands
--no-ansi Disable colored output
Available Commands:
seed Seed database using seed files
migration
migration:refresh Refresh migrations by performing rollback and then running from start
migration:reset Rollback migration to the first batch
migration:rollback Rollback migration to latest batch or a specific batch number
migration:run Run all pending migrations
migration:status Check migrations current status
For convenience, the adonis command proxies all the commands for a given project. For example, running adonis migration:run has the same result as running node ace migration:run .
|
Let’s quickly build an Ace command to pull random quotes from Paul Graham via the wisdom API and output them to the terminal.
> adonis make:command Quote
✔ create app/Commands/Quote.js
┌───────────────────────────────────────────────────────────┐
│ Register command as follows │
│ │
│ 1. Open start/app.js │
│ 2. Add App/Commands/Quote to commands array │
└───────────────────────────────────────────────────────────┘
Following the output instructions, register your newly created command inside the commands
array in the start/app.js
file:
const commands = [
'App/Commands/Quote',
]
Now, if we run adonis
, we should see the quote
command inside the list of available commands.
Replace everything inside the command file with the following code:
'use strict'
const { Command } = use('@adonisjs/ace')
const got = use('got')
class Quote extends Command {
static get signature () {
return 'quote'
}
static get description () {
return 'Shows inspirational quote from Paul Graham'
}
async handle (args, options) {
const response = await got('https://wisdomapi.herokuapp.com/v1/author/paulg/random')
const quote = JSON.parse(response.body)
console.log(`${this.chalk.gray(quote.author.name)} - ${this.chalk.cyan(quote.author.company)}`)
console.log(`${quote.content}`)
}
}
module.exports = Quote
Make sure to install the got package via npm, which is used to consume the HTTP API in the command code above. |
Running adonis quote
prints the retrieved quote to the terminal.
The command signature defines the command name, required/optional options, and flags.
The signature is defined as an expression string, like so:
static get signature () {
return 'greet { name: Name of the user to greet }'
}
In the example signature above:
greet
is the command name
{ name }
is a required argument to be passed when running the command
Everything after the :
is the description of the argument name preceding it
The command signature can span multiple lines using ES6 template literals:
static get signature () {
return `
greet
{ name : Name of the user to greet }
{ age? : User age }
`
}
Arguments can be optional by appending ?
to the argument name:
'greet { name? : Name of the user to greet }'
You can also define a default value for an argument like so:
'greet { name?=virk : Name of the user to greet }'
Flags are prefixed with --
and have the same signature as arguments:
static get signature () {
return `
send:email
{ --log : Log email response to the console }
`
}
Using the example signature above, you would pass the --log
flag when the command is run like so:
> adonis send:email --log
At times you may want to accept values with flags.
This can be done by tweaking the signature expression as follows:
static get signature () {
return `
send:email
{ --driver=@value : Define a custom driver to be used }
`
}
In the example above, =@value
instructs Ace to ensure a value is always passed with the --driver
flag.
The handle
method on the command class is invoked every time a command is executed, and receives an object of arguments
and flags
:
async handle (args, flags) {
console.log(args)
console.log(flags)
}
All arguments and flags are passed in camel case format. For example, a --file-path flag would be set as the key filePath inside the passed flags object.
|
Within your command, you can prompt users for answers and accept values by asking interactive questions.
Prompt the user for textual input:
async handle () {
const name = await this
.ask('Enter project name')
// with default answer
const name = await this
.ask('Enter project name', 'yardstick')
}
The secure
method is similar to ask
, but the user’s input is hidden (useful when asking for sensitive information e.g. a password):
const password = await this
.secure('What is your password?')
Prompt the user for a yes/no
answer:
const deleteFiles = await this
.confirm('Are you sure you want to delete selected files?')
Prompt the user for answers to a multiple choice question:
const lunch = await this
.multiple('Friday lunch ( 2 per person )', [
'Roasted vegetable lasagna',
'Vegetable & feta cheese filo pie',
'Roasted Cauliflower + Aubergine'
])
Your choices
array values can be objects:
const lunch = await this
.multiple('Friday lunch ( 2 per person )', [
{
name: 'Roasted Cauliflower + Aubergine',
value: 'no 1'
},
{
name: 'Carrot + Tabbouleh',
value: 'no 2'
}
])
You can also pass an array of preselected values:
const lunch = await this
.multiple('Friday lunch ( 2 per person )', [
'Roasted vegetable lasagna',
'Vegetable & feta cheese filo pie',
'Roasted Cauliflower + Aubergine'
], [
'Roasted vegetable lasagna',
])
Prompt the user for a single answer to a multiple choice question:
const client = await this
.choice('Client to use for installing dependencies', [
'yarn', 'npm'
])
Your choices
array values can be objects:
const client = await this
.choice('Client to use for installing dependencies', [
{
name: 'Use yarn',
value: 'yarn'
},
{
name: 'Use npm',
value: 'npm'
}
])
You can also pass a preselected value:
const client = await this
.choice('Client to use for installing dependencies', [
{
name: 'Use yarn',
value: 'yarn'
},
{
name: 'Use npm',
value: 'npm'
}
], 'npm')
Ace uses kleur to output colorful log messages to the terminal.
You can access the command kleur instance via this.chalk .
|
The following helper methods log consistently styled messages to the terminal.
Logs an info message to the console with cyan color:
this.info('Something worth sharing')
Logs a success message to the console with green color:
this.success('All went fine')
Logs a warning message to the console with yellow color:
this.warn('Fire in the hole')
warn uses console.warn instead of console.log .
|
Logs an error message to the console with red color:
this.error('Something went bad')
error uses console.error instead of console.log .
|
Prints an action with message to the console:
this.completed('create', 'config/app.js')
create: config/app.js
Prints a failed action with message to the console:
this.failed('create', 'config/app.js')
failed uses console.error instead of console.log .
|
Prints tabular data to the console:
const head = ['Name', 'Age']
const body = [['virk', 22], ['joe', 23]]
this.table(head, body)
┌──────┬─────┐
│ Name │ Age │
├──────┼─────┤
│ virk │ 22 │
├──────┼─────┤
│ joe │ 23 │
└──────┴─────┘
The head row color can also be defined:
const head = ['Name', 'Age']
const body = [['virk', 22], ['joe', 23]]
const options = { head: ['red'] }
this.table(head, body, options)
Returns a colored icon for a given type:
console.log(`${this.icon('success')} Completed`)
✔ Completed
Type | Color | Icon |
---|---|---|
|
cyan |
ℹ |
|
green |
✔ |
|
yellow |
⚠ |
|
red |
✖ |
Ace makes it simple to interact with the file system by offering a Promise first API.
Write file to a given location (automatically creates missing directories):
await this.writeFile(Helpers.appRoot('Models/User.js'), '…')
Ensure file exists, otherwise create an empty file:
await this.ensureFile(Helpers.appRoot('Models/User.js'))
Ensure directory exists, otherwise create an empty directory:
await this.ensureDir(Helpers.appRoot('Models'))
Returns a boolean indicating whether path exists or not:
const exists = await this.pathExists('some-location')
if (exists) {
// do something
}
Read contents of a given file:
const contents = await this.readFile('some-location', 'utf-8')
When using database access in an Ace command (via Lucid or directly), you must remember to manually close the database connection:
Database.close()
A more complete example:
const Database = use('Database')
class Quote extends Command {
static get signature () {
return 'quote'
}
static get description () {
return 'Shows inspirational quote from Paul Graham'
}
async handle (args, options) {
let quote = await Quote.query().orderByRaw('rand()').first()
console.log(quote.content)
// Without the following line, the command will not exit!
Database.close()
}
}