Ask for user input via CLI.
Creates a readline.Interface
(rl
) by default:
rl: createInterface(process.stdin, process.stdout);
const answer = await ask('Question: ');
// output: Question: <input>
// answer: <input>
-
The
trim
option callsString.prototype.trim()
for the provided input.const answer = await ask('Question: ', { trim: true }); // output: Question: <input> // answer: <input.trim()>
-
The
format
option allows you to format theanswer
before it is evaluated through theaccept
option.const answer = await ask('Question: ', { trim: true, format: answer => { return answer.concat('bar').toLowerCase(); } }); // output: Question: <input: Foo > // answer: foobar
-
The
accept
option can be aboolean
,string[]
, or a function returning aboolean
.ask
will continue to ask for input ifaccept
resolves tofalse
.With
boolean
:const answer = await ask('Accept: ', { accept: true }); // output: Accept: <input> // answer: <input>
With
string[]
:const answer = await ask('Continue [y/n]: ', { accept: ['y', 'n'] }); // output: Continue [y/n]: <input: foo> // output: Continue [y/n]: <input: > // output: Continue [y/n]: <input: Y> // output: Continue [y/n]: <input: N> // output: Continue [y/n]: <input: y> // answer: y
With function:
const answer = await ask('Enter 8 or more: ', { accept: answer => answer.length >= 8 }); // output: Enter 8 or more: <input: foo> // output: Enter 8 or more: <input: foobarbaz> // answer: foobarbaz
-
The
on
option allows you to do stuff when an event is triggered.This can be helpful when you want to mute and unmute the output stream before and after the input respectively.
beforeAsk
- Beforerl.question()
is called.ask
- Afterrl.question()
is called.answer
- After user input.
Example:
const answer = await ask('Question: ', { on: event => { /* do stuff */ } });
The Ask callback exposes the context
and props
objects. The callback returns an object (Ask options) including the question
string.
- The
context
object contains theiteration
number (starts at0
) and thepreviousAnswer
(defaults to''
). - The
props
object contains therl
being used. You can add more properties to theprops
object via thecreateAsk
function.
const answer = await ask((context, props) => {
const { iteration, previousAnswer } = context;
const n = iteration + 1;
return {
question: `${n}. Question [${previousAnswer}]: `,
accept: ['y', 'n']
};
});
// output: 1. Question []: <input: foo>
// output: 2. Question [foo]: <input: bar>
// output: 3. Question [bar]: <input: baz>
// output: 4. Question [baz]: <input: n>
// answer: n
You can create your own Ask function with additional properties. Example:
const myAsk = createAsk(() => {
const rl = createInterface({
input: process.stdin,
output: process.stdout
});
return {
rl,
mute: () => {
/* do stuff */
},
unmute: () => {
/* do stuff */
}
};
});
// ...
const answer = await myAsk((context, props) => {
return {
question: 'Question: ',
on: event => {
if (event === 'ask') {
props.mute();
} else {
props.unmute();
}
}
};
});
// <props.unmute()>
// output: Question: <props.mute()><input>
// <props.unmute()>
// answer: <input>
The ask.scoped(callback)
function reuses the same rl
instance and will call rl.close()
after the callback is finished.
The callback(q, props)
parameters include the Ask function and the props
. Example:
const result = await ask.scoped(async (q, props) => {
// basic usage
const a1 = await q('Question 1: ');
// with options
const a2 = await q('Question 2: ', {
accept: ['y', 'n']
});
// with callback
const a3 = await q(context => {
const { previousAnswer } = context;
return {
question: `Question 3 [${previousAnswer}]: `,
accept: answer => answer.length >= 8
};
});
return { a1, a2, a3 };
});
// output: Question 1: <input1>
// output: Question 2: <input2: foo>
// output: Question 2: <input2: y>
// output: Question 3 []: <input3: foo>
// output: Question 3 [foo]: <input3: bar>
// output: Question 3 [bar]: <input3: foobarbaz>
// result: { a1: <input1>, a2: 'y', a3: 'foobarbaz' }