javascriptnode.jscommand-line-interfaceshelljs

How to have cli-spinner run during ShellJS command exec?


I'm currently trying to make a simple command-line node program that allows for me to easily create a boilerplate for a lot of the React/Redux applications that I make. I'm using ShellJS to execute console commands (I've tried using Node's child_process, too.) The problem is getting cli-spinner to work with executing terminal commands. Here's my code:

#! /usr/bin/env node
var shell = require('shelljs');
var userArgs = process.argv.slice(2);
var folderName = userArgs[0];
var Spinner = require('cli-spinner').Spinner;
var depSpin = new Spinner('Installing dependencies.. %s');
depSpin.setSpinnerString(10);

shell.mkdir(folderName);
shell.cd(folderName);

depSpin.start(); 
// I expect for the spinner to start here (before the execution of the commands.)

shell.exec('npm init -y', {silent: true});
shell.exec('npm install --save babel-core babel-loader babel-preset-es2015 babel-preset-react react-dom react-redux redux webpack', {silent: true});
shell.exec('npm install --save-dev babel-preset-env webpack-dev-server', {silent: true});

depSpin.stop(); 
// Since ShellJS should run synchronously, 
// the spinner should stop right after the last command finishes.

shell.touch('webpack.config.js');
shell.mkdir(['build', 'frontend']);

shell.cd('frontend');
shell.mkdir(['components', 'containers', 'reducers', 'store']);
shell.touch('app.js');

But when running the program, it just hangs without displaying anything while it installs the dependencies. It's the same as when the spinner code wasn't even in there. I've also tried removing the depSpin.stop(), which just makes the program hang forever on the spinner. I have a feeling that this issue is caused by a conflict between cli-spinner and ShellJS both using the terminal.


Solution

  • I was able to accomplish this effect by using child_process's spawn. I had to create a child_process.spawn and run all of the commands concatenated with &&. That freed up the console output to be exclusively the cli-spinner output. I then did an event handler for when the child process exited to stop the spinner.

    #! /usr/bin/env node
    var shell = require('shelljs');
    var userArgs = process.argv.slice(2);
    var folderName = userArgs[0];
    var Spinner = require('cli-spinner').Spinner;
    var depSpin = new Spinner('Installing dependencies.. %s');
    var spawn = require('child_process').spawn;
    var commandsDep = [
      'cd ' + folderName,
      'npm init -y',
      'npm install --save babel-core babel-loader babel-preset-es2015 babel-preset-react react-dom react-redux redux webpack',
      'npm install --save-dev babel-preset-env webpack-dev-server'
    ];
    var depChild = spawn(commandsDep.join(' && '), {
      shell: true
    });
    depSpin.setSpinnerString(18);
    
    shell.mkdir(folderName);
    shell.cd(folderName);
    
    depSpin.start();
    depChild.on('exit', () => {
      depSpin.stop();
      shell.exec('clear');
      console.log('Installing dependencies.. ✓');
    })
    
    shell.touch('webpack.config.js');
    shell.mkdir(['build', 'frontend']);
    
    shell.cd('frontend');
    shell.mkdir(['components', 'containers', 'reducers', 'store']);
    shell.touch('app.js');
    
    shell.cd('containers');
    shell.touch(['AppContainer.js', 'Root.js']);
    
    shell.cd('../reducers');
    shell.touch('index.js');
    
    shell.cd('../store');
    shell.touch('configureStore.js');