0
# User Interaction & Prompting
1
2
Interactive prompting system with storage, prefilling, and validation capabilities built on Inquirer.js for collecting user input and preferences.
3
4
## Capabilities
5
6
### Interactive Prompting
7
8
Main prompting system for collecting user input with automatic storage and prefilling.
9
10
```typescript { .api }
11
/**
12
* Prompt user to answer questions with automatic storage and prefilling
13
* On top of Inquirer.js API, provides {store: true} property for automatic storage
14
* @param questions - Array of question descriptor objects or single question
15
* @param storage - Storage object or name (generator property) for storing responses
16
* @returns Promise resolving to user answers
17
*/
18
async prompt<A extends PromptAnswers = PromptAnswers>(
19
questions: PromptQuestions<A>,
20
storage?: string | Storage
21
): Promise<A>;
22
```
23
24
**Usage Examples:**
25
26
```typescript
27
export default class MyGenerator extends Generator {
28
async prompting() {
29
// Basic prompting
30
this.answers = await this.prompt([
31
{
32
type: 'input',
33
name: 'name',
34
message: 'What is your project name?',
35
default: this.appname
36
},
37
{
38
type: 'confirm',
39
name: 'includeTests',
40
message: 'Include test files?',
41
default: true
42
},
43
{
44
type: 'list',
45
name: 'license',
46
message: 'Choose a license:',
47
choices: ['MIT', 'GPL-3.0', 'Apache-2.0', 'ISC'],
48
default: 'MIT'
49
}
50
]);
51
52
// Prompting with automatic storage
53
const userPrefs = await this.prompt([
54
{
55
type: 'input',
56
name: 'authorName',
57
message: 'Your name:',
58
store: true // Automatically store for reuse
59
},
60
{
61
type: 'input',
62
name: 'authorEmail',
63
message: 'Your email:',
64
store: true
65
}
66
]);
67
68
// Prompting with custom storage
69
const dbConfig = await this.prompt([
70
{
71
type: 'list',
72
name: 'database',
73
message: 'Database type:',
74
choices: ['mysql', 'postgresql', 'sqlite']
75
}
76
], this.config.createStorage('database'));
77
}
78
}
79
```
80
81
### Question Registration
82
83
Register prompts with automatic option export and storage configuration.
84
85
```typescript { .api }
86
/**
87
* Register stored config prompts and optional option alternative
88
* @param questions - Inquirer question or questions with export options
89
*/
90
registerConfigPrompts(questions: QuestionRegistrationOptions[]): void;
91
```
92
93
**Usage Example:**
94
95
```typescript
96
export default class MyGenerator extends Generator {
97
constructor(args, opts) {
98
super(args, opts);
99
100
// Register questions that can also be passed as CLI options
101
this.registerConfigPrompts([
102
{
103
type: 'input',
104
name: 'projectName',
105
message: 'Project name:',
106
storage: this.config,
107
exportOption: {
108
type: String,
109
alias: 'n',
110
description: 'Project name'
111
}
112
},
113
{
114
type: 'confirm',
115
name: 'typescript',
116
message: 'Use TypeScript?',
117
storage: this.config,
118
exportOption: true // Export with default option config
119
}
120
]);
121
}
122
}
123
```
124
125
### Question Types
126
127
Comprehensive question type support from Inquirer.js.
128
129
```typescript { .api }
130
interface PromptQuestion<T extends PromptAnswers = PromptAnswers> {
131
name: string;
132
type?: 'input' | 'number' | 'confirm' | 'list' | 'rawlist' | 'expand' | 'checkbox' | 'password' | 'editor';
133
message?: string | ((answers: T) => string);
134
default?: any | ((answers: T) => any);
135
choices?: Array<any> | ((answers: T) => Array<any>);
136
validate?: (input: any, answers?: T) => boolean | string | Promise<boolean | string>;
137
filter?: (input: any, answers?: T) => any | Promise<any>;
138
transformer?: (input: any, answers?: T, flags?: any) => string | Promise<string>;
139
when?: boolean | ((answers: T) => boolean | Promise<boolean>);
140
pageSize?: number;
141
prefix?: string;
142
suffix?: string;
143
askAnswered?: boolean;
144
loop?: boolean;
145
146
// Yeoman-specific extensions
147
storage?: Storage;
148
store?: boolean;
149
}
150
```
151
152
**Usage Examples:**
153
154
```typescript
155
export default class MyGenerator extends Generator {
156
async prompting() {
157
this.answers = await this.prompt([
158
// Input with validation
159
{
160
type: 'input',
161
name: 'port',
162
message: 'Server port:',
163
default: '3000',
164
validate: (input) => {
165
const port = parseInt(input);
166
return (port > 0 && port < 65536) || 'Port must be between 1 and 65535';
167
},
168
filter: (input) => parseInt(input)
169
},
170
171
// Conditional question
172
{
173
type: 'input',
174
name: 'dbUrl',
175
message: 'Database URL:',
176
when: (answers) => answers.database !== 'sqlite'
177
},
178
179
// Choice transformation
180
{
181
type: 'list',
182
name: 'framework',
183
message: 'Choose framework:',
184
choices: [
185
{ name: 'Express.js', value: 'express' },
186
{ name: 'Koa.js', value: 'koa' },
187
{ name: 'Fastify', value: 'fastify' }
188
]
189
},
190
191
// Checkbox with validation
192
{
193
type: 'checkbox',
194
name: 'features',
195
message: 'Select features:',
196
choices: ['Authentication', 'Database', 'Caching', 'Logging'],
197
validate: (choices) => choices.length > 0 || 'Select at least one feature'
198
}
199
]);
200
}
201
}
202
```
203
204
### Storage Integration
205
206
Automatic storage and prefilling of prompt answers from global and local configuration.
207
208
```typescript { .api }
209
interface QuestionRegistrationOptions<T extends PromptAnswers = PromptAnswers>
210
extends PromptQuestion<T> {
211
/**
212
* A value indicating whether an option should be exported for this question
213
*/
214
exportOption?: boolean | Record<string, unknown>;
215
}
216
217
// Utility functions for prompt storage (internal)
218
function prefillQuestions<A extends PromptAnswers = PromptAnswers>(
219
store: Storage,
220
questions: Array<PromptQuestion<A>>
221
): Array<PromptQuestion<A>>;
222
223
function storeAnswers(
224
store: Storage,
225
questions: any,
226
answers: PromptAnswers,
227
storeAll: boolean
228
): void;
229
```
230
231
**Usage Example:**
232
233
```typescript
234
export default class MyGenerator extends Generator {
235
async prompting() {
236
// Questions with storage will be automatically prefilled
237
// from previous runs and stored after completion
238
this.answers = await this.prompt([
239
{
240
type: 'input',
241
name: 'authorName',
242
message: 'Author name:',
243
store: true, // Store globally for reuse
244
storage: this.config // Store in generator config
245
},
246
{
247
type: 'input',
248
name: 'gitRepo',
249
message: 'Git repository URL:',
250
store: true,
251
default: async () => {
252
// Dynamic default using git
253
try {
254
const email = await this.git.email();
255
return `https://github.com/${email?.split('@')[0]}/`;
256
} catch {
257
return '';
258
}
259
}
260
}
261
]);
262
}
263
}
264
```
265
266
### Answer Processing
267
268
Methods for handling and processing prompt answers.
269
270
```typescript { .api }
271
type PromptAnswers = Record<string, any>;
272
273
type PromptQuestions<A extends PromptAnswers = PromptAnswers> =
274
PromptQuestion<A> | Array<PromptQuestion<A>>;
275
```
276
277
**Usage Example:**
278
279
```typescript
280
export default class MyGenerator extends Generator {
281
async prompting() {
282
// Collect answers in stages
283
const basicInfo = await this.prompt([
284
{ name: 'name', type: 'input', message: 'Project name:' },
285
{ name: 'version', type: 'input', message: 'Version:', default: '1.0.0' }
286
]);
287
288
const advancedInfo = await this.prompt([
289
{
290
name: 'features',
291
type: 'checkbox',
292
message: 'Additional features:',
293
choices: this._getFeatureChoices(basicInfo.name)
294
}
295
]);
296
297
// Combine answers
298
this.answers = { ...basicInfo, ...advancedInfo };
299
}
300
301
_getFeatureChoices(projectName) {
302
return [
303
{ name: `${projectName} CLI`, value: 'cli' },
304
{ name: `${projectName} API`, value: 'api' },
305
{ name: `${projectName} Web UI`, value: 'web' }
306
];
307
}
308
}
309
```
310
311
### Advanced Prompting Patterns
312
313
Complex prompting scenarios with conditional logic and validation.
314
315
**Usage Examples:**
316
317
```typescript
318
export default class MyGenerator extends Generator {
319
async prompting() {
320
// Multi-step prompting with dependencies
321
const projectType = await this.prompt({
322
type: 'list',
323
name: 'type',
324
message: 'Project type:',
325
choices: ['library', 'application', 'plugin']
326
});
327
328
let additionalQuestions = [];
329
330
if (projectType.type === 'application') {
331
additionalQuestions.push({
332
type: 'list',
333
name: 'appType',
334
message: 'Application type:',
335
choices: ['web', 'cli', 'desktop']
336
});
337
}
338
339
if (projectType.type === 'library') {
340
additionalQuestions.push({
341
type: 'confirm',
342
name: 'publishToNpm',
343
message: 'Publish to npm?',
344
default: true
345
});
346
}
347
348
const moreAnswers = await this.prompt(additionalQuestions);
349
this.answers = { ...projectType, ...moreAnswers };
350
351
// Validation with async checks
352
const repoInfo = await this.prompt({
353
type: 'input',
354
name: 'repository',
355
message: 'Repository URL:',
356
validate: async (input) => {
357
if (!input) return true; // Optional
358
359
// Check if repo exists (example)
360
try {
361
const response = await fetch(input);
362
return response.ok || 'Repository URL is not accessible';
363
} catch {
364
return 'Invalid URL format';
365
}
366
}
367
});
368
369
this.answers = { ...this.answers, ...repoInfo };
370
}
371
}
372
```
373
374
## Types
375
376
```typescript { .api }
377
interface PromptQuestion<T extends PromptAnswers = PromptAnswers> {
378
name: string;
379
type?: 'input' | 'number' | 'confirm' | 'list' | 'rawlist' | 'expand' | 'checkbox' | 'password' | 'editor';
380
message?: string | ((answers: T) => string);
381
default?: any | ((answers: T) => any);
382
choices?: Array<any> | ((answers: T) => Array<any>);
383
validate?: (input: any, answers?: T) => boolean | string | Promise<boolean | string>;
384
filter?: (input: any, answers?: T) => any | Promise<any>;
385
transformer?: (input: any, answers?: T, flags?: any) => string | Promise<string>;
386
when?: boolean | ((answers: T) => boolean | Promise<boolean>);
387
pageSize?: number;
388
prefix?: string;
389
suffix?: string;
390
askAnswered?: boolean;
391
loop?: boolean;
392
393
// Yeoman extensions
394
storage?: Storage;
395
store?: boolean;
396
}
397
398
interface QuestionRegistrationOptions<T extends PromptAnswers = PromptAnswers>
399
extends PromptQuestion<T> {
400
exportOption?: boolean | Record<string, unknown>;
401
}
402
403
type PromptAnswers = Record<string, any>;
404
405
type PromptQuestions<A extends PromptAnswers = PromptAnswers> =
406
PromptQuestion<A> | Array<PromptQuestion<A>>;
407
```