webpack + babel-loaderでES6デビューしつつ、gulpもES6で書く


こんにちは！[ギャザリー](http://gathery.recruit-lifestyle.co.jp/)で開発を担当している手塚＠inureoです。
今回はタイトル通りwebpack + babel-loaderでES6デビューしつつ、gulpもES6で書く、というところをやってみたいと思います。

# gulpfile.jsをES6で書こうと思った理由
「ES6やりたいなー、しかも業務に直結するところで。」と考えていた時に、[こちらの記事](http://tacamy.hatenablog.com/entry/2015/11/04/163123)のgulpfile.js
をES6で書く理由を拝見して「たしかにgulpfileなら実装に頭使わないし慣れるのにはピッタリだ！」と思ったからです。

## 早速ES6でgulpfileを書いていく
gulpfileをES6で書くには、gulp側でES6対応をするか、node.js側でES6対応をするかの2択になります。

### gulp側での対応
gulpの3.9.0からbabelを標準サポートするようになりました。ファイル名を`gulpfile.babel.js`に変更すればbabelを介してES6の構文を利用することができます。

### node.js側での対応
node.jsはv4.0.0からES6に一部対応をしました。なので、node.jsのバージョンが4.0.0以上であればES6の構文をそのまま利用することができます。
こちらは `gulpfile.js` のままで問題ありません。

参考：[gulpfile.jsをES6で書いてみよう - tacamy.blog](http://tacamy.hatenablog.com/entry/2015/11/04/163123)


### 今回の対応
自身の環境がv0.12.7だったので、gulp側で対応をすることにしました。

## ES6構文を使ってみる
早速次のように `import` を使ったり、 `arrow function` を使ったりしてみます。

```
'use strict';

import gulp from 'gulp';
import runSequence from 'run-sequence';

gulp.task('build', () => {
  runSequence(
  	...
  );
});
```

こちらで実行すると、 `import` でコケてしまいます。

```
[21:53:35] Requiring external module babel-core/register
/Users/tezukaryo/src/es6-gulp-boilerplate/gulpfile.babel.js:3
import gulp from 'gulp';
^^^^^^
```

`import` を使うには次のpresetが必要なようなので、インストールをして`.babelrc`に追記します。

```npm
$ npm install --save-dev babel-preset-es2015
```

```.babelrc
{
  "presets": ["es2015"]
}
```

追記した上で実行すると正しく動作するかと思います。

gulpfileをES6で書いていく上でハマったところはそれぐらいですね。あとは、いつもどおりタスクを記述していけば問題ありません。冒頭で言っていましたが、頭をあまり使わないですむのでES6に慣れるにはとてもよいなーと思いました。

参考：[gulpfileをES6で書く - Qiita](http://qiita.com/kosuke_nishaya/items/cf7f8a2dbb8e47d064fd)

## webpack + babel-loaderを使ってES6をビルドする
さて次は `webpack + babel-loader` でES6をビルドできるようにしてきます。まずは設定ファイルである`webpack.config.js`をルートディレクトリに作ります。

この`webpack.config.js`の設定が長くなるので、gulpfile側では次のような形でタスクを作り、設定ファイルを読み込ませておきます。

```gulpfile.babel.js
import webpackConfig from './webpack.config.js';

gulp.task('webpack', () => {
  gulp.src('source/assets/javascripts/**/*.js')
    .pipe($.webpack(webpackConfig))
    .pipe(gulp.dest('dist/assets/javascripts/'));
});
```

### webpackのオプションの説明
webpackはオプションが豊富です。今回はよく使うであろう（今回使った）ものを簡単に説明していきます。

#### entry
実際に依存関係を解決して出力するファイルになります。オブジェクト形式でキーの部分に名前を入力することで、ファイル名を変えずに指定した名前で出力することができます。

```例
  entry: {
    animal: ['./source/assets/javascripts/animal.js'],
    dog: './source/assets/javascripts/dog.js',
    vendor: ['jquery']
  },
```

animal.jsは`[]`でくくられていますが、dog.jsはくくられていませんね。こちらハマりポイントです。animal.jsはdog.jsでimportをされているので、`[]`でくくっています。くくらずにビルドした場合、次のようなエラーが出ます。

```
ERROR in ./source/assets/javascripts/dog.js
Module not found: Error: a dependency to an entry point is not allowed
 @ ./source/assets/javascripts/dog.js 1:578-597
```

`[]`でくくる理由については下記参考URLをご覧ください。

参考：[Error: a dependency to an entry point is not allowed · Issue #300 · webpack/webpack](https://github.com/webpack/webpack/issues/300)


#### output
出力先やファイル名を設定することができます。filenameに`[name]`とすることで、entryで指定したキー名でファイルが出力されます。

```
  output: {
    path: __dirname + '/dist/assets/javascripts/',
    filename: '[name].js'
  },
```

#### resolve
読み込む際に拡張子を省略できるようにするんだとか。

```
resolve: {
    extenstions: ['', '.js', '.json', '.html']
}
```

#### module
webpackの大きな特徴である `loader` の設定をするところです。webpackはjs以外のどんなファイルでも、loaderさえ使えば読み込むことができます。

今回は次のように`babel-loader`を設定しました。`.js`という拡張子のファイルかつnode_modulesディレクトリは対象外と設定していあす。

```例
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  },
```

#### plugins
こちらもloaderと同じぐらい特徴的な機能です。[ドキュメント](https://github.com/webpack/docs/wiki/list-of-plugins)が大量にあるので詳しくはそちらをご覧ください。今回使ったものだけ簡単に紹介しますね。

##### AggressiveMergingPlugin
ファイルを細かく分析してまとめられるところは可能な限りまとめて圧縮するオプション

##### DedupePlugin
被っているモジュールがあったら1つにまとめるオプション

##### CommonsChunkPlugin
npmとかでインストールしたライブラリをまとめたりするもの…？あまりよくわかっていないですが、皆さんそんな使い方をされていたので、僕もjQueryを固めています。（どなたか教えてください！）

```例
  plugins: [
    new webpack.optimize.AggressiveMergingPlugin(),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      filename: 'vendor.js',
      minChunks: Infinity
    })
  ]
```

参考：[webpackを使い倒す - Thujikun blog](http://thujikun.github.io/blog/2014/12/07/webpack/)

### ES6ファイルをビルドする
さて説明した内容そのままで設定ファイルを完成させれば、babel-loaderを使ってES6構文を使ったファイルが依存関係を解消した状態でビルドできるはずです。

[実際の設定ファイル](https://github.com/inureo/es6-gulp-boilerplate/blob/master/webpack.config.js)を参考に、自身の環境に調整して試してみてください。紹介したものはほんの一部なので、様々なオプションを触って使いこなしていきたいですね。

というところで、導入編はおしまいです。

# 各gulpタスクで使用する技術の選定
次に今回のgulpfileで使用した技術の選定理由などを説明したいと思います。

## webpack
modular には、browserifyではなく`webpack`を使うことにしました。理由としては、個人的にwebpackの `最適なアセットの生成を目的にしている` というところに感銘を受けたためです。

webpackは日本語の情報が少なかったりしますが、最近は日本語のリファレンスも増えてきてそこまで死にそうになることは少ないかなと思います（今回webpack化に挑戦する前に一度諦めてたりしてます）。
また、ビルドの速度がbrowserifyと比べて早いというのも選択理由の1つです。

```
一回（もしくはwatchの初回）のビルド時間
Browserify => 3〜4分
Webpack => 14〜16秒（！）
```

[JavaScript - BrowserifyからWebpackへの移行時の注意点 - Qiita](http://qiita.com/derui@github/items/5233b4ef8fbde1e80d33)

## ESLint
業務でもお世話になっているAirbnbがESLintのconfigファイルを配布していたので、こちらをそのまま使いました。

[javascript/packages/eslint-config-airbnb at master · airbnb/javascript](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb)

## npm
Bowerは個人的に使いたくなかったので（package.jsonとbower.jsonの2重管理がいやだった）今回はnpmでjQueryをインストールしています。Bowerにしかないものもあったりすると思うのですが、それは困ったときにでもという感じです！webpackであれば問題なくBowerで管理されているものを扱えるので。。

## node-sass
Compassを捨てて、autoprefixerを使うようにしました。Compassはとても便利なのですが十分に使いこなせなかったりで、オーバースペック気味になることが多かったです。
であれば速度重視でnode-sassに移行した方が結果的に生産性が上がるだろうなーと思い、今回のタイミングで完全に移行してみました。
個人的なプロジェクトでStylusも触ってみたのですが、Sassをずっと使ってたいたせいか、ゆるゆるな感じ（変数がなくても怒られないとか）が落ち着かなくてSassに帰ってきました。

# 最終的にできたもの
さてさていろんな理由を経て最終的にできたものは、`node.js >=0.12` で動作するスターターキット的なものです。ちょっと見通しが悪いので今後アップデートしていく予定です！

[inureo/es6-gulp-boilerplate](https://github.com/inureo/es6-gulp-boilerplate)

## できること
- JadeやらSassやらES6のコンパイル
- AirbnbのスタイルガイドにそったESlint
- 画像サイズの最適化とスプライト画像生成
- browserSyncを使ったローカルサーバの起動、ブラウザの自動更新

## 今後のアップデート予定

- ~~タスクをファイルごとに分割して見通しをよくする~~
	- 2016/05/28 分割対応した
- ~~Enviromentでminifyをするかどうかわける~~
	- 2016/05/28 webpackタスクに適応した

# 感想
ES6に慣れるにはgulpfileで試すのは結構よいかもしれませんね。個人的にはwebpackのオプションの多さに驚かされたので、早速業務で色々試してみようと思います。

# お世話になった記事
参考にさせていただきました！ありがとうございます！

- [petehunt/webpack-howto](https://github.com/petehunt/webpack-howto)
- [JavaScript - BrowserifyからWebpackへの移行時の注意点 - Qiita](http://qiita.com/derui@github/items/5233b4ef8fbde1e80d33)
- [webpack で始めるイマドキのフロントエンド開発 - Qiita](http://qiita.com/yosisa/items/61cfd3ede598e194813b)
- [Gulpとか使ったサイト作るときに便利なやつ作った - Memo/](http://memo.brdr.jp/post/131625820226/gulp%E3%81%A8%E3%81%8B%E4%BD%BF%E3%81%A3%E3%81%9F%E3%82%B5%E3%82%A4%E3%83%88%E4%BD%9C%E3%82%8B%E3%81%A8%E3%81%8D%E3%81%AB%E4%BE%BF%E5%88%A9%E3%81%AA%E3%82%84%E3%81%A4%E4%BD%9C%E3%81%A3%E3%81%9F)
- [フロントエンドでもES6構文使ってみる【webpack+babel-loader（旧6to5-loader）】 - yutaponのブログ](http://yutapon.hatenablog.com/entry/2015/02/09/090000)
- [JavaScript - Gulp + Babel + Webpack を試してみた - Qiita](http://qiita.com/rei-m/items/2beea6266e82d41cdd96)
- [JavaScript - npm と gulp でフロントエンドライブラリを管理してリポジトリから消してみる - Qiita](http://qiita.com/youhei/items/1621f87f096d3d35da7d)
- [gulpfileをES6で書く - Qiita](http://qiita.com/kosuke_nishaya/items/cf7f8a2dbb8e47d064fd)
- [gulpfile.jsをES6で書いてみよう - tacamy.blog](http://tacamy.hatenablog.com/entry/2015/11/04/163123)