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.
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.