Enquirer 提示类型基类(Prompt Type Classes)完全指南:用 ArrayPrompt、BooleanPrompt、NumberPrompt 与 StringPrompt 打造自定义交互提示
Enquirer 提示类型基类(Prompt Type Classes)完全指南:用 ArrayPrompt、BooleanPrompt、NumberPrompt 与 StringPrompt 打造自定义交互提示
Enquirer 在 lib/types/index.js 中提供了一批低层"起始类"(starter classes),专门用于简化"期望用户返回特定类型响应"的自定义提示(prompt)的创建过程。本文将以 support/src/content/types/index.md 为主体,结合仓库源码逐类剖析 ArrayPrompt、BooleanPrompt、NumberPrompt、StringPrompt 的设计意图、可配置项与按键行为,并演示如何基于它们派生自己的交互提示。读完本文,你将掌握 Enquirer 内置 prompt 的继承体系,并能像写普通类一样快速定制属于自己的终端交互组件。
概览:五类"起始类"
为了让任何用途的提示创建都更简单,Enquirer 提供了几个面向特定响应类型的起始类:
| 起始类 | 期望的返回值 | 对应内置 prompt |
|---|---|---|
ArrayPrompt |
一个数组(一项或多项选择值) | select、multiselect、scale、survey |
BooleanPrompt |
布尔值 true / false |
confirm、toggle |
~~DatePrompt~~ |
日期相关值(文档中已划线删除,尚未实现) | 暂无 |
NumberPrompt |
数值 | numeral |
StringPrompt |
字符串 | invisible、list、password |
从源码可以确认,lib/types/index.js 实际导出的只有五类:ArrayPrompt、AuthPrompt、BooleanPrompt、NumberPrompt 和 StringPrompt——其中 DatePrompt 并未导出,对应文档中的删除线标记。此外还有一个未在索引文档中展开的 AuthPrompt(认证提示工厂),本文也会一并说明。
在 lib/prompts/ 目录中,绝大多数内置 prompt 正是直接继承这些起始类实现的:
- confirm.js 与 toggle.js 继承
BooleanPrompt; - select.js、scale.js、survey.js 继承
ArrayPrompt; - invisible.js、list.js、password.js 继承
StringPrompt。
理解了这一点,你就能明白:所谓"自定义 prompt",本质上就是选一个合适的起始类,覆写它的 format、hint、submit、dispatch 等方法。
StringPrompt:一切文本输入的基础
StringPrompt(lib/types/string.js)是返回字符串的提示基类,它直接继承自核心的 lib/prompt.js。它的职责是管理"一行文本输入"的全部状态:input(当前输入内容)、cursor(光标位置)、initial(初始值)以及剪贴板缓冲区 state.clipboard。
核心行为
- 初始值:构造时会把
initial强制转成字符串,若存在初始值则隐藏光标直到用户开始输入。 - 追加与插入:
append(ch)在光标处插入字符并推进光标;insert(str)是append的别名。 - 删除:
delete()删除光标左侧字符,deleteForward()删除光标处字符。 - 剪切与粘贴:
cutForward()/cutLeft()把光标右侧/左侧单词存入state.clipboard,paste()再插回。 - 光标移动:
left()/right()单步移动,first()/last()跳到首尾,backward()/forward()是它们的别名。 - 多行支持:当
options.multiline为true时,按回车会追加换行而不是提交;keypressTimeout选项则用于控制快速连续回车时的提交行为。
派生提示示例
文档列出的相关提示包括 prompt-split、prompt-text、prompt-invisible、prompt-password。在仓库中:
- invisible.js、list.js、password.js 直接继承
StringPrompt; - input.js 则直接继承
Prompt基类,但行为上同样属于文本输入。
自定义一个字符串提示,只需要继承 StringPrompt 并覆写 format():
const StringPrompt = require('enquirer/lib/types/string');
class MyTextPrompt extends StringPrompt {
async format(input = this.value) {
// 未提交时显示占位符样式,提交后高亮
return this.state.submitted
? this.styles.success(input)
: this.styles.muted(input);
}
}
注意:起始类的默认 format() 使用 lib/placeholder.js 渲染占位符——当用户尚未输入时,光标停在 initial 的末尾附近,这是 Enquirer 文本提示"灰字提示 + 可编辑"体验的来源。
NumberPrompt:数值输入基类
NumberPrompt(lib/types/number.js)继承自 StringPrompt,在其基础上加入数值语义。构造时它会解析一组数值选项:
| 选项 | 默认值 | 作用 |
|---|---|---|
min |
-Infinity |
允许的最小值 |
max |
Infinity |
允许的最大值 |
delay |
1000(毫秒) |
数字输入后的提交延迟 |
float |
true |
是否允许小数(false 时取整) |
round |
false |
是否四舍五入(float === false 时自动为 true) |
major |
10 |
大步进(shift+方向键) |
minor |
1 |
小步进(方向键) |
initial |
'' |
初始数值 |
数值特有的行为
- 步进调整:
up()/down()按minor步进,shiftUp()/shiftDown()按major步进;越界时会触发alert()。 - 合法性校验:
isValue()用正则/^[-+]?[0-9]+((\.)|(\.[0-9]+))?$/判断输入是否为合法数字;append()会拦截第二个小数点以及非法符号。 - 延迟提交:
delay控制数字输入后自动提交的等待时间,配合submit()中"从 input 与 initial 中取第一个合法值"的逻辑,实现边输入边校验的体验。 - 格式化:支持
options.format函数覆写显示;默认使用styles.info。
文档提到 NumberPrompt 的"相关提示"为 prompt-confirm 与 prompt-toggle,这应是早期文档的疏漏——从继承关系看,数值类提示的实际代表是 numeral。自定义数值提示只需继承 NumberPrompt 并设置 min/max/step 等约束:
const NumberPrompt = require('enquirer/lib/types/number');
const prompt = new NumberPrompt({
name: 'age',
message: '请输入你的年龄',
min: 0,
max: 150,
initial: 18
});
BooleanPrompt:布尔响应基类
BooleanPrompt(lib/types/boolean.js)用于创建返回 true/false 的提示。它的核心是一组判断规则:
isTrue(input):匹配/^[ty1]/i,即t、y、1(不区分大小写)开头视为真;isFalse(input):匹配/^[fn0]/i,即f、n、0开头视为假;cast(input):调用isTrue把原始输入转换为布尔值,value的 getter 也做同样转换;dispatch(ch):当输入命中isValue时立即赋值并submit()——这就是 confirm 提示"按下 y/n 即提交"的实现原理。
文档明确指出内置的 确认提示 confirm 基于 BooleanPrompt 实现,其"相关提示"为 prompt-confirm 与 prompt-toggle;仓库中 toggle.js 同样继承 BooleanPrompt。
最简单的自定义布尔提示,就是继承后覆写 format 以改变真/假值的显示样式:
const BooleanPrompt = require('enquirer/lib/types/boolean');
class YesNoPrompt extends BooleanPrompt {
async format(value) {
return this.state.submitted
? this.styles.success(value ? 'yes' : 'no')
: this.styles.primary(value ? 'yes' : 'no');
}
}
ArrayPrompt:数组/选择类提示基类
ArrayPrompt(lib/types/array.js)是所有"在终端中显示一组选项并返回一个或多个值"的提示的基类,是起始类中最复杂、功能最丰富的一类。
按键(Keypresses)
文档给出的按键表如下,结合源码可以补全其真实行为:
| 按键 | 动作 | 源码行为 |
|---|---|---|
| shift+▲ | shiftUp |
当 options.sort === true 时向上交换选项(swap(index - 1)),否则向上滚动列表 |
| shift+▼ | shiftDown |
当 options.sort === true 时向下交换选项,否则向下滚动列表 |
| fn+▲(Mac)/ Page Up(Win) | pageUp |
缩小可视区域 limit(Math.max(limit - 1, 0))并上移光标 |
| fn+▼(Mac)/ Page Down(Win) | pageDown |
扩大可视区域 limit(Math.min(limit + 1, choices.length))并下移光标 |
此外还有更多派生提示共用的按键:space 切换选中、a 全选/全不选、i 反选、number(n) 按数字快速跳转选择、g 切换分组、home/end 跳转首尾等。
选项(Options)
| 名称 | 类型 | 默认值 | 作用 |
|---|---|---|---|
limit |
Number |
options.choices.length |
终端中同时可见的选项数量,超出部分可滚动浏览 |
initial |
Number | String | Array |
undefined |
初始选中的选项索引、名称或名称数组 |
hint |
String |
undefined |
附加的提示文本 |
name |
String |
undefined |
提示名称,作为结果对象的键 |
type |
String |
undefined |
提示类型标识 |
message |
String |
undefined |
显示给用户的提示语 |
choices |
Array |
undefined |
选项列表,支持字符串、对象、嵌套分组、异步函数等 |
源码还确认了其他重要选项:multiple(是否多选)、maxSelected(最多可选数量,默认 Infinity)、delay(数字快速选择延迟)、autofocus(自动聚焦项)、sort(是否启用 shift+方向键排序)、scroll(是否允许滚动,false 时首尾按键会触发 alert)。
options.limit 实例
limit 决定同一时刻屏幕上渲染的选项数量。例如下面这个 8 个字母的提示,任何时刻终端只会显示 3 个选项:
const prompt = new Prompt({
name: 'alphabet',
message: 'Choose some letters',
choices: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
limit: 3
});
limit 的默认值取 choices.length,即默认全部展示;实际渲染时会再取 Math.min(limit, this.height),避免超出终端高度。该行为的效果可参考仓库中的演示图:
实例属性
prompt.choices:由options.choices规范化生成的选项数组。源码中toChoice()会把字符串转为{ name }对象,补全message、value、index、level、indent、path等字段,并支持函数、Promise 与嵌套分组。prompt.list/prompt.visible:可见选项列表。文档称其为list,源码中对应visiblegetter,返回(state.visible || choices).slice(0, limit);未设置limit时就是整个 choices 数组。prompt.cursor:光标在可见列表中的位置,对应源码中的indexgetter(Math.max(0, state.index))。
派生提示
文档列出的相关提示为 prompt-autocompletion、prompt-select、prompt-multiselect、prompt-checkbox、prompt-radio。仓库中 select.js(单选)、scale.js(李克特量表)、survey.js(问卷)均继承 ArrayPrompt,multiselect 相关实现可参考 lib/prompts/multiselect.js。
DatePrompt:文档中已标记"待实现"的日期类型
date.md 以笔记形式记录了 DatePrompt 的规划行为,但它在 lib/types/index.js 的导出列表里并不存在,索引文档中也以删除线 ~~DatePrompt~~ 标注。从源码结构看,这是一个尚未落地的设计,以下内容应视为规划而非当前可用 API:
- 光标移动:←/→ 移动光标,Alt+B/F 按词移动,Ctrl+A/E 跳到首尾;
- 数值增减:▲/▼ 修改当前数字位,并具有进位联动——在 "6:59" 增加分钟会使小时进位,在 "23:00" 增加小时会使日期进位,在 "31 Jan" 增加天数会使月份进位,在 "Dec" 增加月份会使年份进位;
- 提交与取消:Ctrl+C/Escape 取消,Ctrl+J/Return 提交,Ctrl+G 重置,Ctrl+D 退出 REPL;
- 直接键入数字可修改当前位,输入 1 秒后自动复位,全部数字输入完成后应用并右移光标;
- 提交前显示问号,提交后显示对勾,取消后显示叉号。
如需在现有版本中实现日期输入,建议基于 StringPrompt 自行派生并实现上述进位规则。
进阶:用 AuthPrompt 工厂创建认证类提示
除了文档列出的五类起始类,lib/types/auth.js 还提供了一个 AuthPrompt 工厂:它继承自 FormPrompt(lib/prompts/form.js),把 authenticate 校验函数注入到 submit() 流程中,并暴露静态方法 create(authenticate) 生成定制类。2FA、账号密码等"先填表单、再统一校验"的提示都可以基于它构建,仓库示例见 examples/enquirer/2-factor-authentication/。
小结
Enquirer 的起始类设计让自定义提示变得异常简单:选定一个返回类型,继承对应基类,覆写少数方法即可获得完整的按键、渲染、校验与提交体系。理解 lib/types/index.js 中各类的继承关系与核心选项,是深入定制 Enquirer 提示的起点;官方文档 prompts.md 与 custom-prompts.md 可作为下一步扩展阅读。若想看到各类型的真实交互效果,仓库 media 目录收录了对应的 GIF 演示。
