I\'m writing my first Yeoman generator, which prompts the user for various inputs and conditionally creates files based on their responses. I need to be able to call a subrouti
2015-04 update: The yeoman api now includes this.composeWith
as the preferred method for linking generators.
docs: http://yeoman.io/authoring/composability.html
Let's consider you have a generator generator-blog
(BlogGenerator) with two sub generators (blog-server and blog-client):
app\index.js
client\index.js
server\index.js
So when you run yo blog
you what to ask the user for some options and run (optionally) sub generators, right?
To run a subgenerator you need to call this.invoke("generator_namespace", {options: {}})
.
The second argument we passed can have options
field - it's options object which will be passed to the generator.
In app\index.js:
BlogGenerator.prototype.askFor = function askFor() {
var cb = this.async();
// have Yeoman greet the user.
console.log(this.yeoman);
var prompts = [{
name: 'appName',
message: 'Enter your app name',
default: 'MyBlog'
}, {
type: 'confirm',
name: 'createServer',
message: 'Would you like to create server project?',
default: true
}, {
type: 'confirm',
name: 'createClient',
message: 'Whould you like to create client project?',
default: true
}];
this.prompt(prompts, function (props) {
this.appName = props.appName;
this.createServer = props.createServer;
this.createClient = props.createClient;
cb();
}.bind(this));
}
BlogGenerator.prototype.main = function app() {
if (this.createClient) {
// Here: we'are calling the nested generator (via 'invoke' with options)
this.invoke("blog:client", {options: {nested: true, appName: this.appName}});
}
if (this.createServer) {
this.invoke("blog:server", {options: {nested: true, appName: this.appName}});
}
};
In client\index.js:
var BlogGenerator = module.exports = function BlogGenerator(args, options, config) {
var that = this;
yeoman.Base.apply(this, arguments);
// in this.options we have the object passed to 'invoke' in app/index.js:
this.appName = that.options.appName;
this.nested = that.options.nested;
};
BlogGenerator .prototype.askFor = function askFor() {
var cb = this.async();
if (!this.options.nested) {
console.log(this.yeoman);
}
}
UPDATE 2015-12-21:
Using invoke
is deprecated now and should be replaced with composeWith
. But it's not as easy as it could be. The main difference between invoke
and composeWith
is that now you have no ability to control subgenerators. You could only declare using them.
Here's how main
method from above should look like:
BlogGenerator.prototype.main = function app() {
if (this.createClient) {
this.composeWith("blog:client", {
options: {
nested: true,
appName: this.appName
}
}, {
local: require.resolve("./../client")
});
}
if (this.createServer) {
this.composeWith("blog:server", {
options: {
nested: true,
appName: this.appName
}
}, {
local: require.resolve("./../server")
});
}
};
Also I removed replaced yeoman.generators.Base
with yeoman.Base
.
You can cover all possible execution scenarios, condition checking, prompting when composing generators together if you decouple generators and use a 'main-generator' the run context loop will help you. Use the options
of .composeWith('my-genertor', { 'options' : options })
for passing configurations to composed generators.
When using .composeWith
a priority group function (e.g.: prompting
, writing
...) will be executed for all the generators, then the next priority group. If you call .composeWith
to generatorB from inside a generatorA, then execution will be, e.g.:
generatorA.prompting => generatorB.prompting => generatorA.writing => generatorB.writing
If you want to control execution between different generators, I advise you to create a "main" generator which composes them together, like written on http://yeoman.io/authoring/composability.html#order:
// In my-generator/generators/turbo/index.js
module.exports = require('yeoman-generator').Base.extend({
'prompting' : function () {
console.log('prompting - turbo');
},
'writing' : function () {
console.log('prompting - turbo');
}
});
// In my-generator/generators/electric/index.js
module.exports = require('yeoman-generator').Base.extend({
'prompting' : function () {
console.log('prompting - zap');
},
'writing' : function () {
console.log('writing - zap');
}
});
// In my-generator/generators/app/index.js
module.exports = require('yeoman-generator').Base.extend({
'initializing' : function () {
this.composeWith('my-generator:turbo');
this.composeWith('my-generator:electric');
}
});