How to start a process in node.js that thinks its being run from the commandline - node.js

I'm running require('child_process').exec('npm install') as a child process in a node.js script, but I want it to retain console colors. I'm running in windows, but want this script to be portable (e.g. to linux). How do I start a process that think's it's being run from the console?
Note: I'd rather not have npm-specific answers, but an answer that allows me to trick any command.

You can do this by letting the child process inherit the master process' stdio streams. This means you need to user spawn rather than exec, and this what you'd do:
var spawn = require('child_process').spawn;
var child = spawn('npm', ['install'], {
stdio: 'inherit'
});

Related

Executing Command in Nodejs and Attaching the output to the console

I'm working on a CLI tool to add an extra layer of automation and utilities to our workflow and I'm wrapping the webpack development command with an alternative in my CLI (here's a demonstration):-
function runDev(){
this.doSomePreAutomationAndPreperations();
this.runWebpackDevCommand();
}
I'm using NodeJs child_proecess.exec and I'm trying to figure out a way to execute the webpack dev command and attach it to the terminal (like -it in docker if you're familiar with it) or transferring the control to the child process(so output will be directly emitted to the console).
Is there away to do that?
It turns out that I can achieve this but just making the child process inherit the stdio. ex:-
const { spawn } = require('child_process')
const shell = spawn('sh',[], { stdio: 'inherit' })
shell.on('close',(code)=>{console.log('[shell] terminated :',code)})

Minecraft/Node - Writing to process.stdin and Child Process Also Receiving Input

I'm trying to write a terminal application that would allow me to start and stop a Minecraft server, write arguments through readline that would first be checked by the Node process and then, if determined by the script, sent to the Minecraft server for use instead.
Running on Windows 10 with latest Node build and Minecraft JE server.
First I tested out just starting up a node process that spawned in a child process holding the Minecraft server. I set the 3 stdio arguments to 'inherit' and also 'ipc' (not 100% sure why this is necessary, but I saw others do it). Once run, if I typed anything into the terminal the Minecraft server would receive input through Node. This was great. What I wanted, though, was to be able to run readline and parse inputs and determine whether Node should process the input or if they should be sent to the Minecraft server.
So I played around with the stdio arguments and using process.stdin.write() and such, trying to figure out how to write to the child process' stdin so that the Minecraft server would receive the input, but to no avail.
So I tried running a "master" Node process that would fork a secondary Node process. The "master" would run readline, and use child.send() if any input was to be sent there. Then, in the second Node process, I would start the Minecraft server as a spawn with all 3 stdio arguments set to inherit. I would then listen with process.on('message') and if a message was received, write it to process.stdin hoping the Minecraft process would receive it through inherited input. But this also did not work.
I've tried a number of different things, but in the end I haven't had any luck. I've tried looking up as much as I could, but I haven't found anything that would help me out. Sorry if this ends up being a no-brainer.
// First Iteration
// spawns in a minecraft server and listens for input in the terminal
// effectively the same as running the equivalent run command for the minecraft server, except this runs as a Node child process
const spawn = require('child_process').spawn
var minecraft = spawn('java', ['-jar', '-Xms1G','-Xmx1G', '-Dfile.encoding=utf8', '../spigot-1.13.2.jar'], {
stdio: [
'inherit',
'inherit',
'inherit',
'ipc'
],
})
// Second Iteration
// changed cp stdin to pipe and tried to write manually via rl
const spawn = require('child_process').spawn
const readline = require('readline')
var minecraft = spawn('java', ['-jar', '-Xms1G','-Xmx1G', '-Dfile.encoding=utf8', '../spigot-1.13.2.jar'], {
stdio: [
'pipe',
'inherit',
'inherit',
'ipc'
],
})
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
}).on('line',(line)=> {
process.stdin.write(line)
})
// Later iterations involved running nested child processes, basically, but were all more or less more-abstract versions of my second iteration.
What I'd like to achieve, as I said earlier, is being able to listen for admin input from the terminal with readline and, as appropriate, send that input to the Minecraft server process. Otherwise, readline input would be processed in the Node process.
** to clarify, when I say it doesn't work, what I mean is that I get no response from the Minecraft server process. Nothing in-game, nothing on the command line. With my first iteration, I could see output from the Minecraft process both in-game and on the command line. But by the second iteration, nothing.
You cannot write to process.stdin. Did you mean to write to the stdin of the minecraft process mincraft.stdin?
minecraft.stdin.write(line)

Run a binary from Node.JS and relay the behavior exactly on the console, including prompts

I'm making a cli tool with Node.JS. I run another binary file from within node, and the other process asks for a password at some point.
So what I need is to simply launch the other process, and put it "in charge" of the terminal, so the other process handles the prompts and the console output.
You can use 'inherit' for the stdio option of spawn:
const spawn = require( 'child_process' ).spawn;
spawn( '/path/to/binary', [], { stdio: 'inherit' } );

NodeJS: exit parent, leave child alive

i am writing an utility. One command of this utility is to run an external application.
var child_process = require('child_process');
var fs = require('fs');
var out = fs.openSync('.../../log/out.log', 'a');
var err = fs.openSync('.../../log/err.log', 'a');
exports.Unref = function(app, argv) {
var child = child_process.spawn(app, argv, {
detached: true,
stdio: [ 'ignore', out, err ]
});
child.unref();
//process.exit(0);
};
Currently:
$ utility run app --some-args // run external app
// cant enter next command while app is running
My Problem is that if i run this command, the terminal is locked while the "external" Application is running.
But the terminal window shouldn't be locked by the child_process.
i wanna run:
$ utility run app --some-args
$ next-command
$ next-command
The external Application (a desktop application) will be closed by hisself.
Like this:
$ subl server.js // this runs Sublime Text and passes a file to the editor
$ npm start // The terminal does not locked - i can execute next command while Sublime is still running
You know what i mean ^^?
Appending ['>>../../log/out.log', '2>>../../log/err.log'] to the end of argv instead of leaving two files open should work since it's the open file handles that are keeping the process alive.
Passing opened file descriptors in stdio in addition to detached: true will not work the way you expect because there is no way to unref() the file descriptors in the parent process and have it still work for the child process. Even if there was a way, I believe that when the parent process exited, the OS would clean up (close) the file descriptors it had open, which would cause problems for the detached child process.
The only possible way that this might have been able to work would have been by passing file descriptors to child processes, but that functionality was dropped several stable branches ago because the same functionality did not exist on some other platforms (read: Windows).

Running Node app through Grunt

I am trying to run my Node application as a Grunt task. I need to spawn this as a child process, however, to allow me to run the watch task in parallel.
This works:
grunt.registerTask('start', function () {
grunt.util.spawn(
{ cmd: 'node'
, args: ['app.js']
})
grunt.task.run('watch:app')
})
However, when changes are detected by the watch task, this will trigger the start task again. Before I spawn another child process of my Node app, I need to kill the previous one.
I can't figure out how to kill the process, however. Something like this does not work:
var child
grunt.registerTask('start', function () {
if (child) child.kill()
child = grunt.util.spawn(
{ cmd: 'node'
, args: ['app.js']
})
grunt.task.run('watch:app')
})
It appears that:
Even though I store the spawned process in a variable outside of the function context, it does not persist, so the next time the start task is run, child is undefined.
child has no kill function…
Take a look at grunt-nodemon which handles a lot of the headaches related to spawning a child process.
This is because grunt-contrib-watch currently spawns all task runs as child processes. So the variable child is not within the same process context. Fairly soon, grunt-contrib-watch#0.3.0 will be released with a nospawn option. This will let you configure the watch to spawn task runs within the same context and would make your above example work.
Take a look at this issue for a little more information:
https://github.com/gruntjs/grunt-contrib-watch/issues/45

Resources