Search code examples
node.jsyeomanyeoman-generator

Yeoman generator: how to run async command after all files copied


I'm writing a yeoman generator. I need to run some shell script after all files copied. The generator is being called as a child generator so it should wait till script finished.
The script is some command file being run via spawn:

that.spawnCommand('createdb.cmd');

As the script depends on files created by the generator it cannot run right inside generator's methods as all copy/template action are async and have not executed yet:

MyGenerator.prototype.serverApp = function serverApp() {
  if (this.useLocalDb) {
    this.copy('App_Data/createdb.cmd', 'App_Data/createdb.cmd');
    // here I cannot run spawn with createdb.cmd as it doesn't exist
  }
}

So the only place I found where I can run spawn is the 'end' event handler:

var MyGenerator = module.exports = function MyGenerator (args, options, config) {
  this.on('end', function () {
    if (that.useLocalDb) {
      that.spawnCommand('createdb.cmd')
    }
  }
}

The script runs successfully but the generator finishes earlier than the child process. I need to tell Yeoman to wait for my child process. Something like this:

this.on('end', function (done) {
  this.spawnCommand('createdb.cmd')
    .on('close', function () {
      done();
    });
}.bind(this));

But 'end' handler doesn't have the argument with 'dine' callback.

How to do this?

UPDATE:
thanks to @SimonBoudrias I got it working.
The full working code is below.
BTW: end method is described in the docs

var MyGenerator = module.exports = yeoman.generators.Base.extend({
    constructor: function (args, options, config) {
        yeoman.generators.Base.apply(this, arguments);
        this.appName = this.options.appName;
    },

    prompting : function () {   
        // asking user
    },

    writing : function () { 
        // copying files
    },

    end: function () {
        var that = this;
        if (this.useLocalDb) {
            var done = this.async();
            process.chdir('App_Data');

            this.spawnCommand('createdb.cmd').on('close', function () {
                that._sayGoodbay();
                done();
            });

            process.chdir('..');
        } else {
            this._sayGoodbay();
        }
    },

    _sayGoodbay: funciton () {
        // final words to user
    }
});

Solution

  • Never trigger any action in the end event. This event is to be used by implementors, not generator themselves.

    In your case:

    module.exports = generators.Base({
        end: function () {
            var done = this.async();
            this.spawnCommand('createdb.cmd').on('close', done);
        }
    });