Yeomanのgeneratorを自作する

[Yeoman入門(第二部、generatorを作る)](http://yosuke-furukawa.hatenablog.com/entry/2013/07/14/131903)を参考にyoeman generatorを作成していたが、yeomanが更新されて現在は少し作り方が変わっているようなので新しい作成方法をメモ。

# generator-generatorのインストール
generatorの雛形を生成する```generator-generator```をインストール。

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

helloworld generatorを作成するためのディレクトリを用意して移動。

```
mkdir generator-helloworld
cd generator-helloworld
```

# generator-helloworldの雛形作成
generatorの雛形を生成

```
yo generator
```
コマンドを実行すると、gitのユーザ名を聞かれた後、generatorの名前を聞かれるのでhelloworldと入力してenter。
(そのままenterを押すとgeneratorの名前がgenerator-generator-helloworldになります。)
```
[?] What's the base name of your generator? (generator helloworld) helloworld
```

コマンドの実行が終了すると、以下のようなフォルダ構成になっているかと思います。

```generator-helloworld
.
├── README.md
├── node_modules
├── generators
│   └── app
│       ├── index.js
│       └── templates
│           ├── _bower.json
│           ├── _package.json
│           ├── editorconfig
│           └── jshintrc
├── package.json
└── test
    └── test-app.js
```

# generator-helloworldの動作確認
ここで一旦generator-helloworldの動作確認をします。
適当なディレクトリを作って、移動。

```
mkdir test-helloworld
cd test-helloworld
```

さらに、node_modulesディレクトリを作って、node_modulesディレクトリ以下にシンボリックリンクでgenerator-helloworldを配置します。

```
mkdir node_modules
cd node_modules
ln -s <generator-helloworldへのパス> .
```

```test-helloworld
.
├── README.md
├── node_modules
│   └── generator-helloworld
```

Yeomanは、node_modulesに配置されている'generator-'で始まるパッケージをgeneratorと認識するので、これによってgenerator-helloworldがgeneratorと認識されるようになります。

次に、``` yo helloworld ```でgenerator-helloworldを実行。

この際、以下のエラーがでる場合は、generators/app/index.jsの``` require('../package.json') ```を``` require('../../package.json') ```に直して下さい。
([YeomanでError: Cannot find module '../package.json'が表示される問題の対策](http://qiita.com/ysm001/items/901a4ba8a3179807242c))

```
events.js:72
        throw er; // Unhandled 'error' event
              ^
Error: Cannot find module '../package.json'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:280:25)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at module.exports.yeoman.generators.Base.extend.initializing (/Users/ryo/Programming/web/generator-helloworld/generators/app/index.js:8:16)
    at /Users/ryo/Programming/web/generator-helloworld/node_modules/yeoman-generator/lib/base.js:421:16
    at processImmediate [as _immediateCallback] (timers.js:336:15)
```

コマンドの実行が終了すると、以下のような構成のファイルが生成されているかと思います。

```
.
├── .editorconfig
├── .jshintrc
├── bower.json
├── node_modules
│   └── generator-helloworld
└── package.json
```

# generator-helloworldの編集
generatorの中核となる処理は、``` generators/app/index.js ```に書かれているので、このファイルを開いてみます。

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

module.exports = yeoman.generators.Base.extend({
  initializing: function () {
    this.pkg = require('../../package.json');
  },

  prompting: function () {
    var done = this.async();

    // Have Yeoman greet the user.
    this.log(yosay(
      'Welcome to the super-excellent ' + chalk.red('Helloworld') + ' generator!'
    ));

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

    this.prompt(prompts, function (props) {
      this.props = props;
      // To access props later use this.props.someOption;

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

  writing: {
    app: function () {
      this.fs.copy(
        this.templatePath('_package.json'),
        this.destinationPath('package.json')
      );
      this.fs.copy(
        this.templatePath('_bower.json'),
        this.destinationPath('bower.json')
      );
    },

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

  install: function () {
    this.installDependencies();
  }
});
```

ここに、ユーザ名を尋ねるpromptを表示する処理を追加します。

```generators/app/index.js
…
    var prompts = [{
      type: 'confirm',
      name: 'someOption',
      message: 'Would you like to enable this option?',
      default: true
    },
    {
      name: 'yourname',
      message: 'What is your name?',
      default: "someuser"
    }];
…
```

次に、index.htmlをgenerators/app/templatesに配置します。

```generators/app/templates/index.html
<html>
  <body>
    <h1>Hello, <%= yourname %></h1>
  </body>
</html>
```

上記の``` <%= yourname %> ```の部分にpromptで入力されたユーザ名を埋め込む処理をgenerators/app/index.jsに記述します。

```generators/app/index.js
  writing: {
    app: function () {
      this.fs.copy(
        this.templatePath('_package.json'),
        this.destinationPath('package.json')
      );
      this.fs.copy(
        this.templatePath('_bower.json'),
        this.destinationPath('bower.json')
      );

      // generators/app/templates/index.htmlを対象のディレクトリにコピーして、
      // yournameにユーザ名を埋め込む
      this.fs.copyTpl(
        this.templatePath('index.html'),
        this.destinationPath('index.html'),
        {yourname: this.props.yourname}
      );
    },
```

promptで入力された値は、``` this.props ```経由でアクセスできます。

# generator-helloworldの再実行
test-helloworldを一旦削除し、初期と同じ以下の構成に戻します。

```test-helloworld
.
├── README.md
├── node_modules
│   └── generator-helloworld
```

```
rm -rf test-helloworld
mkdir test-helloworld
cd test-helloworld
mkdir node_modules
cd node_modules
ln -s <generator-helloworldのパス> .
```

最後に、test-helloworld内に移動して、``` yo helloworld ```を実行し、promptで入力した名前が表示されていれば成功です。

```
cd test-helloworld
yo helloworld
open index.html
```

# 参考文献
* [Yeoman入門(第二部、generatorを作る)](http://yosuke-furukawa.hatenablog.com/entry/2013/07/14/131903)