にわとりプログラマーの備忘録

覚えたことをすぐ忘れてしまう、自分のための備忘録ブログです。

babel+webpackでビルド可能なchrome拡張の雛形をYoemanで作成

generator-generatorのインストール

$ npm i -g generator-generator

雛形の雛形を生成

$ mkdir generator-chrome-extension
$ cd generator-chrome-extension
$ yo generator

yoemanからgeneratorとして呼び出させるようにするために、以下のコマンドをgenerator-chrome-extension内で実行します。

$ npm link

generator-chrome-extensionが必要になくなった場合は、以下のコマンドでnpm linkを取り消すことができます。

$ npm unlink -g generator-chrome-extension

雛形をカスタマイズ

最初にgenerators/app/templates以下に雛形として生成するファイルやディレクトリを配置します。

.
├── index.js
└── templates
    ├── _bower.json
    ├── _manifest.json
    ├── _package.json
    ├── dist
    ├── js
    │   ├── background.js
    │   └── main.js
    └── webpack.config.js

続いて雛形生成の処理を記述していきます。
yoemanが雛形を生成する処理はgenerators/app/index.jsに記述してあるため、こちらを編集することで雛形生成の処理をカスタマイズすることが出来ます。

'use strict';
var yeoman = require('yeoman-generator');
var chalk = require('chalk');
var yosay = require('yosay');
var mkdirp = require('mkdirp')

module.exports = yeoman.Base.extend({
  prompting: function () {
    // Have Yeoman greet the user.
    this.log(yosay(
      'Welcome to the fine ' + chalk.red('generator-chrome-extension') + ' generator!'
    ));

    // 設定ファイルに記述する値を対話的に入力してもらう処理
    var prompts = [
      {
        name: 'name',
        message: 'What is your app name',
        default: ''
      },
      {
        name: 'description',
        message: 'What is your app description',
        default: ''
      },
      {
        name: 'author',
        message: 'What is your name',
        default: ''
      }
    ];

    return this.prompt(prompts).then(function (props) {
      // To access props later use this.props.someAnswer;
      this.props = props;
    }.bind(this));
  },

  // 雛形のファイルやディレクトリを書き込み
  writing: function () {
    // ディレクトリをコピー
    this.directory(
      this.templatePath('js'),
      this.destinationPath('js')
    )

    // 空のディレクトリを生成
    mkdirp(this.destinationPath('dist'))

    this.fs.copyTpl(
      this.templatePath('_bower.json'),
      this.destinationPath('bower.json'),
      {
        name: this.props.name,
        description: this.props.description
      }
    )

    this.fs.copyTpl(
      this.templatePath('_manifest.json'),
      this.destinationPath('manifest.json'),
      {
        name: this.props.name,
        description: this.props.description
      }
    )

    this.fs.copyTpl(
      this.templatePath('_package.json'),
      this.destinationPath('package.json'),
      {
        name: this.props.name,
        description: this.props.description,
        author: this.props.author
      }
    )

    this.fs.copy(
      this.templatePath('.babelrc'),
      this.destinationPath('.babelrc')
    )

    this.fs.copy(
      this.templatePath('webpack.config.js'),
      this.destinationPath('webpack.config.js')
    )

  },

  // bower & npm のインストール実行
  install: function () {
    this.installDependencies();
  }
});

こちらの記述はプロンプトでユーザーにプロジェクト名等を入力してもらい、prompt変数に格納していきます。

  prompting: function () {
    // Have Yeoman greet the user.
    this.log(yosay(
      'Welcome to the fine ' + chalk.red('generator-chrome-extension') + ' generator!'
    ));

    // 設定ファイルに記述する値を対話的に入力してもらう処理
    var prompts = [
      {
        name: 'name',
        message: 'What is your app name',
        default: ''
      },
      {
        name: 'description',
        message: 'What is your app description',
        default: ''
      },
      {
        name: 'author',
        message: 'What is your name',
        default: ''
      }
    ];

    return this.prompt(prompts).then(function (props) {
      // To access props later use this.props.someAnswer;
      this.props = props;
    }.bind(this));
  },

templates/_package.jsonの中身を見てみます。
<%= %>で囲まれた部分が入力さた値が埋め込まれる箇所を示しています。

{
  "name": "<%= name %>",
  "version": "1.0.0",
  "description": "<%= description %>",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --watch"
  },
  "author": "<%= author %>",
  "license": "ISC",
  "devDependencies": {
    "babel-cli": "^6.11.4",
    "babel-core": "^6.11.4",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.9.0",
    "webpack": "^1.13.1"
  },
  "dependencies": {
    "lodash": "^4.13.1"
  }
}

この雛形ファイルに値を埋め込むにはthis.fs.copyTpl()を利用します。
上記のindex.jsでは次のように処理を書いています。
第1引数にテンプレートファイルのパスを、第2引数に出力先のファイルパスを、第3引数として設定ファイルに埋め込む変数の値をオブジェクトとして渡してあげます。

this.fs.copyTpl(
  this.templatePath('_package.json'),
  this.destinationPath('package.json'),
  {
    name: this.props.name,
    description: this.props.description,
    author: this.props.author
  }
)

generator-chrome-extensionの動作確認

ディレクトリに移動して、generator-chrome-extensionの動作確認をしてみます。

$ mkdir test-chrome-extension
$ cd test-chrome-extension
$ yo chrome-extension

プロンプトが立ち上がり必要な値を入力し終わると、ファイル・ディレクトリの生成が行われた後に、bower install && npm installが実行され雛形が生成されます。

$ tree
.
├── bower.json
├── bower_components (省略)
├── dist
├── js
│   ├── background.js
│   └── main.js
├── manifest.json
├── node_modules (省略)
├── package.json
└── webpack.config.js

参考