Inquirer.js
Inquirer.js copied to clipboard
'process.stdin' in nodejs is not working properly
In my program, I need to listen to the information entered by the user in the console and make other responses. In the past year, everything has been working normally, but today I used '@ inquirer/prompts' to change some programs. After using this npm package, the original 'process.stdin.on' no longer works
How should I solve it? Please help me
I wrote a simple code to illustrate the problem:
const { input, select } = require('@inquirer/prompts');
async function main() {
/* When I commented out the first two lines, the following code works properly */
const text = await input({ message: `your name?\n` });
console.log('inputText', text);
/**
* My code is omitted
*/
/* When I used 'input', 'process.stdin.on' no longer works */
process.stdin.on('data', async inputs => {
let text = inputs.toString().trim();
if (!text) return;
switch (text) {
case 'cls':
console.clear();
break;
case 'bye':
process.exit(0);
default:
console.log('stdinText:', text);
/* I will do some other logic here, but I just streamlined it out */
break;
}
});
}
main();
It's unclear what would cause this - here's what the do when releasing the readline: https://github.com/SBoudrias/Inquirer.js/blob/master/packages/core/src/lib/screen-manager.mts#L125-L129
I think I'm missing a lot of information about what your program is supposed to do to be able to help. You want to listen to data constantly or only after the input prompt is answered?
And can you send a PR with a failing unit test? This would go a long way allowing us to debug this issue.
I had the same problem and found this issue. After playing around a bit with the sample code, I got it partially working with the following code.
process.stdin.removeAllListeners();
process.stdin.on('data', async inputs => {
After entering the name, you have to press Enter once before you can make an normal entry.
In my project, I use raw mode to listen for input, with this mode enabled, I don't have to press Enter before receiving user input.
@LukasLeppich could you provide a minimal code reproduction?
I'd love to look into this and make sure nothing leaks out of Inquirer - or raise an issue to Node is discarding a readline doesn't reset the stdin properly. But it's a bit hard to go ahead with so few details.
@SBoudrias You can use the code @Anmours posted above.
If you execute the script, it will ask for your name and exit afterward.
If you remove the const text = await input({ message: 'your name?\n'});
line, you can type stuff and exit with bye.
If you add the process.stdin.removeAllListeners();
line in front of the process.stdin.on('data', ...
line and keep the const text = await input({ message: 'your name?\n' });
line, it asks for your name, than you have to press Enter once and after that, you can type stuff and exit with bye again.
The correct behavior would be to ask for your name, than repeat everything you type with stdinText: ...
, clear you screen if you type cls
and exit the script if you type bye
.
Thanks! I'm looking into this.
You can also call process.stdin.resume()
before setting the event listener. And weirdly enough that's what the Node.js doc recommend: https://nodejs.org/api/process.html#signal-events
Also this interesting quote:
In "old" streams mode the stdin stream is paused by default, so one must call
process.stdin.resume()
to read from it. Note also that calling process.stdin.resume() itself would switch stream to "old" mode.
Yeah, pretty much what happens. In both scenario, if you log process.stdin.isPaused()
you'll see it's where the difference lie.
So once a readline instance is closed, it'll pause process.stdin
. Calling on
won't resume it; this must be done manually. (I'm not quite sure how I could change this behavior, calling resume in Inquirer will prevent scripts from exiting)
@SBoudrias @LukasLeppich I solved this problem a long time ago, I just didn't give feedback. Compared to the code when I asked the question, I optimized it like this:
const { input, select } = require('@inquirer/prompts');
const readline = require('readline');
async function main() {
/* When I commented out the first two lines, the following code works properly */
const text = await input({ message: `your name?\n` });
console.log('Hello,', text);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.on('SIGINT', () => {
console.log('Ctrl+C Proactively withdraw');
process.exit(1);
});
rl.on('line', async inputs => {
let text = inputs.toString().trim();
if (!text) return;
switch (text) {
case 'cls':
console.clear();
break;
case 'bye':
process.exit(0);
default:
console.log('stdinText:', text);
/* I will do some other logic here, but I just streamlined it out */
break;
}
});
}
main();
After using the 'Inquirer', I created a 'STDin' using the built-in 'Readline' in Node.js, which worked well and solved my problem.
'creatInterface' must be used after 'await input', otherwise the problem persists
Hope it is helpful to others.
createInterface
must be used after 'await input', otherwise the problem persists
Yeah node:readline
module only works when a single instance is created at a time; all events are duplicated otherwise and there's conflict with what either does.