Terminal UI
Ace terminal UI 由 @poppinss/cliui 包提供支持。该包导出辅助工具以显示日志、渲染表格、渲染动画任务 UI 等。
终端 UI 基元是考虑到测试而构建的。在编写测试时,你可以开启 raw
模式以禁用颜色和格式,并在内存中收集所有日志以便对其编写断言。
另请参阅:Testing Ace commands
显示日志消息
你可以使用 CLI 记录器显示日志消息。以下是可用日志方法的列表。
import { BaseCommand } from '@adonisjs/core/ace'
export default class GreetCommand extends BaseCommand {
async run() {
this.logger.debug('Something just happened')
this.logger.info('This is an info message')
this.logger.success('Account created')
this.logger.warning('Running out of disk space')
// 写入到 stderr
this.logger.error(new Error('Unable to write. Disk full'))
this.logger.fatal(new Error('Unable to write. Disk full'))
}
}
添加前缀和后缀
使用选项对象,你可以为日志消息定义 prefix
(前缀)和 suffix
(后缀)。前缀和后缀以较低的透明度显示。
this.logger.info('installing packages', {
suffix: 'npm i --production'
})
this.logger.info('installing packages', {
prefix: process.pid
})
加载动画
带有加载动画的日志消息会在消息后附加动画的三个点(...)。你可以使用 animation.update
方法更新日志消息,并使用 animation.stop
方法停止动画。
const animation = this.logger.await('installing packages', {
suffix: 'npm i'
})
animation.start()
// 更新消息
animation.update('unpacking packages', {
suffix: undefined
})
// 停止动画
animation.stop()
记录器操作
记录器操作可以以一致的样式和颜色显示操作状态。
你可以使用 logger.action
方法创建操作。该方法将操作标题作为第一个参数。
const createFile = this.logger.action('creating config/auth.ts')
try {
await performTasks()
createFile.displayDuration().succeeded()
} catch (error) {
createFile.failed(error)
}
你可以将操作标记为 succeeded
(成功)、failed
(失败)或 skipped
(跳过)。
action.succeeded()
action.skipped('Skip reason')
action.failed(new Error())
使用 ANSI 颜色格式化文本
Ace 使用 kleur 来格式化带有 ANSI 颜色的文本。使用 this.colors
属性,你可以访问 kleur 的链式 API。
this.colors.red('[ERROR]')
this.colors.bgGreen().white(' CREATED ')
渲染表格
可以使用 this.ui.table
方法创建表格。该方法返回 Table
类的一个实例,你可以使用它来定义表格头部和行。
import { BaseCommand } from '@adonisjs/core/ace'
export default class GreetCommand extends BaseCommand {
async run() {
const table = this.ui.table()
table
.head([
'Migration',
'Duration',
'Status',
])
.row([
'1590591892626_tenants.ts',
'2ms',
'DONE'
])
.row([
'1590595949171_entities.ts',
'2ms',
'DONE'
])
.render()
}
}
在渲染表格时,你可以对任何值应用颜色转换。例如:
table.row([
'1590595949171_entities.ts',
'2',
this.colors.green('DONE')
])
右对齐列
你可以通过将列定义为对象并使用 hAlign 属性来右对齐列。请确保也右对齐标题列。
table
.head([
'Migration',
'Batch'
{
content: 'Status',
hAlign: 'right'
},
])
table.row([
'1590595949171_entities.ts',
'2',
{
content: this.colors.green('DONE'),
hAlign: 'right'
}
])
全宽渲染
默认情况下,表格以宽度 auto
渲染,仅占用每列内容所需的空间。
然而,你可以使用 fullWidth
方法以全宽(占用所有终端空间)渲染表格。在全宽模式下:
- 我们将根据内容大小渲染所有列。
- 除了第一列,它将占用所有可用空间。
table.fullWidth().render()
你可以通过调用 table.fluidColumnIndex
方法来更改流体列(占用所有空间的列)的列索引。
table
.fullWidth()
.fluidColumnIndex(1)
打印贴纸
贴纸允许你在带边框的框内渲染内容。当你想引起用户对重要信息的注意时,它们非常有用。
你可以使用 this.ui.sticker
方法创建贴纸实例。
import { BaseCommand } from '@adonisjs/core/ace'
export default class GreetCommand extends BaseCommand {
async run() {
const sticker = this.ui.sticker()
sticker
.add('Started HTTP server')
.add('')
.add(`Local address: ${this.colors.cyan('http://localhost:3333')}`)
.add(`Network address: ${this.colors.cyan('http://192.168.1.2:3333')}`)
.render()
}
}
如果你想在框内显示一组说明,可以使用 this.ui.instructions
方法。说明输出中的每一行都将以箭头符号 >
作为前缀。
渲染任务
任务小部件为共享多个耗时任务的进度提供了出色的 UI。该小部件具有以下两种渲染模式:
- 在
minimal
模式下,当前正在运行的任务的 UI 会展开以一次显示一行日志。 - 在
verbose
模式下,每个日志语句都在其自己的行中渲染。调试任务时必须使用详细渲染器。
你可以使用 this.ui.tasks
方法创建任务小部件的实例。
import { BaseCommand } from '@adonisjs/core/ace'
export default class GreetCommand extends BaseCommand {
async run() {
const tasks = this.ui.tasks()
// ... 其余代码如下
}
}
使用 tasks.add
方法添加单个任务。该方法将任务标题作为第一个参数,将实现回调作为第二个参数。
你必须从回调中返回任务的状态。所有返回值都被视为成功消息,除非你将它们包装在 task.error
方法中或在回调中抛出异常。
import { BaseCommand } from '@adonisjs/core/ace'
export default class GreetCommand extends BaseCommand {
async run() {
const tasks = this.ui.tasks()
await tasks
.add('clone repo', async (task) => {
return 'Completed'
})
.add('update package file', async (task) => {
return task.error('Unable to update package file')
})
.add('install dependencies', async (task) => {
return 'Installed'
})
.run()
}
}
报告任务进度
建议调用 task.update
方法,而不是将任务进度消息写入控制台。
update
方法使用 minimal
渲染器显示最新的日志消息,并使用 verbose
渲染器记录所有消息。
const sleep = () => new Promise<void>((resolve) => setTimeout(resolve, 50))
const tasks = this.ui.tasks()
await tasks
.add('clone repo', async (task) => {
for (let i = 0; i <= 100; i = i + 2) {
await sleep()
task.update(`Downloaded ${i}%`)
}
return 'Completed'
})
.run()
Switching to the verbose renderer
summary:
在创建任务小部件时,你可以切换到详细渲染器。你可以考虑允许命令的用户传递一个标志来启用 verbose
模式。
import { BaseCommand, flags } from '@adonisjs/core/ace'
export default class GreetCommand extends BaseCommand {
@flags.boolean()
declare verbose: boolean
async run() {
const tasks = this.ui.tasks({
verbose: this.verbose
})
}
}