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 been using lots of ace commands to generate controllers, models, or to run migrations.
In this guide, we learn about the internals of Ace 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, and for reusable commands, you must bundle them as npm packages.
Let’s run the following code to see the list of available commands.
node ace
Output
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 a command to pull random quotes of Paul Graham using wisdom API and display it on the terminal.
adonis make:command Quote
Follow the output instructions and register the command inside the commands array in start/app.js
file.
✔ create app/Commands/Quote.js
┌───────────────────────────────────────────────────────────┐
│ Register command as follows │
│ │
│ 1. Open start/app.js │
│ 2. Add App/Commands/Quote to commands array │
└───────────────────────────────────────────────────────────┘
Now, if we run adonis
, we should see the quote
command inside the list of available commands.
Let’s replace everything inside the command file with the following code.
Make sure to install got from npm. It is used to consume the HTTP API. |
'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
Running adonis quote
prints the quote from the terminal.
The command signature defines the command name, required/optional options and flags. The signature is defined as an expression string.
static get signature () {
return 'greet { name: Name of the user to greet }'
}
In above signature greet
is the command name
The { name }
is the required argument to be passed when running the command.
Everything after the :
is the description of the argument.
The command signature can in multiple lines using the ES6 template literals.
static get signature () {
return `
greet
{ name : Name of the user to greet }
{ age? : User age }
`
}
The arguments can be optional by passing ?
to the name.
'greet { name? : Name of the user to greet }'
You can also define default value for an argument
'greet { name?=virk : Name of the user to greet }'
The flags are prefixed with --
and has the same signature as arguments
static get signature () {
return `
send:email
{ --log : Log email response to the console }
`
}
When running the command, we can pass the --log
as follows.
adonis send:email --log
At times you may want to accept values with flags, same can be done by tweaking the expression as follows.
static get signature () {
return `
send:email
{ --driver=@value : Define a custom driver to be used }
`
}
The =@value
instructs ace to make sure a value is always passed to the --driver
flag.
The handle
method on the command class is invoked every time command is executed. It receives an object of arguments
and flags
.
All arguments and flags are passed in camel case format. For example --file-path flag is set as filePath key inside the object.
|
async handle (args, flags) {
console.log(args)
console.log(flags)
}
Within your command, you can prompt users and accept values by asking interactive questions.
Prompt for free text question.
async handle () {
const name = await this
.ask('Enter project name')
// with default answer
const name = await this
.ask('Enter project name', 'yardstick')
}
Prompt user for a Yes/no
question.
const deleteFiles = await this
.confirm('Are you sure you want to delete selected files?')
Prompt user for a secure input like a password or some secret.
const password = await this
.secure('What is your password?')
Prompt for 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'
])
The options can also be an object.
const lunch = await this
.multiple('Friday lunch ( 2 per person )', [
{
name: 'Roasted Cauliflower + Aubergine',
value: 'no 1'
},
{
name: 'Carrot + Tabbouleh',
value: 'no 2'
}
])
Also, you can pass an array of pre selected 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.'
])
choose one value from a list of options.
const client = await this
.choice('Client to use for installing dependencies', [
'yarn', 'npm'
])
Also can also be an object
const client = await this
.choice('Client to use for installing dependencies', [
{
name: 'Use yarn',
value: 'yarn'
},
{
name: 'Use npm',
value: 'npm'
}
])
Also, you can pre select one of the available options.
const client = await this
.choice('Client to use for installing dependencies', [
{
name: 'Use yarn',
value: 'yarn'
},
{
name: 'Use npm',
value: 'npm'
}
], 'npm')
Open default editor and get value on editor window exits
const message = this
.openEditor('Enter commit message')
Ace uses chalk to output colorful messages on the terminal, you can access the instance of chalk as this.chalk
.
Also, there are some helper methods to log consistently styled messages.
Print tabular data
const head = ['Name', 'Age']
const body = [['virk', 22], ['joe', 23]]
this.table(head, body)
Also, you can define the head row color.
const head = ['Name', 'Age']
const body = [['virk', 22], ['joe', 23]]
const options = { head: ['red'] }
this.table(head, body, options)
Print icon for one of the following types.
Icon | Name |
---|---|
ℹ |
info |
✔ |
success |
⚠ |
warn |
✖ |
error |
console.log(`${this.icon('success')} Completed`)
Ace makes it simple to interact with the file system by offering Promise first API.
Write file to a given location. Missing directories are created automatically.
await this.writeFile(Helpers.appRoot('Models/User.js'))
Ensure file exists at a given location, otherwise an empty file is created.
await this.ensureFile(Helpers.appRoot('Models/User.js'))
Ensure directory exists at a given location, otherwise an empty directory is created.
await this.ensureDir(Helpers.appRoot('Models'))
Find a 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 you use database access (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)
// This is the important line. Without it, the command runner will not exit.
Database.close()
}
}