Skip to content

Commit 681331d

Browse files
committed
feat(inquirerer): add skipPrompt flag to skip prompting for optional fields
Adds skipPrompt?: boolean to BaseQuestion. When set to true: - The question is skipped entirely during interactive prompting - CLI flag overrides still work (--fieldName value) - The field still appears in man pages - The field is simply left out of the answers object if not provided This is useful for fields with backend-managed defaults where the CLI should not prompt the user, but still allow explicit overrides.
1 parent 5ae067a commit 681331d

3 files changed

Lines changed: 112 additions & 0 deletions

File tree

packages/inquirerer/__tests__/prompt.test.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,108 @@ describe('Inquirerer', () => {
232232
snap(result);
233233
});
234234

235+
describe('skipPrompt', () => {
236+
it('should skip question with skipPrompt: true and not include it in result', async () => {
237+
enqueueInputResponse({ type: 'read', value: 'my name' });
238+
239+
const prompter = new Inquirerer({
240+
input: mockInput,
241+
output: mockOutput,
242+
noTty: false
243+
});
244+
const questions: Question[] = [
245+
{ name: 'name', type: 'text' },
246+
{ name: 'status', type: 'text', skipPrompt: true },
247+
];
248+
249+
const result = await prompter.prompt({}, questions);
250+
251+
// 'status' should not be in the result since it was skipped
252+
expect(result).toEqual({ name: 'my name' });
253+
expect('status' in result).toBe(false);
254+
});
255+
256+
it('should still allow CLI flag override when skipPrompt is true', async () => {
257+
enqueueInputResponse({ type: 'read', value: 'my name' });
258+
259+
const prompter = new Inquirerer({
260+
input: mockInput,
261+
output: mockOutput,
262+
noTty: false
263+
});
264+
const questions: Question[] = [
265+
{ name: 'name', type: 'text' },
266+
{ name: 'status', type: 'text', skipPrompt: true },
267+
];
268+
269+
// Pass status via argv (simulating --status "active")
270+
const result = await prompter.prompt({ status: 'active' }, questions);
271+
272+
expect(result).toEqual({ name: 'my name', status: 'active' });
273+
});
274+
275+
it('should skip question in noTty mode with skipPrompt: true', async () => {
276+
const prompter = new Inquirerer({
277+
input: mockInput,
278+
output: mockOutput,
279+
noTty: true
280+
});
281+
const questions: Question[] = [
282+
{ name: 'name', type: 'text', required: true },
283+
{ name: 'status', type: 'text', skipPrompt: true },
284+
];
285+
286+
const result = await prompter.prompt({ name: 'test' }, questions);
287+
288+
expect(result).toEqual({ name: 'test' });
289+
expect('status' in result).toBe(false);
290+
});
291+
292+
it('should include skipPrompt question in man page', () => {
293+
const prompter = new Inquirerer({
294+
input: mockInput,
295+
output: mockOutput,
296+
noTty: false
297+
});
298+
const questions: Question[] = [
299+
{ name: 'name', type: 'text', required: true },
300+
{ name: 'status', type: 'text', skipPrompt: true },
301+
];
302+
303+
const manPage = prompter.generateManPage({
304+
commandName: 'test-cmd',
305+
questions,
306+
});
307+
308+
// Both fields should appear in the man page
309+
expect(manPage).toContain('NAME');
310+
expect(manPage).toContain('STATUS');
311+
});
312+
313+
it('should skip multiple skipPrompt questions and only prompt remaining', async () => {
314+
enqueueInputResponse({ type: 'read', value: 'hello' });
315+
316+
const prompter = new Inquirerer({
317+
input: mockInput,
318+
output: mockOutput,
319+
noTty: false
320+
});
321+
const questions: Question[] = [
322+
{ name: 'greeting', type: 'text' },
323+
{ name: 'createdAt', type: 'text', skipPrompt: true },
324+
{ name: 'updatedAt', type: 'text', skipPrompt: true },
325+
{ name: 'internalId', type: 'text', skipPrompt: true },
326+
];
327+
328+
const result = await prompter.prompt({}, questions);
329+
330+
expect(result).toEqual({ greeting: 'hello' });
331+
expect('createdAt' in result).toBe(false);
332+
expect('updatedAt' in result).toBe(false);
333+
expect('internalId' in result).toBe(false);
334+
});
335+
});
336+
235337
it('handles readline inputs', async () => {
236338

237339
const prompter = new Inquirerer({

packages/inquirerer/src/prompt.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,15 @@ export class Inquirerer {
475475
continue;
476476
}
477477

478+
// Skip prompt entirely if skipPrompt is set.
479+
// The question still appears in man pages and CLI flag overrides still work
480+
// (handled by the `question.name in obj` check above), but no interactive
481+
// prompt is shown. The field is simply left out of the answers object.
482+
if (question.skipPrompt) {
483+
ctx.nextQuestion();
484+
continue;
485+
}
486+
478487
// Apply default value if applicable
479488
// this is if useDefault is set, rare! not typical defaults which happen AFTER
480489
// this is mostly to avoid a prompt for "hidden" options

packages/inquirerer/src/question/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export interface BaseQuestion {
3232
pattern?: string;
3333
dependsOn?: string[];
3434
when?: (answers: any) => boolean;
35+
skipPrompt?: boolean;
3536
}
3637

3738
export interface ConfirmQuestion extends BaseQuestion {

0 commit comments

Comments
 (0)