Node.js synchronous prompt - node.js

I'm using the prompt library for Node.js and I have this code:
var fs = require('fs'),
prompt = require('prompt'),
toCreate = toCreate.toLowerCase(),
stats = fs.lstatSync('./' + toCreate);
if(stats.isDirectory()){
prompt.start();
var property = {
name: 'yesno',
message: 'Directory esistente vuoi continuare lo stesso? (y/n)',
validator: /y[es]*|n[o]?/,
warning: 'Must respond yes or no',
default: 'no'
};
prompt.get(property, function(err, result) {
if(result === 'no'){
console.log('Annullato!');
process.exit(0);
}
});
}
console.log("creating ", toCreate);
console.log('\nAll done, exiting'.green.inverse);
If the prompt is show it seems that it doesn't block code execution but the execution continues and the last two messages by the console are shown while I still have to answer the question.
Is there a way to make it blocking?

With flatiron's prompt library, unfortunately, there is no way to have the code blocking. However, I might suggest my own sync-prompt library. Like the name implies, it allows you to synchronously prompt users for input.
With it, you'd simply issue a function call, and get back the user's command line input:
var prompt = require('sync-prompt').prompt;
var name = prompt('What is your name? ');
// User enters "Mike".
console.log('Hello, ' + name + '!');
// -> Hello, Mike!
var hidden = true;
var password = prompt('Password: ', hidden);
// User enters a password, but nothing will be written to the screen.
So give it a try, if you'd like.
Bear in mind: DO NOT use this on web applications. It should only be used on command line applications.
Update: DO NOT USE THIS LIBRARY AT ALL. IT IS A TOTAL JOKE, TO BE PERFECTLY FRANK.

Since Node.js 8, you can do the following using async/await:
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
function readLineAsync(message) {
return new Promise((resolve, reject) => {
rl.question(message, (answer) => {
resolve(answer);
});
});
}
// Leverages Node.js' awesome async/await functionality
async function demoSynchronousPrompt() {
var promptInput = await readLineAsync("Give me some input >");
console.log("Won't be executed until promptInput is received", promptInput);
rl.close();
}

Since IO in Node doesn't block, you're not going to find an easy way to make something like this synchronous. Instead, you should move the code into the callback:
...
prompt.get(property, function (err, result) {
if(result === 'no'){
console.log('Annullato!');
process.exit(0);
}
console.log("creating ", toCreate);
console.log('\nAll done, exiting'.green.inverse);
});
or else extract it and call the extracted function:
...
prompt.get(property, function (err, result) {
if(result === 'no'){
console.log('Annullato!');
process.exit(0);
} else {
doCreate();
}
});
...
function doCreate() {
console.log("creating ", toCreate);
console.log('\nAll done, exiting'.green.inverse);
}

Old question, I know, but I just found the perfect tool for this. readline-sync gives you a synchronous way to collect user input in a node script.
It's dead simple to use and it doesn't require any dependencies (I couldn't use sync-prompt because of gyp issues).
From the github readme:
var readlineSync = require('readline-sync');
// Wait for user's response.
var userName = readlineSync.question('May I have your name? ');
console.log('Hi ' + userName + '!');
I'm not affiliated with the project in any way, but it just made my day, so I had to share.

I've come across this thread and all the solutions either:
Don't actually provide a syncronous prompt solution
Are outdated and don't work with new versions of node.
And for that reason I have created syncprompt
. Install it with npm i --save syncprompt and then just add:
var prompt = require('syncprompt');
For example, this will allow you to do:
var name = prompt("Please enter your name? ");
It also supports prompting for passwords:
var topSecretPassword = prompt("Please enter password: ", true);

Vorpal.js is a library I made that has just recently been released. It provides synchronous command execution with an interactive prompt, like you are asking. The below code will do what you are asking:
var vorpal = require('vorpal')();
vorpal.command('do sync')
.action(function (args) {
return 'i have done sync';
});
With the above, the prompt will come back after a second is up (only after callback is called).

This is dependency free, synchronous and works on Windows, Linux and OSX:
// Synchronously prompt for input
function prompt(message)
{
// Write message
process.stdout.write(message);
// Work out shell command to prompt for a string and echo it to stdout
let cmd;
let args;
if (os.platform() == "win32")
{
cmd = 'cmd';
args = [ '/V:ON', '/C', 'set /p response= && echo !response!' ];
}
else
{
cmd = 'bash';
args = [ '-c', 'read response; echo "$response"' ];
}
// Pipe stdout back to self so we can read the echoed value
let opts = {
stdio: [ 'inherit', 'pipe', 'inherit' ],
shell: false,
};
// Run it
return child_process.spawnSync(cmd, args, opts).stdout.toString().trim();
}

const buffer = Buffer.alloc(1024);
require("fs").readSync(process.stdin.fd, buffer);
console.log(buffer.toString());

You can use prompt-sync
const prompt = require('prompt-sync')()
const ans = prompt('How many more times? ') // get input from the user.
P.S. prompt-sync acts weird, if prompt message contains new line character, so if you need multiline prompt just use console.log():
const prompt = require('prompt-sync')()
console.log('How many more times?\n')
const ans = prompt('') // get input from the user.

Related

Custom Node JS REPL input/output stream

I need to have custom REPL input/output stream. for example I need to pass a piece of script to the REPL when some event happens and get it's output and do something with it.
To describe it more clear to you, I'm working on a vscode plugin (github: source code) which provides REPL. in my case I have a vscode WebView and from there, I get user input and then I want to pass that input to the node REPL and get its output and show it to user.
So, how would I achieve that? If you need more information please tell me. thanks in advance.
EDIT 1:
const replServer = repl.start({
input: /* what should be here? */,
output: /* what should be here? */
});
Edit 2:
can anyone explain me what is the usage of input/output parameters in the above example?
Here is a solution that worked for me.
const {
PassThrough
} = require('stream')
const repl = require('repl')
const input = new PassThrough()
const output = new PassThrough()
output.setEncoding('utf-8')
const _repl = repl.start({
prompt: 'awesomeRepl> ',
input,
output
})
_repl.on('exit', function() {
// Do something when REPL exit
console.log('Exited REPL...')
})
function evaluate(code) {
let evaluatedCode = ''
output.on('data', (chunk) => {
evaluatedCode += chunk.toString()
console.log(evaluatedCode)
})
input.write(`${code}\n`)
return result
}
evaluate('2 + 2') // should return 4
Notice created the REPL instance outside the evaluate function so we don't create a new instance for every call of evaluate
To create a repl server you just need to do
const repl = require('repl')
repl.start({prompt: "> ", input: input_stream, output: output_stream");
prompt is a string that is the prompt, stream is the input. input_stream needs to be a readable stream, output_stream needs to be a writable one. you can read more about streams here. Once the streams are working you can do
output_stream.on('data', (chunk) => {
14 //whatever you do with the data
15 });

My discord.js help command doesn't exactly work

First things first, my help command does work but not in the way I would like it to work.
My first issue is that the commands are being sent in separate messages which is kind of annoying when you have a lot of commands.
My second issue is that when the message is sent in an embed, it shows up like this:
Command
Description
Usage
Undefined
I tried multiple ways to get rid of 'Undefined'.
My code:
const fs = require("fs");
const Discord = require("discord.js");
module.exports.run = async(bot, message, args, con) => {
fs.readdir("./commands/", (err, files) => {
if(err) console.error(err);
let jsfiles = files.filter(f => f.split(".").pop() === "js");
if(jsfiles.length <= 0) {
console.log("No commands to load!");
return;
}
var namelist = "";
var desclist = "";
var usage = "";
let result = jsfiles((f, i) => {
let props = require(`./${f}`);
namelist = props.help.name;
desclist = props.help.description;
usage = props.help.usage;
// send help text
let helpembed = new Discord.RichEmbed()
.setTitle("Commands")
.setFooter("Please report any bugs to Vati#1662")
.setColor("RANDOM")
.addField(`**${namelist}** \n${desclist} \n${usage}`)
message.author.sendEmbed(helpembed);
});
})
}
module.exports.help = {
name: "help",
description: "shows all commands",
usage: "help"
}
When you use RichEmbed.addField() it expects at least two arguments: the title of the field and its value.
.addField(`**${namelist}** \n${desclist} \n${usage}`) // this has only the title argument
Try putting the three "sections" in three different fields.
.addField("Name:", namelist, true) // the 'true' means that they're inline fileds
.addField("Usage:", usage, true) // they will try to fit on the same line
.addField("Description:", desclist) // if there's no third argument, the default is 'false'
The commands are sent in different messages because you're running the whole code for every command, and not only adding the fields for every command. If you don't want to spend time on all this stuff, you can use the discord.js-commando library: it's a framework that deals with the commands and also handles errors, incomplete commands, and a lot of other stuff. If you want to check it out, you can find the docs here.

npm cli with co/yield not ending

I'm attempting to write a node cli application. It works, however, it doesn't return back to the command line (at least not in Windows, haven't tried yet in bash). I have to Ctrl+Break out of the application.
#!/usr/bin/env node --harmony
var chalk = require('chalk');
var co = require('co');
var prompt = require('co-prompt');
var program = require('commander');
program
.version('1.0.0')
.option('-w, --workshop <workshop number>', 'workshop number to build')
.parse(process.argv);
co(function* () {
if (!program.workshop) {
program.workshop = yield prompt('Workshop: ');
}
return yield Promise.resolve(true);
}).then(function() {
console.log(chalk.bold.cyan('You entered: ') + program.workshop);
});
I've also tried it without the line return yield Promise.resolve(true); but that has no affect.
Any suggestions?
Thanks.
I had the same problem and found another solution in this co-prompt Github issue.
var co = require('co');
var prompt = require('co-prompt');
co(function* () {
var value = yield prompt('Value: ')
return value
}).then(function (value) {
// do whatever you need with the value
console.log(value)
}).then(function () {
// when you are done you must pause stdin
process.stdin.pause()
})
// now the process will exit normally
For those interested... I'm not sure if this is the right way, but:
I ended up removing
return yield Promise.resolve(true);
Then adding as the last line of my .then function
process.exit(0);
Again, not sure if this is the best approach, but it does work.

Are node.js variables none blocking

I have some node code and I'm trying to set this up but it seems like it's processing one item before another is complete and I'm wondering if I'm right and if so if there is some kind of work around
var input = function(text){
process.stdout.write(text);
process.stdin.setEncoding('utf-8')
process.stdin.once("data", function(input_data){
return input_data;
}).resume();
}
var test = function(var1, var2){
var var1 = (var1 == null) ? input("Var One: ") : var1;
var var2 = (var2 == null) ? input("Var Two: ").split(" ") : var1;
console.log(var1, var2);
}
Though, when I execute test(null,null) I expected it to ask me for Var one then define var1 as the data then after that I thought it would prompt me with Var Two and then split that by spaces into a list however that did not work and it just errored saying "Cannot call method of undefined "
I came from python where this was possible and it wouldn't do any other executions till the previously defined one was completed so I'm wondering if node has something similar to this and note I am not using a server or website for this I'm just testing code on my computer.
I'm fairly new to node myself, but here's my understanding of how it will execute:
test(null, null);
// enter test() function
// see that var1 == null, run input("Var One: ")
// write "Var One: " to stdout
// set stdin encoding to utf-8
// set an event listener for 'data' on stdin, and provide a callback for that event
... let's pause there for a moment, because this is the important bit.
When you set a listener and a callback, you've entered the land of asynchronicity. Your code will continue to march on without waiting and do the next things that you've told it to do. Which, in this case, is just sending back an undefined return from input(), and then moving on to handle var2 similarly, where you try to call undefined.split(), and the whole process pukes.
If, on the other hand, you just remove .split(), you'll probably get an output like this:
Var One: Var Two : [waiting for input]
At this point, when you enter the first value, it'll take it and return it from your callback to... nowhere... then it'll wait for your next value and do the same.
This is where you have to start to break your mind away form your python background and procedural, synchronous habits.
I'll risk beating a dead horse, and comment up your code directly:
var input = function(text){
process.stdout.write(text);
process.stdin.setEncoding('utf-8')
// .once() is an asynchronous call
process.stdin.once("data", function(input_data){
// the return value of this anonymous function isn't used anywhere
return input_data;
}).resume();
// there is no return value of the "input" function
}
Functionally, what you're looking for is something like the following (though this is ugly, something like async.waterfall makes this sort of structure much more palatable, and there may be even better ways to do it that I haven't learned yet):
function test(var1, var2) {
if (!var1 || !var2) {
process.stdin.resume();
process.stdin.setEncoding('utf-8');
if (!var1) {
process.stdout.write('Var One: ');
process.stdin.once('data', function(input_data) {
// var1 & var2 get pulled in from the parent context
var1 = input_data;
if (!var2) {
process.stdout.write('Var Two: ');
process.stdin.once('data', function(input_data) {
var2 = input_data;
console.log(var1, var2);
});
}
else {
console.log(var1, var2);
}
});
}
else if (!var2) {
process.stdout.write('Var Two: ');
process.stdin.once('data', function(input_data) {
var2 = input_data;
console.log(var1, var2);
});
}
else {
// there is no else, either var1 or var2 is missing
// per the first conditional
}
}
else {
console.log(var1, var2);
}
}
Here is how you can do it :
function input (text, val, cb) {
if (val) return cb(null, val)
process.stdout.write(text)
process.stdin.setEncoding('utf-8')
process.stdin.once('data', function(data){
process.stdin.pause()
cb(null, data)
}).resume()
}
function test (var1, var2) {
input('Var One: ', var1, function (err, var1) {
input('Var Two: ', var2, function (err, var2) {
console.log(var1)
console.log(var2)
})
})
}
test(null, null)
Basically, since stdin is async, so is input function. You need to use callback-based function style. This works, though you strongly recommend not using stdin this way. Try readline core module or some special userland modules from npm.
You can see that writing callback based code can be a little messy (aka callback hell). Here is a fancy way to address this issue using co module (you need node 0.11.x for this and use --harmony-generators flag):
var co = require('co')
function input (text) {
return function (cb) {
process.stdout.write(text)
process.stdin.setEncoding('utf-8')
process.stdin.once('data', function(data){
process.stdin.pause()
cb(null, data)
}).resume()
}
}
function * test (var1, var2) {
var1 = var1 || (yield input('Var One: '))
var2 = var2 || (yield input('Var Two: '))
console.log(var1)
console.log(var2)
}
co(test)(null, null)
I would use this built-in Node.js module: http://nodejs.org/api/readline.html
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question("What do you think of node.js? ", function(answer) {
// TODO: Log the answer in a database
console.log("Thank you for your valuable feedback:", answer);
rl.question("What do you think of JavaScript?", function(answer2) {
console.log("Answer2 is ", answer2);
});
rl.close();
});
There is a relatively new language derived from CoffeeScript that makes async code more readable. It basically looks like sync code, and you don't have to nest indents for callbacks all the time. Its called ToffeeScript https://github.com/jiangmiao/toffee-script Coming from Python you may appreciate it.
readline = require 'readline'
rl = readline.createInterface { input: process.stdin, output: process.stdout }
answer = rl.question! "What do you think of node.js?"
console.log "Thank you for your valuable feedback:", answer
answer2 = rl.question! "What do you think of ToffeeScript?"
console.log "Thanks again."
rl.close()

readline with console.log in the background

I'm having problems with node.js' readline. Shown below is the console output: the stuff in bold is what I am typing, the rest is what is being logged by the server.
> Teslog message
> Testinlog message
> log message
log message
Tlog message
estinglog message
> 123
Put simply, my code looks like this
setInterval(function() { console.log("log message") }, 1000);
var cli = require('readline').createInterface(process.stdin, process.stdout);
cli.setPrompt("> ", 2);
cli.on('line', function(line) {
cli.prompt();
});
cli.prompt();
How can I get the prompt to shift down to give the new output room, without completely trashing whatever I am typing?
This appears to somewhat solve the problem - the prompt at least gets redrawn after the console is logged to.
var log = console.log;
console.log = function() {
// cli.pause();
cli.output.write('\x1b[2K\r');
log.apply(console, Array.prototype.slice.call(arguments));
// cli.resume();
cli._refreshLine();
}
However, the interrupted prompt does not get cleared.
EDIT: adding cli.output.write('\x1b[2K\r'); made it work
EDIT 2: More complete solution, making other things like util.log work as well:
function fixStdoutFor(cli) {
var oldStdout = process.stdout;
var newStdout = Object.create(oldStdout);
newStdout.write = function() {
cli.output.write('\x1b[2K\r');
var result = oldStdout.write.apply(
this,
Array.prototype.slice.call(arguments)
);
cli._refreshLine();
return result;
}
process.__defineGetter__('stdout', function() { return newStdout; });
}
#EDIT 3: Looks like cli.pause() and cli.resume() before and after the call are redundant.

Resources