Yeoman generatorでカスタマイズしたジェネレータを作る


[Yeoman](http://yeoman.io/)で、独自のジェネレータを作る方法。
ここでは [yeoman-generator](https://github.com/yeoman/generator-generator) を使った方法について。

# やり方

## 準備

yeoman と yeoman-generator をインストール

```
npm install -g yo generator-generator
```

(yeoman-generatorのバージョンは0.4.4)

## ひな形の作成

ここからは ジェネレータ名を `sample` とした場合

```bash
$ mkdir generator-sample
$ cd generator-sample
$ yo generator:app
```

色々聴かれるので適当に答えると以下のような

```bash
$ tree -L 2
.
├── README.md
├── app
│   ├── index.js
│   └── templates
│       ├── _bower.json
│       ├── _package.json
│       ├── editorconfig
│       └── jshintrc
├── node_modules
│   ├── chalk
│   ├── mocha
│   ├── yeoman-generator
│   └── yosay
├── package.json
└── test
    ├── test-creation.js
    └── test-load.js
```

`app/templates` 以下に、generatorで生成するファイルの元を置き、
`app/index.js` にgenerator起動時の処理を書いていくことになる。

## app/templates/

ここに元となるファイルを置いておく。
アプリのルート以下を同じ構成で置いておく。

## app/index.js

https://github.com/yeoman/yeoman/wiki/Generators

ここに`yeoman-generator`で実行したときの挙動を実装していくことになる。

```js:app/index.js
'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');
var yosay = require('yosay');
var chalk = require('chalk');

var SampleGenerator = yeoman.generators.Base.extend({
  init: function () {
    this.pkg = require('../package.json');

    this.on('end', function () {
      if (!this.options['skip-install']) {
        this.installDependencies();
      }
    });
  },
  askFor: function () {
    var done = this.async();

    // Have Yeoman greet the user.
    this.log(yosay('Welcome to the marvelous Sample generator!'));

    var prompts = [{
      type: 'confirm',
      name: 'someOption',
      message: 'Would you like to enable this option?',
      default: true
    }];

    this.prompt(prompts, function (props) {
      this.someOption = props.someOption;

      done();
    }.bind(this));
  },

  app: function () {
    this.mkdir('app');
    this.mkdir('app/templates');

    this.copy('_package.json', 'package.json');
    this.copy('_bower.json', 'bower.json');
  },

  projectfiles: function () {
    this.copy('editorconfig', '.editorconfig');
    this.copy('jshintrc', '.jshintrc');
  }
});

module.exports = SampleGenerator;     
```

ジェネレータ実行した時に`SampleGenerator`のメソッドが全部実行されていく。
上の場合なら`init`と`askFor`と`app`と`projectfiles`が順に実行されていく。

ただし`constructor`と`initialize`と頭に`_`がついているメソッド名だった場合、スキップされる。

各メソッドの中で以下のようなプロパティやメソッドを使って
ファイルをコピーしたり、変換したりしていく。

以下、よく使いそうなもの一部抜粋。

## プロパティ

* `appname` — String アプリケーション名 (カレントのディレクトリ名)
* `log` — Function ログの出力

## アクション

`app/templates` 以下にあるファイルをうつしたり、新しくディレクトリ作ったりする。

* `this.copy(src, dest)` — src から dest への ファイルのコピー。dest 省略の場合は、同じ名前になる。
* `this.directory(src, dest)` — src ディレクトリ以下のファイルをdestディレクトリにコピー。dest省略の場合、srcと同じディレクトリにコピー
* `this.mkdir(dest)` — ディレクトリの作成
* `this.write(filepath, content, [writeFile])` — filepathにcontentを書き込む
* `this.template(src, dest, [data], [options])` — srcファイルのテンプレート対応しつつ、destにファイルをコピーする。data が省略されている場合は `this` を参照。

例えば、

```json:app/templates/_package.json
{
  "name": "<%= appname %>"
}
```

となっている場合、`this.template(‘_package.json’, ‘package.json’)`を実行すると、
`<%= appname %>`が変数展開されて、`package.json`に書き出される。

すべてはAPIドキュメント http://yeoman.github.io/generator/ に記載されている。

# 対話形式の取り入れ方

![fee70957c85b21d7285d706d69e892cc.png](https://qiita-image-store.s3.amazonaws.com/0/3163/fccbbbf4-792e-da47-21af-6bc8ee64a344.png)


上のように対話形式で入力したい場合は`this.prompt(questions, callback)`を利用する。
これは中で[inquirer](https://github.com/SBoudrias/Inquirer.js)というライブラリが使われているので、
`questions` の形式は (https://github.com/SBoudrias/Inquirer.js#question)  と同じ。

```js:app/index.js
    var prompts = [{
      type: 'confirm’, // Yes or No
      name: 'coffee',
      message: 'Use CoffeeScript',
      default: false
    },
    {
      type: ‘input’, // テキスト入力
      name: ’name',
      message: 'What is your app name?',
      default: 'sample'
    } 
    ];
    this.prompt(prompts, function (props) {
      this.coffee = props.coffee; // 結果がpropsに入っている
      this.name = props.name; 
      done();
    }.bind(this));
```

ここで値を取得して、テンプレート以下で条件分岐や
値を入れることで、任意のファイルをジェネレートすることが出来る。

# yosay

[yosay](https://github.com/yeoman/yosay)を使うとyeomanが喋ってくれる。

```
this.log(yosay('Yo!'));
```

![2075b316f2dd1408d7790fb4b8474bb3.png](https://qiita-image-store.s3.amazonaws.com/0/3163/d5ac3230-594e-bb36-2c17-17e34c5462cf.png)

# 参考

* [Yeoman入門(第二部、generatorを作る) - from scratch](http://yosuke-furukawa.hatenablog.com/entry/2013/07/14/131903) — 動作確認方法やディレクトリ構造が詳しく書いてあります。
* [Node.js - [日本語訳]Yeoman App Generatorsの書き方 - Qiita](http://qiita.com/sys1yagi/items/da002b32b6663faaa705) — より詳しい機能についてはこっち。