Prompts

本指南介绍如何在自定义命令中使用提示。你将学习以下内容:

  • 显示文本与密码输入提示
  • 创建单选与多选列表
  • 使用确认与切换提示
  • 校验与转换用户输入
  • 使用自动补全处理可搜索列表
  • 对带有提示的命令进行测试

概述

提示通过直观的终端组件(而非命令行参数或标志)让用户输入信息,从而实现交互式命令行体验。对于需要引导用户完成多步骤流程、收集密码等敏感信息,或让用户从选项中进行选择的命令来说,这尤其有用。

Ace 提示由 @poppinss/prompts 包驱动,支持多种提示类型,包括文本输入、密码字段、确认、单选与多选列表,以及自动补全搜索。所有提示都支持校验、默认值,以及在将用户输入返回给你的命令之前对其进行转换。

Ace 提示的一个关键特性是它们的 测试支持 。在编写测试时,你可以拦截(trap)提示并以编程方式作出响应,从而无需手动输入即可轻松测试交互式命令。

显示文本输入

文本输入提示接受用户的自由格式文本。使用 this.prompt.ask 方法显示文本输入提示,第一个参数为提示信息。

commands/make_model.ts
import { BaseCommand } from '@adonisjs/core/ace'

export default class MakeModelCommand extends BaseCommand {
  static commandName = 'make:model'
  
  async run() {
    /**
     * 询问模型名称
     */
    const modelName = await this.prompt.ask('Enter the model name')
    
    this.logger.info(`Creating model: ${modelName}`)
  }
}

添加校验

你可以在选项对象中提供 validate 函数来校验用户输入。该函数接收用户的输入,应返回 true 以接受该值,或返回错误消息字符串以拒绝该值。

commands/make_model.ts
const modelName = await this.prompt.ask('Enter the model name', {
  validate(value) {
    return value.length > 0
      ? true
      : 'Model name is required'
  }
})

如果校验失败,提示会显示错误消息并再次要求输入,直到用户提供有效的值为止。

提供默认值

默认值会作为建议显示,用户可以通过按 Enter 键接受。这对于提供常用值或合理的默认值很有帮助。

commands/make_model.ts
const modelName = await this.prompt.ask('Enter the model name', {
  default: 'User'
})

收集密码

密码提示会屏蔽用户在终端中的输入,用星号或圆点替换每个字符。这对于收集密码、API 密钥或令牌等敏感信息至关重要。

使用 this.prompt.secure 方法显示密码提示。

commands/setup.ts
import { BaseCommand } from '@adonisjs/core/ace'

export default class SetupCommand extends BaseCommand {
  static commandName = 'setup'
  
  async run() {
    /**
     * 安全地收集数据库密码
     */
    const password = await this.prompt.secure('Enter database password')
    
    this.logger.info('Password collected securely')
  }
}

你可以像文本输入一样为密码提示添加校验。

commands/setup.ts
const password = await this.prompt.secure('Enter account password', {
  validate(value) {
    return value.length >= 8
      ? true
      : 'Password must be at least 8 characters long'
  }
})

创建选择列表

选择提示会显示一组选项,用户可以使用方向键导航并使用 Enter 键选择。当你需要让用户从预定义选项中进行挑选时,这最为理想。

使用 this.prompt.choice 方法显示单选列表。该方法第一个参数为提示信息,第二个参数为选项数组。

commands/configure.ts
import { BaseCommand } from '@adonisjs/core/ace'

export default class ConfigureCommand extends BaseCommand {
  static commandName = 'configure'
  
  async run() {
    /**
     * 让用户选择其包管理器
     */
    const packageManager = await this.prompt.choice('Select package manager', [
      'npm',
      'yarn',
      'pnpm'
    ])
    
    this.logger.info(`Using ${packageManager}`)
  }
}

自定义选择显示

当你希望显示的文本与返回的值不同时,可以将选项定义为带有 namemessage 属性的对象。name 是你的命令接收到的值,而 message 是用户看到的内容。

commands/configure.ts
const driver = await this.prompt.choice('Select database driver', [
  {
    name: 'sqlite',
    message: 'SQLite'
  },
  {
    name: 'mysql',
    message: 'MySQL'
  },
  {
    name: 'pg',
    message: 'PostgreSQL'
  }
])

this.logger.info(`Selected driver: ${driver}`)
// 如果用户选择了 "PostgreSQL",driver 的值将为 "pg"

允许多选

多选提示让用户使用空格键来切换选择,从而从列表中选择多个选项。当用户需要选择多个功能、包或配置时,这很有用。

使用 this.prompt.multiple 方法显示多选列表。其参数与选择提示相同。

commands/install.ts
import { BaseCommand } from '@adonisjs/core/ace'

export default class InstallCommand extends BaseCommand {
  static commandName = 'install:packages'
  
  async run() {
    /**
     * 让用户选择多个数据库驱动
     */
    const drivers = await this.prompt.multiple('Select database drivers', [
      {
        name: 'sqlite',
        message: 'SQLite'
      },
      {
        name: 'mysql',
        message: 'MySQL'
      },
      {
        name: 'pg',
        message: 'PostgreSQL'
      }
    ])
    
    this.logger.info(`Installing drivers: ${drivers.join(', ')}`)
  }
}

该方法返回所选值的数组。用户可以选择全部、部分或不选任何选项。

确认操作

确认提示会让用户回答是或否的问题。它们对于破坏性操作或需要用户明确同意的操作来说至关重要。

使用 this.prompt.confirm 方法显示是/否确认。该方法返回一个布尔值。

commands/reset.ts
import { BaseCommand } from '@adonisjs/core/ace'

export default class ResetCommand extends BaseCommand {
  static commandName = 'db:reset'
  
  async run() {
    /**
     * 在删除数据之前进行确认
     */
    const shouldDelete = await this.prompt.confirm(
      'Want to delete all files?'
    )
    
    if (shouldDelete) {
      this.logger.warning('Deleting all files...')
      // 执行删除操作
    } else {
      this.logger.info('Operation cancelled')
    }
  }
}

自定义是/否标签

如果你想把是/否标签自定义为更具语境化的内容,可以使用 this.prompt.toggle 方法。该方法接受一个包含「是」和「否」两个字符串的数组。

commands/reset.ts
const shouldDelete = await this.prompt.toggle(
  'Want to delete all files?',
  ['Yup', 'Nope']
)

if (shouldDelete) {
  this.logger.warning('Deleting all files...')
}

使用自动补全

自动补全提示将选择与搜索功能结合起来,让用户可以模糊搜索大型选项列表。当你面对大量在标准选择列表中显得笨重的选项时,这尤其有用。

使用 this.prompt.autocomplete 方法显示可搜索列表。

commands/select_city.ts
import { BaseCommand } from '@adonisjs/core/ace'

export default class SelectCityCommand extends BaseCommand {
  static commandName = 'select:city'
  
  async run() {
    /**
     * 让用户搜索并从大型列表中选择
     */
    const selectedCity = await this.prompt.autocomplete(
      'Select your city',
      await this.getCitiesList()
    )
    
    this.logger.info(`You selected: ${selectedCity}`)
  }
  
  private async getCitiesList() {
    /**
     * 返回包含大量城市名称的数组
     */
    return [
      'New York',
      'Los Angeles',
      'Chicago',
      'Houston',
      // ... 还有数百个城市
    ]
  }
}

用户可以输入以筛选列表,提示会基于模糊搜索显示匹配的选项。

了解提示选项

所有提示类型都通过第二个参数接受一组通用选项。这些选项让你可以自定义提示行为、校验输入并转换返回值。

选项适用提示说明
default所有提示未输入任何值时使用的默认值。对于选择、多选和自动补全提示,该值必须是选项数组的索引。
name所有提示提示的唯一名称,用于在测试中识别提示。
hint所有提示显示在提示旁边的提示文本,为用户提供额外的上下文。
result所有提示转换提示的返回值。输入值取决于提示类型(例如,多选返回所选选项的数组)。
format所有提示在用户键入时实时格式化输入值。该格式化仅应用于 CLI 输出,而不应用于返回值。
validate所有提示校验用户输入。返回 true 以接受该值,或返回字符串错误消息以拒绝该值。
limitautocomplete限制显示的选项数量。用户需要滚动才能看到更多选项。

转换返回值

result 函数转换提示返回的值。这对于将用户输入转换为不同的格式或类型很有用。

commands/make_model.ts
const modelName = await this.prompt.ask('Enter the model name', {
  result(value) {
    /**
     * 转换为 PascalCase 以用作类名
     */
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
})

格式化显示值

format 函数会改变输入在终端中随用户键入而显示的方式,但不影响实际的返回值。

commands/configure.ts
const email = await this.prompt.ask('Enter your email', {
  format(value) {
    /**
     * 在用户键入时将邮箱显示为小写
     */
    return value.toLowerCase()
  }
})

添加提示

提示(hint)提供额外的上下文或说明,显示在提示旁边。

commands/make_migration.ts
const tableName = await this.prompt.ask('Enter table name', {
  hint: 'Use plural form (e.g., users, posts)'
})