Appearance
Inquirer 中文文档
常用的交互式的命令行用户界面
在你的终端中自己尝试一下!
sh
npx @inquirer/demo@latest
安装
shell
npm install @inquirer/prompts
shell
yarn add @inquirer/prompts
提示
Inquirer 最近进行了彻底的重写,减少了包体积并提升了性能。之前的版本虽然没有再积极开发了,但还在维护。之前版本中提供的数百个来自社区的 prompt 可能尚未迁移至最新的 API。如果你正在找这些包,可以在这里查看之前的包。
使用
js
import { input } from '@inquirer/prompts';
const answer = await input({ message: '请输入名字' });
输入方式
输入 Input
js
import { input } from '@inquirer/prompts';
在 文档 中查看使用示例和其它可选内容。
单选 Select
js
import { select } from '@inquirer/prompts';
在 文档 中查看使用示例和其它可选内容。
多选 Checkbox
js
import { checkbox } from '@inquirer/prompts';
在 文档 中查看使用示例和其它可选内容。
确认 Confirm
js
import { confirm } from '@inquirer/prompts';
在 文档 中查看使用示例和其它可选内容。
搜索 Search
js
import { search } from '@inquirer/prompts';
在 文档 中查看使用示例和其它可选内容。
密码 Password
js
import { password } from '@inquirer/prompts';
在 文档 中查看使用示例和其它可选内容。
折叠 Expand
js
import { expand } from '@inquirer/prompts';
在 文档 中查看使用示例和其它可选内容。
编辑器 Editor
在一个临时文件上启动一个编辑器实例。当用户关闭编辑器,临时文件中的内容将会被读入回答中。使用哪个编辑器取决于环境变量 $VISUAL
或 $EDITOR
。如果二者都没有指定值,将会使用系统默认编辑器,即记事本(Windows)和 Vim(Max/Linux)。
js
import { editor } from '@inquirer/prompts';
在 文档 中查看使用示例和其它可选内容。
数字 Number
和 输入
十分相似,只是添加了内置的数字验证器。
js
import { number } from '@inquirer/prompts';
在 文档 中查看使用示例和其它可选内容。
原始列表 Raw List
js
import { rawlist } from '@inquirer/prompts';
在 文档 中查看使用示例和其它可选内容。
创建你的输入提示
在 这里 查看 API 文档,这里 则是我们提供的测试工具。
高级用法
所有的 inquirer 输入提示都是包含 2 个参数的函数。第一个参数是提示配置(每个输入提示都是独一无二的)。第二个参数是提供上下文或运行时配置。
上下文选项:
属性 | 类型 | 必填 | 描述 |
---|---|---|---|
input | NodeJS.ReadableStream | 否 | 输入流 (默认是 process.stdin ) |
output | NodeJS.WritableStream | 否 | 输出流 (默认是 process.stdout ) |
clearPromptOnDone | boolean | 否 | 如果为真,我们会在输入得到回答后清除屏幕 |
signal | AbortSignal | 否 | 一个中止信号,以异步地进行取消 |
示例:
js
import { confirm } from '@inquirer/prompts';
const allowEmail = await confirm(
{ message: '是否允许我们给你发送电子邮件?' },
{
output: new Stream.Writable({
write(chunk, _encoding, next) {
// 其它业务逻辑
next();
},
}),
clearPromptOnDone: true,
},
);
取消提示
可以通过 AbortController
或 AbortSignal
来实现。
js
// 示例 1:使用内置的中止信号
import { confirm } from '@inquirer/prompts';
const answer = await confirm({ ... }, { signal: AbortSignal.timeout(5000) });
js
// 示例 2:使用 AbortController 来实现一个自定义的中止
import { confirm } from '@inquirer/prompts';
const controller = new AbortController();
setTimeout(() => {
controller.abort(); // 这将 reject 掉当前的 Promise
}, 5000);
const answer = await confirm({ ... }, { signal: controller.signal });
具体应用
更好地处理 Ctrl + C
当用户按下 Ctrl + C
,Inquirer 会 reject 当前提示的 Promise。这是预期中的行为,以便允许你的程序打扫运行后的环境。当使用了 async/await
,被 reject 的 Promise 会抛出错误。当没有捕获错误时,它们的错误信息和堆栈信息将会打印到用户的终端上。
ExitPromptError: User force closed the prompt with 0 null
at file://example/packages/core/dist/esm/lib/create-prompt.js:55:20
at Emitter.emit (file://example/node_modules/signal-exit/dist/mjs/index.js:67:19)
at #processEmit (file://example/node_modules/signal-exit/dist/mjs/index.js:236:27)
at #process.emit (file://example/node_modules/signal-exit/dist/mjs/index.js:187:37)
at process.callbackTrampoline (node:internal/async_hooks:130:17)
这并不是很好的用户体验,所以我们十分建议你能更好地捕获错误。
第一个办法是使用 try/catch
包裹你的脚本,就像 我们在示例程序中做的那样。或者是在你的命令行程序框架中对其进行捕获,例如 Clipanion
的 catch
方法。
最后,你应该可以使用一个事件监听器来全局捕获错误并让它闭嘴了。
ts
process.on('uncaughtException', (error) => {
if (error instanceof Error && error.name === 'ExitPromptError') {
console.log('👋 下次见!');
} else {
// 重新抛出未知的错误
throw error;
}
});
获取对象中的回答
当提问了很多问题时,你一定不想每个答案都是一个单一的值。这时你可以将答案放在一个对象中。
js
import { input, confirm } from '@inquirer/prompts';
const answers = {
firstName: await input({ message: "你姓什么?" }),
allowEmail: await confirm({ message: '是否允许我们给你发送电子邮件?' }),
};
console.log(answers.firstName);
根据条件提问
有时一些问题是否出现取决于其它问题的回答。
js
import { input, confirm } from '@inquirer/prompts';
const allowEmail = await confirm({ message: '是否允许我们给你发送电子邮件?' });
let email;
if (allowEmail) {
email = await input({ message: '你的电子邮件地址是什么?' });
}
超时后获取默认值
js
import { input } from '@inquirer/prompts';
const answer = await input(
{ message: '输入一个值 (5秒后超时)' },
{ signal: AbortSignal.timeout(5000) },
).catch((error) => {
if (error.name === 'AbortPromptError') {
return '默认值';
}
throw error;
});
在预提交/git 钩子或脚本中使用
默认情况下,一些工具中(比如 husky
或 lint-staged
)运行的脚本可能无法在交互式 shell 中运行。但是在非交互式的 shell 中,Inquirer 无法运行,用户无法向该进程发送按键事件。
为了使其可以在这些工具中使用,你必须启动一个 tty
(或者是一个交互式输入流)。
如果那些脚本被配置在你的 package.json
中,你可以像这样定义一个流:
json
"precommit": "my-script < /dev/tty"
或者是在脚本文件中定义的,你可以像这样:(在 Windows 中可能这是你唯一的选择)
sh
#!/bin/sh
exec < /dev/tty
node my-script.js
与 nodemon 一起使用
当 Inquirer 输入提示和 nodemon 一起使用时,你需要在任何命令中加上 --no-stdin
标志来让一切如预期运行。
sh
npx nodemon ./packages/demo/demos/password.mjs --no-stdin
需要注意的是,对于大多数情况来说,你可以使用 Node 内置的全新的 watch-mode 模式。此模式可以与 Inquire 直接配合使用。
sh
# 取决于你的具体需求
node --watch script.js
node --watch-path=packages/ packages/demo/
等待配置
可能一些问题的配置需要等待一个异步结果。
js
import { confirm } from '@inquirer/prompts';
const answer = await confirm({ message: await getMessage() });
与在 bash 脚本中使用的 npx
一起使用
你可以通过 npx
在 shell 中直接使用 Inquirer 输入提示,这对一些快速脚本或者是 package.json
中的命令特别有用。
@inquirer-cli 是一个公共库,为每一个输入提示都暴露了一个独立的命令行接口。
例如,提示用户进行输入:
bash
name=$(npx -y @inquirer-cli/input -r "你的名字是?")
echo "你好, $name!"
或者是创建一个可交互的版本查询工具:
bash
$ npm version $(npx -y @inquirer-cli/select -c patch -c minor -c major 'Select Version')
查看更多: @inquirer-cli。
社区提供的更多输入提示
如果你创建了一个很酷的输入提示,给我们提交一个 PR 来将它添加到下面的列表中!
通过方向键+回车键或是通过快捷键来进行选择。
? 选择一项:
> 运行命令 (D)
退出 (Q)
在列表中选择其中一项,并通过按键来选择一个动作。
? 选择一个文件以 打开 <O> 编辑 <E> 删除 <X>
❯ image.png
audio.mp3
code.py
通过表格视图选择多个答案。
sh
进行你的选择? (按下 <空格> 选中/取消选中, <上下箭头> 移动光标,
<左右箭头> 在列之间移动)
┌──────────┬───────┬───────┐
│ 选项 │ 是? │ 否? |
├──────────┼───────┼───────┤
│ 选项 1 │ [ ◯ ] │ ◯ |
├──────────┼───────┼───────┤
│ 选项 2 │ ◯ │ ◯ |
└──────────┴───────┴───────┘
使用一个切换器进行确认。使用方向键+回车来选择一个选项。
? 是否继续? 否 / 是
和内置的多选提示相同,但是允许使用 Ctrl + 上下箭头来给选项重新排序。
? 哪些 PR 要按照什么顺序进行合并? (<空格> 选中/取消, <a> 全选, <i> 反选, <ctrl+上> 向上移动, <ctrl+下> 向下移动, <enter> 继续)
❯ ◯ PR 1
◯ PR 2
◯ PR 3
支持多选和过滤、搜索的输入提示。
? 选择你的操作系统, IDE, PL等。 (<tab> 选择/取消选择, <退格键> 取消所有已选, <回车> 确定)
>> vue
>[ ] vue
[ ] vuejs
[ ] fuelphp
[ ] venv
[ ] vercel
(使用方向键移动来显示更多选项)
一个文件选择器,你可以在目录间自由导航,可以指定显示任何文件类型,并且是完全可定制的。
sh
? 选择一个文件:
/main/path/
├── folder1/
├── folder2/
├── folder3/
├── file1.txt
├── file2.pdf
└── file3.jpg (禁止选择)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
使用 ↑↓ 在列表间导航
按 <esc> 跳转到上一级
按 <回车> 以选择文件或进入目录
许可
Copyright (c) 2023 Simon Boudrias (twitter: @vaxilart)
Licensed under the MIT license.