Writing Custom Yeoman Generator

Writing a custom Yeoman generator, you need to install generator-generator first.

(generator-generator) Scaffolds out a new basic Yeoman generator with some sensible defaults.

So it creates some basic structure for writing your own custom generators. It is a generator for writing Node generator application.

First Install generator-generator:

$ sudo npm install -g generator-generator

Then generate some basic files for writing your own generator by issuing:

$ yo generator

You can use generator-generator to write both a custom generator and a custom sub-generator. What is sub-generator? For example, Angular directive or service is a sub-generator.

Use naming convention generator-[name].

Create a directory for your new generator:

$ mkdir -p ~/generators/generator-hello && cd $_

Make sure entering the newly created directory (cd $_), otherwise all files generated will be placed under the current directory. Then, scaffold your generator:

$ yo generator

As versions [email protected] and [email protected], the generator creates the following directories and files:

├── app
│ ├── index.js
│ └── templates
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jshintrc
├── node_modules
│ ├── .bin
│ ├── chalk
│ ├── mocha
│ └── yeoman-generator
├── package.json
├── README.md
├── test
│ ├── test-creation.js
│ └── test-load.js
└── .travis.yml
8 directories, 10 files

You might want to make your initial commit:

$ git init && git add '*' && git commit -m 'Initial commit'

While in development, make it accessible globally:

$ sudo npm link

This is similar to:

$ sudo npm install -g [generator-name]

They both will be discoverable from /usr/local/lib/node_modules.

There isn’t much going on there, we need to customize our own generator. The main file is ./app/index.js.

var HelloGenerator = yeoman.generators.Base.extend({
module.exports = HelloGenerator;

The HelloGenerator extends Base generator. There are four properties (functions) that it has extended: init, askFor, app, and projectfiles.

All these functions will be invoked by Yeoman one by one in order (the order is important), meaning that init will be called first to perform initialization, and then askFor will be called to get user prompts, then app and projectfiles will be called afterward to build the application.

However, it is not a must to have four properties, it just brings more structure and modularity. In fact, you can merge all four functions into a single one.

The method name does not matter. Just don’t name it prompt because you will override the existing property inherited from yeoman.generators.Base.

If we need to declare a “private” property, just appending it with an underscore as a hidden property, e.g. _dontAskFor: function(){}, or if you just use it in the same file, you can simply define a global function myFunc().

Personal, I don’t think it is necessary to break it into many functions, three are enough, initialization, prompting user for input, and generating files.

When choosing property names, the Base object contains many properties which is not a good idea to be overridden, such as ["append", "help", "log", "prompt", "remote"]. This design has limitation on choosing a right property name. For example, I was thinking about using prompt instead of askFor, but prompt is an inherited property. Therefore, it is a good idea to keep fewer properties in HelloGenerator. Three are enough. All these properties can be accessible from the context this. From this object you can access everything from Base object to newly defined _dontAskFor hidden property.

Time to test the custom generator:

$ cd /tmp && mkdir hello && yo $_