简体   繁体   中英

Performing command line actions synchronously after Yeoman generator has finished

I'm building a Yeoman generator and after it has finished I want to perform some command line actions like 'npm install', 'bower install' and 'grunt less'. I'm using spawnCommand for this and I nested all actions using event listeners to perform them synchronously. However, to avoid this endless nesting, I'm looking for a cleaner implementation, to make it easily expandable. Perfectly, I would like to have an array with commands (like ['npm install', 'grunt install', 'less:dev']) and have this processed synchronously with proper error detection.

// Install npm packages
this.spawnCommand('npm', ['install'])
    .on('exit', function (err) {
        if (err) {
            this.log.error('npm package installation failed. Please run \'npm install\' and \'bower install\'. Error: ' + err);
        } else {
            // Install bower packages
            this.spawnCommand('bower', ['install'])
                .on('exit', function (err) {
                    if (err) {
                        this.log.error('bower package installation failed. Please run \'bower install\'. Error: ' + err);
                    } else {
                        this.spawnCommand('grunt', ['less'])
                            .on('exit', function (err) {
                                if (err) {
                                    this.log.error('Less compilation failed. Please run \'grunt less:dev\'. Error: ' + err);
                                } else {

                                }

                            }.bind(this));
                    }

                }.bind(this));
        }

    }.bind(this));

Something like this? (untested though):

this.processTask = function (task) {
    this.spawnCommand(task.cmd, task.args)
        .on('exit', function (err) {
            if (err) {
                this.log.error('task failed. Error: ' + err);
            } else {
                this.emit('nextTask');
            }
        });
};

this.on('nextTask' function(){
    var next = this.tasks.shift();
    if (next){
       this.processTask(next);
    } else {
       console.log('we are done');
    }
}.bind(this));

//preparing the list of tasks:
this.tasks = [];
this.tasks.push({cmd: 'npm', args:['install']});
this.tasks.push({cmd: 'bower', args:['install']});
this.tasks.push({cmd: 'grunt', args:['less']});

//start first task

this.processTask(this.tasks.shift());

I used execSync from Node.js and it seems to work, eg:

var child_process = require('child_process');

var result = execSync('grunt less');

Node.js 0.12 and io.js 1.10 support execSync:

child_process.execSync(command[, options])

and returns, "Buffer|String The stdout from the command", which may be an error code. API documentation.

The back story about the synchronous API .

You can make a script like init.sh and put your commands that need to be run in order in it, like:

#!/usr/bin/env bash
npm install
your-funky-command
gulp something-special
gulp

...then wherever you need to put the spawnCommand code (I do it in end method), add somehting like this:

  var done = this.async();
  this.spawnCommand('sh', ['init.sh'], /* maybe cwd? {cwd: 'src'} */)
    .on('close', done);

Ain't pretty or anything but it works, and it's obvious.


Optionally, if you need one command to only run if the prev succeeded, do this:

#!/usr/bin/env bash
npm install \
  && your-funky-command \
  && gulp something-special \
  && gulp

(Bonus advantage is that now your app init logic is no longer tied to Yo.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM