Inquirer.js の使い方 — Node.js 対話型CLIプロンプトの定番ライブラリ
一言でいうと
Inquirer.js は、Node.js のコマンドラインアプリケーションで対話型のユーザー入力(テキスト入力、選択リスト、チェックボックス、確認ダイアログなど)を簡単に実装するためのライブラリです。CLIツールやスキャフォールディングツールの質問フローを構築する際のデファクトスタンダードです。
⚠️ 重要:
inquirer(v13系)はレガシー版です。新規プロジェクトでは後継の@inquirer/promptsの採用が推奨されています。本記事では既存プロジェクトでの利用や移行前の理解を目的にinquirerを解説します。
どんな時に使う?
- CLIツールの初期設定ウィザード — プロジェクトの
initコマンドで、名前・テンプレート・オプションなどを対話的に聞き取る - スキャフォールディング/コード生成 — Yeoman のようにテンプレートからファイルを生成する際、ユーザーの選択に応じて出力を分岐させる
- デプロイ・運用スクリプトの確認 — 危険な操作の前に「本当に実行しますか?」と確認を挟む
インストール
# npm
npm install inquirer
# yarn
yarn add inquirer
# pnpm
pnpm add inquirer
注意: Inquirer v13 は ESM (ES Modules) 専用です。
package.jsonに"type": "module"を設定するか、.mjs拡張子を使用してください。
基本的な使い方
最も一般的なパターンは、質問オブジェクトの配列を prompt() に渡し、回答をまとめて受け取る方法です。
import inquirer from 'inquirer';
interface ProjectAnswers {
projectName: string;
language: 'TypeScript' | 'JavaScript';
useEslint: boolean;
}
const answers = await inquirer.prompt<ProjectAnswers>([
{
type: 'input',
name: 'projectName',
message: 'プロジェクト名を入力してください:',
default: 'my-app',
validate: (input: string) => {
if (/^[a-z0-9-]+$/.test(input)) return true;
return '英小文字・数字・ハイフンのみ使用できます';
},
},
{
type: 'list',
name: 'language',
message: '使用する言語を選択してください:',
choices: ['TypeScript', 'JavaScript'],
},
{
type: 'confirm',
name: 'useEslint',
message: 'ESLint を導入しますか?',
default: true,
},
]);
console.log(`プロジェクト "${answers.projectName}" を ${answers.language} で作成します`);
console.log(`ESLint: ${answers.useEslint ? '有効' : '無効'}`);
よく使うAPI・プロンプトタイプの使い方
1. input — テキスト入力
const { username } = await inquirer.prompt([
{
type: 'input',
name: 'username',
message: 'ユーザー名:',
default: 'admin',
transformer: (input: string) => {
// 表示時のみ加工(回答値には影響しない)
return input.toLowerCase();
},
},
]);
2. list / rawlist — 単一選択
const { region } = await inquirer.prompt([
{
type: 'list',
name: 'region',
message: 'デプロイ先リージョンを選択:',
choices: [
{ name: '東京 (ap-northeast-1)', value: 'ap-northeast-1' },
{ name: '大阪 (ap-northeast-3)', value: 'ap-northeast-3' },
{ name: 'バージニア (us-east-1)', value: 'us-east-1' },
new inquirer.Separator('--- 以下は実験的 ---'),
{ name: 'シンガポール (ap-southeast-1)', value: 'ap-southeast-1' },
],
pageSize: 10,
},
]);
// region: "ap-northeast-1" など
3. checkbox — 複数選択
const { features } = await inquirer.prompt([
{
type: 'checkbox',
name: 'features',
message: '導入する機能を選択してください:',
choices: [
{ name: 'Router', value: 'router', checked: true },
{ name: 'State Management (Pinia)', value: 'pinia' },
{ name: 'Unit Testing (Vitest)', value: 'vitest' },
{ name: 'E2E Testing (Playwright)', value: 'playwright' },
],
validate: (answer: string[]) => {
if (answer.length === 0) return '少なくとも1つ選択してください';
return true;
},
},
]);
// features: ["router", "pinia"] など
4. confirm — Yes/No 確認
const { proceed } = await inquirer.prompt([
{
type: 'confirm',
name: 'proceed',
message: '本番環境にデプロイします。続行しますか?',
default: false,
},
]);
if (!proceed) {
console.log('キャンセルしました');
process.exit(0);
}
5. password — パスワード入力
const { apiKey } = await inquirer.prompt([
{
type: 'password',
name: 'apiKey',
message: 'APIキーを入力してください:',
mask: '*', // 入力文字を * で表示(省略すると非表示)
validate: (input: string) => {
if (input.length < 10) return 'APIキーは10文字以上です';
return true;
},
},
]);
補足: when による条件分岐と filter による値変換
const answers = await inquirer.prompt([
{
type: 'confirm',
name: 'useDatabase',
message: 'データベースを使用しますか?',
default: false,
},
{
type: 'list',
name: 'dbType',
message: 'データベースの種類:',
choices: ['PostgreSQL', 'MySQL', 'SQLite'],
// useDatabase が true の場合のみ表示
when: (answers) => answers.useDatabase,
},
{
type: 'input',
name: 'dbPort',
message: 'ポート番号:',
when: (answers) => answers.useDatabase,
filter: (input: string) => parseInt(input, 10), // 文字列を数値に変換
validate: (input: string) => {
const port = parseInt(input, 10);
if (isNaN(port) || port < 1 || port > 65535) return '有効なポート番号を入力してください';
return true;
},
},
]);
類似パッケージとの比較
| 特徴 | inquirer (v13) | @inquirer/prompts | prompts | enquirer |
|---|---|---|---|---|
| メンテナンス状況 | メンテナンスのみ | 活発に開発中 | 低頻度 | 低頻度 |
| API スタイル | 質問配列を一括渡し | 個別関数呼び出し | 質問配列を一括渡し | 質問配列を一括渡し |
| ESM 対応 | ✅ | ✅ | ✅ | ❌ (CJS) |
| TypeScript 型定義 | 同梱 | 同梱 | @types/prompts | @types/enquirer |
| バンドルサイズ | 大きめ(RxJS依存) | 軽量 | 軽量 | 中程度 |
| プラグイン拡張 | ✅ registerPrompt | ✅ | ❌ | ✅ |
| 推奨度(2025年) | 既存プロジェクト向け | 新規プロジェクト推奨 | 軽量志向に | メンテ不安 |
結論: 新規プロジェクトでは @inquirer/prompts を選択するのがベストです。inquirer からの移行も段階的に行えます。
注意点・Tips
1. ESM 必須(v13以降)
// package.json
{
"type": "module" // これがないと import inquirer from 'inquirer' が失敗する
}
CommonJS (require) で使いたい場合は inquirer@^8 系を使うか、動的 import() を利用してください。
2. TTY でない環境ではエラーになる
CI/CD パイプラインや、stdin がパイプされている環境では isTtyError が発生します。非対話環境ではデフォルト値にフォールバックする設計にしましょう。
try {
const answers = await inquirer.prompt(questions);
} catch (error: any) {
if (error.isTtyError) {
console.log('非対話環境のため、デフォルト設定を使用します');
// デフォルト値でフォールバック
} else {
throw error;
}
}
3. name にドット区切りを使うとネストされる
const answers = await inquirer.prompt([
{ type: 'input', name: 'db.host', message: 'DB Host:' },
{ type: 'input', name: 'db.port', message: 'DB Port:' },
]);
// answers = { db: { host: "localhost", port: "5432" } }
便利ですが、意図せずドットを含む名前を使うとハマるので注意してください。
4. 既存回答のスキップ
prompt() の第2引数に回答オブジェクトを渡すと、該当する質問をスキップできます。CLI引数で渡された値を事前にセットしておくパターンに便利です。
import { parseArgs } from 'node:util';
const { values } = parseArgs({
options: { name: { type: 'string' } },
});
// --name が指定済みなら projectName の質問をスキップ
const answers = await inquirer.prompt(
[{ type: 'input', name: 'projectName', message: 'プロジェクト名:' }],
{ projectName: values.name }, // 事前回答
);
5. @inquirer/prompts への段階的移行
両パッケージは同時に使えるため、新しい質問から順に移行できます。
// 旧 API(inquirer)
import inquirer from 'inquirer';
// 新 API(@inquirer/prompts)— 個別関数で呼び出す
import { input, select, confirm } from '@inquirer/prompts';
const name = await input({ message: 'プロジェクト名:' });
const lang = await select({
message: '言語:',
choices: [
{ name: 'TypeScript', value: 'ts' },
{ name: 'JavaScript', value: 'js' },
],
});
まとめ
Inquirer.js は、Node.js の対話型 CLI を構築するための成熟したライブラリで、input・list・checkbox・confirm・password など豊富なプロンプトタイプと、validate・when・filter による柔軟な制御を提供します。v13 はレガシー版としてメンテナンスされている状態なので、新規プロジェクトでは後継の @inquirer/prompts を、既存プロジェクトでは段階的な移行を検討してください。