Riot.js + webpack + ES6(Babel, buble)での開発環境構築例 ※追記webpack2

[Riot.js Advent Calendar 2016](http://qiita.com/advent-calendar/2016/riotjs)の23日目です！


[Riot.js](http://riotjs.com/)（以下riot）は非常にシンプルかつ軽量で敷居も低く、とても書きやすいコンポーネント指向のUIライブラリです。（ここまでテンプレート）

今更ですが、ちゃんと`ES6`で書こうと思いまして、riot+webpack+ES6の開発環境を作りました。


# 準備
## ディレクトリ構成
今回のサンプルのディレクトリ構成は以下です。

```
./
├ .gitignore
├ node_modules
├ package.json
├ webpack.config.js
├ index.html
├ app
│  └ app.js
│
├ tag
│  └ app.tag
│
└ build
   └ bundle.js
```

以下は`Babel`を使う場合と`buble`を使う場合で微妙に違いがあるため、別々に記述します。

## ▼`Babel`を使う場合
### <i class="fa fa-download" aria-hidden="true"></i> モジュールリスト
インストールするモジュールは以下です。

- babel-core
- babel-preset-es2015-riot
- riot
- riot-route（これは任意）
- webpack
- riot-tag-loader（tag-loader, riotjs-loaderでも可）
- babel-loader

<i class="fa fa-arrow-down" aria-hidden="true"></i>インストール用コマンド

```bash
$ npm install -D babel-core babel-preset-es2015-riot riot riot-route webpack riotjs-loader babel-loader
```

### <i class="fa fa-edit" aria-hidden="true"></i> webpackの設定

まずはwebpack `v1`の設定例。

```js:webpack.config.js（v1）
const webpack = require('webpack')

module.exports = [
   {
      // 対象のjsファイル
      entry: './app/app.js',
      output: {
         path: __dirname + '/build/',
         filename: 'bundle.js'
      },
      module: {
         preLoaders: [
            {
               test: /\.tag$/,
               exclude: /node_modules/,
               loader: 'riot-tag-loader'
            }
         ],
         loaders: [
            {
               test: /\.js|\.tag$/,
               exclude: /node_modules/,
               loader : 'babel-loader',
               query  : {
                  presets: ['es2015-riot']
               }
            }
         ]
      },
      resolve: {
         extensions: ['', '.js', '.tag']
      },
      plugins: [
         new webpack.optimize.OccurenceOrderPlugin(),
         new webpack.ProvidePlugin({ riot: 'riot' })
      ]
   }
]
```

次に`v2 (2.3.3)`の設定例。

```js:webpack.config.js（v2)
const webpack = require('webpack')

module.exports = [
  {
      entry: './app/app.js',
      output: {
         path: __dirname + '/build/',
         filename: 'bundle.js'
      },
      module: {
         rules: [
            {
               test: /\.tag$/,
               enforce: 'pre',
               exclude: /node_modules/,
               use: [
                  {
                     loader: 'riot-tag-loader',
                     options: {
                        template: 'pug',   // テンプレートを指定（任意）
                        debug: true
                     }
                  }
               ]
            },
            {
               test: /\.js|\.tag$/,
               enforce: 'post',
               exclude: /node_modules/,
               use: [
                  {
                     loader: 'babel-loader',
                     options: {
                        presets: `es2015-riot`
                     }
                  }
               ]
            }
         ]
      },
      resolve: {
         extensions: ['.js', '.tag']
      },
      plugins: [
         new webpack.ProvidePlugin({ riot: 'riot' })
      ]
   }
]
```


## ▼`buble`を使う場合
### <i class="fa fa-download" aria-hidden="true"></i> モジュールリスト
インストールするモジュールは以下です。

- buble
- riot
- riot-route（これは任意）
- webpack
- riot-tag-loader（riotjs-loader, tag-loaderでも可）
- buble-loader

```bash
$ npm install -D buble riot riot-route webpack riotjs-loader buble-loader
```

### <i class="fa fa-edit" aria-hidden="true"></i> webpackの設定
上記のwebpack.config.jsを以下のように変更します。
まずは`v1`の設定例。


```diff:webpack.config.js（v1）
const webpack = require('webpack')

module.exports = [
   {
      // 対象のjsファイル
      entry: './app/app.js',
      output: {
         path: __dirname + '/build/',
         filename: 'bundle.js'
      },
      module: {
         preLoaders: [
            {
               test: /\.tag$/,
               exclude: /node_modules/,
               loader: 'riot-tag-loader'
            }
         ],
         loaders: [
            {
               test: /\.js|\.tag$/,
               exclude: /node_modules/,
-              loader : 'babel-loader',
-              query  : {
-                 presets: ['es2015-riot']
-              }
+              loader : 'buble-loader'
            }
         ]
      },
      resolve: {
         extensions: ['', '.js', '.tag']
      },
      plugins: [
         new webpack.optimize.OccurenceOrderPlugin(),
         new webpack.ProvidePlugin({ riot: 'riot' })
      ]
   }
]
```

次に`v2 (2.3.3)`の設定例。

```diff:webpack.config.js（v2)
const webpack = require('webpack')

module.exports = [
  {
      entry: './app/app.js',
      output: {
         path: __dirname + '/build/',
         filename: 'bundle.js'
      },
      module: {
         rules: [
            {
               test: /\.tag$/,
               enforce: 'pre',
               exclude: /node_modules/,
               use: [
                  {
                     loader: 'riot-tag-loader',
                     options: {
                        template: 'pug',   // テンプレートを指定（任意）
                        debug: true
                     }
                  }
               ]
            },
            {
               test: /\.js|\.tag$/,
               enforce: 'post',
               exclude: /node_modules/,
-              use: [
-                 {
-                    loader: 'babel-loader',
-                    options: {
-                       presets: `es2015-riot`
-                    }
-                 }
-              ]
+              use: ['buble-loader']
            }
         ]
      },
      resolve: {
         extensions: ['.js', '.tag']
      },
      plugins: [
         new webpack.ProvidePlugin({ riot: 'riot' })
      ]
   }
]
```

ぶっちゃけますと、Babelでは`babel-preset-es2015-riot`モジュールをインストールし、presetを指定しないと正しくトランスパイルされないのに、bubleでは上記の設定で動いているのが何故なのかちょっと不思議でならんです。（※私はbubleの方が気に入ってます！）

***

以降は`Babel`、`buble`共通です。

## <i class="fa fa-code" aria-hidden="true"></i> サンプルコード

```html:index.html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>sample</title>
  </head>
  <body>
    <app></app>
    <script src="build/bundle.js"></script> 
  </body>
</html>
```

```js:app/app.js
require('../app.tag');
 
riot.mount('app', { title: 'hogehoge' });
```

```html:tag/app.tag
<app>
   <h2>{ opts.title }</h2>
   <button onclick="{ show_count }">click</button>

   <script>
      self = this
      let count = 0

      show_count() {
         count += 1
         alert(`count: ${count}`)
      }
   </script>
</app>
```

## <i class="fa fa-edit" aria-hidden="true"></i> コマンドの設定および、コンパイル実行

コマンドラインからコンパイルを実行できるように、package.jsonにコマンドを追加。

```json:package.json
"scripts": {
   "build": "webpack --watch --progress"
}
```

以下のコマンドからコンパイル実行！

```bash
$ npm run build
```

ブラウザで`index.html`を開き、`click`ボタンをクリックして以下のようにアラートが表示され、クリックの度にカウントがインクリメントされれば成功です！
***

![スクリーンショット 2017-01-18 17.06.39.png](https://qiita-image-store.s3.amazonaws.com/0/48034/789918e8-b7a8-29a0-e002-24716224f719.png)

***

<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> 注意点
`riotjs-loader`ですが__バージョン`3.0.0`だとコンパイルエラー__になりますので、`4.0.0`にアップグレードして下さい。


皆さんそれぞれにベストな環境があると思いますので、ご参考までに〜。


# <i class="fa fa-external-link" aria-hidden="true"></i> 参考

- [npmとwebpackで作るRiot.js、学習のためのいい感じのフロントエンド開発環境 2016年6月の場合。](http://shigekitakeguchi.github.io/2016/06/24/1.html)
- [buble-loader](https://www.npmjs.com/package/buble-loader)
- [riotjs-loader](https://www.npmjs.com/package/riotjs-loader)
- [tag-loader](https://www.npmjs.com/package/tag-loader)